feat(admin): category SEO fields, Templates admin, safe project PATCH
Build backend images / build content-svc (push) Failing after 21s
Build backend images / build file-svc (push) Failing after 3m49s
Build backend images / build gateway (push) Failing after 1m2s
Build backend images / build identity-svc (push) Failing after 1m1s
Build backend images / build notification-svc (push) Failing after 1m2s
Build backend images / build render-svc (push) Failing after 1m0s
Build backend images / build studio-svc (push) Failing after 58s

- categories/tags admin forms: add meta title/description/keywords, bot-follow,
  sort, is_active (backend already supported these)
- new Templates admin (/admin/templates): container CRUD with description,
  keywords, publishing, premium, primary mode, category/tag assignment, plus
  editable per-variant aspect & resolution
- content-svc: PATCH /v1/projects/{id} partial update so aspect/resolution edits
  never wipe render/colour data (SharedColorsSvg, RenderAepComp, Folder)
- admin resource proxy: add PATCH passthrough

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 14:26:44 +03:30
parent cd95ca2c6f
commit cf5dd4f195
10 changed files with 362 additions and 5 deletions
+1
View File
@@ -17,6 +17,7 @@ export default async function AdminLayout({
const t = await getTranslations("auto.appAdminLayout");
const links: { href: string; label: string }[] = [
{ href: "/admin/categories", label: t("categories") },
{ href: "/admin/templates", label: t("templates") },
{ href: "/admin/tags", label: t("tags") },
{ href: "/admin/fonts", label: t("fonts") },
{ href: "/admin/blogs", label: t("blogs") },
@@ -0,0 +1,7 @@
"use client";
import { TemplatesAdmin } from "@/components/admin/TemplatesAdmin";
export default function Page() {
return <TemplatesAdmin />;
}
@@ -19,7 +19,7 @@ export const dynamic = "force-dynamic";
async function forward(
req: NextRequest,
path: string[],
method: "GET" | "POST" | "PUT" | "DELETE"
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
): Promise<NextResponse> {
const token = await getAccessToken();
if (!token) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
@@ -37,7 +37,7 @@ async function forward(
const gwPath = `/v1/${joined}${path.length === 1 && method === "GET" ? "/" : ""}${search}`;
let body: string | undefined;
if (method === "POST" || method === "PUT") {
if (method === "POST" || method === "PUT" || method === "PATCH") {
const json = await req.json().catch(() => ({}));
body = JSON.stringify(json);
}
@@ -91,6 +91,9 @@ export async function POST(req: NextRequest, ctx: { params: { path: string[] } }
export async function PUT(req: NextRequest, ctx: { params: { path: string[] } }) {
return forward(req, ctx.params.path, "PUT");
}
export async function PATCH(req: NextRequest, ctx: { params: { path: string[] } }) {
return forward(req, ctx.params.path, "PATCH");
}
export async function DELETE(req: NextRequest, ctx: { params: { path: string[] } }) {
return forward(req, ctx.params.path, "DELETE");
}