239 lines
7.4 KiB
JavaScript
239 lines
7.4 KiB
JavaScript
const path = require("path");
|
|
const {
|
|
PERMISSION_LEVELS,
|
|
REVIEW_STATES,
|
|
STATUS_VALUES,
|
|
VISIBILITY_VALUES,
|
|
accessForUser,
|
|
createEntry,
|
|
ensureTables,
|
|
getEditableEntry,
|
|
getEntryBySlug,
|
|
grantPermission,
|
|
listEntries,
|
|
listPermissions,
|
|
listVersions,
|
|
revokePermission,
|
|
restoreVersion,
|
|
searchForAi,
|
|
setEntryWorkflow,
|
|
updateEntry
|
|
} = require("./backend/okf_store");
|
|
const { renderMarkdown } = require("./backend/markdown");
|
|
|
|
const PLUGIN_ID = "okf";
|
|
|
|
module.exports = {
|
|
id: PLUGIN_ID,
|
|
init({ web, db }) {
|
|
ensureTables(db);
|
|
if (!global.lumiFrameworks) {
|
|
global.lumiFrameworks = {};
|
|
}
|
|
global.lumiFrameworks.okf = {
|
|
search: ({ query, user, limit } = {}) => searchForAi(db, query || "", user, limit || 5),
|
|
accessForUser: (user) => accessForUser(db, user)
|
|
};
|
|
|
|
const router = web.createRouter();
|
|
|
|
router.use((req, res, next) => {
|
|
res.locals.okfAccess = accessForUser(db, req.session.user);
|
|
next();
|
|
});
|
|
|
|
router.get("/", requireLogin, (req, res) => {
|
|
const filters = {
|
|
q: req.query.q || "",
|
|
category: req.query.category || "",
|
|
tag: req.query.tag || ""
|
|
};
|
|
const entries = listEntries(db, filters, req.session.user);
|
|
res.render(view("index"), {
|
|
title: "Knowledge",
|
|
entries,
|
|
filters,
|
|
categories: categoriesFor(entries),
|
|
tags: tagsFor(entries)
|
|
});
|
|
});
|
|
|
|
router.get("/admin", requireOkfManagement(db), (req, res) => {
|
|
renderAdmin(req, res, db);
|
|
});
|
|
|
|
router.post("/admin/entries", requireOkfEdit(db), (req, res) => {
|
|
try {
|
|
const entry = createEntry(db, req.body, req.session.user);
|
|
req.session.flash = { type: "success", message: "OKF entry created." };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(entry.slug)}`);
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin`);
|
|
}
|
|
});
|
|
|
|
router.post("/admin/entries/:slug", requireOkfEdit(db), (req, res) => {
|
|
try {
|
|
const entry = updateEntry(db, req.params.slug, req.body, req.session.user);
|
|
req.session.flash = { type: "success", message: "OKF entry updated." };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(entry.slug)}`);
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(req.params.slug)}`);
|
|
}
|
|
});
|
|
|
|
router.post("/admin/entries/:slug/:action", requireOkfManagement(db), (req, res) => {
|
|
try {
|
|
const entry = setEntryWorkflow(db, req.params.slug, req.params.action, req.session.user, req.body.note || "");
|
|
req.session.flash = { type: "success", message: "OKF workflow updated." };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(entry.slug)}`);
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(req.params.slug)}`);
|
|
}
|
|
});
|
|
|
|
router.post("/admin/entries/:slug/versions/:version/restore", requireOkfManagement(db), (req, res) => {
|
|
try {
|
|
const selected = getEditableEntry(db, req.params.slug);
|
|
if (!selected) throw new Error("OKF entry was not found.");
|
|
const entry = restoreVersion(
|
|
db,
|
|
selected.id,
|
|
req.params.version,
|
|
req.session.user,
|
|
req.body.note || ""
|
|
);
|
|
req.session.flash = { type: "success", message: "OKF version restored." };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(entry.slug)}`);
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin?edit=${encodeURIComponent(req.params.slug)}`);
|
|
}
|
|
});
|
|
|
|
router.post("/admin/permissions", requireAdmin, (req, res) => {
|
|
try {
|
|
grantPermission(db, req.body, req.session.user);
|
|
req.session.flash = { type: "success", message: "OKF permission granted." };
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
}
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin#okf-permissions`);
|
|
});
|
|
|
|
router.post("/admin/permissions/:id/revoke", requireAdmin, (req, res) => {
|
|
try {
|
|
revokePermission(db, req.params.id, req.session.user);
|
|
req.session.flash = { type: "success", message: "OKF permission revoked." };
|
|
} catch (error) {
|
|
req.session.flash = { type: "error", message: error.message };
|
|
}
|
|
res.redirect(`/plugins/${PLUGIN_ID}/admin#okf-permissions`);
|
|
});
|
|
|
|
router.get("/:slug", requireLogin, (req, res) => {
|
|
const entry = getEntryBySlug(db, req.params.slug, req.session.user);
|
|
if (!entry) {
|
|
return res.status(404).render("error", {
|
|
title: "Knowledge entry unavailable",
|
|
message: "That OKF entry was not found or is not visible to your account."
|
|
});
|
|
}
|
|
res.render(view("entry"), {
|
|
title: entry.title,
|
|
entry,
|
|
renderMarkdown
|
|
});
|
|
});
|
|
|
|
web.mount(`/plugins/${PLUGIN_ID}`, router, {
|
|
label: "Knowledge",
|
|
role: "public",
|
|
authRequired: true,
|
|
section: "community",
|
|
canAccess: (user) => Boolean(user)
|
|
});
|
|
web.addNavItem({
|
|
label: "OKF Management",
|
|
path: `/plugins/${PLUGIN_ID}/admin`,
|
|
role: "public",
|
|
authRequired: true,
|
|
section: "admin",
|
|
canAccess: (user) => accessForUser(db, user).canEdit
|
|
});
|
|
}
|
|
};
|
|
|
|
function renderAdmin(req, res, db) {
|
|
const filters = {
|
|
q: req.query.q || "",
|
|
status: req.query.status || "",
|
|
category: req.query.category || "",
|
|
tag: req.query.tag || ""
|
|
};
|
|
const entries = listEntries(db, filters, req.session.user, { management: true });
|
|
const selected = req.query.edit ? getEditableEntry(db, req.query.edit) : null;
|
|
const versions = selected ? listVersions(db, selected.id) : [];
|
|
res.render(view("admin"), {
|
|
title: "OKF Management",
|
|
entries,
|
|
filters,
|
|
selected,
|
|
versions,
|
|
permissions: listPermissions(db),
|
|
levels: PERMISSION_LEVELS,
|
|
statuses: STATUS_VALUES,
|
|
visibilityValues: VISIBILITY_VALUES,
|
|
reviewStates: REVIEW_STATES,
|
|
categories: categoriesFor(entries),
|
|
tags: tagsFor(entries),
|
|
renderMarkdown
|
|
});
|
|
}
|
|
|
|
function view(name) {
|
|
return path.join(__dirname, "views", `${name}.ejs`);
|
|
}
|
|
|
|
function requireLogin(req, res, next) {
|
|
if (req.session.user) return next();
|
|
return res.redirect("/login");
|
|
}
|
|
|
|
function requireAdmin(req, res, next) {
|
|
if (req.session.user?.isAdmin) return next();
|
|
return denied(res);
|
|
}
|
|
|
|
function requireOkfManagement(db) {
|
|
return (req, res, next) => {
|
|
if (accessForUser(db, req.session.user).canEdit) return next();
|
|
return denied(res);
|
|
};
|
|
}
|
|
|
|
function requireOkfEdit(db) {
|
|
return (req, res, next) => {
|
|
if (accessForUser(db, req.session.user).canEdit) return next();
|
|
return denied(res);
|
|
};
|
|
}
|
|
|
|
function denied(res) {
|
|
return res.status(403).render("error", {
|
|
title: "Access denied",
|
|
message: "You do not have access to that page."
|
|
});
|
|
}
|
|
|
|
function categoriesFor(entries) {
|
|
return Array.from(new Set(entries.map((entry) => entry.category).filter(Boolean))).sort();
|
|
}
|
|
|
|
function tagsFor(entries) {
|
|
return Array.from(new Set(entries.flatMap((entry) => entry.tags || []).filter(Boolean))).sort();
|
|
}
|