Whatever
This commit is contained in:
48
backend/package-lock.json
generated
48
backend/package-lock.json
generated
@@ -13,12 +13,28 @@
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1",
|
||||
"mongoose": "^9.7.4",
|
||||
"simple-git": "^3.36.0",
|
||||
"socket.io": "^4.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/promise-deferred": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.12.tgz",
|
||||
@@ -28,6 +44,21 @@
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@simple-git/args-pathspec": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz",
|
||||
"integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@simple-git/argv-parser": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz",
|
||||
"integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@simple-git/args-pathspec": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
@@ -1406,6 +1437,23 @@
|
||||
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/simple-git": {
|
||||
"version": "3.36.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz",
|
||||
"integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@kwsites/file-exists": "^1.1.1",
|
||||
"@kwsites/promise-deferred": "^1.1.1",
|
||||
"@simple-git/args-pathspec": "^1.0.3",
|
||||
"@simple-git/argv-parser": "^1.1.0",
|
||||
"debug": "^4.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1",
|
||||
"mongoose": "^9.7.4",
|
||||
"simple-git": "^3.36.0",
|
||||
"socket.io": "^4.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -9,6 +9,8 @@ const siteSchema = new mongoose.Schema(
|
||||
themeColor: { type: String, default: "#2563eb" },
|
||||
targetWindow: { type: String, default: "_blank", enum: ["_blank", "_self"] },
|
||||
enabled: { type: Boolean, default: true },
|
||||
gitRepoUrl: { type: String, default: "", trim: true },
|
||||
gitBranch: { type: String, default: "main", trim: true },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
const express = require("express");
|
||||
const Site = require("../models/Site");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const simpleGit = require("simple-git");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const SITES_GIT_DIR = path.join(__dirname, "../../sites-git-data");
|
||||
|
||||
if (!fs.existsSync(SITES_GIT_DIR)) {
|
||||
fs.mkdirSync(SITES_GIT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// GET /api/sites - List all sites
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
@@ -16,8 +25,8 @@ router.get("/", async (req, res) => {
|
||||
// POST /api/sites - Create a new site
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { id, name, url, description, themeColor, targetWindow, enabled } = req.body;
|
||||
const site = new Site({ id, name, url, description, themeColor, targetWindow, enabled });
|
||||
const { id, name, url, description, themeColor, targetWindow, enabled, gitRepoUrl, gitBranch } = req.body;
|
||||
const site = new Site({ id, name, url, description, themeColor, targetWindow, enabled, gitRepoUrl: gitRepoUrl || "", gitBranch: gitBranch || "main" });
|
||||
await site.save();
|
||||
res.status(201).json(site);
|
||||
} catch (err) {
|
||||
@@ -28,9 +37,10 @@ router.post("/", async (req, res) => {
|
||||
// PUT /api/sites/:id - Update a site
|
||||
router.put("/:id", async (req, res) => {
|
||||
try {
|
||||
const { name, url, description, themeColor, targetWindow, enabled } = req.body;
|
||||
const updates = { name, url, description, themeColor, targetWindow, enabled };
|
||||
const site = await Site.findOneAndUpdate({ id: req.params.id }, updates, { new: true, runValidators: true });
|
||||
const { name, url, description, themeColor, targetWindow, enabled, gitRepoUrl, gitBranch } = req.body;
|
||||
console.log("[PUT /sites] id:", req.params.id, "body:", JSON.stringify({name,url,description,themeColor,targetWindow,enabled,gitRepoUrl,gitBranch}));
|
||||
const updates = { name, url, description, themeColor, targetWindow, enabled, gitRepoUrl: gitRepoUrl || "", gitBranch: gitBranch || "main" };
|
||||
const site = await Site.findOneAndUpdate({ id: req.params.id }, { $set: updates }, { new: true, runValidators: true });
|
||||
if (!site) return res.status(404).json({ error: "Site not found" });
|
||||
res.json(site);
|
||||
} catch (err) {
|
||||
@@ -43,10 +53,116 @@ router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
const site = await Site.findOneAndDelete({ id: req.params.id });
|
||||
if (!site) return res.status(404).json({ error: "Site not found" });
|
||||
|
||||
const siteGitDir = path.join(SITES_GIT_DIR, site.id);
|
||||
if (fs.existsSync(siteGitDir)) {
|
||||
fs.rmSync(siteGitDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
res.json({ message: "Site deleted", id: site.id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/sites/:id/git-log - Get git commit log for a site's repository
|
||||
router.get("/:id/git-log", async (req, res) => {
|
||||
try {
|
||||
const site = await Site.findOne({ id: req.params.id });
|
||||
if (!site) return res.status(404).json({ error: "Site not found" });
|
||||
if (!site.gitRepoUrl) return res.status(400).json({ error: "No git repo URL configured" });
|
||||
|
||||
const siteGitDir = path.join(SITES_GIT_DIR, site.id);
|
||||
try {
|
||||
const git = simpleGit(siteGitDir);
|
||||
const logs = await git.log();
|
||||
res.json({ commits: logs.all || [] });
|
||||
} catch (gitErr) {
|
||||
res.status(500).json({ error: "Failed to read git log. The repository may need to be cloned first. Use the sync endpoint." });
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/sites/:id/sync-git - Clone or pull the git repository for a site
|
||||
router.post("/:id/sync-git", async (req, res) => {
|
||||
try {
|
||||
const site = await Site.findOne({ id: req.params.id });
|
||||
if (!site) return res.status(404).json({ error: "Site not found" });
|
||||
if (!site.gitRepoUrl) return res.status(400).json({ error: "No git repo URL configured" });
|
||||
|
||||
const siteGitDir = path.join(SITES_GIT_DIR, site.id);
|
||||
const git = simpleGit(siteGitDir);
|
||||
|
||||
try {
|
||||
await git.pull();
|
||||
res.json({ message: "Repository pulled successfully", commits: (await git.log()).all || [] });
|
||||
} catch (pullErr) {
|
||||
if (pullErr.message.includes("repo does not exist") || pullErr.message.includes("No local restart")) {
|
||||
const branch = site.gitBranch || "main";
|
||||
const gitInstance = simpleGit();
|
||||
await gitInstance.clone(site.gitRepoUrl, siteGitDir);
|
||||
try {
|
||||
await gitInstance.checkout(branch);
|
||||
} catch (e) {
|
||||
// If branch doesn't exist, stay on default branch
|
||||
}
|
||||
const logs = await gitInstance.log();
|
||||
res.json({ message: "Repository cloned successfully", cloned: true, commits: logs.all || [] });
|
||||
} else {
|
||||
res.status(500).json({ error: pullErr.message });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/sites/:id/files - Get file tree for a site's synced repository
|
||||
router.get("/:id/files", async (req, res) => {
|
||||
try {
|
||||
const site = await Site.findOne({ id: req.params.id });
|
||||
if (!site) return res.status(404).json({ error: "Site not found" });
|
||||
|
||||
const siteGitDir = path.join(SITES_GIT_DIR, site.id);
|
||||
if (!fs.existsSync(siteGitDir)) {
|
||||
return res.json({ tree: [], message: "Repository not synced yet. Use Sync Git first." });
|
||||
}
|
||||
|
||||
function buildTree(dir, relativePath = "") {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const result = [];
|
||||
|
||||
// Folders first, then files
|
||||
const dirs = entries.filter(e => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
|
||||
const files = entries.filter(e => !e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
for (const d of dirs) {
|
||||
result.push({
|
||||
name: d.name,
|
||||
path: relativePath ? `${relativePath}/${d.name}` : d.name,
|
||||
type: "folder",
|
||||
children: buildTree(path.join(dir, d.name), relativePath ? `${relativePath}/${d.name}` : d.name),
|
||||
});
|
||||
}
|
||||
|
||||
for (const f of files) {
|
||||
result.push({
|
||||
name: f.name,
|
||||
path: relativePath ? `${relativePath}/${f.name}` : f.name,
|
||||
type: "file",
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const tree = buildTree(siteGitDir);
|
||||
res.json({ tree });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user