domma-cms 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/js/app.js +4 -4
- package/config/plugins.json +0 -9
- package/package.json +1 -1
- package/plugins/analytics/stats.json +1 -1
- package/server/server.js +10 -2
- package/server/services/plugins.js +9 -3
- package/plugins/domma-effects/admin/templates/domma-effects.html +0 -577
- package/plugins/domma-effects/admin/views/domma-effects.js +0 -56
- package/plugins/domma-effects/config.js +0 -9
- package/plugins/domma-effects/plugin.js +0 -147
- package/plugins/domma-effects/plugin.json +0 -36
- package/plugins/domma-effects/public/celebrations/core/canvas.js +0 -9
- package/plugins/domma-effects/public/celebrations/core/particles.js +0 -1
- package/plugins/domma-effects/public/celebrations/core/physics.js +0 -1
- package/plugins/domma-effects/public/celebrations/index.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/christmas.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/guy-fawkes.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/halloween.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/st-andrews.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/st-davids.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/st-georges.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/st-patricks.js +0 -1
- package/plugins/domma-effects/public/celebrations/themes/valentines.js +0 -1
- package/plugins/domma-effects/public/inject-body.html +0 -287
package/admin/js/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as
|
|
2
|
-
<span id="topbar-user-name" class="topbar-user-name">${
|
|
3
|
-
<span class="topbar-role-badge topbar-role-badge--${
|
|
1
|
+
import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as H}from"./views/index.js";import{api as o,getUser as l,isAuthenticated as r,logout as q}from"./api.js";$(()=>{(async()=>{try{const t=r()?await o.settings.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const P=["jb-company","jb-agent","jb-candidate"],x=["/job-board","/my-profile"];function d(t){return t&&P.includes(t.role)}function m(t){return x.some(i=>t===i||t.startsWith(i+"/"))}R.use(async(t,i,a)=>{if(t.path==="/login"||t.path==="/reset-password")return a();if(!r()){R.navigate("/login");return}if(d(l())&&!m(t.path)){R.navigate("/job-board");return}a()});let c=null;async function j(){if(!r())return{};try{const[t,i,a,s,e,n,_,B,T,V,I,O,U]=await Promise.all([o.pages.list().catch(()=>[]),o.media.list().catch(()=>[]),o.users.list().catch(()=>[]),o.plugins.list().catch(()=>[]),o.collections.list().catch(()=>[]),o.forms.list().catch(()=>[]),o.themes.list().catch(()=>[]),o.views.list().catch(()=>[]),o.actions.list().catch(()=>[]),o.blocks.list().catch(()=>[]),o.navigation.get().catch(()=>({})),o.layouts.get().catch(()=>({})),o.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]}))]),D=I.items||[],K=D.filter(h=>!h.hidden).length,y=D.length,F=s.filter(h=>h.enabled).length,k=s.length;return{pages:t.length,media:i.length,users:a.length,plugins:k>0?`${F}/${k}`:null,collections:e.length,forms:n.length,themes:_.length,views:B.length,actions:T.length,blocks:V.length,navigation:y>0?`${K}/${y}`:null,layouts:Object.keys(O).length,roles:(U.entries||[]).length}}catch{return{}}}async function g(){try{return(await o.get("/auth/permissions")).permissions||[]}catch{return[]}}async function A(){try{return await o.get("/settings/db-status")}catch{return{configured:!1}}}async function u(t){c&&$("#admin-sidebar").empty();const[i,a]=await Promise.all([j(),A()]),s=w.map(e=>{if(!e.countKey)return e;const n=i[e.countKey];return{...e,badge:n!=null&&n>0?String(n):null}});if(c=Domma.elements.sidebar("#admin-sidebar",{header:{title:"CMS Admin",icon:"layout"},items:J(t,i,s),collapsible:!0,collapseAt:992,push:!0,contentSelector:".dashboard-main",top:"60px"}),L(),N(),a?.configured){const e=$("#admin-sidebar").find(".sidebar-header-title").first();e.length&&!e.find(".db-badge").length&&e.append('<span class="db-badge" title="MongoDB connected" style="display:inline-flex;align-items:center;background:#16a34a;color:#fff;font-size:10px;font-weight:700;padding:2px 6px;border-radius:9999px;letter-spacing:.5px;margin-left:6px;vertical-align:middle;line-height:1;">DB</span>')}}function L(){$("#admin-sidebar .sidebar-link").each(function(){const t=$(this).find(".sidebar-text").text().trim();t&&$(this).attr("data-tooltip",t)}),E.tooltip("#admin-sidebar [data-tooltip]",{placement:"right"})}function N(){const t="sidebar_collapsed_sections",i=new Set(S.get(t)||[]);$("#admin-sidebar .sidebar-heading").each(function(){const a=$(this);a.addClass("sidebar-heading--collapsible"),a.append('<span class="sidebar-heading-toggle"><span data-icon="chevron-down"></span></span>');const s=a.text().trim();if(i.has(s)){a.addClass("is-collapsed");let e=a.next();for(;e.length&&!e.hasClass("sidebar-heading")&&!e.hasClass("sidebar-divider");)e.hide(),e=e.next()}a.on("click",function(){const e=!a.hasClass("is-collapsed");a.toggleClass("is-collapsed",e);let n=a.next();for(;n.length&&!n.hasClass("sidebar-heading")&&!n.hasClass("sidebar-divider");)n.toggle(!e),n=n.next();e?i.add(s):i.delete(s),S.set(t,[...i])})}),Domma.icons.scan("#admin-sidebar")}M.subscribe("router:afterChange",({to:t})=>{c&&t.path!=="/login"&&t.path!=="/reset-password"&&c.setActive("#"+t.path)});const W=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/navigation",view:"navigation",title:"Navigation - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if(d(l())&&!m(t.path)){R.navigate("/job-board");return}if($("#admin-sidebar").show(),$("#admin-topbar").show(),i?.path==="/login"||i?.path==="/reset-password"){$("#topbar-user-name").remove(),await f();const a=await g();u(a)}v()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){$(t.target).closest("button, a").length||$(this).closest(".card").toggleClass("card-collapsed")}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const b={...H},p=[...W];let w=[];async function f(){if(r())try{const t=await o.plugins.adminConfig();w=t.sidebar||[],t.routes?.length&&p.push(...t.routes);for(const[i,a]of Object.entries(t.views||{}))try{const s=await import(`/plugins/${a.entry}`);b[i]=s[a.exportName]}catch{}}catch{}}function v(){const t=l();if(!t||$("#topbar-user-name").length)return;const a={admin:"Admin",manager:"Manager",editor:"Editor",subscriber:"Subscriber","jb-company":"Company","jb-agent":"Agent","jb-candidate":"Candidate"}[t.role]||t.role;$("#topbar-user").html(`
|
|
2
|
+
<span id="topbar-user-name" class="topbar-user-name">${C(t.name)}</span>
|
|
3
|
+
<span class="topbar-role-badge topbar-role-badge--${C(t.role)}">${a}</span>
|
|
4
4
|
`),$("#topbar-actions").html(`
|
|
5
5
|
<a href="#/my-profile" class="topbar-action-link" data-tooltip="My Profile" data-tooltip-placement="bottom">
|
|
6
6
|
<span data-icon="user"></span>
|
|
@@ -14,4 +14,4 @@ import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as K}
|
|
|
14
14
|
<span data-icon="log-out"></span>
|
|
15
15
|
<span>Sign out</span>
|
|
16
16
|
</a>
|
|
17
|
-
`),$("#topbar-logout-btn").on("click",
|
|
17
|
+
`),$("#topbar-logout-btn").on("click",s=>{s.preventDefault(),q()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"})}function C(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}(async()=>{if(!r()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const s=(window.location.hash||"#/").slice(1)||"/";if(d(l())&&!m(s)&&(window.location.hash="#/job-board"),await f(),l()){const n=await g();await u(n),v()}}R.init({container:"#view-container",routes:p,views:b,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(s,e){if(s.endsWith("/*")){const n=s.slice(0,-2);return e.startsWith(n+"/")?{}:null}return t(s,e)};const i=(window.location.hash||"#/").slice(1)||"/";p.filter(s=>s.path.endsWith("/*")).some(s=>i.startsWith(s.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
|
package/config/plugins.json
CHANGED
|
@@ -17,15 +17,6 @@
|
|
|
17
17
|
"debounceMs": 300
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
-
"domma-effects": {
|
|
21
|
-
"enabled": true,
|
|
22
|
-
"settings": {
|
|
23
|
-
"respectMotion": false,
|
|
24
|
-
"defaultDuration": 600,
|
|
25
|
-
"defaultAnimation": "fade",
|
|
26
|
-
"defaultThreshold": 0.1
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
20
|
"job-board": {
|
|
30
21
|
"enabled": false,
|
|
31
22
|
"settings": {}
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -45,7 +45,11 @@ if (!JWT_SECRET || JWT_SECRET === 'CHANGE_ME' || JWT_SECRET.length < 32) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const app = Fastify({
|
|
48
|
-
logger: {
|
|
48
|
+
logger: {level: process.env.NODE_ENV === 'development' ? 'info' : 'warn'},
|
|
49
|
+
// When running behind a reverse proxy (e.g. domma-cms-manager), trust the
|
|
50
|
+
// X-Forwarded-For header so @fastify/rate-limit keys on the real client IP
|
|
51
|
+
// rather than the proxy's loopback address.
|
|
52
|
+
trustProxy: !!process.env.TRUST_PROXY,
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
// Register process error handlers immediately so startup errors are captured
|
|
@@ -85,7 +89,11 @@ await app.register(multipart, { limits: { fileSize: serverConfig.uploads.maxFile
|
|
|
85
89
|
await app.register(rateLimit, {
|
|
86
90
|
global: true, // apply default limit to all routes; stricter per-route limits override this
|
|
87
91
|
max: 100,
|
|
88
|
-
timeWindow: '1 minute'
|
|
92
|
+
timeWindow: '1 minute',
|
|
93
|
+
// When running behind domma-cms-manager, all traffic arrives from 127.0.0.1.
|
|
94
|
+
// Exempt loopback so the proxy and health checks are never rate-limited;
|
|
95
|
+
// external rate limiting is the manager's responsibility.
|
|
96
|
+
allowList: process.env.TRUST_PROXY ? ['127.0.0.1', '::1', '::ffff:127.0.0.1'] : [],
|
|
89
97
|
});
|
|
90
98
|
|
|
91
99
|
// ---------------------------------------------------------------------------
|
|
@@ -20,6 +20,12 @@ const PLUGINS_DIR = path.resolve('plugins');
|
|
|
20
20
|
|
|
21
21
|
const REQUIRED_MANIFEST_FIELDS = ['name', 'displayName', 'version', 'description', 'author', 'date', 'icon'];
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Core plugins — always loaded regardless of plugins.json enabled state.
|
|
25
|
+
* These are considered first-class CMS features, not optional add-ons.
|
|
26
|
+
*/
|
|
27
|
+
const CORE_PLUGINS = new Set(['domma-effects']);
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* Scan the plugins/ directory and return all valid manifests.
|
|
25
31
|
* Validates mandatory fields and required files (plugin.js, config.js).
|
|
@@ -147,7 +153,7 @@ export async function registerPlugins(fastify) {
|
|
|
147
153
|
const loaded = [];
|
|
148
154
|
for (const manifest of manifests) {
|
|
149
155
|
const state = states[manifest.name] || {};
|
|
150
|
-
if (!state.enabled) continue;
|
|
156
|
+
if (!state.enabled && !CORE_PLUGINS.has(manifest.name)) continue;
|
|
151
157
|
|
|
152
158
|
const entryPath = path.join(PLUGINS_DIR, manifest.name, 'plugin.js');
|
|
153
159
|
try {
|
|
@@ -201,7 +207,7 @@ export async function getInjectionSnippets() {
|
|
|
201
207
|
|
|
202
208
|
for (const manifest of manifests) {
|
|
203
209
|
const state = states[manifest.name] || {};
|
|
204
|
-
if (!state.enabled || !manifest.inject) continue;
|
|
210
|
+
if ((!state.enabled && !CORE_PLUGINS.has(manifest.name)) || !manifest.inject) continue;
|
|
205
211
|
|
|
206
212
|
const pluginRoot = path.resolve(PLUGINS_DIR, manifest.name);
|
|
207
213
|
|
|
@@ -271,7 +277,7 @@ export async function getAdminPluginConfig() {
|
|
|
271
277
|
|
|
272
278
|
for (const manifest of manifests) {
|
|
273
279
|
const state = states[manifest.name] || {};
|
|
274
|
-
if (!state.enabled || !manifest.admin) continue;
|
|
280
|
+
if ((!state.enabled && !CORE_PLUGINS.has(manifest.name)) || !manifest.admin) continue;
|
|
275
281
|
|
|
276
282
|
if (manifest.admin.sidebar) sidebar.push(...manifest.admin.sidebar);
|
|
277
283
|
if (manifest.admin.routes) routes.push(...manifest.admin.routes);
|