- Introduced Caddy server for serving frontend and API backend. - Implemented admin dashboard for creating, editing, and managing events. - Replaced session-based authentication with token-based OAuth using Gitea. - Added support for drag-and-drop event reordering in the admin interface. - Standardized Fastify route validation with JSON schemas. - Enhanced authentication flow with cookie-based state and secure token storage. - Reworked backend routes to handle publishing, event management, and content updates. - Updated `Dockerfile.caddy` and `fly.toml` for deployment configuration.
128 lines
3.6 KiB
TypeScript
128 lines
3.6 KiB
TypeScript
import { FastifyPluginAsync } from 'fastify';
|
|
import { z } from 'zod';
|
|
import { GitService } from '../services/git.service.js';
|
|
import { FileGeneratorService } from '../services/file-generator.service.js';
|
|
import { db } from '../config/database.js';
|
|
import { events, galleryImages, contentSections, publishHistory } from '../db/schema.js';
|
|
import { eq } from 'drizzle-orm';
|
|
|
|
// Fastify JSON schema for publish body
|
|
const publishBodyJsonSchema = {
|
|
type: 'object',
|
|
required: ['commitMessage'],
|
|
properties: {
|
|
commitMessage: { type: 'string', minLength: 1, maxLength: 200 },
|
|
},
|
|
} as const;
|
|
|
|
const publishRoute: FastifyPluginAsync = async (fastify) => {
|
|
fastify.post('/publish', {
|
|
schema: {
|
|
body: publishBodyJsonSchema,
|
|
},
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
try {
|
|
const { commitMessage } = request.body as any;
|
|
const userId = request.user.id;
|
|
|
|
fastify.log.info('Starting publish process...');
|
|
|
|
// Initialize git service
|
|
const gitService = new GitService();
|
|
await gitService.initialize();
|
|
|
|
fastify.log.info('Git repository initialized');
|
|
|
|
// Fetch all content from database
|
|
const eventsData = await db
|
|
.select()
|
|
.from(events)
|
|
.where(eq(events.isPublished, true))
|
|
.orderBy(events.displayOrder);
|
|
|
|
const galleryData = await db
|
|
.select()
|
|
.from(galleryImages)
|
|
.where(eq(galleryImages.isPublished, true))
|
|
.orderBy(galleryImages.displayOrder);
|
|
|
|
const sectionsData = await db.select().from(contentSections);
|
|
const sectionsMap = new Map<string, any>(
|
|
(sectionsData as any[]).map((s: any) => [s.sectionName as string, s.contentJson as any])
|
|
);
|
|
|
|
fastify.log.info(`Fetched ${eventsData.length} events, ${galleryData.length} images, ${sectionsData.length} sections`);
|
|
|
|
// Generate and write files
|
|
const fileGenerator = new FileGeneratorService();
|
|
await fileGenerator.writeFiles(
|
|
gitService.getWorkspacePath(''),
|
|
(eventsData as any[]).map((e: any) => ({
|
|
title: e.title,
|
|
date: e.date,
|
|
description: e.description,
|
|
imageUrl: e.imageUrl,
|
|
})),
|
|
(galleryData as any[]).map((g: any) => ({
|
|
imageUrl: g.imageUrl,
|
|
altText: g.altText,
|
|
})),
|
|
sectionsMap
|
|
);
|
|
|
|
fastify.log.info('Files generated successfully');
|
|
|
|
// Commit and push
|
|
const commitHash = await gitService.commitAndPush(commitMessage);
|
|
|
|
fastify.log.info(`Changes committed: ${commitHash}`);
|
|
|
|
// Record in history
|
|
await db.insert(publishHistory).values({
|
|
userId,
|
|
commitHash,
|
|
commitMessage,
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
commitHash,
|
|
message: 'Changes published successfully',
|
|
};
|
|
|
|
} catch (error) {
|
|
fastify.log.error({ err: error }, 'Publish error');
|
|
|
|
// Attempt to reset git state on error
|
|
try {
|
|
const gitService = new GitService();
|
|
await gitService.reset();
|
|
} catch (resetError) {
|
|
fastify.log.error({ err: resetError }, 'Failed to reset git state');
|
|
}
|
|
|
|
return reply.code(500).send({
|
|
success: false,
|
|
error: 'Failed to publish changes',
|
|
details: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
}
|
|
});
|
|
|
|
// Get publish history
|
|
fastify.get('/publish/history', {
|
|
preHandler: [fastify.authenticate],
|
|
}, async (request, reply) => {
|
|
const history = await db
|
|
.select()
|
|
.from(publishHistory)
|
|
.orderBy(publishHistory.publishedAt)
|
|
.limit(20);
|
|
|
|
return { history };
|
|
});
|
|
};
|
|
|
|
export default publishRoute;
|