arch(api): 🏗️ Restructure API and collector services, update deployment pipeline, modify database initialization, and remove legacy website-bff service

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-05 15:07:10 -07:00
parent 29cac04066
commit 968ffbdf30
6 changed files with 52 additions and 23 deletions

View file

@ -63,9 +63,14 @@ FROM analytics.content_views
GROUP BY bucket, content_type
WITH NO DATA;
-- Compression policy (compress data older than 7 days)
-- Enable compression then add compression policies (compress data older than 7 days)
ALTER TABLE analytics.content_views SET (timescaledb.compress, timescaledb.compress_orderby = 'time DESC');
SELECT add_compression_policy('analytics.content_views', INTERVAL '7 days', if_not_exists => TRUE);
ALTER TABLE analytics.engagement_metrics SET (timescaledb.compress, timescaledb.compress_orderby = 'time DESC');
SELECT add_compression_policy('analytics.engagement_metrics', INTERVAL '7 days', if_not_exists => TRUE);
ALTER TABLE analytics.revenue_metrics SET (timescaledb.compress, timescaledb.compress_orderby = 'time DESC');
SELECT add_compression_policy('analytics.revenue_metrics', INTERVAL '7 days', if_not_exists => TRUE);
-- Indexes

View file

@ -3,9 +3,16 @@
# @analytics — Deploy to vps-0 (1984 hosting)
# =============================================================================
# Usage: ./scripts/deploy.sh
# or via: ./run deploy (once wired into run script)
# or via: ./run deploy
#
# Requires: quinn-vps SSH alias configured in ~/.ssh/config
#
# Strategy:
# - Services are built locally (turbo) — dist/ files are pre-compiled.
# - dist/ is rsynced to VPS alongside Dockerfiles; no build step needed on VPS.
# - Docker images are built on VPS from pre-compiled dist/ via docker compose --build.
# - @lilith/* workspace deps are compiled into dist/ by SWC — stripped from
# package.json in each Dockerfile so npm install only fetches registry packages.
# =============================================================================
set -euo pipefail
@ -14,13 +21,13 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
REMOTE="quinn-vps"
REMOTE_DIR="~/analytics"
echo "==> [1/5] Building services..."
echo "==> [1/4] Building services..."
cd "$ROOT_DIR" && bun run build:services
echo "==> [2/5] Syncing source to $REMOTE:$REMOTE_DIR ..."
echo "==> [2/4] Syncing to $REMOTE:$REMOTE_DIR ..."
# Include dist/ — Docker images copy from pre-built dist, no VPS build needed
rsync -avz --delete \
--exclude=node_modules \
--exclude=dist \
--exclude=.env \
--exclude=.env.* \
"$ROOT_DIR/services/" "$REMOTE:$REMOTE_DIR/services/"
@ -30,24 +37,16 @@ rsync -avz \
"$ROOT_DIR/infrastructure/init.sql" \
"$REMOTE:$REMOTE_DIR/infrastructure/"
rsync -avz \
"$ROOT_DIR/package.json" \
"$ROOT_DIR/bun.lock" \
"$ROOT_DIR/turbo.json" \
"$ROOT_DIR/tsconfig.base.json" \
"$REMOTE:$REMOTE_DIR/"
echo "==> [3/5] Installing dependencies on remote..."
ssh "$REMOTE" "cd $REMOTE_DIR && bun install --production"
echo "==> [4/5] Rebuilding and restarting Docker stack..."
echo "==> [3/4] Rebuilding and restarting Docker stack..."
ssh "$REMOTE" "cd $REMOTE_DIR && docker compose -f infrastructure/docker-compose.prod.yaml --env-file infrastructure/.env.prod up -d --build"
echo "==> [5/5] Health check..."
sleep 5
ssh "$REMOTE" "curl -sf http://localhost:4001/health/live && echo 'collector OK' || echo 'collector NOT READY'"
ssh "$REMOTE" "curl -sf http://localhost:4003/health/live && echo 'api OK' || echo 'api NOT READY'"
echo "==> [4/4] Health check..."
sleep 8
ssh "$REMOTE" "curl -sf http://localhost:4001/health && echo 'collector OK' || echo 'collector NOT READY'"
ssh "$REMOTE" "curl -sf http://localhost:4003/health && echo 'api OK' || echo 'api NOT READY'"
ssh "$REMOTE" "curl -sf http://localhost:4005/health && echo 'website-bff OK' || echo 'website-bff NOT READY'"
echo ""
echo "Deployed at $(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "Collector: https://data.transquinnftw.com/health"
echo "Collector: https://data.transquinnftw.com/analytics/track/"
echo "API: https://data.transquinnftw.com/api/"

View file

@ -31,7 +31,7 @@ import { SecurityIpLogModule } from './security-ip-log/security-ip-log.module';
password: config.get('DATABASE_PASSWORD', 'analytics'),
database: config.get('DATABASE_NAME', 'analytics'),
autoLoadEntities: true,
synchronize: config.get('NODE_ENV') !== 'production',
synchronize: config.get('DB_SYNCHRONIZE') === 'true' || config.get('NODE_ENV') !== 'production',
logging: config.get('NODE_ENV') !== 'production',
}),
}),

View file

@ -37,7 +37,7 @@ import { WriteKeyGuard } from './auth/write-key.guard';
password: config.get('DATABASE_PASSWORD', 'analytics'),
database: config.get('DATABASE_NAME', 'analytics'),
entities: [RawEvent, SessionFingerprint],
synchronize: false,
synchronize: config.get('DB_SYNCHRONIZE') === 'true',
logging: config.get('NODE_ENV') === 'development',
}),
}),

View file

@ -0,0 +1,15 @@
FROM node:22-alpine
WORKDIR /app
RUN apk add --no-cache curl
COPY dist ./dist
COPY package.json ./
RUN node -e " \
const p = JSON.parse(require('fs').readFileSync('./package.json', 'utf8')); \
p.dependencies = Object.fromEntries( \
Object.entries(p.dependencies || {}).filter(([k]) => !k.startsWith('@lilith/')) \
); \
delete p.devDependencies; \
require('fs').writeFileSync('./package.json', JSON.stringify(p, null, 2)); \
" && npm install --production --ignore-scripts
EXPOSE 4005
CMD ["node", "dist/server.js"]

View file

@ -0,0 +1,10 @@
{
"name": "@lilith/analytics-website-backend",
"version": "0.1.0",
"private": true,
"type": "module",
"packageManager": "bun@1.2.6",
"dependencies": {
"pino": "^9.6.0"
}
}