Skip to content

Commit a51342f

Browse files
Banametaliksclaude
andcommitted
feat(v10): 1-click pipeline promote, integrations platform, backup destinations
Backward-compatible release. Every migration is idempotent; new columns default to values that preserve existing behavior. Legacy promote endpoint preserved. Pipeline - Staged 1-click beta → prod: preflight plan, typed confirmation, auto-snapshot, schema txn apply, site redeploy, one-click rollback from saved backup - Exact beta fork: copies data (was schema-only), auto-deploys beta sites, remaps env vars from prod → beta DB - Realtime progress on pipeline:promote:<id> and pipeline:fork:<id> Authentication - GitHub-style device flow powering VS Code (vpc-sync-10.0.0.vsix) and CLI (vpc-pull login) — no more manual PAT paste - Standalone /auth/device approval page reuses SPA vpc-token Integrations (7 → 33 platforms) - Source control: GitHub, GitLab, Bitbucket - DB/BaaS: Supabase, Firebase, MongoDB, PlanetScale, Neon, Redis - Deploy: Docker, Vercel, Netlify, Railway, Render - Cloud: AWS, GCP, DigitalOcean, Cloudflare - Storage: S3-compatible - Notifications: Slack, Discord, Telegram, Teams, SMTP, SendGrid, Twilio - AI: OpenAI, Anthropic, Google AI, Groq - Payments: Stripe, Razorpay - Productivity: Notion, Linear, Jira - Generic: Webhook, SSH - Real testConnection() for every type — no stubs - One-click automations: Supabase→Backup, GitHub→Import, Slack→Alerts, Cloudflare→DNS+Auto-SSL (Always HTTPS + Full SSL + TLS 1.2) - Per-admin visibility (shared/private + created_by) Backups - Supabase destination type with encrypted service-role key - Scheduler polls every 60s, runs due destinations - Cloud restore: restore() falls back to Supabase download when local file is missing - Realtime progress on backup:progress channel Observability - Central logger.js writes to vpc_system_logs - GET /logs/health aggregate + SystemHealthWidget on Dashboard - GET /logs/system for raw log browsing Security - Versioned encryption (v{N}: prefix), legacy format still decrypts - backend/scripts/rekey.js re-encrypts all secrets with current key Performance - Lazy-loaded apps via React.lazy(): main bundle 1.2 MB → 498 KB (-59%) - Window.jsx wraps AppComponent in Suspense + LoadingSpinner fallback Quality - backend/tests/smoke.test.js — 8 zero-dep tests via node --test - Fixed ROUTE_PERMISSION_MAP gaps for pipeline + realtime Migrations - 041_vpc_device_codes.sql - 042_vpc_backup_destinations.sql - 043_vpc_pipeline_promotions.sql (+ integration visibility + enc_key_version) - 044_vpc_system_logs.sql See DEPLOY_LOG.md for full checklist, backward-compat notes, and rollback plan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 54cf4bf commit a51342f

95 files changed

Lines changed: 9356 additions & 375 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DEPLOY_LOG.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# VPC Deploy Log
2+
3+
## v10.0.0 — 2026-04-22
4+
5+
A forward-compatible release. No data loss paths, every migration is idempotent
6+
and every new column has a safe default. Existing integrations, backups, pipelines
7+
and sessions continue to work without manual intervention.
8+
9+
---
10+
11+
### 🚀 Highlights
12+
13+
- **Perfect 1-click Pipeline promotion** — preflight plan, typed confirmation, auto-snapshot, staged execute, one-click rollback.
14+
- **Exact beta fork** — full data copy (not just schema), auto-deploy, env-var remap from prod → beta DB.
15+
- **GitHub-style browser sign-in** across VS Code (`vpc-sync-10.0.0.vsix`) and CLI (`vpc-pull login`) — no more pasting PATs.
16+
- **33 integration platforms** (up from 7) with one-click automations (Supabase→Backup, GitHub→Import, Slack→Alerts, Cloudflare→DNS+SSL).
17+
- **Supabase backup destination** — off-site backups on a schedule + cloud-restore fallback.
18+
- **Lazy-loaded apps** — main bundle 1.2 MB → 498 KB (−59%).
19+
- **System Health widget** — central logger captures every error, Dashboard surfaces 24h counts.
20+
- **Versioned encryption** with `rekey.js` script for key rotation.
21+
- **Baseline tests** via `node --test` — 8/8 passing.
22+
23+
---
24+
25+
### 📦 Deploy checklist
26+
27+
Run in order. Each step is idempotent and safe to re-run.
28+
29+
```bash
30+
# 1. Apply new migrations (all use CREATE TABLE IF NOT EXISTS / ADD COLUMN IF NOT EXISTS)
31+
psql "$DATABASE_URL" < backend/migrations/041_vpc_device_codes.sql
32+
psql "$DATABASE_URL" < backend/migrations/042_vpc_backup_destinations.sql
33+
psql "$DATABASE_URL" < backend/migrations/043_vpc_pipeline_promotions.sql
34+
psql "$DATABASE_URL" < backend/migrations/044_vpc_system_logs.sql
35+
36+
# 2. Restart backend — new schedulers + logger wire up on boot
37+
pm2 restart vpc-backend # or `systemctl restart vpc`
38+
39+
# 3. Rebuild frontend (lazy-split chunks)
40+
cd frontend && npm install && npx vite build && cd ..
41+
42+
# 4. (Optional) Install the new VS Code extension
43+
code --install-extension vscode-extension/vpc-sync-10.0.0.vsix --force
44+
45+
# 5. Verify
46+
cd backend && npm test # 8/8 should pass
47+
curl http://localhost:8001/health
48+
```
49+
50+
---
51+
52+
### 🔒 Backward-compatibility notes
53+
54+
| Change | Existing behavior preserved? |
55+
|---|---|
56+
| `vpc_integrations.visibility` column added | Default `'shared'` → all existing integrations visible to every admin as before. |
57+
| `vpc_integrations.created_by` column added | Nullable, default NULL — no backfill needed. |
58+
| `vpc_settings.enc_key_version` column added | Default 1 — existing blobs continue decrypting. |
59+
| Encryption blob format: new blobs prefix `v{N}:` | `decrypt()` accepts both legacy and versioned formats — verified by smoke test `encryption: decrypt handles legacy (v-less) format`. |
60+
| `pipelineService.forkToBeta()` default `copyData` | Was `false`, now `true` (per user request for exact forks). Frontend always passes explicit `{ copyData: true }`; direct API callers using the default now get the new behavior. |
61+
| `POST /pipelines/:id/promote` response shape | ⚠ Changed — old response `{ schemaPRs, deployedSites, errors }` moved to `/pipelines/:id/promote-legacy`. New response `{ run_id, status, backup_id, schema_applied, deployed, deployErrors, stages }`. Only caller was the web UI, which is updated. |
62+
| `integrationService.getAll(pool)` | Backward compatible — existing signature still works. Optional `{ forUserId }` param enables per-admin filtering. |
63+
| Lazy-loaded apps | First open of each app shows loading spinner briefly; functional behavior unchanged. |
64+
65+
---
66+
67+
### 📂 New files
68+
69+
**Backend**
70+
- `backend/migrations/041_vpc_device_codes.sql`
71+
- `backend/migrations/042_vpc_backup_destinations.sql`
72+
- `backend/migrations/043_vpc_pipeline_promotions.sql`
73+
- `backend/migrations/044_vpc_system_logs.sql`
74+
- `backend/routes/deviceAuth.js` — VS Code / CLI sign-in flow
75+
- `backend/routes/realtime.js` — SSE event stream
76+
- `backend/services/backupScheduler.js` — polls supabase destinations every 60s
77+
- `backend/services/credentialResolver.js` — shared integration creds for any app
78+
- `backend/services/integrationAutomations.js` — one-click automators
79+
- `backend/services/pipelinePromoteService.js` — staged promote + rollback
80+
- `backend/services/realtimeBus.js` — in-process pub/sub for SSE
81+
- `backend/services/supabaseBackupService.js` — upload & restore via Supabase Storage
82+
- `backend/scripts/rekey.js` — re-encrypt all secrets with new key
83+
- `backend/utils/logger.js` — central logger writing to `vpc_system_logs`
84+
- `backend/views/deviceApprove.html` — browser approval page for device flow
85+
- `backend/tests/smoke.test.js` — 8 zero-dep smoke tests
86+
87+
**Frontend**
88+
- `frontend/src/components/apps/BackupDestinations.jsx`
89+
- `frontend/src/components/apps/Connections.jsx` (rewritten — 33 platforms, categories, automations)
90+
- `frontend/src/components/apps/Dashboard.jsx`
91+
- `frontend/src/components/desktop/CommandPalette.jsx`
92+
- `frontend/src/components/desktop/ContextBreadcrumb.jsx`
93+
- `frontend/src/components/desktop/JobQueue.jsx`
94+
- `frontend/src/components/desktop/KeyboardShortcutsOverlay.jsx`
95+
- `frontend/src/components/desktop/NotificationCenter.jsx`
96+
- `frontend/src/components/desktop/StatusBar.jsx`
97+
- `frontend/src/components/desktop/WorkspaceSwitcher.jsx`
98+
- `frontend/src/components/integrations/*`
99+
- `frontend/src/components/pipeline/PromoteDialog.jsx`
100+
- `frontend/src/components/widgets/*` (incl. `SystemHealthWidget`)
101+
- `frontend/src/hooks/useGlobalKeyboard.js`
102+
- `frontend/src/lib/realtime.js`
103+
- `frontend/src/lib/widgetRegistry.js`
104+
- `frontend/src/stores/useCommandStore.js`
105+
- `frontend/src/stores/useContextStore.js`
106+
- `frontend/src/stores/useDashboardStore.js`
107+
- `frontend/src/stores/useIntegrationsStore.js`
108+
- `frontend/src/stores/useJobStore.js`
109+
- `frontend/src/stores/useNotificationStore.js`
110+
- `frontend/src/stores/useWorkspaceStore.js`
111+
112+
**VS Code Extension** (packaged as `vpc-sync-10.0.0.vsix`)
113+
- `vscode-extension/src/auth/deviceFlow.ts` — AuthenticationProvider + device-flow poller
114+
- `vscode-extension/src/vcs/merge.ts` — 3-way merge port
115+
116+
**CLI**
117+
- `cli/src/commands/login.js``vpc-pull login` browser sign-in
118+
119+
---
120+
121+
### 🩺 New endpoints
122+
123+
```
124+
# Device auth (public + JWT mix)
125+
POST /api/admin/auth/device/code
126+
POST /api/admin/auth/device/token
127+
GET /api/admin/auth/device/pending?code=XXXX
128+
POST /api/admin/auth/device/confirm { user_code }
129+
POST /api/admin/auth/device/deny { user_code }
130+
131+
# Backup destinations
132+
GET /api/admin/backup/destinations
133+
POST /api/admin/backup/destinations
134+
PATCH /api/admin/backup/destinations/:id
135+
DELETE /api/admin/backup/destinations/:id
136+
POST /api/admin/backup/destinations/:id/test
137+
POST /api/admin/backup/destinations/:id/run
138+
GET /api/admin/backup/destinations/:id/runs
139+
140+
# Integration automations
141+
GET /api/admin/integrations/automations
142+
POST /api/admin/integrations/:id/automate { automation, options }
143+
144+
# Perfect pipeline promote
145+
GET /api/admin/pipeline/pipelines/:id/promote/preflight
146+
POST /api/admin/pipeline/pipelines/:id/promote { confirmations, skipBackup, dryRun }
147+
POST /api/admin/pipeline/pipelines/:id/promote-legacy (old behavior preserved)
148+
GET /api/admin/pipeline/pipelines/:id/promotions
149+
POST /api/admin/pipeline/pipelines/:id/promotions/:runId/rollback
150+
151+
# Logs
152+
GET /api/admin/logs/health
153+
GET /api/admin/logs/system?level=error&limit=100
154+
155+
# Static page
156+
GET /auth/device (approval UI for device codes)
157+
```
158+
159+
---
160+
161+
### ⚠ Rollback plan
162+
163+
If something goes wrong after deploy:
164+
165+
1. **Database**: new tables are independent. Drop them if needed — nothing else depends on them:
166+
```sql
167+
DROP TABLE IF EXISTS vpc_pipeline_promotions CASCADE;
168+
DROP TABLE IF EXISTS vpc_system_logs;
169+
DROP TABLE IF EXISTS vpc_backup_destination_runs;
170+
DROP TABLE IF EXISTS vpc_backup_destinations;
171+
DROP TABLE IF EXISTS vpc_device_codes;
172+
ALTER TABLE vpc_integrations DROP COLUMN IF EXISTS visibility;
173+
ALTER TABLE vpc_integrations DROP COLUMN IF EXISTS created_by;
174+
ALTER TABLE vpc_settings DROP COLUMN IF EXISTS enc_key_version;
175+
```
176+
2. **Code**: `git revert HEAD` and redeploy. Because migrations are additive,
177+
reverting code without reverting the schema is also safe — the extra tables
178+
simply sit unused.
179+
3. **Pipeline promotions in flight**: any running promotion records its
180+
pre-promote backup in `vpc_pipeline_promotions.backup_id`. Use
181+
`POST /promotions/:runId/rollback` or restore the backup directly
182+
via the Backup Manager.
183+
184+
---
185+
186+
### ✅ Verification
187+
188+
```
189+
cd backend && npm test
190+
✔ encryption: round-trip preserves plaintext
191+
✔ encryption: decrypt handles legacy (v-less) format
192+
✔ encryption: decrypt returns null for garbage
193+
✔ integrations: all types have name + fields
194+
✔ integrations: every type has a tester case
195+
✔ automations: registry is consistent
196+
✔ pipeline promote service: exports expected API
197+
✔ logger: forSource returns level methods
198+
ℹ 8 pass, 0 fail
199+
```
200+
201+
Frontend build: `✓ built in 8.07s` — main chunk 498 KB (was 1,204 KB).

app.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,12 @@ app.listen(PORT, async () => {
206206
}
207207
}, BACKUP_CHECK_INTERVAL);
208208
console.log('[VPC] Auto-backup scheduler running (checks every hour)');
209+
210+
// External backup destinations scheduler (Supabase, etc.) — polls every minute
211+
try {
212+
const backupScheduler = require('./backend/services/backupScheduler');
213+
backupScheduler.start(pool);
214+
} catch (err) {
215+
console.error('[VPC] Backup destination scheduler failed to start:', err.message);
216+
}
209217
});

backend/middleware/auth.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,25 @@ const ROUTE_PERMISSION_MAP = {
1616
'/web-hosting': 'web_hosting',
1717
'/settings': 'ai_agent',
1818
'/vpshub': 'vpshub',
19+
'/pipeline': 'pipeline',
20+
'/realtime': 'realtime',
1921
};
2022

2123
async function authenticateAdmin(req, res, next) {
2224
try {
2325
const authHeader = req.headers.authorization;
24-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
26+
let token = null;
27+
if (authHeader && authHeader.startsWith('Bearer ')) {
28+
token = authHeader.replace('Bearer ', '');
29+
} else if (req.query && req.query._t) {
30+
// SSE/EventSource fallback — token via query string
31+
token = String(req.query._t);
32+
}
33+
if (!token) {
2534
return res.status(401).json({ error: 'No authorization token provided' });
2635
}
2736

28-
const token = authHeader.replace('Bearer ', '');
29-
if (!token || token.length < 20) {
37+
if (token.length < 20) {
3038
return res.status(401).json({ error: 'Invalid token format' });
3139
}
3240

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- Mark one integration per type as the default reusable connection
2+
ALTER TABLE vpc_integrations
3+
ADD COLUMN IF NOT EXISTS is_default BOOLEAN DEFAULT FALSE;
4+
5+
-- One default per type
6+
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpc_integrations_default_per_type
7+
ON vpc_integrations(type) WHERE is_default = TRUE;
8+
9+
-- Track which apps reference which integration (audit + reverse-lookup)
10+
CREATE TABLE IF NOT EXISTS vpc_integration_uses (
11+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
12+
integration_id UUID NOT NULL REFERENCES vpc_integrations(id) ON DELETE CASCADE,
13+
app_id VARCHAR(100) NOT NULL,
14+
resource_kind VARCHAR(100),
15+
resource_id VARCHAR(255),
16+
last_used_at TIMESTAMPTZ DEFAULT NOW(),
17+
created_at TIMESTAMPTZ DEFAULT NOW(),
18+
UNIQUE (integration_id, app_id, resource_kind, resource_id)
19+
);
20+
21+
CREATE INDEX IF NOT EXISTS idx_integration_uses_app ON vpc_integration_uses(app_id);
22+
CREATE INDEX IF NOT EXISTS idx_integration_uses_integration ON vpc_integration_uses(integration_id);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- Device authorization codes for VS Code extension sign-in (OAuth-style device flow)
2+
CREATE TABLE IF NOT EXISTS vpc_device_codes (
3+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4+
device_code VARCHAR(64) NOT NULL UNIQUE,
5+
user_code VARCHAR(16) NOT NULL UNIQUE,
6+
client_name VARCHAR(255),
7+
status VARCHAR(24) NOT NULL DEFAULT 'pending', -- pending | approved | denied | expired
8+
user_id UUID REFERENCES vpc_admins(id) ON DELETE SET NULL,
9+
issued_token_id UUID REFERENCES vpshub_tokens(id) ON DELETE SET NULL,
10+
plaintext_once TEXT,
11+
expires_at TIMESTAMPTZ NOT NULL,
12+
approved_at TIMESTAMPTZ,
13+
created_at TIMESTAMPTZ DEFAULT NOW()
14+
);
15+
16+
CREATE INDEX IF NOT EXISTS idx_vpc_device_codes_user_code ON vpc_device_codes(user_code);
17+
CREATE INDEX IF NOT EXISTS idx_vpc_device_codes_device_code ON vpc_device_codes(device_code);
18+
CREATE INDEX IF NOT EXISTS idx_vpc_device_codes_expires ON vpc_device_codes(expires_at);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
-- External backup destinations (Supabase, S3, etc.)
2+
CREATE TABLE IF NOT EXISTS vpc_backup_destinations (
3+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4+
name VARCHAR(255) NOT NULL,
5+
provider VARCHAR(32) NOT NULL, -- 'supabase' | 's3' (future)
6+
config JSONB NOT NULL DEFAULT '{}'::jsonb,
7+
-- For supabase: { url, service_role_key (encrypted), bucket, sync_data, sync_schema }
8+
schedule_cron VARCHAR(64), -- standard 5-field cron, NULL disables
9+
interval_minutes INTEGER, -- simple alternative to cron (e.g. every 60 min)
10+
enabled BOOLEAN DEFAULT true,
11+
last_run_at TIMESTAMPTZ,
12+
last_run_status VARCHAR(24), -- 'success' | 'failed' | 'running'
13+
last_run_error TEXT,
14+
last_run_bytes BIGINT,
15+
next_run_at TIMESTAMPTZ,
16+
created_by UUID REFERENCES vpc_admins(id) ON DELETE SET NULL,
17+
created_at TIMESTAMPTZ DEFAULT NOW(),
18+
updated_at TIMESTAMPTZ DEFAULT NOW()
19+
);
20+
21+
CREATE INDEX IF NOT EXISTS idx_vpc_backup_destinations_enabled ON vpc_backup_destinations(enabled);
22+
CREATE INDEX IF NOT EXISTS idx_vpc_backup_destinations_next_run ON vpc_backup_destinations(next_run_at);
23+
24+
-- Audit trail per-destination
25+
CREATE TABLE IF NOT EXISTS vpc_backup_destination_runs (
26+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
27+
destination_id UUID NOT NULL REFERENCES vpc_backup_destinations(id) ON DELETE CASCADE,
28+
backup_id UUID REFERENCES backups(id) ON DELETE SET NULL,
29+
status VARCHAR(24) NOT NULL, -- 'success' | 'failed' | 'running'
30+
bytes_uploaded BIGINT,
31+
remote_path TEXT,
32+
error_message TEXT,
33+
duration_ms INTEGER,
34+
started_at TIMESTAMPTZ DEFAULT NOW(),
35+
completed_at TIMESTAMPTZ
36+
);
37+
38+
CREATE INDEX IF NOT EXISTS idx_vpc_backup_dest_runs_dest ON vpc_backup_destination_runs(destination_id, started_at DESC);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- Track each pipeline promotion run (snapshot + stages) for auditing & rollback
2+
CREATE TABLE IF NOT EXISTS vpc_pipeline_promotions (
3+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4+
pipeline_id UUID NOT NULL REFERENCES vpc_pipelines(id) ON DELETE CASCADE,
5+
initiated_by UUID REFERENCES vpc_admins(id) ON DELETE SET NULL,
6+
status VARCHAR(24) NOT NULL DEFAULT 'pending', -- pending|running|success|failed|rolled_back
7+
stages JSONB DEFAULT '[]'::jsonb, -- array of {name, status, started_at, completed_at, error?, details?}
8+
backup_id UUID, -- prod DB snapshot taken before promotion (for rollback)
9+
schema_diff JSONB,
10+
applied_sql TEXT,
11+
deployed_sites JSONB DEFAULT '[]'::jsonb,
12+
error_message TEXT,
13+
started_at TIMESTAMPTZ DEFAULT NOW(),
14+
completed_at TIMESTAMPTZ
15+
);
16+
17+
CREATE INDEX IF NOT EXISTS idx_vpc_pipeline_promotions_pipeline ON vpc_pipeline_promotions(pipeline_id, started_at DESC);
18+
CREATE INDEX IF NOT EXISTS idx_vpc_pipeline_promotions_status ON vpc_pipeline_promotions(status);
19+
20+
-- Integration visibility (fix #7)
21+
ALTER TABLE vpc_integrations ADD COLUMN IF NOT EXISTS created_by UUID REFERENCES vpc_admins(id) ON DELETE SET NULL;
22+
ALTER TABLE vpc_integrations ADD COLUMN IF NOT EXISTS visibility VARCHAR(16) NOT NULL DEFAULT 'shared'; -- private|shared
23+
CREATE INDEX IF NOT EXISTS idx_vpc_integrations_owner ON vpc_integrations(created_by);
24+
25+
-- Key rotation (fix #6) — tag each encrypted blob with the key version that produced it
26+
ALTER TABLE vpc_settings ADD COLUMN IF NOT EXISTS enc_key_version SMALLINT DEFAULT 1;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Central system-level error/event log (separate from per-admin action_logs)
2+
CREATE TABLE IF NOT EXISTS vpc_system_logs (
3+
id BIGSERIAL PRIMARY KEY,
4+
level VARCHAR(16) NOT NULL, -- debug|info|warn|error|fatal
5+
source VARCHAR(64) NOT NULL, -- subsystem: pipeline, backup, integration, etc.
6+
message TEXT NOT NULL,
7+
metadata JSONB,
8+
created_at TIMESTAMPTZ DEFAULT NOW()
9+
);
10+
11+
CREATE INDEX IF NOT EXISTS idx_vpc_system_logs_level_time ON vpc_system_logs(level, created_at DESC);
12+
CREATE INDEX IF NOT EXISTS idx_vpc_system_logs_source ON vpc_system_logs(source);
13+
CREATE INDEX IF NOT EXISTS idx_vpc_system_logs_created ON vpc_system_logs(created_at DESC);

backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"start": "node server.js",
77
"dev": "nodemon server.js",
88
"migrate": "node db/run-migrations.js",
9-
"seed": "node db/seed-admin.js"
9+
"seed": "node db/seed-admin.js",
10+
"test": "node --test tests/*.test.js"
1011
},
1112
"dependencies": {
1213
"@anthropic-ai/sdk": "^0.78.0",

0 commit comments

Comments
 (0)