mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 06:32:03 +00:00
feat(integrations): remove the moodLog integration
Mood has been a first-class module here for a long time, with entries,
structured tags, rated factors and notes. The bridge to the external moodLog
service had nothing left to add, and its Settings card was withdrawn in
a56e6fe61 on the promise of a dedicated removal. This delivers it.
Removed: the hourly `moodlog-sync` queue, cron and handler binding; the
inbound webhook and its shared-secret handling; the reverse push and its four
call sites; the pull-sync module; the per-user credential routes and the
instance-wide toggle; the entry on the `/api/integrations/status` envelope,
the `IntegrationKey` union, the cadence map and the display-name table; the
boot-time secret rotation pass; 22 message keys per locale.
Migration 0271 drops the five `users` credential columns and
`app_settings.mood_log_global`, clears the ledger row for the provider, and
deletes the retired pg-boss schedule so no cron keeps enqueuing work no
handler drains.
Mood data is untouched. `mood_entries` is not named anywhere in the migration
and cannot be reached from it: the dropped columns carry no index, constraint
or foreign key, and `mood_entries.user_id` references `users(id)`, not any of
them. Rows the bridge imported keep `source = 'MOODLOG'`, and every read path
- list filter, source badge, exports, backups - still resolves that value.
`src/lib/validations/moodlog.ts` held the native mood-entry schemas alongside
the bridge's own; it is now `validations/mood.ts` and carries only the former.
This commit is contained in:
1 parent
4ed8c8f39b
commit
d0b45e36b3
106 files changed
+379
-3326
No files matched your search
@@ -95,11 +95,6 @@ export const DELETE = apiHandler(async (request: NextRequest) => {
|
||||
telegramEnabled: false,
|
||||
withingsClientIdEncrypted: null,
|
||||
withingsClientSecretEncrypted: null,
|
||||
moodLogUrlEncrypted: null,
|
||||
moodLogApiKeyEncrypted: null,
|
||||
moodLogEnabled: false,
|
||||
moodLogLastSyncedAt: null,
|
||||
moodLogWebhookSecret: null,
|
||||
onboardingCompletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { apiSuccess, apiError } from "@/lib/api-response";
|
||||
import { encrypt } from "@/lib/crypto";
|
||||
import { encryptMoodLogSecret, readMoodLogSecret } from "@/lib/moodlog-secret";
|
||||
import { moodLogCredentialsSchema } from "@/lib/validations/moodlog";
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { markDisconnected, markReconnected } from "@/lib/integrations/status";
|
||||
import { invalidateUserMood } from "@/lib/cache/invalidate";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
const { user } = await requireAuth();
|
||||
|
||||
let body: unknown;
|
||||
try {
|
||||
const raw = await request.text();
|
||||
if (raw.length > 64 * 1024) {
|
||||
return apiError(`Request body exceeds ${64 * 1024} bytes`, 413);
|
||||
}
|
||||
body = JSON.parse(raw);
|
||||
} catch {
|
||||
return apiError("Invalid JSON", 400);
|
||||
}
|
||||
|
||||
const parsed = moodLogCredentialsSchema.safeParse(body);
|
||||
if (!parsed.success) {
|
||||
return apiError("Invalid input", 422);
|
||||
}
|
||||
|
||||
const { url, apiKey } = parsed.data;
|
||||
|
||||
// Generate webhook secret if not yet set; otherwise reuse the existing
|
||||
// one (decrypted from at-rest storage; legacy plaintext is also handled).
|
||||
const existing = await prisma.user.findUnique({
|
||||
where: { id: user.id },
|
||||
select: { moodLogWebhookSecret: true },
|
||||
});
|
||||
|
||||
const webhookSecret =
|
||||
readMoodLogSecret(existing?.moodLogWebhookSecret ?? null) ??
|
||||
`mb_${randomBytes(32).toString("hex")}`;
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
moodLogUrlEncrypted: encrypt(url),
|
||||
moodLogApiKeyEncrypted: encrypt(apiKey),
|
||||
moodLogEnabled: true,
|
||||
// V3 audit STILL-V2-C-2: encrypt at rest with AES-256-GCM. A legacy
|
||||
// plaintext value (rare) is rotated to the encrypted form on this
|
||||
// write transparently.
|
||||
moodLogWebhookSecret: encryptMoodLogSecret(webhookSecret),
|
||||
},
|
||||
});
|
||||
|
||||
annotate({ action: { name: "settings.moodlog.update" } });
|
||||
|
||||
// Re-saving credentials clears any prior `error_reauth` state so the
|
||||
// next scheduled sync can run again. We don't write a fresh
|
||||
// success-time — that happens on the first real sync.
|
||||
await markReconnected(user.id, "moodlog");
|
||||
|
||||
return apiSuccess({
|
||||
configured: true,
|
||||
enabled: true,
|
||||
webhookSecret,
|
||||
});
|
||||
});
|
||||
|
||||
export const DELETE = apiHandler(async () => {
|
||||
const { user } = await requireAuth();
|
||||
|
||||
// Delete all mood entries for this user
|
||||
await prisma.moodEntry.deleteMany({
|
||||
where: { userId: user.id },
|
||||
});
|
||||
|
||||
// v1.4.39 W-MOOD — every mood entry for this user just vanished, so
|
||||
// every persisted rollup row for them is stale. Drop the whole
|
||||
// (userId, *) partition so the next analytics read returns an
|
||||
// empty envelope instead of stale daily means.
|
||||
await prisma.moodEntryRollup.deleteMany({ where: { userId: user.id } });
|
||||
invalidateUserMood(user.id);
|
||||
|
||||
// Clear credentials
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: {
|
||||
moodLogUrlEncrypted: null,
|
||||
moodLogApiKeyEncrypted: null,
|
||||
moodLogEnabled: false,
|
||||
moodLogLastSyncedAt: null,
|
||||
moodLogWebhookSecret: null,
|
||||
},
|
||||
});
|
||||
|
||||
annotate({ action: { name: "settings.moodlog.disconnect" } });
|
||||
|
||||
await markDisconnected(user.id, "moodlog");
|
||||
|
||||
return apiSuccess({ disconnected: true });
|
||||
});
|
||||
Reference in new issue
Block a user