fix(server): unbuffered logging + reject operator token on contact sync
- logger: emit straight to fd 1/2 (unbuffered). The buffered process.std* streams block-buffer to a pipe under systemd, so low-volume logs never flushed and were invisible. - /client/imessage/contacts: return 401 (like /sync/batch) when the caller presents the operator/service token instead of a device token, instead of 500ing on a null deviceId downstream. - systemd unit: reflect the working deploy (root + /root/.bun, Redis dependency, file logging since the droplet journald is volatile). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f6ce05d864
commit
f9cf50e695
3 changed files with 19 additions and 5 deletions
|
|
@ -1,16 +1,22 @@
|
|||
[Unit]
|
||||
Description=Mac Sync Server
|
||||
After=network.target
|
||||
After=network.target redis-server.service
|
||||
Wants=redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=lilith
|
||||
# Runs as root on the backend droplet using the root-owned bun install — matches
|
||||
# the other services on that host (no dedicated service user is provisioned).
|
||||
User=root
|
||||
WorkingDirectory=/opt/mac-sync-server
|
||||
ExecStart=/home/lilith/.bun/bin/bun run src/main.ts
|
||||
ExecStart=/root/.bun/bin/bun run src/main.ts
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
Environment=NODE_ENV=production
|
||||
EnvironmentFile=/etc/mac-sync-server/env
|
||||
# journald on this droplet is volatile and captures nothing, so log to a file.
|
||||
StandardOutput=append:/var/log/mac-sync-server.log
|
||||
StandardError=append:/var/log/mac-sync-server.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
import { writeSync } from 'node:fs';
|
||||
|
||||
type Level = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
function emit(level: Level, msg: string, meta?: Record<string, unknown>): void {
|
||||
const line = { ts: new Date().toISOString(), level, msg, ...(meta ?? {}) };
|
||||
const stream = level === 'error' || level === 'warn' ? process.stderr : process.stdout;
|
||||
stream.write(`${JSON.stringify(line)}\n`);
|
||||
// Write straight to the fd (1=stdout, 2=stderr). The buffered process.std*
|
||||
// streams block-buffer to a pipe (systemd journal), so low-volume logs never
|
||||
// flush and stay invisible; writeSync emits each line immediately.
|
||||
const fd = level === 'error' || level === 'warn' ? 2 : 1;
|
||||
writeSync(fd, `${JSON.stringify(line)}\n`);
|
||||
}
|
||||
|
||||
export const logger = {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ export const imessageClientRouter = new Hono()
|
|||
})
|
||||
.post('/contacts', async (c) => {
|
||||
const deviceId = c.get('deviceId');
|
||||
if (!deviceId || deviceId === 'operator') {
|
||||
return c.json({ error: 'device token required for contact sync', code: 'bad_token' }, 401);
|
||||
}
|
||||
const payload = syncContactsSchema.parse(await c.req.json());
|
||||
const result = await withSyncHistory(deviceId, 'contacts', () => ingestContacts(deviceId, payload));
|
||||
return c.json({ success: true, data: result });
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue