@isardsat/editorial-server 6.0.4 → 6.0.5

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/dist/app.js CHANGED
@@ -7,7 +7,6 @@ import { createHooks } from "./lib/hooks.js";
7
7
  import { createStorage } from "./lib/storage.js";
8
8
  import { createActionRoutes } from "./routes/actions.js";
9
9
  import { createAdminRoutes } from "./routes/admin.js";
10
- import { createConfigRoutes } from "./routes/config.js";
11
10
  import { createDataRoutes } from "./routes/data.js";
12
11
  import { createFilesRoutes } from "./routes/files.js";
13
12
  export const BASE_EDITORIAL_PATH = "./editorial";
@@ -17,7 +16,6 @@ export async function createEditorialServer({ configDirectory = BASE_EDITORIAL_P
17
16
  const storage = createStorage(editorialDirectory);
18
17
  const hooks = await createHooks(configDirectory);
19
18
  app.use(logger());
20
- app.route("/api/v1", createConfigRoutes(config));
21
19
  app.route("/api/v1", createDataRoutes(storage));
22
20
  app.route("/api/v1", createFilesRoutes());
23
21
  app.route("/api/v1", createActionRoutes(storage, hooks));
@@ -2,7 +2,7 @@ export declare function createHooks(configDirectory: string): Promise<{
2
2
  onPublish: (content: any, schema: any) => Promise<any>;
3
3
  onLocalize: (content: any, schema: any) => Promise<any>;
4
4
  onLocalizeEnd: (content: any, schema: any) => Promise<any>;
5
- onPull: (author: string) => Promise<any>;
6
- onPush: (author: string) => Promise<any>;
5
+ onPull: () => Promise<any>;
6
+ onPush: () => Promise<any>;
7
7
  }>;
8
8
  export type Hooks = Awaited<ReturnType<typeof createHooks>>;
package/dist/lib/hooks.js CHANGED
@@ -1,10 +1,10 @@
1
- import { join } from "node:path";
1
+ import { join } from 'node:path';
2
2
  export async function createHooks(configDirectory) {
3
- const hooksDirPath = join(configDirectory, "hooks");
3
+ const hooksDirPath = join(configDirectory, 'hooks');
4
4
  async function loadHook(name) {
5
5
  const hookScript = join(process.cwd(), hooksDirPath, name);
6
6
  const module = await import(`${hookScript}.mjs`);
7
- const hookFunction = typeof module.default === "function" ? module.default : null;
7
+ const hookFunction = typeof module.default === 'function' ? module.default : null;
8
8
  return hookFunction;
9
9
  }
10
10
  async function executeHook(hookName, ...args) {
@@ -14,27 +14,19 @@ export async function createHooks(configDirectory) {
14
14
  return hook(...args);
15
15
  }
16
16
  async function onPublish(content, schema) {
17
- return executeHook("onPublish", content, schema);
17
+ return executeHook('onPublish', content, schema);
18
18
  }
19
19
  async function onLocalize(content, schema) {
20
- return executeHook("onLocalize", content, schema);
20
+ return executeHook('onLocalize', content, schema);
21
21
  }
22
22
  async function onLocalizeEnd(content, schema) {
23
- return executeHook("onLocalizeEnd", content, schema);
23
+ return executeHook('onLocalizeEnd', content, schema);
24
24
  }
25
- async function onPull(author) {
26
- if (process.env.NODE_ENV !== "production") {
27
- console.info("Dropping onPull hook event in development environment");
28
- return;
29
- }
30
- return executeHook("onPull", author);
25
+ async function onPull() {
26
+ return executeHook('onPull');
31
27
  }
32
- async function onPush(author) {
33
- if (process.env.NODE_ENV !== "production") {
34
- console.info("Dropping onPush hook event in development environment");
35
- return;
36
- }
37
- return executeHook("onPush", author);
28
+ async function onPush() {
29
+ return executeHook('onPush');
38
30
  }
39
31
  return { onPublish, onLocalize, onLocalizeEnd, onPull, onPush };
40
32
  }
@@ -1,4 +1,4 @@
1
- import type { EditorialData, EditorialDataObjectWithType } from "@isardsat/editorial-common";
1
+ import type { EditorialData, EditorialDataObjectWithType } from '@isardsat/editorial-common';
2
2
  export declare function createStorage(dataDirectory: string): {
3
3
  getSchema: () => Promise<Record<string, {
4
4
  displayName: string;
@@ -13,7 +13,6 @@ export declare function createStorage(dataDirectory: string): {
13
13
  singleton?: boolean | undefined;
14
14
  }>>;
15
15
  getContent: () => Promise<EditorialData>;
16
- getLocalisationMessages: (langCode: string) => Promise<any>;
17
16
  saveLocalisationMessages: (messages: any) => Promise<boolean>;
18
17
  createItem: (item: EditorialDataObjectWithType) => Promise<import("zod").objectOutputType<{
19
18
  id: import("zod").ZodString;
@@ -29,8 +28,6 @@ export declare function createStorage(dataDirectory: string): {
29
28
  createdAt: import("zod").ZodDefault<import("zod").ZodString>;
30
29
  updatedAt: import("zod").ZodDefault<import("zod").ZodString>;
31
30
  }, import("zod").ZodTypeAny, "passthrough">>>>;
32
- saveContent: ({ production }: {
33
- production?: boolean;
34
- }) => Promise<boolean>;
31
+ saveProdContent: () => Promise<boolean>;
35
32
  };
36
33
  export type Storage = ReturnType<typeof createStorage>;
@@ -1,15 +1,15 @@
1
- import { EditorialDataItemSchema, EditorialDataSchema, EditorialSchemaSchema, } from "@isardsat/editorial-common";
2
- import { readFile } from "fs/promises";
3
- import { join } from "path";
4
- import { parse } from "yaml";
5
- import { writeFileSafe } from "./utils/fs.js";
1
+ import { EditorialDataItemSchema, EditorialDataSchema, EditorialSchemaSchema, } from '@isardsat/editorial-common';
2
+ import { readFile } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import { parse } from 'yaml';
5
+ import { writeFileSafe } from './utils/fs.js';
6
6
  export function createStorage(dataDirectory) {
7
- const schemaPath = join(dataDirectory, "schema.yaml");
8
- const dataPath = join(dataDirectory, "data.json");
9
- const dataProdPath = join(dataDirectory, "data.prod.json");
10
- const dataExtractedPath = join(dataDirectory, "data.messages.json");
7
+ const schemaPath = join(dataDirectory, 'schema.yaml');
8
+ const dataPath = join(dataDirectory, 'data.json');
9
+ const dataProdPath = join(dataDirectory, 'data.prod.json');
10
+ const dataExtractedPath = join(dataDirectory, 'data.messages.json');
11
11
  async function getSchema() {
12
- const schemaFile = await readFile(schemaPath, "utf-8").then((value) => parse(value));
12
+ const schemaFile = await readFile(schemaPath, 'utf-8').then((value) => parse(value));
13
13
  const schema = EditorialSchemaSchema.parse(schemaFile);
14
14
  return schema;
15
15
  }
@@ -17,14 +17,11 @@ export function createStorage(dataDirectory) {
17
17
  * TODO: This should ideally cache the result of reading the file until an update occurs.
18
18
  */
19
19
  async function getContent() {
20
- return await readFile(dataPath, "utf-8").then((value) => JSON.parse(value));
20
+ return await readFile(dataPath, 'utf-8').then((value) => JSON.parse(value));
21
21
  }
22
- async function getLocalisationMessages(langCode) {
23
- return await readFile(join(dataDirectory, "locales", "messages", `${langCode}.json`), "utf-8").then((value) => JSON.parse(value));
24
- }
25
- async function saveContent({ production }) {
22
+ async function saveProdContent() {
26
23
  const content = await getContent();
27
- await writeFileSafe(production ? dataProdPath : dataPath, JSON.stringify(EditorialDataSchema.parse(content), null, 2));
24
+ await writeFileSafe(dataProdPath, JSON.stringify(EditorialDataSchema.parse(content), null, 2));
28
25
  return true;
29
26
  }
30
27
  async function saveLocalisationMessages(messages) {
@@ -61,11 +58,10 @@ export function createStorage(dataDirectory) {
61
58
  return {
62
59
  getSchema,
63
60
  getContent,
64
- getLocalisationMessages,
65
61
  saveLocalisationMessages,
66
62
  createItem,
67
63
  updateItem,
68
64
  deleteItem,
69
- saveContent,
65
+ saveProdContent,
70
66
  };
71
67
  }
@@ -1,4 +1,4 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
2
- import type { Hooks } from "../lib/hooks.js";
3
- import type { Storage } from "../lib/storage.js";
1
+ import { OpenAPIHono } from '@hono/zod-openapi';
2
+ import type { Hooks } from '../lib/hooks.js';
3
+ import type { Storage } from '../lib/storage.js';
4
4
  export declare function createActionRoutes(storage: Storage, hooks: Hooks): OpenAPIHono<import("hono").Env, {}, "/">;
@@ -1,35 +1,23 @@
1
- import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
2
- const ActionRequestSchema = z.object({
3
- author: z.string(),
4
- });
1
+ import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
5
2
  export function createActionRoutes(storage, hooks) {
6
3
  const app = new OpenAPIHono();
7
4
  app.openapi(createRoute({
8
- method: "post",
9
- path: "/publish",
10
- request: {
11
- body: {
12
- content: {
13
- "application/json": {
14
- schema: ActionRequestSchema,
15
- },
16
- },
17
- },
18
- },
5
+ method: 'post',
6
+ path: '/publish',
19
7
  responses: {
20
8
  202: {
21
9
  content: {
22
- "application/json": {
10
+ 'application/json': {
23
11
  schema: z.boolean(),
24
12
  },
25
13
  },
26
- description: "Trigger the publishing process",
14
+ description: 'Trigger the publishing process',
27
15
  },
28
16
  },
29
17
  }),
30
18
  // TODO: Don't async, let the promises run in the background.
31
19
  async (c) => {
32
- await storage.saveContent({ production: false });
20
+ await storage.saveProdContent();
33
21
  const content = await storage.getContent();
34
22
  const schema = await storage.getSchema();
35
23
  try {
@@ -37,53 +25,33 @@ export function createActionRoutes(storage, hooks) {
37
25
  await storage.saveLocalisationMessages(scriptResult);
38
26
  }
39
27
  catch (error) {
40
- console.error("Error executing script:", error);
28
+ console.error('Error executing script:', error);
41
29
  return c.json(false);
42
30
  }
43
31
  return c.json(true);
44
32
  });
45
33
  app.openapi(createRoute({
46
- method: "post",
47
- path: "/pull",
48
- request: {
49
- body: {
50
- content: {
51
- "application/json": {
52
- schema: ActionRequestSchema,
53
- },
54
- },
55
- },
56
- },
34
+ method: 'post',
35
+ path: '/pull',
57
36
  responses: {
58
37
  200: {
59
- description: "Trigger the pull process",
38
+ description: 'Trigger the pull process',
60
39
  },
61
40
  },
62
41
  }), async (c) => {
63
- const { author } = c.req.valid("json");
64
- await hooks.onPull(author);
42
+ await hooks.onPull();
65
43
  return c.json(true);
66
44
  });
67
45
  app.openapi(createRoute({
68
- method: "post",
69
- path: "/push",
70
- request: {
71
- body: {
72
- content: {
73
- "application/json": {
74
- schema: ActionRequestSchema,
75
- },
76
- },
77
- },
78
- },
46
+ method: 'post',
47
+ path: '/push',
79
48
  responses: {
80
49
  200: {
81
- description: "Trigger the push process",
50
+ description: 'Trigger the push process',
82
51
  },
83
52
  },
84
53
  }), async (c) => {
85
- const { author } = c.req.valid("json");
86
- await hooks.onPush(author);
54
+ await hooks.onPush();
87
55
  return c.json(true);
88
56
  });
89
57
  return app;
@@ -45,14 +45,12 @@ export function createDataRoutes(storage) {
45
45
  param: { name: "itemType", in: "path" },
46
46
  example: "newsItem",
47
47
  }),
48
- }),
49
- query: z.object({
50
- lang: z
48
+ locale: z
51
49
  .string()
52
50
  .optional()
53
51
  .openapi({
54
- param: { name: "lang", in: "query" },
55
- example: "es",
52
+ param: { name: "locale", in: "query" },
53
+ example: "es_ES",
56
54
  }),
57
55
  }),
58
56
  },
@@ -68,20 +66,8 @@ export function createDataRoutes(storage) {
68
66
  },
69
67
  }), async (c) => {
70
68
  const { itemType } = c.req.valid("param");
71
- const { lang } = c.req.valid("query");
72
69
  const content = await storage.getContent();
73
- const collection = content[itemType];
74
- // TODO: Formalize this process.
75
- if (lang) {
76
- const messages = await storage.getLocalisationMessages(lang);
77
- for (const [key, message] of Object.entries(messages)) {
78
- const [contentKey, typeKey, fieldKey, hash] = key.split(".");
79
- if (contentKey === itemType) {
80
- collection[typeKey][fieldKey] = message.defaultMessage;
81
- }
82
- }
83
- }
84
- return c.json(collection);
70
+ return c.json(content[itemType]);
85
71
  });
86
72
  app.openapi(createRoute({
87
73
  method: "get",
@@ -122,14 +108,12 @@ export function createDataRoutes(storage) {
122
108
  param: { name: "id", in: "path" },
123
109
  example: "about-us",
124
110
  }),
125
- }),
126
- query: z.object({
127
- lang: z
111
+ locale: z
128
112
  .string()
129
113
  .optional()
130
114
  .openapi({
131
- param: { name: "lang", in: "query" },
132
- example: "es",
115
+ param: { name: "locale", in: "query" },
116
+ example: "es_ES",
133
117
  }),
134
118
  }),
135
119
  },
@@ -145,20 +129,8 @@ export function createDataRoutes(storage) {
145
129
  },
146
130
  }), async (c) => {
147
131
  const { itemType, id } = c.req.valid("param");
148
- const { lang } = c.req.valid("query");
149
132
  const content = await storage.getContent();
150
- const item = content[itemType][id];
151
- // TODO: Formalize this process.
152
- if (lang) {
153
- const messages = await storage.getLocalisationMessages(lang);
154
- for (const [key, message] of Object.entries(messages)) {
155
- const [contentKey, typeKey, fieldKey, hash] = key.split(".");
156
- if (typeKey === id) {
157
- item[fieldKey] = message.defaultMessage;
158
- }
159
- }
160
- }
161
- return c.json(item);
133
+ return c.json(content[itemType][id]);
162
134
  });
163
135
  app.openapi(createRoute({
164
136
  method: "put",
@@ -1,2 +1,2 @@
1
- import { OpenAPIHono } from "@hono/zod-openapi";
1
+ import { OpenAPIHono } from '@hono/zod-openapi';
2
2
  export declare function createFilesRoutes(): OpenAPIHono<import("hono").Env, {}, "/">;
@@ -1,41 +1,30 @@
1
- import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
2
- import { EditorialFilesResponseSchema, } from "@isardsat/editorial-common";
3
- import { readdirSync, statSync } from "node:fs";
4
- import { access, constants, mkdir, rename } from "node:fs/promises";
5
- import { basename, join, normalize } from "node:path";
1
+ import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
2
+ import { EditorialFilesSchema, } from '@isardsat/editorial-common';
3
+ import { readdirSync, statSync } from 'node:fs';
4
+ import { access, constants, mkdir, rename } from 'node:fs/promises';
5
+ import { basename, join, normalize } from 'node:path';
6
6
  export function createFilesRoutes() {
7
7
  const app = new OpenAPIHono();
8
- const publicDirPath = "public";
9
- const deletedDirPath = "public/.deleted";
8
+ const publicDirPath = 'public';
9
+ const deletedDirPath = 'public/.deleted';
10
10
  app.openapi(createRoute({
11
- method: "get",
12
- path: "/files",
11
+ method: 'get',
12
+ path: '/files',
13
13
  responses: {
14
14
  200: {
15
15
  content: {
16
- "application/json": {
17
- schema: EditorialFilesResponseSchema,
16
+ 'application/json': {
17
+ schema: EditorialFilesSchema,
18
18
  },
19
19
  },
20
- description: "Get tree of public files with total size",
20
+ description: 'Get tree of public files',
21
21
  },
22
22
  },
23
23
  }), async (c) => {
24
- function calculateTotalSize(files) {
25
- return files.reduce((total, file) => {
26
- if (file.type === "file") {
27
- return total + file.size;
28
- }
29
- else if (file.children) {
30
- return total + calculateTotalSize(file.children);
31
- }
32
- return total;
33
- }, 0);
34
- }
35
24
  function readDirectoryChildren(path) {
36
25
  const directory = readdirSync(path);
37
26
  return directory
38
- .filter((fileName) => !fileName.startsWith("."))
27
+ .filter((fileName) => !fileName.startsWith('.'))
39
28
  .map((fileName) => {
40
29
  const file = statSync(join(path, fileName));
41
30
  const isDirectory = file.isDirectory();
@@ -43,25 +32,24 @@ export function createFilesRoutes() {
43
32
  name: basename(fileName),
44
33
  path: join(path, fileName),
45
34
  size: file.size,
46
- type: isDirectory ? "directory" : "file",
35
+ type: isDirectory ? 'directory' : 'file',
47
36
  children: isDirectory
48
37
  ? readDirectoryChildren(join(path, fileName))
49
38
  : undefined,
50
39
  };
51
40
  })
52
- .sort((a, b) => (a.type === "directory" ? -1 : 0));
41
+ .sort((a, b) => (a.type === 'directory' ? -1 : 0));
53
42
  }
54
43
  const files = readDirectoryChildren(publicDirPath);
55
- const totalSize = calculateTotalSize(files);
56
- return c.json({ files, totalSize });
44
+ return c.json(files);
57
45
  });
58
46
  app.openapi(createRoute({
59
- method: "delete",
60
- path: "/files",
47
+ method: 'delete',
48
+ path: '/files',
61
49
  request: {
62
50
  body: {
63
51
  content: {
64
- "application/json": {
52
+ 'application/json': {
65
53
  schema: z.object({
66
54
  path: z.string(),
67
55
  }),
@@ -73,55 +61,55 @@ export function createFilesRoutes() {
73
61
  responses: {
74
62
  200: {
75
63
  content: {
76
- "application/json": {
64
+ 'application/json': {
77
65
  schema: z.boolean(),
78
66
  },
79
67
  },
80
- description: "",
68
+ description: '',
81
69
  },
82
70
  400: {
83
71
  content: {
84
- "application/json": {
72
+ 'application/json': {
85
73
  schema: z.object({
86
74
  error: z.string(),
87
75
  }),
88
76
  },
89
77
  },
90
- description: "Invalid file path",
78
+ description: 'Invalid file path',
91
79
  },
92
80
  404: {
93
81
  content: {
94
- "application/json": {
82
+ 'application/json': {
95
83
  schema: z.object({
96
84
  error: z.string(),
97
85
  }),
98
86
  },
99
87
  },
100
- description: "File not found",
88
+ description: 'File not found',
101
89
  },
102
90
  500: {
103
91
  content: {
104
- "application/json": {
92
+ 'application/json': {
105
93
  schema: z.object({
106
94
  error: z.string(),
107
95
  }),
108
96
  },
109
97
  },
110
- description: "Server error",
98
+ description: 'Server error',
111
99
  },
112
100
  },
113
101
  }), async (c) => {
114
- const { path: filePath } = c.req.valid("json");
102
+ const { path: filePath } = c.req.valid('json');
115
103
  try {
116
104
  const normalizedPath = normalize(filePath);
117
105
  if (!normalizedPath.startsWith(publicDirPath)) {
118
- return c.json({ error: "Invalid file path" }, 400);
106
+ return c.json({ error: 'Invalid file path' }, 400);
119
107
  }
120
108
  const exists = await access(filePath, constants.W_OK)
121
109
  .then(() => true)
122
110
  .catch(() => false);
123
111
  if (!exists) {
124
- return c.json({ error: "File not found" }, 404);
112
+ return c.json({ error: 'File not found' }, 404);
125
113
  }
126
114
  // Move to deleted directory
127
115
  await mkdir(deletedDirPath, { recursive: true });
@@ -129,8 +117,8 @@ export function createFilesRoutes() {
129
117
  return c.json(true, 200);
130
118
  }
131
119
  catch (err) {
132
- console.error("Delete failed:", err);
133
- return c.json({ error: "Server error" }, 500);
120
+ console.error('Delete failed:', err);
121
+ return c.json({ error: 'Server error' }, 500);
134
122
  }
135
123
  });
136
124
  return app;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isardsat/editorial-server",
3
- "version": "6.0.4",
3
+ "version": "6.0.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,8 +14,8 @@
14
14
  "hono": "^4.6.20",
15
15
  "yaml": "^2.7.0",
16
16
  "zod": "^3.24.1",
17
- "@isardsat/editorial-admin": "^6.0.4",
18
- "@isardsat/editorial-common": "^6.0.4"
17
+ "@isardsat/editorial-common": "^6.0.5",
18
+ "@isardsat/editorial-admin": "^6.0.5"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@tsconfig/node22": "^22.0.0",