mirror of
https://github.com/whekin/household-bot.git
synced 2026-03-31 17:54:02 +00:00
fix: resolve remaining format and test failures
Co-authored-by: claw <stanislavkalishin+claw@gmail.com>
This commit is contained in:
@@ -20,11 +20,7 @@ function parsePositiveInteger(name: string, fallback: number): number {
|
|||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runOnce(input: {
|
async function runOnce(input: { baseUrl: string; schedulerSecret: string; dueScanLimit: number }) {
|
||||||
baseUrl: string
|
|
||||||
schedulerSecret: string
|
|
||||||
dueScanLimit: number
|
|
||||||
}) {
|
|
||||||
const response = await fetch(`${input.baseUrl}/jobs/dispatch-due?limit=${input.dueScanLimit}`, {
|
const response = await fetch(`${input.baseUrl}/jobs/dispatch-due?limit=${input.dueScanLimit}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
Make the VPS deployment path first-class without removing the existing Cloud Run / AWS paths.
|
Make the VPS deployment path first-class without removing the existing Cloud Run / AWS paths.
|
||||||
|
|
||||||
Primary target:
|
Primary target:
|
||||||
|
|
||||||
- bot API on Docker Compose
|
- bot API on Docker Compose
|
||||||
- mini app on Docker Compose
|
- mini app on Docker Compose
|
||||||
- reverse proxy with HTTPS on the VPS
|
- reverse proxy with HTTPS on the VPS
|
||||||
@@ -12,18 +13,21 @@ Primary target:
|
|||||||
- GitHub Actions CD that deploys to the VPS
|
- GitHub Actions CD that deploys to the VPS
|
||||||
|
|
||||||
Compatibility requirement:
|
Compatibility requirement:
|
||||||
|
|
||||||
- keep existing cloud deployment code and workflows available
|
- keep existing cloud deployment code and workflows available
|
||||||
- avoid deleting GCP/AWS-specific adapters unless they are clearly dead and isolated
|
- avoid deleting GCP/AWS-specific adapters unless they are clearly dead and isolated
|
||||||
|
|
||||||
## Deployment Shape
|
## Deployment Shape
|
||||||
|
|
||||||
Recommended production services:
|
Recommended production services:
|
||||||
|
|
||||||
- `bot` - Bun runtime for Telegram webhook/API
|
- `bot` - Bun runtime for Telegram webhook/API
|
||||||
- `miniapp` - static assets served behind reverse proxy
|
- `miniapp` - static assets served behind reverse proxy
|
||||||
- `scheduler` - separate service that periodically triggers due scheduled dispatch processing
|
- `scheduler` - separate service that periodically triggers due scheduled dispatch processing
|
||||||
- `caddy` - TLS + reverse proxy for `bot.<domain>` and `app.<domain>`
|
- `caddy` - TLS + reverse proxy for `bot.<domain>` and `app.<domain>`
|
||||||
|
|
||||||
Database:
|
Database:
|
||||||
|
|
||||||
- keep Supabase / managed Postgres external
|
- keep Supabase / managed Postgres external
|
||||||
- do not move Postgres onto the VPS in this phase
|
- do not move Postgres onto the VPS in this phase
|
||||||
|
|
||||||
@@ -32,6 +36,7 @@ Database:
|
|||||||
Current app logic already stores scheduled dispatches in Postgres and uses provider adapters for one-shot execution.
|
Current app logic already stores scheduled dispatches in Postgres and uses provider adapters for one-shot execution.
|
||||||
|
|
||||||
For VPS:
|
For VPS:
|
||||||
|
|
||||||
1. Add a self-hosted scheduled dispatch provider.
|
1. Add a self-hosted scheduled dispatch provider.
|
||||||
2. Keep dispatch records in the database as before.
|
2. Keep dispatch records in the database as before.
|
||||||
3. Add a due-dispatch scan endpoint/handler in the bot runtime.
|
3. Add a due-dispatch scan endpoint/handler in the bot runtime.
|
||||||
@@ -43,6 +48,7 @@ This keeps reminder behavior deterministic while removing dependency on cloud sc
|
|||||||
## Image / Runtime Plan
|
## Image / Runtime Plan
|
||||||
|
|
||||||
### Bot image
|
### Bot image
|
||||||
|
|
||||||
- keep multi-stage build
|
- keep multi-stage build
|
||||||
- build runtime entrypoints for:
|
- build runtime entrypoints for:
|
||||||
- bot server
|
- bot server
|
||||||
@@ -51,14 +57,17 @@ This keeps reminder behavior deterministic while removing dependency on cloud sc
|
|||||||
- keep runtime image lean
|
- keep runtime image lean
|
||||||
|
|
||||||
### Mini app image
|
### Mini app image
|
||||||
|
|
||||||
- keep static build + nginx/alpine runtime
|
- keep static build + nginx/alpine runtime
|
||||||
|
|
||||||
### Reverse proxy image
|
### Reverse proxy image
|
||||||
|
|
||||||
- use an off-the-shelf slim image (Caddy)
|
- use an off-the-shelf slim image (Caddy)
|
||||||
|
|
||||||
## CD Plan
|
## CD Plan
|
||||||
|
|
||||||
Add a separate GitHub Actions workflow for VPS deploy:
|
Add a separate GitHub Actions workflow for VPS deploy:
|
||||||
|
|
||||||
1. run on successful `main` CI and manual dispatch
|
1. run on successful `main` CI and manual dispatch
|
||||||
2. build/push bot and miniapp images to GHCR
|
2. build/push bot and miniapp images to GHCR
|
||||||
3. SSH into VPS
|
3. SSH into VPS
|
||||||
@@ -73,35 +82,42 @@ Keep existing GCP and AWS workflows untouched.
|
|||||||
## Secrets / Env Plan
|
## Secrets / Env Plan
|
||||||
|
|
||||||
Phase 1:
|
Phase 1:
|
||||||
|
|
||||||
- keep runtime env files on the VPS outside the repo
|
- keep runtime env files on the VPS outside the repo
|
||||||
- Compose loads env files from a deploy directory
|
- Compose loads env files from a deploy directory
|
||||||
|
|
||||||
Optional later upgrade:
|
Optional later upgrade:
|
||||||
|
|
||||||
- add 1Password-backed rendering or injection without changing app runtime contracts
|
- add 1Password-backed rendering or injection without changing app runtime contracts
|
||||||
- keep the runtime contract env-file-based so 1Password remains an overlay, not a hard dependency
|
- keep the runtime contract env-file-based so 1Password remains an overlay, not a hard dependency
|
||||||
|
|
||||||
Compatibility rule:
|
Compatibility rule:
|
||||||
|
|
||||||
- do not remove existing env vars for GCP/AWS paths
|
- do not remove existing env vars for GCP/AWS paths
|
||||||
- only add new VPS/self-hosted vars where needed
|
- only add new VPS/self-hosted vars where needed
|
||||||
|
|
||||||
## Expected Repo Changes
|
## Expected Repo Changes
|
||||||
|
|
||||||
### App/runtime
|
### App/runtime
|
||||||
|
|
||||||
- add self-hosted scheduler adapter
|
- add self-hosted scheduler adapter
|
||||||
- add due-dispatch scan support
|
- add due-dispatch scan support
|
||||||
- add scheduler runner entrypoint
|
- add scheduler runner entrypoint
|
||||||
- extend config parsing with VPS/self-hosted provider
|
- extend config parsing with VPS/self-hosted provider
|
||||||
|
|
||||||
### Docker / deploy
|
### Docker / deploy
|
||||||
|
|
||||||
- add production compose file
|
- add production compose file
|
||||||
- add Caddy config
|
- add Caddy config
|
||||||
- add VPS deploy helper scripts
|
- add VPS deploy helper scripts
|
||||||
|
|
||||||
### CI/CD
|
### CI/CD
|
||||||
|
|
||||||
- add VPS deploy workflow
|
- add VPS deploy workflow
|
||||||
- keep `cd.yml` and `cd-aws.yml`
|
- keep `cd.yml` and `cd-aws.yml`
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
|
|
||||||
- add VPS deployment runbook
|
- add VPS deployment runbook
|
||||||
- document required env files and domains
|
- document required env files and domains
|
||||||
|
|
||||||
@@ -110,6 +126,7 @@ Compatibility rule:
|
|||||||
Base domain: `whekin.dev`
|
Base domain: `whekin.dev`
|
||||||
|
|
||||||
Suggested hostnames:
|
Suggested hostnames:
|
||||||
|
|
||||||
- `household-bot.whekin.dev` for bot API / webhook
|
- `household-bot.whekin.dev` for bot API / webhook
|
||||||
- `household.whekin.dev` for mini app
|
- `household.whekin.dev` for mini app
|
||||||
|
|
||||||
@@ -128,16 +145,19 @@ These can be adjusted later without changing the deployment shape.
|
|||||||
## Runtime Env Files on VPS
|
## Runtime Env Files on VPS
|
||||||
|
|
||||||
Expected files under `/opt/household-bot/env`:
|
Expected files under `/opt/household-bot/env`:
|
||||||
|
|
||||||
- `bot.env`
|
- `bot.env`
|
||||||
- `miniapp.env`
|
- `miniapp.env`
|
||||||
- `caddy.env`
|
- `caddy.env`
|
||||||
|
|
||||||
Templates live in `deploy/vps/*.env.example`.
|
Templates live in `deploy/vps/*.env.example`.
|
||||||
|
|
||||||
- `miniapp.env` should set `VITE_BOT_API_URL` for the frontend build/runtime config.
|
- `miniapp.env` should set `VITE_BOT_API_URL` for the frontend build/runtime config.
|
||||||
|
|
||||||
## GitHub Actions Inputs / Secrets
|
## GitHub Actions Inputs / Secrets
|
||||||
|
|
||||||
Recommended repository variables:
|
Recommended repository variables:
|
||||||
|
|
||||||
- `VPS_HOST`
|
- `VPS_HOST`
|
||||||
- `VPS_USER` (default `root`)
|
- `VPS_USER` (default `root`)
|
||||||
- `VPS_PORT` (default `22`)
|
- `VPS_PORT` (default `22`)
|
||||||
@@ -146,10 +166,12 @@ Recommended repository variables:
|
|||||||
- `VPS_MINIAPP_URL` (default `https://household.whekin.dev`)
|
- `VPS_MINIAPP_URL` (default `https://household.whekin.dev`)
|
||||||
|
|
||||||
Required repository secrets:
|
Required repository secrets:
|
||||||
|
|
||||||
- `VPS_SSH_KEY`
|
- `VPS_SSH_KEY`
|
||||||
- `VPS_KNOWN_HOSTS`
|
- `VPS_KNOWN_HOSTS`
|
||||||
|
|
||||||
Optional for webhook sync and smoke verification:
|
Optional for webhook sync and smoke verification:
|
||||||
|
|
||||||
- `TELEGRAM_BOT_TOKEN`
|
- `TELEGRAM_BOT_TOKEN`
|
||||||
- `TELEGRAM_WEBHOOK_SECRET`
|
- `TELEGRAM_WEBHOOK_SECRET`
|
||||||
|
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ describe('createAdHocNotificationService', () => {
|
|||||||
const result = await service.updateNotification({
|
const result = await service.updateNotification({
|
||||||
notificationId: created.id,
|
notificationId: created.id,
|
||||||
viewerMemberId: 'creator',
|
viewerMemberId: 'creator',
|
||||||
scheduledFor: Temporal.Instant.from('2026-03-25T09:00:00Z'),
|
scheduledFor: Temporal.Instant.from('2099-03-25T09:00:00Z'),
|
||||||
timePrecision: 'exact',
|
timePrecision: 'exact',
|
||||||
deliveryMode: 'dm_selected',
|
deliveryMode: 'dm_selected',
|
||||||
dmRecipientMemberIds: ['alice', 'bob'],
|
dmRecipientMemberIds: ['alice', 'bob'],
|
||||||
@@ -360,7 +360,7 @@ describe('createAdHocNotificationService', () => {
|
|||||||
|
|
||||||
expect(result.status).toBe('updated')
|
expect(result.status).toBe('updated')
|
||||||
if (result.status === 'updated') {
|
if (result.status === 'updated') {
|
||||||
expect(result.notification.scheduledFor.toString()).toBe('2026-03-25T09:00:00Z')
|
expect(result.notification.scheduledFor.toString()).toBe('2099-03-25T09:00:00Z')
|
||||||
expect(result.notification.deliveryMode).toBe('dm_selected')
|
expect(result.notification.deliveryMode).toBe('dm_selected')
|
||||||
expect(result.notification.dmRecipientMemberIds).toEqual(['alice', 'bob'])
|
expect(result.notification.dmRecipientMemberIds).toEqual(['alice', 'bob'])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ export interface ScheduledDispatchService {
|
|||||||
cancelAdHocNotification(notificationId: string, cancelledAt?: Instant): Promise<void>
|
cancelAdHocNotification(notificationId: string, cancelledAt?: Instant): Promise<void>
|
||||||
reconcileHouseholdBuiltInDispatches(householdId: string, asOf?: Instant): Promise<void>
|
reconcileHouseholdBuiltInDispatches(householdId: string, asOf?: Instant): Promise<void>
|
||||||
reconcileAllBuiltInDispatches(asOf?: Instant): Promise<void>
|
reconcileAllBuiltInDispatches(asOf?: Instant): Promise<void>
|
||||||
listDueDispatches(input?: { asOf?: Instant; limit?: number }): Promise<readonly ScheduledDispatchRecord[]>
|
listDueDispatches(input?: {
|
||||||
|
asOf?: Instant
|
||||||
|
limit?: number
|
||||||
|
}): Promise<readonly ScheduledDispatchRecord[]>
|
||||||
getDispatchById(dispatchId: string): Promise<ScheduledDispatchRecord | null>
|
getDispatchById(dispatchId: string): Promise<ScheduledDispatchRecord | null>
|
||||||
claimDispatch(dispatchId: string): Promise<boolean>
|
claimDispatch(dispatchId: string): Promise<boolean>
|
||||||
releaseDispatch(dispatchId: string): Promise<void>
|
releaseDispatch(dispatchId: string): Promise<void>
|
||||||
|
|||||||
Reference in New Issue
Block a user