Tonne of debugging - getting ready for the production builds

This commit is contained in:
2026-02-16 10:44:18 -07:00
parent a77306fac2
commit 7895ce683e
1367 changed files with 404191 additions and 2005 deletions

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export { router as blocksRouter };
//# sourceMappingURL=blocks.routes.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blocks.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/blocks.routes.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA4ExB,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,CAAC"}

69
api/dist/modules/pages/blocks.routes.js vendored Normal file
View File

@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.blocksRouter = void 0;
const express_1 = require("express");
const client_1 = require("@prisma/client");
const blocks_service_1 = require("./blocks.service");
const pages_schemas_1 = require("./pages.schemas");
const validate_1 = require("../../middleware/validate");
const auth_middleware_1 = require("../../middleware/auth.middleware");
const rbac_middleware_1 = require("../../middleware/rbac.middleware");
const ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.INFLUENCE_ADMIN, client_1.UserRole.MAP_ADMIN];
const router = (0, express_1.Router)();
exports.blocksRouter = router;
router.use(auth_middleware_1.authenticate);
router.use((0, rbac_middleware_1.requireRole)(...ADMIN_ROLES));
// GET /api/page-blocks — list all blocks
router.get('/', (0, validate_1.validate)(pages_schemas_1.listPageBlocksSchema, 'query'), async (req, res, next) => {
try {
const blocks = await blocks_service_1.blocksService.findAll(req.query);
res.json(blocks);
}
catch (err) {
next(err);
}
});
// GET /api/page-blocks/:id — get single block
router.get('/:id', async (req, res, next) => {
try {
const id = req.params.id;
const block = await blocks_service_1.blocksService.findById(id);
res.json(block);
}
catch (err) {
next(err);
}
});
// POST /api/page-blocks — create block
router.post('/', (0, validate_1.validate)(pages_schemas_1.createPageBlockSchema), async (req, res, next) => {
try {
const block = await blocks_service_1.blocksService.create(req.body);
res.status(201).json(block);
}
catch (err) {
next(err);
}
});
// PUT /api/page-blocks/:id — update block
router.put('/:id', (0, validate_1.validate)(pages_schemas_1.updatePageBlockSchema), async (req, res, next) => {
try {
const id = req.params.id;
const block = await blocks_service_1.blocksService.update(id, req.body);
res.json(block);
}
catch (err) {
next(err);
}
});
// DELETE /api/page-blocks/:id — delete block
router.delete('/:id', async (req, res, next) => {
try {
const id = req.params.id;
await blocks_service_1.blocksService.delete(id);
res.status(204).send();
}
catch (err) {
next(err);
}
});
//# sourceMappingURL=blocks.routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blocks.routes.js","sourceRoot":"","sources":["../../../src/modules/pages/blocks.routes.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,54 @@
import { Prisma } from '@prisma/client';
import type { CreatePageBlockInput, UpdatePageBlockInput, ListPageBlocksInput } from './pages.schemas';
export declare const blocksService: {
findAll(filters: ListPageBlocksInput): Promise<{
type: string;
id: string;
createdAt: Date;
updatedAt: Date;
schema: Prisma.JsonValue;
category: string | null;
label: string;
sortOrder: number;
defaults: Prisma.JsonValue;
thumbnail: string | null;
}[]>;
findById(id: string): Promise<{
type: string;
id: string;
createdAt: Date;
updatedAt: Date;
schema: Prisma.JsonValue;
category: string | null;
label: string;
sortOrder: number;
defaults: Prisma.JsonValue;
thumbnail: string | null;
}>;
create(data: CreatePageBlockInput): Promise<{
type: string;
id: string;
createdAt: Date;
updatedAt: Date;
schema: Prisma.JsonValue;
category: string | null;
label: string;
sortOrder: number;
defaults: Prisma.JsonValue;
thumbnail: string | null;
}>;
update(id: string, data: UpdatePageBlockInput): Promise<{
type: string;
id: string;
createdAt: Date;
updatedAt: Date;
schema: Prisma.JsonValue;
category: string | null;
label: string;
sortOrder: number;
defaults: Prisma.JsonValue;
thumbnail: string | null;
}>;
delete(id: string): Promise<void>;
};
//# sourceMappingURL=blocks.service.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blocks.service.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/blocks.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAevG,eAAO,MAAM,aAAa;qBACD,mBAAmB;;;;;;;;;;;;iBAcvB,MAAM;;;;;;;;;;;;iBAaN,oBAAoB;;;;;;;;;;;;eAWtB,MAAM,QAAQ,oBAAoB;;;;;;;;;;;;eAsBlC,MAAM;CAQxB,CAAC"}

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.blocksService = void 0;
const database_1 = require("../../config/database");
const error_handler_1 = require("../../middleware/error-handler");
const blockSelect = {
id: true,
type: true,
label: true,
schema: true,
defaults: true,
thumbnail: true,
category: true,
sortOrder: true,
createdAt: true,
updatedAt: true,
};
exports.blocksService = {
async findAll(filters) {
const where = {};
if (filters.category) {
where.category = filters.category;
}
return database_1.prisma.pageBlock.findMany({
where,
select: blockSelect,
orderBy: { sortOrder: 'asc' },
});
},
async findById(id) {
const block = await database_1.prisma.pageBlock.findUnique({
where: { id },
select: blockSelect,
});
if (!block) {
throw new error_handler_1.AppError(404, 'Page block not found', 'BLOCK_NOT_FOUND');
}
return block;
},
async create(data) {
return database_1.prisma.pageBlock.create({
data: {
...data,
schema: data.schema,
defaults: data.defaults,
},
select: blockSelect,
});
},
async update(id, data) {
const existing = await database_1.prisma.pageBlock.findUnique({ where: { id } });
if (!existing) {
throw new error_handler_1.AppError(404, 'Page block not found', 'BLOCK_NOT_FOUND');
}
const updateData = { ...data };
if (data.schema !== undefined) {
updateData.schema = data.schema;
}
if (data.defaults !== undefined) {
updateData.defaults = data.defaults;
}
return database_1.prisma.pageBlock.update({
where: { id },
data: updateData,
select: blockSelect,
});
},
async delete(id) {
const existing = await database_1.prisma.pageBlock.findUnique({ where: { id } });
if (!existing) {
throw new error_handler_1.AppError(404, 'Page block not found', 'BLOCK_NOT_FOUND');
}
await database_1.prisma.pageBlock.delete({ where: { id } });
},
};
//# sourceMappingURL=blocks.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"blocks.service.js","sourceRoot":"","sources":["../../../src/modules/pages/blocks.service.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export { router as pagesAdminRouter };
//# sourceMappingURL=pages-admin.routes.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages-admin.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/pages-admin.routes.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAsGxB,OAAO,EAAE,MAAM,IAAI,gBAAgB,EAAE,CAAC"}

View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pagesAdminRouter = void 0;
const express_1 = require("express");
const client_1 = require("@prisma/client");
const pages_service_1 = require("./pages.service");
const pages_schemas_1 = require("./pages.schemas");
const validate_1 = require("../../middleware/validate");
const auth_middleware_1 = require("../../middleware/auth.middleware");
const rbac_middleware_1 = require("../../middleware/rbac.middleware");
const ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.INFLUENCE_ADMIN, client_1.UserRole.MAP_ADMIN];
const router = (0, express_1.Router)();
exports.pagesAdminRouter = router;
router.use(auth_middleware_1.authenticate);
router.use((0, rbac_middleware_1.requireRole)(...ADMIN_ROLES));
// POST /api/pages/sync — sync MkDocs overrides (must be before /:id routes)
router.post('/sync', async (_req, res, next) => {
try {
const result = await pages_service_1.pagesService.syncOverrides();
res.json(result);
}
catch (err) {
next(err);
}
});
// POST /api/pages/validate — validate and repair MkDocs exports
router.post('/validate', async (_req, res, next) => {
try {
const result = await pages_service_1.pagesService.validateExports();
res.json(result);
}
catch (err) {
next(err);
}
});
// GET /api/pages — list landing pages with pagination/filters
router.get('/', (0, validate_1.validate)(pages_schemas_1.listLandingPagesSchema, 'query'), async (req, res, next) => {
try {
const result = await pages_service_1.pagesService.findAll(req.query);
res.json(result);
}
catch (err) {
next(err);
}
});
// GET /api/pages/:id — get single landing page
router.get('/:id', async (req, res, next) => {
try {
const id = req.params.id;
const page = await pages_service_1.pagesService.findById(id);
res.json(page);
}
catch (err) {
next(err);
}
});
// POST /api/pages — create landing page
router.post('/', (0, validate_1.validate)(pages_schemas_1.createLandingPageSchema), async (req, res, next) => {
try {
const page = await pages_service_1.pagesService.create(req.body);
res.status(201).json(page);
}
catch (err) {
next(err);
}
});
// PUT /api/pages/:id — update landing page
router.put('/:id', (0, validate_1.validate)(pages_schemas_1.updateLandingPageSchema), async (req, res, next) => {
try {
const id = req.params.id;
const page = await pages_service_1.pagesService.update(id, req.body);
res.json(page);
}
catch (err) {
next(err);
}
});
// DELETE /api/pages/:id — delete landing page
router.delete('/:id', async (req, res, next) => {
try {
const id = req.params.id;
await pages_service_1.pagesService.delete(id);
res.status(204).send();
}
catch (err) {
next(err);
}
});
//# sourceMappingURL=pages-admin.routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages-admin.routes.js","sourceRoot":"","sources":["../../../src/modules/pages/pages-admin.routes.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export { router as pagesPublicRouter };
//# sourceMappingURL=pages-public.routes.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages-public.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/pages-public.routes.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAgBxB,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,CAAC"}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pagesPublicRouter = void 0;
const express_1 = require("express");
const pages_service_1 = require("./pages.service");
const router = (0, express_1.Router)();
exports.pagesPublicRouter = router;
// GET /api/pages/:slug/view — get published page by slug (public)
router.get('/:slug/view', async (req, res, next) => {
try {
const slug = req.params.slug;
const page = await pages_service_1.pagesService.findBySlugPublic(slug);
res.json(page);
}
catch (err) {
next(err);
}
});
//# sourceMappingURL=pages-public.routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages-public.routes.js","sourceRoot":"","sources":["../../../src/modules/pages/pages-public.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,mDAA+C;AAE/C,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAgBL,mCAAiB;AAdpC,kEAAkE;AAClE,MAAM,CAAC,GAAG,CACR,aAAa,EACb,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAc,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,4BAAY,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC"}

View File

@@ -0,0 +1,186 @@
import { z } from 'zod';
export declare const createLandingPageSchema: z.ZodObject<{
title: z.ZodString;
description: z.ZodOptional<z.ZodString>;
editorMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<["VISUAL", "CODE"]>>>;
blocks: z.ZodDefault<z.ZodOptional<z.ZodAny>>;
htmlOutput: z.ZodOptional<z.ZodString>;
cssOutput: z.ZodOptional<z.ZodString>;
mkdocsPath: z.ZodOptional<z.ZodString>;
mkdocsExportMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<["THEMED", "STANDALONE"]>>>;
mkdocsHideNav: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
mkdocsHideToc: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
mkdocsSkipExport: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
seoTitle: z.ZodOptional<z.ZodString>;
seoDescription: z.ZodOptional<z.ZodString>;
seoImage: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
title: string;
editorMode: "VISUAL" | "CODE";
mkdocsExportMode: "THEMED" | "STANDALONE";
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
blocks?: any;
description?: string | undefined;
htmlOutput?: string | undefined;
cssOutput?: string | undefined;
mkdocsPath?: string | undefined;
seoTitle?: string | undefined;
seoDescription?: string | undefined;
seoImage?: string | undefined;
}, {
title: string;
blocks?: any;
description?: string | undefined;
editorMode?: "VISUAL" | "CODE" | undefined;
htmlOutput?: string | undefined;
cssOutput?: string | undefined;
mkdocsPath?: string | undefined;
mkdocsExportMode?: "THEMED" | "STANDALONE" | undefined;
mkdocsHideNav?: boolean | undefined;
mkdocsHideToc?: boolean | undefined;
mkdocsSkipExport?: boolean | undefined;
published?: boolean | undefined;
seoTitle?: string | undefined;
seoDescription?: string | undefined;
seoImage?: string | undefined;
}>;
export declare const updateLandingPageSchema: z.ZodObject<{
title: z.ZodOptional<z.ZodString>;
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
editorMode: z.ZodOptional<z.ZodEnum<["VISUAL", "CODE"]>>;
blocks: z.ZodOptional<z.ZodAny>;
htmlOutput: z.ZodOptional<z.ZodNullable<z.ZodString>>;
cssOutput: z.ZodOptional<z.ZodNullable<z.ZodString>>;
mkdocsPath: z.ZodOptional<z.ZodNullable<z.ZodString>>;
mkdocsExportMode: z.ZodOptional<z.ZodEnum<["THEMED", "STANDALONE"]>>;
mkdocsHideNav: z.ZodOptional<z.ZodBoolean>;
mkdocsHideToc: z.ZodOptional<z.ZodBoolean>;
mkdocsSkipExport: z.ZodOptional<z.ZodBoolean>;
published: z.ZodOptional<z.ZodBoolean>;
seoTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
seoDescription: z.ZodOptional<z.ZodNullable<z.ZodString>>;
seoImage: z.ZodOptional<z.ZodNullable<z.ZodString>>;
}, "strip", z.ZodTypeAny, {
blocks?: any;
title?: string | undefined;
description?: string | null | undefined;
editorMode?: "VISUAL" | "CODE" | undefined;
htmlOutput?: string | null | undefined;
cssOutput?: string | null | undefined;
mkdocsPath?: string | null | undefined;
mkdocsExportMode?: "THEMED" | "STANDALONE" | undefined;
mkdocsHideNav?: boolean | undefined;
mkdocsHideToc?: boolean | undefined;
mkdocsSkipExport?: boolean | undefined;
published?: boolean | undefined;
seoTitle?: string | null | undefined;
seoDescription?: string | null | undefined;
seoImage?: string | null | undefined;
}, {
blocks?: any;
title?: string | undefined;
description?: string | null | undefined;
editorMode?: "VISUAL" | "CODE" | undefined;
htmlOutput?: string | null | undefined;
cssOutput?: string | null | undefined;
mkdocsPath?: string | null | undefined;
mkdocsExportMode?: "THEMED" | "STANDALONE" | undefined;
mkdocsHideNav?: boolean | undefined;
mkdocsHideToc?: boolean | undefined;
mkdocsSkipExport?: boolean | undefined;
published?: boolean | undefined;
seoTitle?: string | null | undefined;
seoDescription?: string | null | undefined;
seoImage?: string | null | undefined;
}>;
export declare const listLandingPagesSchema: z.ZodObject<{
page: z.ZodDefault<z.ZodNumber>;
limit: z.ZodDefault<z.ZodNumber>;
search: z.ZodOptional<z.ZodString>;
published: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
}, "strip", z.ZodTypeAny, {
limit: number;
page: number;
search?: string | undefined;
published?: "true" | "false" | undefined;
}, {
search?: string | undefined;
limit?: number | undefined;
page?: number | undefined;
published?: "true" | "false" | undefined;
}>;
export declare const createPageBlockSchema: z.ZodObject<{
type: z.ZodString;
label: z.ZodString;
schema: z.ZodDefault<z.ZodOptional<z.ZodAny>>;
defaults: z.ZodDefault<z.ZodOptional<z.ZodAny>>;
thumbnail: z.ZodOptional<z.ZodString>;
category: z.ZodOptional<z.ZodString>;
sortOrder: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
}, "strip", z.ZodTypeAny, {
type: string;
label: string;
sortOrder: number;
schema?: any;
category?: string | undefined;
defaults?: any;
thumbnail?: string | undefined;
}, {
type: string;
label: string;
schema?: any;
category?: string | undefined;
sortOrder?: number | undefined;
defaults?: any;
thumbnail?: string | undefined;
}>;
export declare const updatePageBlockSchema: z.ZodObject<{
type: z.ZodOptional<z.ZodString>;
label: z.ZodOptional<z.ZodString>;
schema: z.ZodOptional<z.ZodAny>;
defaults: z.ZodOptional<z.ZodAny>;
thumbnail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
category: z.ZodOptional<z.ZodNullable<z.ZodString>>;
sortOrder: z.ZodOptional<z.ZodNumber>;
}, "strip", z.ZodTypeAny, {
type?: string | undefined;
schema?: any;
category?: string | null | undefined;
label?: string | undefined;
sortOrder?: number | undefined;
defaults?: any;
thumbnail?: string | null | undefined;
}, {
type?: string | undefined;
schema?: any;
category?: string | null | undefined;
label?: string | undefined;
sortOrder?: number | undefined;
defaults?: any;
thumbnail?: string | null | undefined;
}>;
export declare const listPageBlocksSchema: z.ZodObject<{
category: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
category?: string | undefined;
}, {
category?: string | undefined;
}>;
export declare const pageIdSchema: z.ZodObject<{
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
id: string;
}, {
id: string;
}>;
export type CreateLandingPageInput = z.infer<typeof createLandingPageSchema>;
export type UpdateLandingPageInput = z.infer<typeof updateLandingPageSchema>;
export type ListLandingPagesInput = z.infer<typeof listLandingPagesSchema>;
export type CreatePageBlockInput = z.infer<typeof createPageBlockSchema>;
export type UpdatePageBlockInput = z.infer<typeof updatePageBlockSchema>;
export type ListPageBlocksInput = z.infer<typeof listPageBlocksSchema>;
//# sourceMappingURL=pages.schemas.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/pages.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBlC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;EAKjC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAQhC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAQhC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;EAE/B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;EAEvB,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC7E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC7E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC3E,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}

71
api/dist/modules/pages/pages.schemas.js vendored Normal file
View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pageIdSchema = exports.listPageBlocksSchema = exports.updatePageBlockSchema = exports.createPageBlockSchema = exports.listLandingPagesSchema = exports.updateLandingPageSchema = exports.createLandingPageSchema = void 0;
const zod_1 = require("zod");
const editorModeEnum = zod_1.z.enum(['VISUAL', 'CODE']);
const mkdocsExportModeEnum = zod_1.z.enum(['THEMED', 'STANDALONE']);
exports.createLandingPageSchema = zod_1.z.object({
title: zod_1.z.string().min(1, 'Title is required'),
description: zod_1.z.string().optional(),
editorMode: editorModeEnum.optional().default('VISUAL'),
blocks: zod_1.z.any().optional().default({}),
htmlOutput: zod_1.z.string().optional(),
cssOutput: zod_1.z.string().optional(),
mkdocsPath: zod_1.z.string().optional(),
mkdocsExportMode: mkdocsExportModeEnum.optional().default('THEMED'),
mkdocsHideNav: zod_1.z.boolean().optional().default(true),
mkdocsHideToc: zod_1.z.boolean().optional().default(true),
mkdocsSkipExport: zod_1.z.boolean().optional().default(false),
published: zod_1.z.boolean().optional().default(false),
seoTitle: zod_1.z.string().optional(),
seoDescription: zod_1.z.string().optional(),
seoImage: zod_1.z.string().optional(),
});
exports.updateLandingPageSchema = zod_1.z.object({
title: zod_1.z.string().min(1).optional(),
description: zod_1.z.string().nullable().optional(),
editorMode: editorModeEnum.optional(),
blocks: zod_1.z.any().optional(),
htmlOutput: zod_1.z.string().nullable().optional(),
cssOutput: zod_1.z.string().nullable().optional(),
mkdocsPath: zod_1.z.string().nullable().optional(),
mkdocsExportMode: mkdocsExportModeEnum.optional(),
mkdocsHideNav: zod_1.z.boolean().optional(),
mkdocsHideToc: zod_1.z.boolean().optional(),
mkdocsSkipExport: zod_1.z.boolean().optional(),
published: zod_1.z.boolean().optional(),
seoTitle: zod_1.z.string().nullable().optional(),
seoDescription: zod_1.z.string().nullable().optional(),
seoImage: zod_1.z.string().nullable().optional(),
});
exports.listLandingPagesSchema = zod_1.z.object({
page: zod_1.z.coerce.number().int().positive().default(1),
limit: zod_1.z.coerce.number().int().positive().max(100).default(20),
search: zod_1.z.string().optional(),
published: zod_1.z.enum(['true', 'false']).optional(),
});
exports.createPageBlockSchema = zod_1.z.object({
type: zod_1.z.string().min(1, 'Type is required'),
label: zod_1.z.string().min(1, 'Label is required'),
schema: zod_1.z.any().optional().default({}),
defaults: zod_1.z.any().optional().default({}),
thumbnail: zod_1.z.string().optional(),
category: zod_1.z.string().optional(),
sortOrder: zod_1.z.number().int().optional().default(0),
});
exports.updatePageBlockSchema = zod_1.z.object({
type: zod_1.z.string().min(1).optional(),
label: zod_1.z.string().min(1).optional(),
schema: zod_1.z.any().optional(),
defaults: zod_1.z.any().optional(),
thumbnail: zod_1.z.string().nullable().optional(),
category: zod_1.z.string().nullable().optional(),
sortOrder: zod_1.z.number().int().optional(),
});
exports.listPageBlocksSchema = zod_1.z.object({
category: zod_1.z.string().optional(),
});
exports.pageIdSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
});
//# sourceMappingURL=pages.schemas.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages.schemas.js","sourceRoot":"","sources":["../../../src/modules/pages/pages.schemas.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,138 @@
import { Prisma } from '@prisma/client';
import type { CreateLandingPageInput, UpdateLandingPageInput, ListLandingPagesInput } from './pages.schemas';
export declare const pagesService: {
findAll(filters: ListLandingPagesInput): Promise<{
pages: {
id: string;
createdAt: Date;
updatedAt: Date;
blocks: Prisma.JsonValue;
title: string;
description: string | null;
slug: string;
editorMode: import(".prisma/client").$Enums.EditorMode;
htmlOutput: string | null;
cssOutput: string | null;
mkdocsPath: string | null;
mkdocsExportMode: import(".prisma/client").$Enums.MkdocsExportMode;
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
seoTitle: string | null;
seoDescription: string | null;
seoImage: string | null;
mkdocsStubPath: string | null;
}[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}>;
findById(id: string): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
blocks: Prisma.JsonValue;
title: string;
description: string | null;
slug: string;
editorMode: import(".prisma/client").$Enums.EditorMode;
htmlOutput: string | null;
cssOutput: string | null;
mkdocsPath: string | null;
mkdocsExportMode: import(".prisma/client").$Enums.MkdocsExportMode;
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
seoTitle: string | null;
seoDescription: string | null;
seoImage: string | null;
mkdocsStubPath: string | null;
}>;
findBySlugPublic(slug: string): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
blocks: Prisma.JsonValue;
title: string;
description: string | null;
slug: string;
editorMode: import(".prisma/client").$Enums.EditorMode;
htmlOutput: string | null;
cssOutput: string | null;
mkdocsPath: string | null;
mkdocsExportMode: import(".prisma/client").$Enums.MkdocsExportMode;
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
seoTitle: string | null;
seoDescription: string | null;
seoImage: string | null;
mkdocsStubPath: string | null;
}>;
create(data: CreateLandingPageInput): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
blocks: Prisma.JsonValue;
title: string;
description: string | null;
slug: string;
editorMode: import(".prisma/client").$Enums.EditorMode;
htmlOutput: string | null;
cssOutput: string | null;
mkdocsPath: string | null;
mkdocsExportMode: import(".prisma/client").$Enums.MkdocsExportMode;
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
seoTitle: string | null;
seoDescription: string | null;
seoImage: string | null;
mkdocsStubPath: string | null;
}>;
update(id: string, data: UpdateLandingPageInput): Promise<{
id: string;
createdAt: Date;
updatedAt: Date;
blocks: Prisma.JsonValue;
title: string;
description: string | null;
slug: string;
editorMode: import(".prisma/client").$Enums.EditorMode;
htmlOutput: string | null;
cssOutput: string | null;
mkdocsPath: string | null;
mkdocsExportMode: import(".prisma/client").$Enums.MkdocsExportMode;
mkdocsHideNav: boolean;
mkdocsHideToc: boolean;
mkdocsSkipExport: boolean;
published: boolean;
seoTitle: string | null;
seoDescription: string | null;
seoImage: string | null;
mkdocsStubPath: string | null;
}>;
delete(id: string): Promise<void>;
syncOverrides(): Promise<{
imported: number;
updated: number;
stubs: number;
}>;
validateExports(): Promise<{
validated: number;
repaired: number;
errors: Array<{
pageId: string;
slug: string;
error: string;
}>;
}>;
};
//# sourceMappingURL=pages.service.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"pages.service.d.ts","sourceRoot":"","sources":["../../../src/modules/pages/pages.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAOtE,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AA4Q7G,eAAO,MAAM,YAAY;qBACA,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuCzB,MAAM;;;;;;;;;;;;;;;;;;;;;;2BAaI,MAAM;;;;;;;;;;;;;;;;;;;;;;iBAahB,sBAAsB;;;;;;;;;;;;;;;;;;;;;;eAqBxB,MAAM,QAAQ,sBAAsB;;;;;;;;;;;;;;;;;;;;;;eA2EpC,MAAM;qBAcA,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;uBAmG3D,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CAqF1I,CAAC"}

554
api/dist/modules/pages/pages.service.js vendored Normal file
View File

@@ -0,0 +1,554 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pagesService = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const database_1 = require("../../config/database");
const env_1 = require("../../config/env");
const error_handler_1 = require("../../middleware/error-handler");
const logger_1 = require("../../utils/logger");
const landingPageSelect = {
id: true,
slug: true,
title: true,
description: true,
editorMode: true,
blocks: true,
htmlOutput: true,
cssOutput: true,
mkdocsPath: true,
mkdocsStubPath: true,
mkdocsExportMode: true,
mkdocsHideNav: true,
mkdocsHideToc: true,
mkdocsSkipExport: true,
published: true,
seoTitle: true,
seoDescription: true,
seoImage: true,
createdAt: true,
updatedAt: true,
};
function generateSlug(title) {
return title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 80);
}
async function resolveSlugCollision(slug, excludeId) {
let candidate = slug;
let suffix = 2;
while (true) {
const existing = await database_1.prisma.landingPage.findUnique({
where: { slug: candidate },
select: { id: true },
});
if (!existing || (excludeId && existing.id === excludeId)) {
return candidate;
}
candidate = `${slug}-${suffix}`;
suffix++;
}
}
const MKDOCS_OVERRIDES = path_1.default.join(env_1.env.MKDOCS_DOCS_PATH, 'overrides');
const MKDOCS_DOCS_ROOT = env_1.env.MKDOCS_DOCS_PATH;
function validateMkdocsPath(mkdocsPath) {
// Check for null bytes
if (mkdocsPath.includes('\0')) {
throw new error_handler_1.AppError(400, 'Invalid path: null byte detected', 'INVALID_MKDOCS_PATH');
}
// Normalize and check for traversal
const normalized = path_1.default.normalize(mkdocsPath);
if (normalized.includes('..') || path_1.default.isAbsolute(normalized)) {
throw new error_handler_1.AppError(400, 'Path traversal not allowed', 'INVALID_MKDOCS_PATH');
}
// Check for encoded traversal sequences
if (mkdocsPath.includes('%2e') || mkdocsPath.includes('%2E')) {
throw new error_handler_1.AppError(400, 'Encoded path traversal not allowed', 'INVALID_MKDOCS_PATH');
}
if (!mkdocsPath.endsWith('.html')) {
throw new error_handler_1.AppError(400, 'Path must end with .html', 'INVALID_MKDOCS_PATH');
}
}
function validateStubPath(stubPath) {
// Check for null bytes
if (stubPath.includes('\0')) {
throw new error_handler_1.AppError(400, 'Invalid path: null byte detected', 'INVALID_STUB_PATH');
}
// Normalize and check for traversal
const normalized = path_1.default.normalize(stubPath);
if (normalized.includes('..') || path_1.default.isAbsolute(normalized)) {
throw new error_handler_1.AppError(400, 'Path traversal not allowed', 'INVALID_STUB_PATH');
}
// Check for encoded traversal sequences
if (stubPath.includes('%2e') || stubPath.includes('%2E')) {
throw new error_handler_1.AppError(400, 'Encoded path traversal not allowed', 'INVALID_STUB_PATH');
}
}
function wrapInMaterialOverride(html, css) {
const styleBlock = css ? `<style>\n${css}\n</style>` : '';
return `{% extends "main.html" %}
{% block content %}
${styleBlock}
${html}
{% endblock %}
`;
}
function wrapInStandaloneDocument(html, css, title, description) {
const metaDesc = description ? `\n <meta name="description" content="${description.replace(/"/g, '&quot;')}">` : '';
const styleBlock = css ? `\n <style>\n${css}\n </style>` : '';
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title.replace(/</g, '&lt;')}</title>${metaDesc}${styleBlock}
</head>
<body>
${html}
</body>
</html>
`;
}
function deriveStubPath(overridePath) {
return overridePath.replace(/\.html$/, '.md');
}
function generateMdStub(opts) {
const hideItems = [];
if (opts.hideNav)
hideItems.push(' - navigation');
if (opts.hideToc)
hideItems.push(' - toc');
const hideBlock = hideItems.length > 0 ? `hide:\n${hideItems.join('\n')}\n` : '';
const descLine = opts.description ? `description: "${opts.description.replace(/"/g, '\\"')}"\n` : '';
return `---
template: ${opts.overrideFilename}
${hideBlock}title: "${opts.title.replace(/"/g, '\\"')}"
${descLine}---
`;
}
async function writeStubFile(stubPath, content) {
try {
validateStubPath(stubPath);
const filePath = path_1.default.join(MKDOCS_DOCS_ROOT, stubPath);
await promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
await promises_1.default.writeFile(filePath, content, 'utf-8');
logger_1.logger.info(`Wrote MkDocs stub: ${stubPath}`);
}
catch (err) {
if (err instanceof error_handler_1.AppError)
throw err;
logger_1.logger.warn(`Failed to write stub: ${stubPath}`, err);
}
}
async function removeStubFile(stubPath) {
try {
const filePath = path_1.default.join(MKDOCS_DOCS_ROOT, stubPath);
await promises_1.default.unlink(filePath);
logger_1.logger.info(`Removed MkDocs stub: ${stubPath}`);
}
catch {
// File may not exist — ignore
}
}
async function exportToMkDocs(opts) {
const { mkdocsPath, html, css, editorMode, exportMode, title, seoTitle, seoDescription, hideNav, hideToc } = opts;
try {
validateMkdocsPath(mkdocsPath);
// Write the override template
const filePath = path_1.default.join(MKDOCS_OVERRIDES, mkdocsPath);
await promises_1.default.mkdir(path_1.default.dirname(filePath), { recursive: true });
let content;
if (editorMode === 'CODE') {
content = html;
}
else if (exportMode === 'STANDALONE') {
content = wrapInStandaloneDocument(html, css, seoTitle || title, seoDescription);
}
else {
content = wrapInMaterialOverride(html, css);
}
await promises_1.default.writeFile(filePath, content, 'utf-8');
logger_1.logger.info(`Exported landing page to MkDocs: ${mkdocsPath} (${editorMode}/${exportMode})`);
// Write the .md stub
const stubPath = deriveStubPath(mkdocsPath);
const stubContent = generateMdStub({
overrideFilename: mkdocsPath,
title: seoTitle || title,
description: seoDescription,
hideNav,
hideToc,
});
await writeStubFile(stubPath, stubContent);
return stubPath;
}
catch (err) {
if (err instanceof error_handler_1.AppError)
throw err;
logger_1.logger.warn(`Failed to export to MkDocs: ${mkdocsPath}`, err);
return deriveStubPath(mkdocsPath);
}
}
async function removeFromMkDocs(mkdocsPath, stubPath) {
try {
const filePath = path_1.default.join(MKDOCS_OVERRIDES, mkdocsPath);
await promises_1.default.unlink(filePath);
logger_1.logger.info(`Removed MkDocs export: ${mkdocsPath}`);
}
catch {
// File may not exist — ignore
}
// Also remove the stub
const effectiveStubPath = stubPath || deriveStubPath(mkdocsPath);
await removeStubFile(effectiveStubPath);
}
async function scanOverrideFiles(dir, base = '') {
const results = [];
try {
const entries = await promises_1.default.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const rel = base ? `${base}/${entry.name}` : entry.name;
const full = path_1.default.join(dir, entry.name);
if (entry.isDirectory()) {
results.push(...await scanOverrideFiles(full, rel));
}
else if (entry.name.endsWith('.html')) {
results.push({ relativePath: rel, fullPath: full });
}
}
}
catch {
// Directory may not exist
}
return results;
}
async function stubExistsOnDisk(stubPath) {
try {
await promises_1.default.access(path_1.default.join(MKDOCS_DOCS_ROOT, stubPath));
return true;
}
catch {
return false;
}
}
exports.pagesService = {
async findAll(filters) {
const { page, limit, search, published } = filters;
const skip = (page - 1) * limit;
const where = {};
if (search) {
where.OR = [
{ title: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } },
{ slug: { contains: search, mode: 'insensitive' } },
];
}
if (published === 'true')
where.published = true;
else if (published === 'false')
where.published = false;
const [pages, total] = await Promise.all([
database_1.prisma.landingPage.findMany({
where,
select: landingPageSelect,
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
database_1.prisma.landingPage.count({ where }),
]);
return {
pages,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
};
},
async findById(id) {
const page = await database_1.prisma.landingPage.findUnique({
where: { id },
select: landingPageSelect,
});
if (!page) {
throw new error_handler_1.AppError(404, 'Landing page not found', 'PAGE_NOT_FOUND');
}
return page;
},
async findBySlugPublic(slug) {
const page = await database_1.prisma.landingPage.findUnique({
where: { slug },
select: landingPageSelect,
});
if (!page || !page.published) {
throw new error_handler_1.AppError(404, 'Page not found', 'PAGE_NOT_FOUND');
}
return page;
},
async create(data) {
const baseSlug = generateSlug(data.title);
const slug = await resolveSlugCollision(baseSlug);
// Auto-generate mkdocsPath from slug if not provided
const mkdocsPath = data.mkdocsPath ?? `${slug}.html`;
validateMkdocsPath(mkdocsPath);
const page = await database_1.prisma.landingPage.create({
data: {
...data,
slug,
mkdocsPath,
blocks: data.blocks,
},
select: landingPageSelect,
});
return page;
},
async update(id, data) {
const existing = await database_1.prisma.landingPage.findUnique({ where: { id } });
if (!existing) {
throw new error_handler_1.AppError(404, 'Landing page not found', 'PAGE_NOT_FOUND');
}
if (data.mkdocsPath) {
validateMkdocsPath(data.mkdocsPath);
}
const updateData = { ...data };
// Handle blocks JSON field
if (data.blocks !== undefined) {
updateData.blocks = data.blocks;
}
// Regenerate slug if title changes, and keep mkdocsPath in sync
if (data.title && data.title !== existing.title) {
const baseSlug = generateSlug(data.title);
const newSlug = await resolveSlugCollision(baseSlug, id);
updateData.slug = newSlug;
// Update mkdocsPath if it was auto-generated (matches old slug pattern)
const autoPath = `${existing.slug}.html`;
if (existing.mkdocsPath === autoPath) {
const oldMkdocsPath = existing.mkdocsPath;
updateData.mkdocsPath = `${newSlug}.html`;
// Clean up old override + old stub
await removeFromMkDocs(oldMkdocsPath, existing.mkdocsStubPath);
updateData.mkdocsStubPath = null;
}
}
const page = await database_1.prisma.landingPage.update({
where: { id },
data: updateData,
select: landingPageSelect,
});
// Handle MkDocs export/cleanup based on publish state
if (page.published && !page.mkdocsSkipExport && page.mkdocsPath && page.htmlOutput) {
const stubPath = await exportToMkDocs({
mkdocsPath: page.mkdocsPath,
html: page.htmlOutput,
css: page.cssOutput,
editorMode: page.editorMode,
exportMode: page.mkdocsExportMode,
title: page.title,
seoTitle: page.seoTitle,
seoDescription: page.seoDescription,
hideNav: page.mkdocsHideNav,
hideToc: page.mkdocsHideToc,
});
// Store stubPath if changed
if (stubPath !== page.mkdocsStubPath) {
await database_1.prisma.landingPage.update({
where: { id },
data: { mkdocsStubPath: stubPath },
});
}
}
else if ((!page.published || page.mkdocsSkipExport) && existing.mkdocsPath) {
await removeFromMkDocs(existing.mkdocsPath, existing.mkdocsStubPath);
if (existing.mkdocsStubPath) {
await database_1.prisma.landingPage.update({
where: { id },
data: { mkdocsStubPath: null },
});
}
}
return page;
},
async delete(id) {
const existing = await database_1.prisma.landingPage.findUnique({ where: { id } });
if (!existing) {
throw new error_handler_1.AppError(404, 'Landing page not found', 'PAGE_NOT_FOUND');
}
// Remove MkDocs export + stub if exists
if (existing.mkdocsPath) {
await removeFromMkDocs(existing.mkdocsPath, existing.mkdocsStubPath);
}
await database_1.prisma.landingPage.delete({ where: { id } });
},
async syncOverrides() {
let imported = 0;
let updated = 0;
let stubs = 0;
const files = await scanOverrideFiles(MKDOCS_OVERRIDES);
if (files.length === 0) {
logger_1.logger.info('syncOverrides: No HTML files found in overrides directory');
}
// Get all existing pages with mkdocsPath set
const existingPages = await database_1.prisma.landingPage.findMany({
where: { mkdocsPath: { not: null } },
select: {
id: true,
mkdocsPath: true,
mkdocsStubPath: true,
mkdocsExportMode: true,
mkdocsHideNav: true,
mkdocsHideToc: true,
editorMode: true,
title: true,
seoTitle: true,
seoDescription: true,
published: true,
},
});
const pathMap = new Map(existingPages.map(p => [p.mkdocsPath, p]));
for (const file of files) {
const content = await promises_1.default.readFile(file.fullPath, 'utf-8');
const tracked = pathMap.get(file.relativePath);
if (!tracked) {
// Untracked file — import as CODE page
const title = path_1.default.basename(file.relativePath, '.html');
const baseSlug = generateSlug(title);
const slug = await resolveSlugCollision(baseSlug);
await database_1.prisma.landingPage.create({
data: {
slug,
title,
editorMode: 'CODE',
htmlOutput: content,
mkdocsPath: file.relativePath,
published: true,
blocks: {},
},
});
imported++;
logger_1.logger.info(`syncOverrides: Imported ${file.relativePath} as CODE page`);
}
else if (tracked.editorMode === 'CODE') {
// Tracked CODE page — update from disk (disk wins)
await database_1.prisma.landingPage.update({
where: { id: tracked.id },
data: { htmlOutput: content },
});
updated++;
logger_1.logger.info(`syncOverrides: Updated ${file.relativePath} from disk`);
}
// VISUAL pages: don't overwrite from disk (managed by GrapesJS)
}
// Backfill missing .md stubs for published pages with overrides
for (const page of existingPages) {
if (!page.published || !page.mkdocsPath)
continue;
const expectedStubPath = page.mkdocsStubPath || deriveStubPath(page.mkdocsPath);
const exists = await stubExistsOnDisk(expectedStubPath);
if (!exists) {
const stubContent = generateMdStub({
overrideFilename: page.mkdocsPath,
title: page.seoTitle || page.title,
description: page.seoDescription,
hideNav: page.mkdocsHideNav,
hideToc: page.mkdocsHideToc,
});
await writeStubFile(expectedStubPath, stubContent);
if (page.mkdocsStubPath !== expectedStubPath) {
await database_1.prisma.landingPage.update({
where: { id: page.id },
data: { mkdocsStubPath: expectedStubPath },
});
}
stubs++;
logger_1.logger.info(`syncOverrides: Created missing stub ${expectedStubPath}`);
}
}
logger_1.logger.info(`syncOverrides: Imported ${imported}, updated ${updated}, stubs created ${stubs}`);
return { imported, updated, stubs };
},
async validateExports() {
let validated = 0;
let repaired = 0;
const errors = [];
const pages = await database_1.prisma.landingPage.findMany({
where: {
published: true,
mkdocsSkipExport: false,
mkdocsPath: { not: null },
htmlOutput: { not: null },
},
select: {
id: true,
slug: true,
title: true,
mkdocsPath: true,
mkdocsStubPath: true,
htmlOutput: true,
cssOutput: true,
editorMode: true,
mkdocsExportMode: true,
mkdocsHideNav: true,
mkdocsHideToc: true,
seoTitle: true,
seoDescription: true,
},
});
logger_1.logger.info(`Validating MkDocs exports for ${pages.length} published pages`);
for (const page of pages) {
validated++;
try {
// Check override HTML exists
const overridePath = path_1.default.join(MKDOCS_OVERRIDES, page.mkdocsPath);
let overrideExists = false;
try {
await promises_1.default.access(overridePath);
overrideExists = true;
}
catch {
// File doesn't exist
}
// Check stub exists
const expectedStubPath = page.mkdocsStubPath || deriveStubPath(page.mkdocsPath);
const stubExists = await stubExistsOnDisk(expectedStubPath);
// Repair if either missing
if (!overrideExists || !stubExists) {
logger_1.logger.warn(`Missing exports for ${page.slug}: override=${overrideExists}, stub=${stubExists}. Re-exporting...`);
const stubPath = await exportToMkDocs({
mkdocsPath: page.mkdocsPath,
html: page.htmlOutput,
css: page.cssOutput,
editorMode: page.editorMode,
exportMode: page.mkdocsExportMode,
title: page.title,
seoTitle: page.seoTitle,
seoDescription: page.seoDescription,
hideNav: page.mkdocsHideNav,
hideToc: page.mkdocsHideToc,
});
if (stubPath !== page.mkdocsStubPath) {
await database_1.prisma.landingPage.update({
where: { id: page.id },
data: { mkdocsStubPath: stubPath },
});
}
repaired++;
logger_1.logger.info(`✓ Repaired exports for ${page.slug}`);
}
}
catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
logger_1.logger.error(`Failed to validate/repair ${page.slug}:`, err);
errors.push({ pageId: page.id, slug: page.slug, error: errorMsg });
}
}
logger_1.logger.info(`Validation complete: ${validated} validated, ${repaired} repaired, ${errors.length} errors`);
return { validated, repaired, errors };
},
};
//# sourceMappingURL=pages.service.js.map

File diff suppressed because one or more lines are too long