@maxlkatze/cms
Version:
A git based Nuxt Module CMS - zero effort, zero cost
176 lines (175 loc) • 5.3 kB
JavaScript
import fs from "node:fs/promises";
import path from "node:path";
import { createStorage } from "unstorage";
import fsDriver from "unstorage/drivers/fs";
import { useAuthentication } from "../../client/composables/cms/useAuthentication.js";
import { useContentStorage } from "../../storage/ContentStorage.js";
import { defineEventHandler, readBody, useRuntimeConfig, createError } from "#imports";
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event) || {};
const { token, action, content } = body;
await verifyAuth(event, token);
switch (action) {
case "save":
return await handleSaveContent(event, content);
case "imageList":
return await handleImageList();
case "deploy":
return await handleDeploy();
default:
return createError({
statusCode: 400,
statusMessage: "Invalid action"
});
}
} catch (error) {
return {
success: false,
body: {
message: error || "An error occurred"
}
};
}
});
async function verifyAuth(event, token) {
if (!token) {
throw createError({
statusCode: 401,
statusMessage: "No token provided"
});
}
const runtimeConfig = useRuntimeConfig();
const authentication = useAuthentication();
const isValid = await authentication.verifyToken(token, runtimeConfig.secret || "");
if (!isValid) {
throw createError({
statusCode: 401,
statusMessage: "Invalid token"
});
}
}
async function handleSaveContent(event, content) {
if (!content || Object.keys(content).length === 0) {
throw createError({
statusCode: 400,
statusMessage: "No content provided"
});
}
const runtimeConfig = useRuntimeConfig();
const storage = await useContentStorage(runtimeConfig);
let savedContent = await storage.getItem(runtimeConfig.storageKey);
savedContent = { ...savedContent, ...content };
await storage.setItem(runtimeConfig.storageKey, savedContent);
if (runtimeConfig.storage.type === "fs") {
console.log("\x1B[42m\x1B[30m Katze \x1B[0m Restarting Nuxt...");
try {
await triggerNuxtRestart();
} catch (error) {
console.warn("Could not trigger Nuxt restart:", error);
}
}
return {
success: true,
body: {
message: "Content saved"
}
};
}
async function handleImageList() {
const runtimeConfig = useRuntimeConfig();
const extensions = [".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"];
const localStorage = createStorage({
driver: fsDriver({ base: `${runtimeConfig.projectLocation}/public/` })
});
const imageStorage = createStorage({
driver: fsDriver({ base: `${runtimeConfig.projectLocation}/images/` })
});
const imageKeys = await localStorage.getKeys("", {});
const imageKeys2 = await imageStorage.getKeys("", {});
const mergedKeys = [...imageKeys, ...imageKeys2];
const filteredImages = mergedKeys.filter((key) => extensions.some((ext) => key.toLowerCase().endsWith(ext))).map((key) => `/${key.replace(/:/g, "/")}`);
return {
success: true,
body: {
message: "Images fetched",
images: filteredImages
}
};
}
async function handleDeploy() {
const runtimeConfig = useRuntimeConfig();
const deployHookURL = runtimeConfig.deployHookURL;
if (!deployHookURL) {
return {
success: false,
missingDeployHookURL: true,
body: {
message: "No deploy hook URL provided"
}
};
}
try {
const response = await fetch(deployHookURL, {
method: "GET"
});
if (!response.ok) {
throw new Error(`Deploy failed with status: ${response.status}`);
}
return {
success: true,
body: {
message: "Content deployed successfully"
}
};
} catch (error) {
return {
success: false,
body: {
message: `Deploy failed: ${error}`
}
};
}
}
async function triggerNuxtRestart() {
const projectRoot = process.cwd();
const restartFilePath = path.join(projectRoot, ".nuxt", "restart.mjs");
try {
try {
await fs.access(restartFilePath);
const now = /* @__PURE__ */ new Date();
await fs.utimes(restartFilePath, now, now);
} catch {
const restartContent = `
// This file is used to trigger Nuxt restart
// Updated: ${(/* @__PURE__ */ new Date()).toISOString()}
export default {}
`;
await fs.writeFile(restartFilePath, restartContent, "utf-8");
}
try {
const configFiles = [
path.join(projectRoot, "nuxt.config.js"),
path.join(projectRoot, "nuxt.config.mjs"),
path.join(projectRoot, "nuxt.config.ts"),
path.join(projectRoot, "playground", "nuxt.config.ts")
];
for (const configFile of configFiles) {
try {
await fs.access(configFile);
const now = /* @__PURE__ */ new Date();
await fs.utimes(configFile, now, now);
console.log("\x1B[42m\x1B[30m Katze \x1B[0m Touched configuration file:", configFile);
break;
} catch {
}
}
} catch (error) {
console.warn("Could not touch nuxt.config file:", error);
}
return true;
} catch (error) {
console.error("Failed to trigger Nuxt restart:", error);
return false;
}
}