fix: nextjs primary group + auto-create asset folders on entity create
Deploy to VPS / deploy (push) Has been cancelled

THREE INTERLOCKING FIXES so editors stop hitting permission walls.

1) DOCKERFILE — gid 65533 (nogroup) on uploaded files
The container was creating files as 1001:65533 because Alpine's
`adduser --system --uid 1001 nextjs` doesn't set a primary group.
Files written through /api/assets ended up with `nogroup` ownership,
which surprised host sysadmins and made `chown -R 1001:1001` revert
on each fresh container start.

Fix: `adduser --system --uid 1001 --ingroup nodejs nextjs`. Now
every file written by the container is 1001:1001 (nextjs:nodejs),
matching the host conventions and the existing chown automation.

2) ENTRYPOINT — recursively normalise existing files
The recursive chown in scripts/docker-entrypoint.sh now sweeps every
subfolder of /app/public/branding|footage|applications|cases|news|
parts|operations-inbox|heritage on each container start, fixing any
files that previously slipped through with the wrong group. Single
fast pass, idempotent. Adds /app/public/heritage to the list (was
missing).

3) AUTO-CREATE ASSET BUCKETS on entity create
The big editor UX win: when an admin creates a Case (GlobalNode), an
Application or a News article in HQ Command, the server now also
mkdir's the well-known asset subfolders for that entity. So after
creating "Acme Industries" as a case, the editor immediately gets
/public/cases/acme-industries/{videos,renders,gallery,datasheet,models}
ready — no more "EACCES because the dir wasn't created" gotcha
when they upload their first video.

Implementation:
- src/lib/assetFolders.ts: typed helper with per-scope bucket lists
  + a titleToSlug helper that mirrors the front-end's slugger so the
  folder name matches what ApplicationClient expects when rendering
  /cases/<slug>/videos/<file>.
- network/actions.ts: createNode -> ensureAssetFolders("cases", slug).
  Plus a new server action ensureNodeAssetFolders(id) so the editor
  can fix existing nodes without recreating them (one-click "Repair").
- news/actions.ts: createNewsArticle -> ensureAssetFolders("news",slug)
- applications/actions.ts: createApplication -> ensureAssetFolders(...)

DEPLOY (David)
  cd /opt/flux-srl
  git pull
  docker compose up -d --build app
  # The entrypoint will fix existing 1001:65533 files automatically
  # as the container boots — no manual chown needed.
This commit is contained in:
2026-05-05 08:01:45 -05:00
parent aa95be45d0
commit 9b28f8ffaf
6 changed files with 120 additions and 13 deletions
@@ -7,6 +7,7 @@ import { revalidatePath } from "next/cache";
import { unstable_noStore as noStore } from "next/cache"; // 🔥 ANTI-CACHÉ
// 🔥 IMPORTAMOS EL TRADUCTOR DE IA
import { translateContentForCMS } from "@/lib/aiTranslator";
import { ensureAssetFolders } from "@/lib/assetFolders";
const generateSlug = (title: string) => {
return title.toLowerCase().trim().replace(/[^\w\s-]/g, '').replace(/[\s_-]+/g, '-').replace(/^-+|-+$/g, '');
@@ -70,12 +71,16 @@ export async function createApplication(formData: FormData) {
translationsJson
}
});
// Pre-create the asset bucket folders so the editor's first upload
// (videos, renders, gallery, datasheet) lands somewhere that exists.
ensureAssetFolders("applications", slug);
revalidatePath("/hq-command/dashboard/applications");
revalidatePath("/[locale]", "layout");
return { success: true };
} catch (error) {
return { error: "Failed to create application. Title might already exist." };
} catch (error) {
return { error: "Failed to create application. Title might already exist." };
}
}