@arch-cadre/blog-module 0.0.1 → 1.0.2

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.
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = BlogStatsWidget;
7
+ var _server = require("@arch-cadre/core/server");
8
+ var _server2 = require("@arch-cadre/intl/server");
9
+ var _ui = require("@arch-cadre/ui");
10
+ var _drizzleOrm = require("drizzle-orm");
11
+ var _lucideReact = require("lucide-react");
12
+ var React = _interopRequireWildcard(require("react"));
13
+ var _schema = require("../schema.cjs");
14
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
+ async function BlogStatsWidget() {
17
+ const {
18
+ t
19
+ } = await (0, _server2.getTranslation)();
20
+ const [postsCount] = await _server.db.select({
21
+ count: (0, _drizzleOrm.sql)`count(*)`
22
+ }).from(_schema.postsTable);
23
+ const [commentsCount] = await _server.db.select({
24
+ count: (0, _drizzleOrm.sql)`count(*)`
25
+ }).from(_schema.commentsTable);
26
+ return /* @__PURE__ */React.createElement("div", {
27
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-2"
28
+ }, /* @__PURE__ */React.createElement(_ui.Card, null, /* @__PURE__ */React.createElement(_ui.CardHeader, {
29
+ className: "flex flex-row items-center justify-between space-y-0 pb-2"
30
+ }, /* @__PURE__ */React.createElement(_ui.CardTitle, {
31
+ className: "text-sm font-medium"
32
+ }, t("Total Posts")), /* @__PURE__ */React.createElement(_lucideReact.FileText, {
33
+ className: "h-4 w-4 text-muted-foreground"
34
+ })), /* @__PURE__ */React.createElement(_ui.CardContent, null, /* @__PURE__ */React.createElement("div", {
35
+ className: "text-2xl font-bold"
36
+ }, postsCount?.count || 0))), /* @__PURE__ */React.createElement(_ui.Card, null, /* @__PURE__ */React.createElement(_ui.CardHeader, {
37
+ className: "flex flex-row items-center justify-between space-y-0 pb-2"
38
+ }, /* @__PURE__ */React.createElement(_ui.CardTitle, {
39
+ className: "text-sm font-medium"
40
+ }, t("Total Comments")), /* @__PURE__ */React.createElement(_lucideReact.MessageSquare, {
41
+ className: "h-4 w-4 text-muted-foreground"
42
+ })), /* @__PURE__ */React.createElement(_ui.CardContent, null, /* @__PURE__ */React.createElement("div", {
43
+ className: "text-2xl font-bold"
44
+ }, commentsCount?.count || 0))));
45
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export default function BlogStatsWidget(): Promise<React.JSX.Element>;
@@ -0,0 +1,13 @@
1
+ import { db } from "@arch-cadre/core/server";
2
+ import { getTranslation } from "@arch-cadre/intl/server";
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@arch-cadre/ui";
4
+ import { sql } from "drizzle-orm";
5
+ import { FileText, MessageSquare } from "lucide-react";
6
+ import * as React from "react";
7
+ import { commentsTable, postsTable } from "../schema.mjs";
8
+ export default async function BlogStatsWidget() {
9
+ const { t } = await getTranslation();
10
+ const [postsCount] = await db.select({ count: sql`count(*)` }).from(postsTable);
11
+ const [commentsCount] = await db.select({ count: sql`count(*)` }).from(commentsTable);
12
+ return /* @__PURE__ */ React.createElement("div", { className: "grid gap-4 md:grid-cols-2 lg:grid-cols-2" }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React.createElement(CardTitle, { className: "text-sm font-medium" }, t("Total Posts")), /* @__PURE__ */ React.createElement(FileText, { className: "h-4 w-4 text-muted-foreground" })), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-bold" }, postsCount?.count || 0))), /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React.createElement(CardTitle, { className: "text-sm font-medium" }, t("Total Comments")), /* @__PURE__ */ React.createElement(MessageSquare, { className: "h-4 w-4 text-muted-foreground" })), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement("div", { className: "text-2xl font-bold" }, commentsCount?.count || 0))));
13
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = RecentCommentsWidget;
7
+ var _server = require("@arch-cadre/core/server");
8
+ var _server2 = require("@arch-cadre/intl/server");
9
+ var _ui = require("@arch-cadre/ui");
10
+ var _drizzleOrm = require("drizzle-orm");
11
+ var React = _interopRequireWildcard(require("react"));
12
+ var _schema = require("../schema.cjs");
13
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
+ async function RecentCommentsWidget() {
16
+ const {
17
+ t
18
+ } = await (0, _server2.getTranslation)();
19
+ const comments = await _server.db.select({
20
+ id: _schema.commentsTable.id,
21
+ content: _schema.commentsTable.content,
22
+ createdAt: _schema.commentsTable.createdAt,
23
+ author: {
24
+ name: _server.userTable.name,
25
+ image: _server.userTable.image
26
+ }
27
+ }).from(_schema.commentsTable).leftJoin(_server.userTable, (0, _drizzleOrm.eq)(_schema.commentsTable.authorId, _server.userTable.id)).orderBy((0, _drizzleOrm.desc)(_schema.commentsTable.createdAt)).limit(5);
28
+ return /* @__PURE__ */React.createElement(_ui.Card, null, /* @__PURE__ */React.createElement(_ui.CardHeader, null, /* @__PURE__ */React.createElement(_ui.CardTitle, null, t("Recent Comments")), /* @__PURE__ */React.createElement(_ui.CardDescription, null, t("What readers are saying about your posts."))), /* @__PURE__ */React.createElement(_ui.CardContent, {
29
+ className: "grid gap-8"
30
+ }, comments.map(comment => /* @__PURE__ */React.createElement("div", {
31
+ key: comment.id,
32
+ className: "flex items-center gap-4"
33
+ }, /* @__PURE__ */React.createElement(_ui.Avatar, {
34
+ className: "h-9 w-9"
35
+ }, /* @__PURE__ */React.createElement(_ui.AvatarImage, {
36
+ src: comment.author?.image || "",
37
+ alt: "Avatar"
38
+ }), /* @__PURE__ */React.createElement(_ui.AvatarFallback, null, comment.author?.name?.substring(0, 2).toUpperCase() || "CM")), /* @__PURE__ */React.createElement("div", {
39
+ className: "grid gap-1"
40
+ }, /* @__PURE__ */React.createElement("p", {
41
+ className: "text-sm font-medium leading-none line-clamp-1"
42
+ }, comment.content), /* @__PURE__ */React.createElement("p", {
43
+ className: "text-sm text-muted-foreground"
44
+ }, comment.author?.name || t("Anonymous"), " \u2022", " ", new Date(comment.createdAt).toLocaleDateString())))), comments.length === 0 && /* @__PURE__ */React.createElement("div", {
45
+ className: "text-center py-4 text-muted-foreground"
46
+ }, t("No comments yet."))));
47
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export default function RecentCommentsWidget(): Promise<React.JSX.Element>;
@@ -0,0 +1,28 @@
1
+ import { db, userTable } from "@arch-cadre/core/server";
2
+ import { getTranslation } from "@arch-cadre/intl/server";
3
+ import {
4
+ Avatar,
5
+ AvatarFallback,
6
+ AvatarImage,
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle
12
+ } from "@arch-cadre/ui";
13
+ import { desc, eq } from "drizzle-orm";
14
+ import * as React from "react";
15
+ import { commentsTable } from "../schema.mjs";
16
+ export default async function RecentCommentsWidget() {
17
+ const { t } = await getTranslation();
18
+ const comments = await db.select({
19
+ id: commentsTable.id,
20
+ content: commentsTable.content,
21
+ createdAt: commentsTable.createdAt,
22
+ author: {
23
+ name: userTable.name,
24
+ image: userTable.image
25
+ }
26
+ }).from(commentsTable).leftJoin(userTable, eq(commentsTable.authorId, userTable.id)).orderBy(desc(commentsTable.createdAt)).limit(5);
27
+ return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Recent Comments")), /* @__PURE__ */ React.createElement(CardDescription, null, t("What readers are saying about your posts."))), /* @__PURE__ */ React.createElement(CardContent, { className: "grid gap-8" }, comments.map((comment) => /* @__PURE__ */ React.createElement("div", { key: comment.id, className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement(Avatar, { className: "h-9 w-9" }, /* @__PURE__ */ React.createElement(AvatarImage, { src: comment.author?.image || "", alt: "Avatar" }), /* @__PURE__ */ React.createElement(AvatarFallback, null, comment.author?.name?.substring(0, 2).toUpperCase() || "CM")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-1" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-medium leading-none line-clamp-1" }, comment.content), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-muted-foreground" }, comment.author?.name || t("Anonymous"), " \u2022", " ", new Date(comment.createdAt).toLocaleDateString())))), comments.length === 0 && /* @__PURE__ */ React.createElement("div", { className: "text-center py-4 text-muted-foreground" }, t("No comments yet."))));
28
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = RecentPostsWidget;
7
+ var _server = require("@arch-cadre/core/server");
8
+ var _server2 = require("@arch-cadre/intl/server");
9
+ var _ui = require("@arch-cadre/ui");
10
+ var _drizzleOrm = require("drizzle-orm");
11
+ var React = _interopRequireWildcard(require("react"));
12
+ var _schema = require("../schema.cjs");
13
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
+ async function RecentPostsWidget() {
16
+ const {
17
+ t
18
+ } = await (0, _server2.getTranslation)();
19
+ const posts = await _server.db.select({
20
+ id: _schema.postsTable.id,
21
+ title: _schema.postsTable.title,
22
+ createdAt: _schema.postsTable.createdAt,
23
+ author: {
24
+ name: _server.userTable.name,
25
+ image: _server.userTable.image
26
+ }
27
+ }).from(_schema.postsTable).leftJoin(_server.userTable, (0, _drizzleOrm.eq)(_schema.postsTable.authorId, _server.userTable.id)).orderBy((0, _drizzleOrm.desc)(_schema.postsTable.createdAt)).limit(5);
28
+ return /* @__PURE__ */React.createElement(_ui.Card, null, /* @__PURE__ */React.createElement(_ui.CardHeader, null, /* @__PURE__ */React.createElement(_ui.CardTitle, null, t("Recent Posts")), /* @__PURE__ */React.createElement(_ui.CardDescription, null, t("The latest articles published on your blog."))), /* @__PURE__ */React.createElement(_ui.CardContent, {
29
+ className: "grid gap-8"
30
+ }, posts.map(post => /* @__PURE__ */React.createElement("div", {
31
+ key: post.id,
32
+ className: "flex items-center gap-4"
33
+ }, /* @__PURE__ */React.createElement(_ui.Avatar, {
34
+ className: "h-9 w-9"
35
+ }, /* @__PURE__ */React.createElement(_ui.AvatarImage, {
36
+ src: post.author?.image || "",
37
+ alt: "Avatar"
38
+ }), /* @__PURE__ */React.createElement(_ui.AvatarFallback, null, post.author?.name?.substring(0, 2).toUpperCase() || "AU")), /* @__PURE__ */React.createElement("div", {
39
+ className: "grid gap-1"
40
+ }, /* @__PURE__ */React.createElement("p", {
41
+ className: "text-sm font-medium leading-none"
42
+ }, post.title), /* @__PURE__ */React.createElement("p", {
43
+ className: "text-sm text-muted-foreground"
44
+ }, new Date(post.createdAt).toLocaleDateString())))), posts.length === 0 && /* @__PURE__ */React.createElement("div", {
45
+ className: "text-center py-4 text-muted-foreground"
46
+ }, t("No posts found."))));
47
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export default function RecentPostsWidget(): Promise<React.JSX.Element>;
@@ -0,0 +1,28 @@
1
+ import { db, userTable } from "@arch-cadre/core/server";
2
+ import { getTranslation } from "@arch-cadre/intl/server";
3
+ import {
4
+ Avatar,
5
+ AvatarFallback,
6
+ AvatarImage,
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle
12
+ } from "@arch-cadre/ui";
13
+ import { desc, eq } from "drizzle-orm";
14
+ import * as React from "react";
15
+ import { postsTable } from "../schema.mjs";
16
+ export default async function RecentPostsWidget() {
17
+ const { t } = await getTranslation();
18
+ const posts = await db.select({
19
+ id: postsTable.id,
20
+ title: postsTable.title,
21
+ createdAt: postsTable.createdAt,
22
+ author: {
23
+ name: userTable.name,
24
+ image: userTable.image
25
+ }
26
+ }).from(postsTable).leftJoin(userTable, eq(postsTable.authorId, userTable.id)).orderBy(desc(postsTable.createdAt)).limit(5);
27
+ return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Recent Posts")), /* @__PURE__ */ React.createElement(CardDescription, null, t("The latest articles published on your blog."))), /* @__PURE__ */ React.createElement(CardContent, { className: "grid gap-8" }, posts.map((post) => /* @__PURE__ */ React.createElement("div", { key: post.id, className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement(Avatar, { className: "h-9 w-9" }, /* @__PURE__ */ React.createElement(AvatarImage, { src: post.author?.image || "", alt: "Avatar" }), /* @__PURE__ */ React.createElement(AvatarFallback, null, post.author?.name?.substring(0, 2).toUpperCase() || "AU")), /* @__PURE__ */ React.createElement("div", { className: "grid gap-1" }, /* @__PURE__ */ React.createElement("p", { className: "text-sm font-medium leading-none" }, post.title), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-muted-foreground" }, new Date(post.createdAt).toLocaleDateString())))), posts.length === 0 && /* @__PURE__ */ React.createElement("div", { className: "text-center py-4 text-muted-foreground" }, t("No posts found."))));
28
+ }
@@ -1,7 +1,7 @@
1
1
  import { type VariantProps } from "class-variance-authority";
2
2
  import * as React from "react";
3
3
  declare const buttonVariants: (props?: ({
4
- variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
4
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
5
5
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
6
6
  } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
7
7
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
package/dist/index.cjs CHANGED
@@ -5,24 +5,87 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
 
7
7
  var _server = require("@arch-cadre/core/server");
8
+ var _drizzleOrm = require("drizzle-orm");
8
9
  var _manifest = _interopRequireDefault(require("../manifest.json"));
10
+ var _BlogStatsWidget = _interopRequireDefault(require("./components/BlogStatsWidget.cjs"));
11
+ var _RecentCommentsWidget = _interopRequireDefault(require("./components/RecentCommentsWidget.cjs"));
12
+ var _RecentPostsWidget = _interopRequireDefault(require("./components/RecentPostsWidget.cjs"));
9
13
  var _navigation = require("./navigation.cjs");
10
14
  var _routes = require("./routes.cjs");
11
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ const BLOG_PERMISSIONS = [{
17
+ name: "post:create",
18
+ description: "Allow creating blog posts"
19
+ }, {
20
+ name: "post:update",
21
+ description: "Allow updating blog posts"
22
+ }, {
23
+ name: "post:delete",
24
+ description: "Allow deleting blog posts"
25
+ }, {
26
+ name: "comment:create",
27
+ description: "Allow creating comments"
28
+ }, {
29
+ name: "comment:update",
30
+ description: "Allow updating comments"
31
+ }, {
32
+ name: "comment:delete",
33
+ description: "Allow deleting comments"
34
+ }];
12
35
  const blogModule = {
13
36
  manifest: _manifest.default,
14
37
  init: async () => {
15
38
  console.log("[BlogModule] ready.");
16
39
  },
40
+ widgets: [{
41
+ id: "blog-stats",
42
+ name: "Blog Stats",
43
+ area: "dashboard-stats",
44
+ component: _BlogStatsWidget.default,
45
+ priority: 20
46
+ }, {
47
+ id: "recent-posts",
48
+ name: "Recent Posts",
49
+ area: "dashboard-main",
50
+ component: _RecentPostsWidget.default,
51
+ priority: 20
52
+ }, {
53
+ id: "recent-comments",
54
+ name: "Recent Comments",
55
+ area: "dashboard-main",
56
+ component: _RecentCommentsWidget.default,
57
+ priority: 30
58
+ }],
17
59
  onEnable: async () => {
60
+ console.log("[BlogModule] enabling and registering permissions...");
61
+ try {
62
+ for (const perm of BLOG_PERMISSIONS) {
63
+ await (0, _server.createPermission)(perm.name, perm.description);
64
+ }
65
+ const roles = await (0, _server.getRoles)();
66
+ const adminRole = roles.find(r => r.name === "admin");
67
+ if (adminRole) {
68
+ const blogPermNames = BLOG_PERMISSIONS.map(p => p.name);
69
+ const blogPerms = await _server.db.select().from(_server.permissionsTable).where((0, _drizzleOrm.inArray)(_server.permissionsTable.name, blogPermNames));
70
+ for (const p of blogPerms) {
71
+ await (0, _server.assignPermissionToRole)(adminRole.id, p.id);
72
+ }
73
+ console.log("[BlogModule] Permissions assigned to admin role.");
74
+ }
75
+ } catch (error) {
76
+ console.error("[BlogModule] Error during permission registration:", error);
77
+ }
18
78
  console.log("[BlogModule] enabled.");
19
79
  },
20
80
  onDisable: async () => {
21
- console.log("[Blog] onDisable: Dropping all tables physically...");
81
+ console.log("[Blog] onDisable: Cleaning up tables and permissions...");
22
82
  try {
83
+ const blogPermNames = BLOG_PERMISSIONS.map(p => p.name);
84
+ await _server.db.delete(_server.permissionsTable).where((0, _drizzleOrm.inArray)(_server.permissionsTable.name, blogPermNames));
85
+ console.log("[BlogModule] Permissions and mappings removed.");
23
86
  const tables = ["blog_posts", "blog_comments"];
24
87
  for (const table of tables) {
25
- await _server.db.execute(`DROP TABLE IF EXISTS ${table} CASCADE`);
88
+ await _server.db.execute(_drizzleOrm.sql.raw(`DROP TABLE IF EXISTS ${table} CASCADE`));
26
89
  }
27
90
  } catch (e) {
28
91
  console.error("[Blog] onDisable Error:", e);
package/dist/index.mjs CHANGED
@@ -1,21 +1,86 @@
1
- import { db } from "@arch-cadre/core/server";
1
+ import {
2
+ assignPermissionToRole,
3
+ createPermission,
4
+ db,
5
+ getRoles,
6
+ permissionsTable
7
+ } from "@arch-cadre/core/server";
8
+ import { inArray, sql } from "drizzle-orm";
2
9
  import manifest from "../manifest.json" with { type: "json" };
10
+ import BlogStatsWidget from "./components/BlogStatsWidget.mjs";
11
+ import RecentCommentsWidget from "./components/RecentCommentsWidget.mjs";
12
+ import RecentPostsWidget from "./components/RecentPostsWidget.mjs";
3
13
  import { navigation } from "./navigation.mjs";
4
14
  import { privateRoutes, publicRoutes } from "./routes.mjs";
15
+ const BLOG_PERMISSIONS = [
16
+ { name: "post:create", description: "Allow creating blog posts" },
17
+ { name: "post:update", description: "Allow updating blog posts" },
18
+ { name: "post:delete", description: "Allow deleting blog posts" },
19
+ { name: "comment:create", description: "Allow creating comments" },
20
+ { name: "comment:update", description: "Allow updating comments" },
21
+ { name: "comment:delete", description: "Allow deleting comments" }
22
+ ];
5
23
  const blogModule = {
6
24
  manifest,
7
25
  init: async () => {
8
26
  console.log("[BlogModule] ready.");
9
27
  },
28
+ widgets: [
29
+ {
30
+ id: "blog-stats",
31
+ name: "Blog Stats",
32
+ area: "dashboard-stats",
33
+ component: BlogStatsWidget,
34
+ priority: 20
35
+ },
36
+ {
37
+ id: "recent-posts",
38
+ name: "Recent Posts",
39
+ area: "dashboard-main",
40
+ component: RecentPostsWidget,
41
+ priority: 20
42
+ },
43
+ {
44
+ id: "recent-comments",
45
+ name: "Recent Comments",
46
+ area: "dashboard-main",
47
+ component: RecentCommentsWidget,
48
+ priority: 30
49
+ }
50
+ ],
10
51
  onEnable: async () => {
52
+ console.log("[BlogModule] enabling and registering permissions...");
53
+ try {
54
+ for (const perm of BLOG_PERMISSIONS) {
55
+ await createPermission(perm.name, perm.description);
56
+ }
57
+ const roles = await getRoles();
58
+ const adminRole = roles.find((r) => r.name === "admin");
59
+ if (adminRole) {
60
+ const blogPermNames = BLOG_PERMISSIONS.map((p) => p.name);
61
+ const blogPerms = await db.select().from(permissionsTable).where(inArray(permissionsTable.name, blogPermNames));
62
+ for (const p of blogPerms) {
63
+ await assignPermissionToRole(adminRole.id, p.id);
64
+ }
65
+ console.log("[BlogModule] Permissions assigned to admin role.");
66
+ }
67
+ } catch (error) {
68
+ console.error(
69
+ "[BlogModule] Error during permission registration:",
70
+ error
71
+ );
72
+ }
11
73
  console.log("[BlogModule] enabled.");
12
74
  },
13
75
  onDisable: async () => {
14
- console.log("[Blog] onDisable: Dropping all tables physically...");
76
+ console.log("[Blog] onDisable: Cleaning up tables and permissions...");
15
77
  try {
78
+ const blogPermNames = BLOG_PERMISSIONS.map((p) => p.name);
79
+ await db.delete(permissionsTable).where(inArray(permissionsTable.name, blogPermNames));
80
+ console.log("[BlogModule] Permissions and mappings removed.");
16
81
  const tables = ["blog_posts", "blog_comments"];
17
82
  for (const table of tables) {
18
- await db.execute(`DROP TABLE IF EXISTS ${table} CASCADE`);
83
+ await db.execute(sql.raw(`DROP TABLE IF EXISTS ${table} CASCADE`));
19
84
  }
20
85
  } catch (e) {
21
86
  console.error("[Blog] onDisable Error:", e);
@@ -15,7 +15,9 @@ const navigation = exports.navigation = {
15
15
  [(0, _intl.i18n)("CMS")]: [{
16
16
  title: (0, _intl.i18n)("Blog Manager"),
17
17
  url: "/blog",
18
- icon: "solar:posts-carousel-vertical-broken"
18
+ icon: "solar:posts-carousel-vertical-broken",
19
+ roles: ["admin"],
20
+ permissions: ["post:create", "post:update", "post:delete"]
19
21
  }]
20
22
  }
21
23
  };
@@ -12,7 +12,9 @@ export const navigation = {
12
12
  {
13
13
  title: i18n("Blog Manager"),
14
14
  url: "/blog",
15
- icon: "solar:posts-carousel-vertical-broken"
15
+ icon: "solar:posts-carousel-vertical-broken",
16
+ roles: ["admin"],
17
+ permissions: ["post:create", "post:update", "post:delete"]
16
18
  }
17
19
  ]
18
20
  }
package/dist/routes.cjs CHANGED
@@ -46,12 +46,15 @@ const privateRoutes = exports.privateRoutes = [{
46
46
  posts
47
47
  });
48
48
  },
49
- auth: true
49
+ auth: true,
50
+ roles: ["admin"],
51
+ permissions: ["post:create", "post:update", "post:delete"]
50
52
  }, {
51
53
  path: "/blog/new",
52
54
  component: _views.CreatePostForm,
53
55
  auth: true,
54
- roles: ["admin"]
56
+ roles: ["admin"],
57
+ permissions: ["post:create"]
55
58
  }, {
56
59
  path: "/blog/edit/:id",
57
60
  component: async ({
@@ -66,5 +69,6 @@ const privateRoutes = exports.privateRoutes = [{
66
69
  });
67
70
  },
68
71
  auth: true,
69
- roles: ["admin"]
72
+ roles: ["admin"],
73
+ permissions: ["post:update"]
70
74
  }];
package/dist/routes.mjs CHANGED
@@ -43,13 +43,16 @@ export const privateRoutes = [
43
43
  const posts = await getPosts();
44
44
  return /* @__PURE__ */ React.createElement(BlogAdminPage, { posts });
45
45
  },
46
- auth: true
46
+ auth: true,
47
+ roles: ["admin"],
48
+ permissions: ["post:create", "post:update", "post:delete"]
47
49
  },
48
50
  {
49
51
  path: "/blog/new",
50
52
  component: CreatePostForm,
51
53
  auth: true,
52
- roles: ["admin"]
54
+ roles: ["admin"],
55
+ permissions: ["post:create"]
53
56
  },
54
57
  {
55
58
  path: "/blog/edit/:id",
@@ -59,6 +62,7 @@ export const privateRoutes = [
59
62
  return /* @__PURE__ */ React.createElement(EditPostForm, { post });
60
63
  },
61
64
  auth: true,
62
- roles: ["admin"]
65
+ roles: ["admin"],
66
+ permissions: ["post:update"]
63
67
  }
64
68
  ];