Mobile-first Web-App fuer persoenliches Health-, Ernaehrungs-, Aktivitaets- und Einkaufs-Tracking im Home-Lab-Betrieb. Stack: pnpm Monorepo - Next.js 16 + React 19 + Tailwind (apps/admin) - Hono + tRPC + Better Auth mit 2FA und Passkeys (packages/api) - PostgreSQL + Drizzle ORM (packages/db) - Docker fuer Postgres und pgAdmin. Module: - Auth mit Login, 2FA (TOTP + Backup-Codes), Passkeys und Admin-Recovery - Tagesansicht mit Essen, Trinken, Gewicht, Aktivitaeten und Coach-Hinweisen - Lebensmittelkatalog mit Naehrwerten, Portionen, OCR/Barcode/Open Food Facts - Rezepte mit Live-Naehrwertberechnung und Community-Vorschlaegen - Essensplanung und Wochenbudget mit Sportbonus - Einkaufslisten mit Maerkten und Laufweg-Sortierung - Hellfireclub mit Aktivitaetspunkten und gemeinsamer Aktivitaetsplanung - Wearables-Vorbereitung (Fitbit / Google Fit / Health Connect) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.8 KiB
SQL
69 lines
2.8 KiB
SQL
CREATE TYPE "public"."tenant_user_status" AS ENUM('invited', 'active', 'suspended');--> statement-breakpoint
|
|
CREATE TABLE "member_user_links" (
|
|
"id" text PRIMARY KEY NOT NULL,
|
|
"tenantId" text NOT NULL,
|
|
"memberId" text NOT NULL,
|
|
"userId" text NOT NULL,
|
|
"createdAt" timestamp DEFAULT now() NOT NULL,
|
|
"updatedAt" timestamp DEFAULT now() NOT NULL
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE TABLE "tenant_users" (
|
|
"id" text PRIMARY KEY NOT NULL,
|
|
"tenantId" text NOT NULL,
|
|
"userId" text NOT NULL,
|
|
"status" "tenant_user_status" DEFAULT 'invited' NOT NULL,
|
|
"invitedAt" timestamp DEFAULT now() NOT NULL,
|
|
"activatedAt" timestamp,
|
|
"suspendedAt" timestamp,
|
|
"createdAt" timestamp DEFAULT now() NOT NULL,
|
|
"updatedAt" timestamp DEFAULT now() NOT NULL
|
|
);
|
|
--> statement-breakpoint
|
|
ALTER TABLE "member_user_links" ADD CONSTRAINT "member_user_links_tenantId_tenants_id_fk" FOREIGN KEY ("tenantId") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
ALTER TABLE "member_user_links" ADD CONSTRAINT "member_user_links_memberId_members_id_fk" FOREIGN KEY ("memberId") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
ALTER TABLE "member_user_links" ADD CONSTRAINT "member_user_links_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
ALTER TABLE "tenant_users" ADD CONSTRAINT "tenant_users_tenantId_tenants_id_fk" FOREIGN KEY ("tenantId") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
ALTER TABLE "tenant_users" ADD CONSTRAINT "tenant_users_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "member_user_links_tenant_member_unique" ON "member_user_links" USING btree ("tenantId","memberId");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "member_user_links_tenant_user_unique" ON "member_user_links" USING btree ("tenantId","userId");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "tenant_users_tenant_user_unique" ON "tenant_users" USING btree ("tenantId","userId");--> statement-breakpoint
|
|
|
|
INSERT INTO "tenant_users" (
|
|
"id",
|
|
"tenantId",
|
|
"userId",
|
|
"status",
|
|
"activatedAt",
|
|
"createdAt",
|
|
"updatedAt"
|
|
)
|
|
SELECT
|
|
concat('tu_', md5(tm."tenantId" || ':' || tm."userId")),
|
|
tm."tenantId",
|
|
tm."userId",
|
|
'active'::"tenant_user_status",
|
|
now(),
|
|
tm."createdAt",
|
|
now()
|
|
FROM "tenant_members" tm
|
|
ON CONFLICT ("tenantId","userId") DO NOTHING;--> statement-breakpoint
|
|
|
|
INSERT INTO "member_user_links" (
|
|
"id",
|
|
"tenantId",
|
|
"memberId",
|
|
"userId",
|
|
"createdAt",
|
|
"updatedAt"
|
|
)
|
|
SELECT
|
|
concat('mul_', md5(m."tenantId" || ':' || m."id")),
|
|
m."tenantId",
|
|
m."id",
|
|
m."userId",
|
|
m."createdAt",
|
|
now()
|
|
FROM "members" m
|
|
WHERE m."userId" IS NOT NULL
|
|
ON CONFLICT ("tenantId","memberId") DO NOTHING;
|