migrate from hono

This commit is contained in:
JCEEE
2026-08-17 07:41:26 +01:00
parent e2b99c45b6
commit 5204e7bdbc
56 changed files with 1815 additions and 3357 deletions
+3 -4
View File
@@ -1,5 +1,4 @@
// Single source of truth for the three service ports (dev/build-time only,
// used by the Hono proxy). Runtime URLs are set via env (see proxy/src/env.ts).
// Single source of truth for service ports (dev/build-time only). Runtime URLs
// come from env (see frontend/src/env.ts).
export const FRONTEND_PORT = "2080";
export const PROXY_PORT = "3456";
export const PB_PORT = "8090";
export const PB_PORT = "8090";
+83 -16
View File
@@ -69,20 +69,43 @@ export function rel(name: string, collectionId: string, required = false): Field
};
}
// ── Per-user access rules ──
// The app writes directly to PB as the authenticated user (their own token),
// so PB enforces famId scoping instead of running everything as superuser.
// Only genuinely privileged app-level actions (signup, OTP join, member
// creation, superuser dashboard, CRON/webhook) use the superuser client.
//
// Admin-only collections require role='parent'; child-accessible collections
// (completions toggle, reward claim, chat) are scoped by famId alone.
export const RULE_PARENT_WRITE =
"@request.body.famId = @request.auth.famId && @request.auth.role = 'parent'";
export const RULE_PARENT_SCOPED =
"famId = @request.auth.famId && @request.auth.role = 'parent'";
export const RULE_FAM_WRITE = "@request.body.famId = @request.auth.famId";
export const RULE_FAM_SCOPED = "famId = @request.auth.famId";
// fams has no self-referencing famId field; its record id IS the famId.
export const RULE_OWN_FAM = "id = @request.auth.famId";
// ── Collection builder ──
export function col(
name: string,
fields: FieldDef[],
rules: { listRule?: string | null; viewRule?: string | null } = {},
rules: {
listRule?: string | null;
viewRule?: string | null;
createRule?: string | null;
updateRule?: string | null;
deleteRule?: string | null;
} = {},
): (ids: Record<string, string>) => CollectionDef {
return (ids) => ({
name,
type: "base",
listRule: rules.listRule ?? "",
viewRule: rules.viewRule ?? "",
createRule: null,
updateRule: null,
deleteRule: null,
createRule: rules.createRule ?? null,
updateRule: rules.updateRule ?? null,
deleteRule: rules.deleteRule ?? null,
fields,
});
}
@@ -107,7 +130,7 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
jsonField("featureFlags"),
jsonField("seasons"),
],
{ listRule: null, viewRule: null },
{ listRule: RULE_OWN_FAM, viewRule: RULE_OWN_FAM, updateRule: RULE_OWN_FAM },
)(ids),
},
{
@@ -124,12 +147,20 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
text("rewardValue", true),
number("criteriaValue"),
select("period", ["schedule", "daily", "weekly", "monthly"]),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "settings",
build: (ids) =>
col("settings", [rel("famId", ids.fams, true), text("webhookUrl")])(ids),
col("settings", [rel("famId", ids.fams, true), text("webhookUrl")], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "chore_templates",
@@ -141,7 +172,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
select("defaultFrequency", ["daily", "weekly"], true),
select("defaultType", ["points", "money"], true),
number("defaultValue", true),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "bonus_configs",
@@ -159,7 +194,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
rel("memberId", ids.users),
select("period", ["schedule", "daily", "weekly", "monthly"]),
select("status", ["active", "completed"], true),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "weekly_history",
@@ -172,7 +211,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
number("moneyEarned"),
number("choresCompleted"),
number("bonusEarned"),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "seasons",
@@ -184,7 +227,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
bool("active"),
date("autoDisable"),
date("autoStart"),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "messages",
@@ -199,7 +246,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
// Explicit createdAt: PB 0.39 does NOT auto-add createdAt to
// API-created collections (0.25 did). Chat filters/sorts on it.
date("createdAt"),
])(ids),
], {
createRule: RULE_FAM_WRITE,
updateRule: RULE_FAM_SCOPED,
deleteRule: RULE_FAM_SCOPED,
})(ids),
},
{
name: "chat_typing",
@@ -211,7 +262,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
text("authorName", true),
text("authorColor"),
bool("typing"),
])(ids),
], {
createRule: RULE_FAM_WRITE,
updateRule: RULE_FAM_SCOPED,
deleteRule: RULE_FAM_SCOPED,
})(ids),
},
{
name: "rewards",
@@ -229,7 +284,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
date("claimedAt"),
date("requestedAt"),
text("date"),
])(ids),
], {
createRule: RULE_FAM_WRITE,
updateRule: RULE_FAM_SCOPED,
deleteRule: RULE_FAM_SCOPED,
})(ids),
},
{
name: "assigned_chores",
@@ -243,7 +302,11 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
number("value", true),
text("customName"),
jsonField("seasonIds"),
])(ids),
], {
createRule: RULE_PARENT_WRITE,
updateRule: RULE_PARENT_SCOPED,
deleteRule: RULE_PARENT_SCOPED,
})(ids),
},
{
name: "completions",
@@ -254,6 +317,10 @@ export const SCHEMA_PLAN: CollectionPlanEntry[] = [
rel("assignedChoreId", ids.assigned_chores, true),
date("date"),
date("completedAt"),
])(ids),
], {
createRule: RULE_FAM_WRITE,
updateRule: RULE_FAM_SCOPED,
deleteRule: RULE_FAM_SCOPED,
})(ids),
},
];