Seed template recommendations

This commit is contained in:
adilallo
2026-05-23 19:35:38 -06:00
parent 6d0335a86a
commit f0e193746c
6 changed files with 45 additions and 18 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"title": "Community Rule",
"author": "MEDLab",
"description": "Community governance and rule-building app",
"version": "0.1.7",
"version": "0.1.8",
"httpPort": 3000,
"healthCheckPath": "/api/health",
"memoryLimit": 805306368,
+3
View File
@@ -48,6 +48,9 @@ COPY --from=builder --chown=node:node /app/public ./public
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
COPY --from=builder --chown=node:node /app/prisma ./prisma
# Facet/template seed JSON — NOT under /app/data (localstorage mount overlays that).
COPY --from=builder --chown=node:node /app/data ./seed-data
ENV SEED_DATA_DIR=/app/seed-data
# Prisma CLI (devDependency) is not in the Next.js standalone trace. Install
# globally in the runner so start.sh can run `prisma migrate deploy`.
+12
View File
@@ -390,6 +390,16 @@ production env vars, and verify the vertical slice before apex cutover
custom-from allowed — §3).
5. **Confirm the app is running** in the Cloudron dashboard (Logs tab). Look
for a clean `prisma migrate deploy` and Next.js listening on port 3000.
6. **Seed facet data (one-time per environment)** — templates + `MethodFacet`
rows for create-flow "Recommended" tags are **not** applied at boot. After
first install (or when recommendations return all-zero scores), run:
```bash
cloudron exec --app staging.communityrule.info -- \
node prisma/seed.bundle.cjs
```
JSON lives at `/app/seed-data/` (`SEED_DATA_DIR`); do not use `/app/data`
(Cloudron localstorage overwrites it). Re-run after deploy is safe
(idempotent upserts / per-section swaps).
**Smoke checklist (acceptance):**
@@ -419,6 +429,8 @@ steps below are still required.
| Magic link not sent | Mail addon or `SMTP_FROM` | Cloudron mail logs; `CLOUDRON_MAIL_SMTP_*` vars |
| Upload `server_misconfigured` | `UPLOAD_ROOT` unset | Set to `/app/data/uploads` (§3) |
| Container crash on start | Migration failure | App logs around `prisma migrate deploy` |
| No "Recommended" on method cards | `MethodFacet` not seeded | §10 step 6; API should return `matches.score > 0` for some methods when `facet.*` set |
| `seed.bundle.cjs` ENOENT on `/app/data/...` | Old image without `/app/seed-data` | Deploy ≥ 0.1.8; JSON is at `SEED_DATA_DIR=/app/seed-data` |
**Done when:** all smoke checklist items pass. Then proceed to soft-launch
(§5 phase 2) and, when ready, [CR-99](https://linear.app/community-rule/issue/CR-99/backend-cloudron-production-install-apex-cutover)
+3 -6
View File
@@ -10,17 +10,14 @@ import {
resolveFacetMatch,
sectionFacetsSchema,
} from "../../lib/server/validation/methodFacetsSchemas";
// Bundled seed runs from repo root (`process.cwd()`); __dirname breaks under esbuild.
const REPO_ROOT = process.cwd();
const DATA_DIR = path.join(REPO_ROOT, "data", "create", "customRule");
import { methodFacetsJsonDir } from "./seedDataPaths";
/**
* Reads + Zod-validates `data/create/customRule/<section>.json`.
* Throws on schema failures so the seed aborts before any DB write.
*/
async function loadSectionFacets(section: SectionId) {
const file = path.join(DATA_DIR, `${section}.json`);
const file = path.join(methodFacetsJsonDir(), `${section}.json`);
const raw = await readFile(file, "utf8");
const parsed = JSON.parse(raw) as unknown;
const result = sectionFacetsSchema.safeParse(parsed);
@@ -33,7 +30,7 @@ async function loadSectionFacets(section: SectionId) {
}
async function loadFacetGroups() {
const file = path.join(DATA_DIR, "_facetGroups.json");
const file = path.join(methodFacetsJsonDir(), "_facetGroups.json");
const raw = await readFile(file, "utf8");
const parsed = JSON.parse(raw) as unknown;
const result = facetGroupsFileSchema.safeParse(parsed);
+22
View File
@@ -0,0 +1,22 @@
import path from "node:path";
/**
* Root for committed seed JSON (`data/` in dev; `/app/seed-data` on Cloudron).
*
* Cloudron's localstorage addon mounts `/app/data` at runtime, so facet JSON
* must not live there. The Dockerfile copies `data/` → `/app/seed-data` and
* sets `SEED_DATA_DIR=/app/seed-data`.
*/
export function seedDataRoot(): string {
const override = process.env.SEED_DATA_DIR?.trim();
if (override) return override;
return path.join(process.cwd(), "data");
}
export function methodFacetsJsonDir(): string {
return path.join(seedDataRoot(), "create", "customRule");
}
export function templateFacetJsonPath(): string {
return path.join(seedDataRoot(), "templates", "templateFacet.json");
}
+4 -11
View File
@@ -1,16 +1,8 @@
import { readFile } from "node:fs/promises";
import path from "node:path";
import type { PrismaClient } from "@prisma/client";
import { FACET_GROUP_IDS } from "../../lib/server/validation/methodFacetsSchemas";
import { templateFacetFileSchema } from "../../lib/server/validation/templateFacetSchema";
const REPO_ROOT = process.cwd();
const TEMPLATE_FACET_FILE = path.join(
REPO_ROOT,
"data",
"templates",
"templateFacet.json",
);
import { templateFacetJsonPath } from "./seedDataPaths";
type TemplateFacetRow = {
templateSlug: string;
@@ -20,12 +12,13 @@ type TemplateFacetRow = {
};
async function loadTemplateFacets() {
const raw = await readFile(TEMPLATE_FACET_FILE, "utf8");
const templateFacetFile = templateFacetJsonPath();
const raw = await readFile(templateFacetFile, "utf8");
const parsed = JSON.parse(raw) as unknown;
const result = templateFacetFileSchema.safeParse(parsed);
if (!result.success) {
throw new Error(
`Invalid template facet file ${TEMPLATE_FACET_FILE}: ${JSON.stringify(
`Invalid template facet file ${templateFacetFile}: ${JSON.stringify(
result.error.flatten(),
null,
2,