@monok/server 1.3.0 → 1.4.0
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/AGENTS.md +318 -0
- package/README.md +58 -15
- package/dist/client.d.ts +1 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -5
- package/dist/client.js.map +1 -1
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js +115 -15
- package/dist/express.js.map +1 -1
- package/dist/types.d.ts +70 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +40 -4
- package/dist/utils.js.map +1 -1
- package/dist-cjs/client.js +7 -5
- package/dist-cjs/client.js.map +1 -1
- package/dist-cjs/express.js +115 -15
- package/dist-cjs/express.js.map +1 -1
- package/dist-cjs/utils.js +40 -4
- package/dist-cjs/utils.js.map +1 -1
- package/package.json +3 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# Implementing `@monok/server` in a host application
|
|
2
|
+
|
|
3
|
+
This file is a self-contained implementation contract for AI coding assistants
|
|
4
|
+
and developers integrating Monok with an Express application or CMS. Follow the
|
|
5
|
+
requirements below rather than inventing alternate endpoint paths or response
|
|
6
|
+
shapes.
|
|
7
|
+
|
|
8
|
+
## Objective
|
|
9
|
+
|
|
10
|
+
By default, expose a public HTTPS Monok integration at these routes:
|
|
11
|
+
|
|
12
|
+
- `POST /api/monok/push`
|
|
13
|
+
- `GET /api/monok/info`
|
|
14
|
+
- `GET /api/monok/postfilter` when article discovery is supported
|
|
15
|
+
|
|
16
|
+
Use `createMonokRouter` from `@monok/server`. Mount it at `/api/monok` for the
|
|
17
|
+
default API base, or at an intentional custom base such as
|
|
18
|
+
`/something/specialAPI/monok`. When connecting the integration, enter either the
|
|
19
|
+
site origin to select the default or the complete custom base URL. Monok appends
|
|
20
|
+
`/info`, `/push`, and `/postfilter` to that base.
|
|
21
|
+
|
|
22
|
+
## Required implementation
|
|
23
|
+
|
|
24
|
+
Install the package and Express:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @monok/server express
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Create one router with the same secret API key configured for the Monok
|
|
31
|
+
workspace:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import express from "express";
|
|
35
|
+
import {
|
|
36
|
+
createMonokRouter,
|
|
37
|
+
sendMonokLinkback,
|
|
38
|
+
type MonokArticle,
|
|
39
|
+
type PostFilterQuery,
|
|
40
|
+
type PostFilterResult,
|
|
41
|
+
type SiteInfoInput
|
|
42
|
+
} from "@monok/server";
|
|
43
|
+
|
|
44
|
+
const app = express();
|
|
45
|
+
|
|
46
|
+
app.use(
|
|
47
|
+
"/api/monok",
|
|
48
|
+
createMonokRouter({
|
|
49
|
+
apiKey: process.env.MONOK_API_KEY!,
|
|
50
|
+
imageDir: "./uploads/monok",
|
|
51
|
+
getSiteInfo: async (): Promise<SiteInfoInput> => loadSiteInfo(),
|
|
52
|
+
searchArticles: async (
|
|
53
|
+
filters: PostFilterQuery
|
|
54
|
+
): Promise<PostFilterResult[]> => searchPublishedArticles(filters),
|
|
55
|
+
onArticle: async (article, context) => {
|
|
56
|
+
await upsertMonokArticle(article, context);
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`onArticle` and the application's persistence work are always required.
|
|
63
|
+
`getSiteInfo` is optional. The router always exposes authenticated `/info`; when
|
|
64
|
+
the callback is omitted it returns minimal `Custom CMS` metadata so the standard
|
|
65
|
+
Monok dashboard connection and sync flow can still detect the installation.
|
|
66
|
+
`searchArticles` and `imageDir` are optional; omitting article search disables
|
|
67
|
+
covered-article and backlink discovery.
|
|
68
|
+
|
|
69
|
+
Do not add another JSON body parser solely for these routes. The router already
|
|
70
|
+
parses JSON with a 20 MB limit. A reverse proxy in front of the application must
|
|
71
|
+
also permit the request size.
|
|
72
|
+
|
|
73
|
+
## Receiving and storing articles
|
|
74
|
+
|
|
75
|
+
`onArticle` receives:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
async function onArticle(
|
|
79
|
+
article: MonokArticle,
|
|
80
|
+
context: {
|
|
81
|
+
action?: "draft" | "publish";
|
|
82
|
+
saveThumbImage(fileNameBase?: string): Promise<string | undefined>;
|
|
83
|
+
}
|
|
84
|
+
): Promise<void>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The host must:
|
|
88
|
+
|
|
89
|
+
1. Validate any application-specific constraints.
|
|
90
|
+
2. Upsert by `article.id`, or enqueue an idempotent job keyed by `article.id`, so
|
|
91
|
+
repeated delivery cannot create duplicate content.
|
|
92
|
+
3. Preserve `article.id` with the CMS record or queued job for later linkbacks
|
|
93
|
+
and updates.
|
|
94
|
+
4. Store `article.content` as the rendered HTML body when processing the job.
|
|
95
|
+
5. Apply the supplied slug, author, categories, sections, tags, language, and
|
|
96
|
+
CMS post type when the host supports them.
|
|
97
|
+
6. Treat the requested action as draft unless `context.action === "publish"`.
|
|
98
|
+
7. Before returning, await either durable CMS persistence or durable queue
|
|
99
|
+
acceptance. Do not acknowledge an in-memory, fire-and-forget task.
|
|
100
|
+
8. Throw when persistence or durable queueing fails.
|
|
101
|
+
|
|
102
|
+
The draft/publish action normally arrives in the JSON body. The package also
|
|
103
|
+
supports the legacy query parameter. Do not read `article.action` directly when
|
|
104
|
+
deciding publication state; use the validated `context.action` value.
|
|
105
|
+
|
|
106
|
+
`onArticle` returns `void` or `Promise<void>`. It must not construct an HTTP
|
|
107
|
+
response. After the callback durably saves or queues the work, the package
|
|
108
|
+
returns:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"ok": true,
|
|
113
|
+
"status": "accepted",
|
|
114
|
+
"monokId": "the-article-id"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This response acknowledges delivery only. It does not promise that a draft URL,
|
|
119
|
+
CMS record ID, thumbnail URL, or public canonical URL already exists. Custom CMS
|
|
120
|
+
implementations may render, moderate, deploy, or publish asynchronously.
|
|
121
|
+
|
|
122
|
+
Thrown errors become JSON error responses. An error with a numeric `status`
|
|
123
|
+
property uses that HTTP status; other errors return `500`.
|
|
124
|
+
|
|
125
|
+
## Article payload
|
|
126
|
+
|
|
127
|
+
The important `MonokArticle` fields are:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
interface MonokArticle {
|
|
131
|
+
id: string | number;
|
|
132
|
+
title: string;
|
|
133
|
+
shortTitle?: string;
|
|
134
|
+
description?: string;
|
|
135
|
+
content?: string;
|
|
136
|
+
text?: string;
|
|
137
|
+
slug?: string;
|
|
138
|
+
action?: "draft" | "publish";
|
|
139
|
+
author?: { id: string | number; name: string };
|
|
140
|
+
categories?: Array<{ name: string; weight?: number }>;
|
|
141
|
+
sections?: string[];
|
|
142
|
+
tags?: string[] | null;
|
|
143
|
+
language?: string;
|
|
144
|
+
type?: string;
|
|
145
|
+
thumbnail?: string;
|
|
146
|
+
thumbImageData?: string;
|
|
147
|
+
thumbAlt?: string;
|
|
148
|
+
thumbImageDescription?: string;
|
|
149
|
+
keywords?: string[];
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Treat unknown additional fields as forward-compatible metadata. Do not reject a
|
|
154
|
+
payload merely because it contains fields added by a newer Monok version.
|
|
155
|
+
|
|
156
|
+
## JPEG thumbnail handling
|
|
157
|
+
|
|
158
|
+
Monok sends `thumbImageData` as JPEG base64. It is normally raw base64, while a
|
|
159
|
+
legacy path can wrap it in `data:image/jpeg;base64,...`.
|
|
160
|
+
|
|
161
|
+
When `imageDir` is configured, call `await context.saveThumbImage()` to validate
|
|
162
|
+
and save it. The return value is the local `.jpg` path, or `undefined` when no
|
|
163
|
+
image directory or image data was provided. The default decoded-image limit is
|
|
164
|
+
15 MiB and can be changed with `maxImageBytes`.
|
|
165
|
+
|
|
166
|
+
Saving a file is not the same as making it public. If the CMS has a media API or
|
|
167
|
+
object storage, the host may instead process `article.thumbImageData` itself.
|
|
168
|
+
Preserve `thumbAlt` or fall back to `thumbImageDescription`, then the title, for
|
|
169
|
+
accessible alt text.
|
|
170
|
+
|
|
171
|
+
Do not label the saved image as PNG and do not log its base64 data.
|
|
172
|
+
|
|
173
|
+
## Site information
|
|
174
|
+
|
|
175
|
+
Implement `getSiteInfo` when Monok should offer real CMS authors, categories,
|
|
176
|
+
tags, post types, or languages. Return this shape:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const info: SiteInfoInput = {
|
|
180
|
+
js_scripts: "Enabled",
|
|
181
|
+
name: "Site name",
|
|
182
|
+
logo: "https://example.com/logo.jpg",
|
|
183
|
+
categories: ["News", "Analysis"],
|
|
184
|
+
post_types: ["post", "page"],
|
|
185
|
+
tags: ["Technology", "Business"],
|
|
186
|
+
authors: [
|
|
187
|
+
{
|
|
188
|
+
id: "author-1",
|
|
189
|
+
name: "Jane Doe",
|
|
190
|
+
profile: "Editor",
|
|
191
|
+
avatar: "https://example.com/jane.jpg",
|
|
192
|
+
roles: ["editor"]
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
languages: {
|
|
196
|
+
en: { name: "English", slug: "en", locale: "en_US" }
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Do not return `version` from `getSiteInfo`. The router always injects the
|
|
202
|
+
installed `@monok/server` package version into the `/info` response.
|
|
203
|
+
|
|
204
|
+
Return empty arrays when a supported collection has no entries. Values may be
|
|
205
|
+
loaded dynamically on every request. The router sends private no-cache headers.
|
|
206
|
+
If `getSiteInfo` is omitted, `/info` returns this authenticated fallback:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"js_scripts": "Disabled",
|
|
211
|
+
"version": "1.4.0",
|
|
212
|
+
"name": "Custom CMS",
|
|
213
|
+
"logo": "",
|
|
214
|
+
"categories": [],
|
|
215
|
+
"post_types": ["post"],
|
|
216
|
+
"tags": [],
|
|
217
|
+
"authors": []
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Article discovery with `/postfilter`
|
|
222
|
+
|
|
223
|
+
Implement `searchArticles` to expose published host content to Monok. The
|
|
224
|
+
callback receives normalized values:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
interface PostFilterQuery {
|
|
228
|
+
postType: string; // defaults to "post"
|
|
229
|
+
section?: string;
|
|
230
|
+
tag?: string;
|
|
231
|
+
query?: string;
|
|
232
|
+
amount: number; // defaults to 10
|
|
233
|
+
page: number; // defaults to 1
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Apply all supplied filters, return only published/public articles, and paginate
|
|
238
|
+
deterministically. Return an array using these exact snake-case keys:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
const result: PostFilterResult = {
|
|
242
|
+
canonical_url: "https://example.com/articles/example",
|
|
243
|
+
post_id: "cms-record-id",
|
|
244
|
+
title: "Example article",
|
|
245
|
+
description: "A short plain-text description"
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Return `[]` when nothing matches. `title` and `description` may be `null`.
|
|
250
|
+
Do not return drafts, private content, article bodies, credentials, or internal
|
|
251
|
+
metadata. If `searchArticles` is omitted, `/postfilter` returns `404`.
|
|
252
|
+
|
|
253
|
+
## Authentication and cache behavior
|
|
254
|
+
|
|
255
|
+
The router performs authentication. The host should not duplicate it inside
|
|
256
|
+
callbacks.
|
|
257
|
+
|
|
258
|
+
- `/push` accepts Monok's `token` header, `X-API-Key`, or legacy `?apikey=`.
|
|
259
|
+
- `/info` and `/postfilter` accept `X-API-Key` or legacy `?apikey=`.
|
|
260
|
+
- Comparisons are timing-safe.
|
|
261
|
+
- `/info` and `/postfilter` responses include private no-cache headers.
|
|
262
|
+
|
|
263
|
+
Keep `MONOK_API_KEY` server-side. Never expose it to browser JavaScript, commit
|
|
264
|
+
it, include it in a URL shown to users, or log it. Prefer `X-API-Key` over query
|
|
265
|
+
authentication for custom calls because URLs are commonly logged by proxies.
|
|
266
|
+
|
|
267
|
+
## Linkback after publication
|
|
268
|
+
|
|
269
|
+
When an article is actually published, notify Monok only after the public
|
|
270
|
+
canonical URL exists. This applies both to a draft published later and to an
|
|
271
|
+
asynchronous `action: "publish"` job:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
await sendMonokLinkback({
|
|
275
|
+
monokId: storedMonokId,
|
|
276
|
+
externalId: cmsArticleId,
|
|
277
|
+
url: canonicalUrl,
|
|
278
|
+
title,
|
|
279
|
+
thumbnailUrl: publicThumbnailUrl
|
|
280
|
+
}, {
|
|
281
|
+
apiKey: process.env.MONOK_API_KEY
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Call this from the host's real publication lifecycle, not merely when `/push`
|
|
286
|
+
returns or when a job enters a queue. The linkback is the authoritative signal
|
|
287
|
+
that publication completed and a canonical URL exists. A retry should be safe.
|
|
288
|
+
When useful, `externalId` is the stable article ID assigned by the host CMS; it
|
|
289
|
+
is optional because the canonical URL also identifies the publication. When
|
|
290
|
+
supplied, `thumbnailUrl` is the public thumbnail URL.
|
|
291
|
+
|
|
292
|
+
## Verification checklist
|
|
293
|
+
|
|
294
|
+
Before declaring the integration complete, verify all applicable items:
|
|
295
|
+
|
|
296
|
+
- `GET /api/monok/info` returns `401` without a key.
|
|
297
|
+
- The same route succeeds with `X-API-Key` and with legacy `?apikey=`.
|
|
298
|
+
- Info responses contain `Cache-Control: no-store`.
|
|
299
|
+
- `POST /api/monok/push` accepts a body containing `action: "draft"`.
|
|
300
|
+
- A body containing `action: "publish"` is durably persisted or queued with the
|
|
301
|
+
requested publication intent.
|
|
302
|
+
- Successful pushes return `ok: true`, `status: "accepted"`, and the same
|
|
303
|
+
`monokId`, without requiring a canonical URL.
|
|
304
|
+
- Repeating the same `article.id` updates one record instead of duplicating it.
|
|
305
|
+
- Invalid action values return `400`.
|
|
306
|
+
- JPEG data saves as `.jpg`, and invalid or oversized image data is rejected.
|
|
307
|
+
- `/postfilter` applies search, type, section, tag, amount, and page filters.
|
|
308
|
+
- `/postfilter` returns exact `canonical_url`, `post_id`, `title`, and
|
|
309
|
+
`description` fields.
|
|
310
|
+
- Synchronous or asynchronous publication sends a linkback only after the public
|
|
311
|
+
canonical URL exists.
|
|
312
|
+
- The integration is reachable through its production HTTPS reverse proxy.
|
|
313
|
+
|
|
314
|
+
Do not claim completion based only on TypeScript compilation. Exercise the HTTP
|
|
315
|
+
routes and confirm one durable draft or draft job, one publication or publication
|
|
316
|
+
job, one repeated delivery, and one discovery query against the host
|
|
317
|
+
application's persistence layer. For asynchronous publishing, also complete one
|
|
318
|
+
job and verify its eventual linkback.
|
package/README.md
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
Monok server integration helpers for Node.js/Express.
|
|
6
6
|
Easily connect your backend to Monok to receive articles, expose site info, and send linkbacks.
|
|
7
7
|
|
|
8
|
+
Using an AI coding assistant? Give it [`AGENTS.md`](./AGENTS.md). The guide contains
|
|
9
|
+
the complete host-implementation contract, response shapes, and verification checklist.
|
|
10
|
+
|
|
8
11
|
---
|
|
9
12
|
|
|
10
13
|
## ⚡ Quick Start
|
|
@@ -16,7 +19,7 @@ import { createMonokRouter, type MonokArticle } from "@monok/server";
|
|
|
16
19
|
const app = express();
|
|
17
20
|
|
|
18
21
|
app.use(
|
|
19
|
-
"/monok",
|
|
22
|
+
"/api/monok",
|
|
20
23
|
createMonokRouter({
|
|
21
24
|
apiKey: process.env.MONOK_API_KEY!,
|
|
22
25
|
onArticle: async (article: MonokArticle, { saveThumbImage, action }) => {
|
|
@@ -59,19 +62,21 @@ import {
|
|
|
59
62
|
const app = express();
|
|
60
63
|
|
|
61
64
|
app.use(
|
|
62
|
-
"/monok",
|
|
65
|
+
"/api/monok",
|
|
63
66
|
createMonokRouter({
|
|
64
67
|
apiKey: process.env.MONOK_API_KEY!,
|
|
65
68
|
imageDir: "./uploads", // optional
|
|
66
69
|
// Optional: provide site info if you want Monok to query it
|
|
67
70
|
getSiteInfo: {
|
|
68
71
|
js_scripts: "Enabled",
|
|
69
|
-
version: "1.0.0",
|
|
70
72
|
name: "My Site",
|
|
71
73
|
logo: "https://example.com/logo.png",
|
|
72
74
|
categories: ["Content", "News Articles", "Uncategorized"],
|
|
73
75
|
post_types: ["post", "page"],
|
|
74
76
|
tags: ["Tech", "Health", "News"],
|
|
77
|
+
languages: {
|
|
78
|
+
en: { name: "English", slug: "en", locale: "en_US" }
|
|
79
|
+
},
|
|
75
80
|
authors: [
|
|
76
81
|
{
|
|
77
82
|
name: "Jane Doe",
|
|
@@ -82,6 +87,15 @@ app.use(
|
|
|
82
87
|
}
|
|
83
88
|
]
|
|
84
89
|
},
|
|
90
|
+
searchArticles: async ({ postType, section, tag, query, amount, page }) => {
|
|
91
|
+
// Search published content in your CMS and return the requested page.
|
|
92
|
+
return [{
|
|
93
|
+
canonical_url: "https://example.com/blog/an-article",
|
|
94
|
+
post_id: "cms-123",
|
|
95
|
+
title: "An article",
|
|
96
|
+
description: "Article summary"
|
|
97
|
+
}];
|
|
98
|
+
},
|
|
85
99
|
onArticle: async (article: MonokArticle, { saveThumbImage, action }) => {
|
|
86
100
|
const imagePath = await saveThumbImage();
|
|
87
101
|
console.log("Received article:", article.title);
|
|
@@ -95,9 +109,9 @@ app.use(
|
|
|
95
109
|
// when you publish later, notify Monok:
|
|
96
110
|
// await sendMonokLinkback({
|
|
97
111
|
// monokId: article.id,
|
|
98
|
-
//
|
|
112
|
+
// externalId: savedArticle.id,
|
|
113
|
+
// url: "https://example.com/blog/" + (article.slug ?? article.id),
|
|
99
114
|
// title: article.title,
|
|
100
|
-
// articleTextSample: article.text?.slice(0, 500),
|
|
101
115
|
// thumbnailUrl: imagePath
|
|
102
116
|
// ? "https://example.com/uploads/" + require("path").basename(imagePath)
|
|
103
117
|
// : undefined
|
|
@@ -118,7 +132,7 @@ const { createMonokRouter } = require("@monok/server");
|
|
|
118
132
|
const app = express();
|
|
119
133
|
|
|
120
134
|
app.use(
|
|
121
|
-
"/monok",
|
|
135
|
+
"/api/monok",
|
|
122
136
|
createMonokRouter({
|
|
123
137
|
apiKey: process.env.MONOK_API_KEY,
|
|
124
138
|
onArticle: async (article, { saveThumbImage, action }) => {
|
|
@@ -141,29 +155,56 @@ app.listen(3000);
|
|
|
141
155
|
|
|
142
156
|
## 🔑 Endpoints
|
|
143
157
|
|
|
144
|
-
|
|
158
|
+
Mounting the router at `/api/monok` uses Monok's default API base. A custom or
|
|
159
|
+
versioned base is also supported, for example:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
app.use("/something/specialAPI/monok", createMonokRouter({ /* ... */ }));
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Enter that complete base URL when connecting the integration in Monok. Monok
|
|
166
|
+
appends `/info`, `/push`, and `/postfilter` to the configured base.
|
|
167
|
+
|
|
168
|
+
* **`POST /api/monok/push`**
|
|
145
169
|
Monok pushes new article data here.
|
|
146
170
|
Header: `token: <your-api-key>`
|
|
147
171
|
Body: JSON payload of the article.
|
|
148
172
|
|
|
149
|
-
* `action`
|
|
173
|
+
* `action` is read from the JSON body and may be `draft` or `publish`.
|
|
174
|
+
* A legacy `?action=draft|publish` query parameter is also accepted.
|
|
175
|
+
* After `onArticle` durably saves or queues the work, the route acknowledges
|
|
176
|
+
it with `{ "ok": true, "status": "accepted", "monokId": "..." }`. This
|
|
177
|
+
does not claim that asynchronous publication has completed.
|
|
178
|
+
|
|
179
|
+
* **`GET /api/monok/info`**
|
|
180
|
+
Monok fetches this to authenticate and learn your site branding, categories, tags, and authors.
|
|
181
|
+
If `getSiteInfo` is not provided, the route remains available and returns a
|
|
182
|
+
minimal `Custom CMS` response with empty metadata collections.
|
|
150
183
|
|
|
151
|
-
* **`GET /monok/
|
|
152
|
-
If `
|
|
153
|
-
|
|
184
|
+
* **`GET /api/monok/postfilter`**
|
|
185
|
+
If `searchArticles` is configured, Monok uses this route to search published
|
|
186
|
+
content. Supported parameters are `post_type`, `section`, `tag`, `q`, `amount`,
|
|
187
|
+
and `page`. If it is not configured, the route responds with `404`.
|
|
188
|
+
|
|
189
|
+
`GET /info` and `GET /postfilter` accept the API key in the `X-API-Key` header
|
|
190
|
+
(preferred) or the legacy `?apikey=` query parameter. Their responses include
|
|
191
|
+
private no-cache headers. `POST /push` also accepts Monok's `token` header.
|
|
154
192
|
|
|
155
193
|
---
|
|
156
194
|
|
|
157
195
|
## 🔗 Linkback after publishing
|
|
158
196
|
|
|
197
|
+
The push acknowledgement confirms delivery only. If publication is asynchronous,
|
|
198
|
+
send the linkback later, after the public canonical URL actually exists.
|
|
199
|
+
|
|
159
200
|
```ts
|
|
160
201
|
import { sendMonokLinkback } from "@monok/server";
|
|
161
202
|
|
|
162
203
|
await sendMonokLinkback({
|
|
163
204
|
monokId: article.id,
|
|
164
|
-
|
|
205
|
+
externalId: cmsArticle.id,
|
|
206
|
+
url: "https://example.com/blog/" + (article.slug ?? article.id),
|
|
165
207
|
title: article.title,
|
|
166
|
-
articleTextSample: article.text?.slice(0, 500),
|
|
167
208
|
thumbnailUrl: "https://example.com/uploads/" + imageFile
|
|
168
209
|
});
|
|
169
210
|
```
|
|
@@ -176,8 +217,10 @@ await sendMonokLinkback({
|
|
|
176
217
|
| ------------- | ----------------------------------------- | -------- | --------------------------------------------------------------------------------- |
|
|
177
218
|
| `apiKey` | `string` | ✅ | Token Monok sends in headers / query. |
|
|
178
219
|
| `onArticle` | `(article, ctx) => void \| Promise<void>` | ✅ | Handle new article pushes. `ctx` includes `saveThumbImage` and optional `action`. |
|
|
179
|
-
| `getSiteInfo` | `
|
|
180
|
-
| `
|
|
220
|
+
| `getSiteInfo` | `SiteInfoInput \| () => SiteInfoInput` | ❌ | Return host metadata; the router injects its package version. A minimal fallback is used when omitted. |
|
|
221
|
+
| `searchArticles` | `(query) => PostFilterResult[]` | ❌ | Enables authenticated article discovery through `GET /postfilter`. |
|
|
222
|
+
| `imageDir` | `string` | ❌ | Directory in which validated JPEG thumbnails are saved. |
|
|
223
|
+
| `maxImageBytes` | `number` | ❌ | Maximum decoded JPEG size; defaults to 15 MiB. |
|
|
181
224
|
| `basePath` | `string` | ❌ | Custom prefix for routes (default ""). |
|
|
182
225
|
|
|
183
226
|
---
|
package/dist/client.d.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type { LinkbackPayload } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Send a linkback to Monok after you publish the article.
|
|
4
4
|
*/
|
|
5
|
-
export declare function sendMonokLinkback({ monokId,
|
|
5
|
+
export declare function sendMonokLinkback({ monokId, externalId, url, title, thumbnailUrl }: LinkbackPayload, opts?: {
|
|
6
6
|
monokBaseUrl?: string;
|
|
7
|
-
userId?: string;
|
|
8
7
|
apiKey?: string;
|
|
9
8
|
}): Promise<void>;
|
|
10
9
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,eAAe,EAClE,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
package/dist/client.js
CHANGED
|
@@ -2,14 +2,16 @@ import fetch from "node-fetch";
|
|
|
2
2
|
/**
|
|
3
3
|
* Send a linkback to Monok after you publish the article.
|
|
4
4
|
*/
|
|
5
|
-
export async function sendMonokLinkback({ monokId,
|
|
5
|
+
export async function sendMonokLinkback({ monokId, externalId, url, title, thumbnailUrl }, opts) {
|
|
6
6
|
const endpoint = (opts?.monokBaseUrl ?? "https://www.monok.com") + "/api/v1/linkback";
|
|
7
7
|
const payload = {
|
|
8
|
-
puff: { title
|
|
8
|
+
puff: { title },
|
|
9
9
|
id: monokId,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
...(externalId !== undefined && externalId !== null
|
|
11
|
+
? { externalId: String(externalId) }
|
|
12
|
+
: {}),
|
|
13
|
+
url,
|
|
14
|
+
source: new URL(url).hostname,
|
|
13
15
|
...(thumbnailUrl ? { thumbnail: thumbnailUrl } : {})
|
|
14
16
|
};
|
|
15
17
|
const resp = await fetch(endpoint, {
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAG/B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAG/B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAmB,EAClE,IAAiD;IAEjD,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,YAAY,IAAI,uBAAuB,CAAC,GAAG,kBAAkB,CAAC;IACtF,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,EAAE,KAAK,EAAE;QACf,EAAE,EAAE,OAAO;QACX,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI;YACjD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE;YACpC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG;QACH,MAAM,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ;QAC7B,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrD,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtD;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
|
package/dist/express.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwD,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwD,MAAM,SAAS,CAAC;AAEvF,OAAO,KAAK,EACV,mBAAmB,EAKpB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAmFnE"}
|
package/dist/express.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Router, json } from "express";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { assertApiKey, makeThumbSaver } from "./utils.js";
|
|
4
|
+
const PACKAGE_VERSION = readPackageVersion();
|
|
3
5
|
export function createMonokRouter(opts) {
|
|
4
6
|
const r = Router();
|
|
5
7
|
const base = opts.basePath ?? "";
|
|
@@ -7,34 +9,132 @@ export function createMonokRouter(opts) {
|
|
|
7
9
|
r.use(json({ limit: "20mb" }));
|
|
8
10
|
// POST /push
|
|
9
11
|
r.post(base + "/push", wrap(async (req, res) => {
|
|
10
|
-
|
|
12
|
+
assertRequestApiKey(req, opts.apiKey, true);
|
|
11
13
|
const body = req.body;
|
|
12
|
-
if (
|
|
14
|
+
if (body?.id === undefined
|
|
15
|
+
|| body.id === null
|
|
16
|
+
|| (typeof body.id === "string" && body.id.trim() === "")
|
|
17
|
+
|| typeof body.title !== "string"
|
|
18
|
+
|| body.title.trim() === "") {
|
|
13
19
|
return res.status(400).json({ error: "Invalid payload" });
|
|
14
20
|
}
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
const
|
|
21
|
+
// Monok's API integration sends action in the body. Retain query support
|
|
22
|
+
// for older callers and give an explicit query value precedence.
|
|
23
|
+
const action = parseAction(firstQueryValue(req.query.action) ?? body.action);
|
|
24
|
+
const saver = makeThumbSaver(opts.imageDir, opts.maxImageBytes);
|
|
18
25
|
await Promise.resolve(opts.onArticle(body, {
|
|
19
|
-
saveThumbImage: async (fileNameBase) => saver(fileNameBase ?? body.slug ?? body.id, body.thumbImageData),
|
|
26
|
+
saveThumbImage: async (fileNameBase) => saver(fileNameBase ?? body.slug ?? String(body.id), body.thumbImageData),
|
|
20
27
|
action
|
|
21
28
|
}));
|
|
22
|
-
res.json({ ok: true, monokId: body.id });
|
|
29
|
+
res.json({ ok: true, status: "accepted", monokId: body.id });
|
|
23
30
|
}));
|
|
24
31
|
// GET /info
|
|
25
32
|
r.get(base + "/info", wrap(async (req, res) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
setPrivateNoCacheHeaders(res);
|
|
34
|
+
assertRequestApiKey(req, opts.apiKey);
|
|
35
|
+
const configuredInfo = !opts.getSiteInfo
|
|
36
|
+
? undefined
|
|
37
|
+
: typeof opts.getSiteInfo === "function"
|
|
38
|
+
? await Promise.resolve(opts.getSiteInfo())
|
|
39
|
+
: opts.getSiteInfo;
|
|
40
|
+
const info = configuredInfo
|
|
41
|
+
? { ...configuredInfo, version: PACKAGE_VERSION }
|
|
42
|
+
: defaultSiteInfo();
|
|
34
43
|
res.json(info);
|
|
35
44
|
}));
|
|
45
|
+
// GET /postfilter
|
|
46
|
+
r.get(base + "/postfilter", wrap(async (req, res) => {
|
|
47
|
+
setPrivateNoCacheHeaders(res);
|
|
48
|
+
if (!opts.searchArticles) {
|
|
49
|
+
return res.status(404).json({ error: "Article search not configured" });
|
|
50
|
+
}
|
|
51
|
+
assertRequestApiKey(req, opts.apiKey);
|
|
52
|
+
const amount = parsePositiveInteger(firstQueryValue(req.query.amount), 10, "amount");
|
|
53
|
+
const page = parsePositiveInteger(firstQueryValue(req.query.page), 1, "page");
|
|
54
|
+
const query = {
|
|
55
|
+
postType: firstQueryValue(req.query.post_type)?.trim() || "post",
|
|
56
|
+
amount,
|
|
57
|
+
page,
|
|
58
|
+
...optionalTrimmed("section", firstQueryValue(req.query.section)),
|
|
59
|
+
...optionalTrimmed("tag", firstQueryValue(req.query.tag)),
|
|
60
|
+
...optionalTrimmed("query", firstQueryValue(req.query.q))
|
|
61
|
+
};
|
|
62
|
+
const articles = await Promise.resolve(opts.searchArticles(query));
|
|
63
|
+
if (!Array.isArray(articles)) {
|
|
64
|
+
throw httpError(500, "searchArticles must return an array");
|
|
65
|
+
}
|
|
66
|
+
res.json(articles);
|
|
67
|
+
}));
|
|
36
68
|
return r;
|
|
37
69
|
}
|
|
70
|
+
function assertRequestApiKey(req, expected, allowToken = false) {
|
|
71
|
+
const provided = ((allowToken ? req.header("token") : undefined)
|
|
72
|
+
?? req.header("x-api-key")
|
|
73
|
+
?? firstQueryValue(req.query.apikey));
|
|
74
|
+
assertApiKey(provided, expected);
|
|
75
|
+
}
|
|
76
|
+
function parseAction(value) {
|
|
77
|
+
if (value === undefined || value === null || value === "")
|
|
78
|
+
return undefined;
|
|
79
|
+
if (value === "draft" || value === "publish")
|
|
80
|
+
return value;
|
|
81
|
+
throw httpError(400, "action must be either draft or publish");
|
|
82
|
+
}
|
|
83
|
+
function parsePositiveInteger(value, fallback, name) {
|
|
84
|
+
if (value === undefined || value === "")
|
|
85
|
+
return fallback;
|
|
86
|
+
if (!/^\d+$/.test(value))
|
|
87
|
+
throw httpError(400, `${name} must be a positive integer`);
|
|
88
|
+
const parsed = Number(value);
|
|
89
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
90
|
+
throw httpError(400, `${name} must be a positive integer`);
|
|
91
|
+
}
|
|
92
|
+
return parsed;
|
|
93
|
+
}
|
|
94
|
+
function firstQueryValue(value) {
|
|
95
|
+
if (typeof value === "string")
|
|
96
|
+
return value;
|
|
97
|
+
if (Array.isArray(value) && typeof value[0] === "string")
|
|
98
|
+
return value[0];
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function optionalTrimmed(key, value) {
|
|
102
|
+
const trimmed = value?.trim();
|
|
103
|
+
return trimmed ? { [key]: trimmed } : {};
|
|
104
|
+
}
|
|
105
|
+
function setPrivateNoCacheHeaders(res) {
|
|
106
|
+
res.set({
|
|
107
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0, private",
|
|
108
|
+
"Pragma": "no-cache",
|
|
109
|
+
"Expires": "0",
|
|
110
|
+
"Surrogate-Control": "no-store",
|
|
111
|
+
"Vary": "Accept-Encoding"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function defaultSiteInfo() {
|
|
115
|
+
return {
|
|
116
|
+
js_scripts: "Disabled",
|
|
117
|
+
version: PACKAGE_VERSION,
|
|
118
|
+
name: "Custom CMS",
|
|
119
|
+
logo: "",
|
|
120
|
+
categories: [],
|
|
121
|
+
post_types: ["post"],
|
|
122
|
+
tags: [],
|
|
123
|
+
authors: []
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function readPackageVersion() {
|
|
127
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
128
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
129
|
+
throw new Error("@monok/server package version is missing");
|
|
130
|
+
}
|
|
131
|
+
return packageJson.version;
|
|
132
|
+
}
|
|
133
|
+
function httpError(status, message) {
|
|
134
|
+
const error = new Error(message);
|
|
135
|
+
error.status = status;
|
|
136
|
+
return error;
|
|
137
|
+
}
|
|
38
138
|
function wrap(fn) {
|
|
39
139
|
return (req, res, next) => fn(req, res, next).catch((err) => {
|
|
40
140
|
const status = typeof err?.status === "number" ? err.status : 500;
|
package/dist/express.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAkD,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAkD,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQvC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;AAE7C,MAAM,UAAU,iBAAiB,CAAC,IAAyB;IACzD,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IAEjC,kEAAkE;IAClE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAE/B,aAAa;IACb,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAChE,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAE5C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAoB,CAAC;QACtC,IACE,IAAI,EAAE,EAAE,KAAK,SAAS;eACnB,IAAI,CAAC,EAAE,KAAK,IAAI;eAChB,CAAC,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;eACtD,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;eAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAC3B,CAAC;YACD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,MAAM,GAAG,WAAW,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;QAE7E,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAChE,MAAM,OAAO,CAAC,OAAO,CACnB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YACnB,cAAc,EAAE,KAAK,EAAE,YAAqB,EAAE,EAAE,CAC9C,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC;YAC1E,MAAM;SACP,CAAC,CACH,CAAC;QAEF,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC,CAAC;IAEJ,YAAY;IACZ,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC/D,wBAAwB,CAAC,GAAG,CAAC,CAAC;QAC9B,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEtC,MAAM,cAAc,GAAG,CAAC,IAAI,CAAC,WAAW;YACtC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU;gBACtC,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC3C,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;QACvB,MAAM,IAAI,GAAa,cAAc;YACnC,CAAC,CAAC,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,eAAe,EAAE;YACjD,CAAC,CAAC,eAAe,EAAE,CAAC;QAEtB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC,CAAC;IAEJ,kBAAkB;IAClB,CAAC,CAAC,GAAG,CAAC,IAAI,GAAG,aAAa,EAAE,IAAI,CAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACrE,wBAAwB,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAEtC,MAAM,MAAM,GAAG,oBAAoB,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QACrF,MAAM,IAAI,GAAG,oBAAoB,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAoB;YAC7B,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,MAAM;YAChE,MAAM;YACN,IAAI;YACJ,GAAG,eAAe,CAAC,SAAS,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjE,GAAG,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzD,GAAG,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAC1D,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,SAAS,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;QAC9D,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC,CAAC;IAEJ,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY,EAAE,QAAgB,EAAE,UAAU,GAAG,KAAK;IAC7E,MAAM,QAAQ,GAAG,CACf,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;WAC3C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;WACvB,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CACrC,CAAC;IACF,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5E,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC3D,MAAM,SAAS,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAyB,EAAE,QAAgB,EAAE,IAAY;IACrF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IACzD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,6BAA6B,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,6BAA6B,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAqB,GAAQ,EAAE,KAAc;IACnE,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAyB,CAAC,CAAC,CAAC,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,wBAAwB,CAAC,GAAa;IAC7C,GAAG,CAAC,GAAG,CAAC;QACN,eAAe,EAAE,yDAAyD;QAC1E,QAAQ,EAAE,UAAU;QACpB,SAAS,EAAE,GAAG;QACd,mBAAmB,EAAE,UAAU;QAC/B,MAAM,EAAE,iBAAiB;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe;IACtB,OAAO;QACL,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,eAAe;QACxB,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,EAAE;QACR,UAAU,EAAE,EAAE;QACd,UAAU,EAAE,CAAC,MAAM,CAAC;QACpB,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB;IACzB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CACzC,CAAC;IAE3B,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,OAAe;IAChD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAA+B,CAAC;IAC/D,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,IAAI,CACX,EAAyE;IAEzE,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE,CACzD,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,OAAO,GAAG,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;QAClE,IAAI,MAAM,IAAI,GAAG;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,cAAc,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export type VFImage = Record<string, unknown>;
|
|
2
|
+
export type MonokArticleId = string | number;
|
|
3
|
+
export type MonokArticleAction = "draft" | "publish";
|
|
4
|
+
export interface MonokArticleAuthor {
|
|
5
|
+
id: string | number;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export interface MonokArticleCategory {
|
|
9
|
+
name: string;
|
|
10
|
+
weight?: number;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
2
13
|
export interface MonokArticle {
|
|
3
14
|
title: string;
|
|
4
15
|
shortTitle?: string;
|
|
@@ -6,15 +17,21 @@ export interface MonokArticle {
|
|
|
6
17
|
content?: string;
|
|
7
18
|
text?: string;
|
|
8
19
|
slug?: string;
|
|
9
|
-
id:
|
|
20
|
+
id: MonokArticleId;
|
|
21
|
+
action?: MonokArticleAction;
|
|
22
|
+
author?: MonokArticleAuthor;
|
|
23
|
+
categories?: MonokArticleCategory[];
|
|
24
|
+
thumbnail?: string;
|
|
10
25
|
thumbImageData?: string;
|
|
26
|
+
thumbAlt?: string;
|
|
11
27
|
thumbImageDescription?: string;
|
|
12
28
|
language?: string;
|
|
13
|
-
type?:
|
|
29
|
+
type?: string;
|
|
14
30
|
keywords?: string[];
|
|
15
|
-
tags?: string[];
|
|
31
|
+
tags?: string[] | null;
|
|
16
32
|
vfImage?: VFImage;
|
|
17
|
-
sections?: string;
|
|
33
|
+
sections?: string[];
|
|
34
|
+
[key: string]: unknown;
|
|
18
35
|
}
|
|
19
36
|
export interface SiteAuthor {
|
|
20
37
|
name: string;
|
|
@@ -23,24 +40,53 @@ export interface SiteAuthor {
|
|
|
23
40
|
avatar: string;
|
|
24
41
|
roles: string[];
|
|
25
42
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
export interface SiteLanguage {
|
|
44
|
+
id?: string | number;
|
|
45
|
+
name?: string;
|
|
46
|
+
slug?: string;
|
|
47
|
+
locale?: string;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
export type SiteLanguages = SiteLanguage[] | Record<string, SiteLanguage>;
|
|
51
|
+
interface SiteInfoFields {
|
|
30
52
|
js_scripts: "Enabled" | "Disabled";
|
|
31
|
-
version: string;
|
|
32
53
|
name: string;
|
|
33
54
|
logo: string;
|
|
34
55
|
categories: string[];
|
|
35
56
|
post_types: string[];
|
|
36
57
|
tags: string[];
|
|
37
58
|
authors: SiteAuthor[];
|
|
59
|
+
languages?: SiteLanguages;
|
|
60
|
+
[key: string]: unknown;
|
|
61
|
+
}
|
|
62
|
+
/** Site metadata supplied by the host. The router owns the package version. */
|
|
63
|
+
export interface SiteInfoInput extends SiteInfoFields {
|
|
64
|
+
version?: never;
|
|
65
|
+
}
|
|
66
|
+
/** Exact response shape Monok receives from GET /info. */
|
|
67
|
+
export interface SiteInfo extends SiteInfoFields {
|
|
68
|
+
version: string;
|
|
69
|
+
}
|
|
70
|
+
export interface PostFilterQuery {
|
|
71
|
+
postType: string;
|
|
72
|
+
section?: string;
|
|
73
|
+
tag?: string;
|
|
74
|
+
query?: string;
|
|
75
|
+
amount: number;
|
|
76
|
+
page: number;
|
|
77
|
+
}
|
|
78
|
+
/** Exact result shape consumed by Monok's covered-article and link search. */
|
|
79
|
+
export interface PostFilterResult {
|
|
80
|
+
canonical_url: string;
|
|
81
|
+
post_id: string | number;
|
|
82
|
+
title: string | null;
|
|
83
|
+
description: string | null;
|
|
38
84
|
}
|
|
39
85
|
export interface LinkbackPayload {
|
|
40
|
-
monokId:
|
|
41
|
-
|
|
86
|
+
monokId: MonokArticleId;
|
|
87
|
+
externalId?: string | number;
|
|
88
|
+
url: string;
|
|
42
89
|
title: string;
|
|
43
|
-
articleTextSample?: string;
|
|
44
90
|
thumbnailUrl?: string;
|
|
45
91
|
}
|
|
46
92
|
export interface CreateRouterOptions {
|
|
@@ -53,19 +99,28 @@ export interface CreateRouterOptions {
|
|
|
53
99
|
*/
|
|
54
100
|
onArticle: (article: MonokArticle, ctx: {
|
|
55
101
|
saveThumbImage: (fileNameBase?: string) => Promise<string | undefined>;
|
|
56
|
-
action?:
|
|
102
|
+
action?: MonokArticleAction | undefined;
|
|
57
103
|
}) => Promise<void> | void;
|
|
58
104
|
/**
|
|
59
|
-
* Optional. If
|
|
105
|
+
* Optional. If omitted, GET /info returns a minimal Custom CMS fallback.
|
|
60
106
|
*/
|
|
61
|
-
getSiteInfo?:
|
|
107
|
+
getSiteInfo?: SiteInfoInput | (() => Promise<SiteInfoInput> | SiteInfoInput);
|
|
108
|
+
/**
|
|
109
|
+
* Optional. Enables GET /postfilter for article discovery and search.
|
|
110
|
+
*/
|
|
111
|
+
searchArticles?: (query: PostFilterQuery) => Promise<PostFilterResult[]> | PostFilterResult[];
|
|
62
112
|
/**
|
|
63
113
|
* Directory where a decoded `thumbImageData` will be saved if present.
|
|
64
114
|
*/
|
|
65
115
|
imageDir?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Maximum decoded JPEG size. Defaults to 15 MiB.
|
|
118
|
+
*/
|
|
119
|
+
maxImageBytes?: number;
|
|
66
120
|
/**
|
|
67
121
|
* Customize route base path (defaults to "").
|
|
68
122
|
*/
|
|
69
123
|
basePath?: string;
|
|
70
124
|
}
|
|
125
|
+
export {};
|
|
71
126
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare function timingSafeEqual(a: string, b: string): boolean;
|
|
2
2
|
export declare function assertApiKey(headerValue: string | undefined, expected: string): void;
|
|
3
|
-
export declare function makeThumbSaver(imageDir?: string): (base?: string, data?: string) => Promise<string | undefined>;
|
|
3
|
+
export declare function makeThumbSaver(imageDir?: string, maxImageBytes?: number): (base?: string, data?: string) => Promise<string | undefined>;
|
|
4
4
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAK7D;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CASpF;AAED,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,aAAa,SAA0B,IACzE,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4BzE"}
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
|
+
const DEFAULT_MAX_IMAGE_BYTES = 15 * 1024 * 1024;
|
|
4
5
|
export function timingSafeEqual(a, b) {
|
|
5
6
|
const abuf = Buffer.from(a);
|
|
6
7
|
const bbuf = Buffer.from(b);
|
|
@@ -9,24 +10,59 @@ export function timingSafeEqual(a, b) {
|
|
|
9
10
|
return crypto.timingSafeEqual(abuf, bbuf);
|
|
10
11
|
}
|
|
11
12
|
export function assertApiKey(headerValue, expected) {
|
|
12
|
-
|
|
13
|
+
const candidate = headerValue?.trim();
|
|
14
|
+
const configured = expected.trim();
|
|
15
|
+
if (!candidate || !configured || !timingSafeEqual(candidate, configured)) {
|
|
13
16
|
const err = new Error("Unauthorized");
|
|
14
17
|
// @ts-expect-error custom status
|
|
15
18
|
err.status = 401;
|
|
16
19
|
throw err;
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
|
-
export function makeThumbSaver(imageDir) {
|
|
22
|
+
export function makeThumbSaver(imageDir, maxImageBytes = DEFAULT_MAX_IMAGE_BYTES) {
|
|
20
23
|
return async (base, data) => {
|
|
21
24
|
if (!imageDir || !data)
|
|
22
25
|
return undefined;
|
|
23
|
-
|
|
26
|
+
if (!Number.isSafeInteger(maxImageBytes) || maxImageBytes <= 0) {
|
|
27
|
+
throw httpError(500, "maxImageBytes must be a positive safe integer");
|
|
28
|
+
}
|
|
29
|
+
const encoded = normalizeJpegBase64(data);
|
|
30
|
+
const estimatedBytes = Math.floor((encoded.length * 3) / 4);
|
|
31
|
+
if (estimatedBytes > maxImageBytes) {
|
|
32
|
+
throw httpError(413, `Thumbnail exceeds the ${maxImageBytes}-byte limit`);
|
|
33
|
+
}
|
|
34
|
+
const bytes = Buffer.from(encoded, "base64");
|
|
35
|
+
if (bytes.length === 0 || bytes.length > maxImageBytes) {
|
|
36
|
+
throw httpError(413, `Thumbnail exceeds the ${maxImageBytes}-byte limit`);
|
|
37
|
+
}
|
|
38
|
+
if (bytes.length < 3 || bytes[0] !== 0xff || bytes[1] !== 0xd8 || bytes[2] !== 0xff) {
|
|
39
|
+
throw httpError(400, "thumbImageData is not a valid JPEG image");
|
|
40
|
+
}
|
|
24
41
|
const safeName = (base || crypto.randomUUID()).replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
25
|
-
const fileName = `${safeName}.
|
|
42
|
+
const fileName = `${safeName}.jpg`;
|
|
26
43
|
await fs.mkdir(imageDir, { recursive: true });
|
|
27
44
|
const full = path.join(imageDir, fileName);
|
|
28
45
|
await fs.writeFile(full, bytes);
|
|
29
46
|
return full;
|
|
30
47
|
};
|
|
31
48
|
}
|
|
49
|
+
function normalizeJpegBase64(data) {
|
|
50
|
+
const trimmed = data.trim();
|
|
51
|
+
const dataUrl = /^data:image\/(?:jpeg|jpg);base64,/i;
|
|
52
|
+
if (/^data:/i.test(trimmed) && !dataUrl.test(trimmed)) {
|
|
53
|
+
throw httpError(400, "thumbImageData must be a JPEG data URL");
|
|
54
|
+
}
|
|
55
|
+
const encoded = trimmed.replace(dataUrl, "").replace(/\s+/g, "");
|
|
56
|
+
if (encoded.length === 0
|
|
57
|
+
|| encoded.length % 4 === 1
|
|
58
|
+
|| !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) {
|
|
59
|
+
throw httpError(400, "thumbImageData is not valid base64");
|
|
60
|
+
}
|
|
61
|
+
return encoded;
|
|
62
|
+
}
|
|
63
|
+
function httpError(status, message) {
|
|
64
|
+
const error = new Error(message);
|
|
65
|
+
error.status = status;
|
|
66
|
+
return error;
|
|
67
|
+
}
|
|
32
68
|
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7B,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAA+B,EAAE,QAAgB;IAC5E,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/dist-cjs/client.js
CHANGED
|
@@ -2,14 +2,16 @@ import fetch from "node-fetch";
|
|
|
2
2
|
/**
|
|
3
3
|
* Send a linkback to Monok after you publish the article.
|
|
4
4
|
*/
|
|
5
|
-
export async function sendMonokLinkback({ monokId,
|
|
5
|
+
export async function sendMonokLinkback({ monokId, externalId, url, title, thumbnailUrl }, opts) {
|
|
6
6
|
const endpoint = (opts?.monokBaseUrl ?? "https://www.monok.com") + "/api/v1/linkback";
|
|
7
7
|
const payload = {
|
|
8
|
-
puff: { title
|
|
8
|
+
puff: { title },
|
|
9
9
|
id: monokId,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
...(externalId !== undefined && externalId !== null
|
|
11
|
+
? { externalId: String(externalId) }
|
|
12
|
+
: {}),
|
|
13
|
+
url,
|
|
14
|
+
source: new URL(url).hostname,
|
|
13
15
|
...(thumbnailUrl ? { thumbnail: thumbnailUrl } : {})
|
|
14
16
|
};
|
|
15
17
|
const resp = await fetch(endpoint, {
|
package/dist-cjs/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAG/B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAG/B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAmB,EAClE,IAAiD;IAEjD,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,YAAY,IAAI,uBAAuB,CAAC,GAAG,kBAAkB,CAAC;IACtF,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,EAAE,KAAK,EAAE;QACf,EAAE,EAAE,OAAO;QACX,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI;YACjD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE;YACpC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG;QACH,MAAM,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ;QAC7B,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrD,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtD;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
|
package/dist-cjs/express.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Router, json } from "express";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { assertApiKey, makeThumbSaver } from "./utils.js";
|
|
4
|
+
const PACKAGE_VERSION = readPackageVersion();
|
|
3
5
|
export function createMonokRouter(opts) {
|
|
4
6
|
const r = Router();
|
|
5
7
|
const base = opts.basePath ?? "";
|
|
@@ -7,34 +9,132 @@ export function createMonokRouter(opts) {
|
|
|
7
9
|
r.use(json({ limit: "20mb" }));
|
|
8
10
|
// POST /push
|
|
9
11
|
r.post(base + "/push", wrap(async (req, res) => {
|
|
10
|
-
|
|
12
|
+
assertRequestApiKey(req, opts.apiKey, true);
|
|
11
13
|
const body = req.body;
|
|
12
|
-
if (
|
|
14
|
+
if (body?.id === undefined
|
|
15
|
+
|| body.id === null
|
|
16
|
+
|| (typeof body.id === "string" && body.id.trim() === "")
|
|
17
|
+
|| typeof body.title !== "string"
|
|
18
|
+
|| body.title.trim() === "") {
|
|
13
19
|
return res.status(400).json({ error: "Invalid payload" });
|
|
14
20
|
}
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
const
|
|
21
|
+
// Monok's API integration sends action in the body. Retain query support
|
|
22
|
+
// for older callers and give an explicit query value precedence.
|
|
23
|
+
const action = parseAction(firstQueryValue(req.query.action) ?? body.action);
|
|
24
|
+
const saver = makeThumbSaver(opts.imageDir, opts.maxImageBytes);
|
|
18
25
|
await Promise.resolve(opts.onArticle(body, {
|
|
19
|
-
saveThumbImage: async (fileNameBase) => saver(fileNameBase ?? body.slug ?? body.id, body.thumbImageData),
|
|
26
|
+
saveThumbImage: async (fileNameBase) => saver(fileNameBase ?? body.slug ?? String(body.id), body.thumbImageData),
|
|
20
27
|
action
|
|
21
28
|
}));
|
|
22
|
-
res.json({ ok: true, monokId: body.id });
|
|
29
|
+
res.json({ ok: true, status: "accepted", monokId: body.id });
|
|
23
30
|
}));
|
|
24
31
|
// GET /info
|
|
25
32
|
r.get(base + "/info", wrap(async (req, res) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
setPrivateNoCacheHeaders(res);
|
|
34
|
+
assertRequestApiKey(req, opts.apiKey);
|
|
35
|
+
const configuredInfo = !opts.getSiteInfo
|
|
36
|
+
? undefined
|
|
37
|
+
: typeof opts.getSiteInfo === "function"
|
|
38
|
+
? await Promise.resolve(opts.getSiteInfo())
|
|
39
|
+
: opts.getSiteInfo;
|
|
40
|
+
const info = configuredInfo
|
|
41
|
+
? { ...configuredInfo, version: PACKAGE_VERSION }
|
|
42
|
+
: defaultSiteInfo();
|
|
34
43
|
res.json(info);
|
|
35
44
|
}));
|
|
45
|
+
// GET /postfilter
|
|
46
|
+
r.get(base + "/postfilter", wrap(async (req, res) => {
|
|
47
|
+
setPrivateNoCacheHeaders(res);
|
|
48
|
+
if (!opts.searchArticles) {
|
|
49
|
+
return res.status(404).json({ error: "Article search not configured" });
|
|
50
|
+
}
|
|
51
|
+
assertRequestApiKey(req, opts.apiKey);
|
|
52
|
+
const amount = parsePositiveInteger(firstQueryValue(req.query.amount), 10, "amount");
|
|
53
|
+
const page = parsePositiveInteger(firstQueryValue(req.query.page), 1, "page");
|
|
54
|
+
const query = {
|
|
55
|
+
postType: firstQueryValue(req.query.post_type)?.trim() || "post",
|
|
56
|
+
amount,
|
|
57
|
+
page,
|
|
58
|
+
...optionalTrimmed("section", firstQueryValue(req.query.section)),
|
|
59
|
+
...optionalTrimmed("tag", firstQueryValue(req.query.tag)),
|
|
60
|
+
...optionalTrimmed("query", firstQueryValue(req.query.q))
|
|
61
|
+
};
|
|
62
|
+
const articles = await Promise.resolve(opts.searchArticles(query));
|
|
63
|
+
if (!Array.isArray(articles)) {
|
|
64
|
+
throw httpError(500, "searchArticles must return an array");
|
|
65
|
+
}
|
|
66
|
+
res.json(articles);
|
|
67
|
+
}));
|
|
36
68
|
return r;
|
|
37
69
|
}
|
|
70
|
+
function assertRequestApiKey(req, expected, allowToken = false) {
|
|
71
|
+
const provided = ((allowToken ? req.header("token") : undefined)
|
|
72
|
+
?? req.header("x-api-key")
|
|
73
|
+
?? firstQueryValue(req.query.apikey));
|
|
74
|
+
assertApiKey(provided, expected);
|
|
75
|
+
}
|
|
76
|
+
function parseAction(value) {
|
|
77
|
+
if (value === undefined || value === null || value === "")
|
|
78
|
+
return undefined;
|
|
79
|
+
if (value === "draft" || value === "publish")
|
|
80
|
+
return value;
|
|
81
|
+
throw httpError(400, "action must be either draft or publish");
|
|
82
|
+
}
|
|
83
|
+
function parsePositiveInteger(value, fallback, name) {
|
|
84
|
+
if (value === undefined || value === "")
|
|
85
|
+
return fallback;
|
|
86
|
+
if (!/^\d+$/.test(value))
|
|
87
|
+
throw httpError(400, `${name} must be a positive integer`);
|
|
88
|
+
const parsed = Number(value);
|
|
89
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
90
|
+
throw httpError(400, `${name} must be a positive integer`);
|
|
91
|
+
}
|
|
92
|
+
return parsed;
|
|
93
|
+
}
|
|
94
|
+
function firstQueryValue(value) {
|
|
95
|
+
if (typeof value === "string")
|
|
96
|
+
return value;
|
|
97
|
+
if (Array.isArray(value) && typeof value[0] === "string")
|
|
98
|
+
return value[0];
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function optionalTrimmed(key, value) {
|
|
102
|
+
const trimmed = value?.trim();
|
|
103
|
+
return trimmed ? { [key]: trimmed } : {};
|
|
104
|
+
}
|
|
105
|
+
function setPrivateNoCacheHeaders(res) {
|
|
106
|
+
res.set({
|
|
107
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0, private",
|
|
108
|
+
"Pragma": "no-cache",
|
|
109
|
+
"Expires": "0",
|
|
110
|
+
"Surrogate-Control": "no-store",
|
|
111
|
+
"Vary": "Accept-Encoding"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function defaultSiteInfo() {
|
|
115
|
+
return {
|
|
116
|
+
js_scripts: "Disabled",
|
|
117
|
+
version: PACKAGE_VERSION,
|
|
118
|
+
name: "Custom CMS",
|
|
119
|
+
logo: "",
|
|
120
|
+
categories: [],
|
|
121
|
+
post_types: ["post"],
|
|
122
|
+
tags: [],
|
|
123
|
+
authors: []
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function readPackageVersion() {
|
|
127
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
128
|
+
if (typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
129
|
+
throw new Error("@monok/server package version is missing");
|
|
130
|
+
}
|
|
131
|
+
return packageJson.version;
|
|
132
|
+
}
|
|
133
|
+
function httpError(status, message) {
|
|
134
|
+
const error = new Error(message);
|
|
135
|
+
error.status = status;
|
|
136
|
+
return error;
|
|
137
|
+
}
|
|
38
138
|
function wrap(fn) {
|
|
39
139
|
return (req, res, next) => fn(req, res, next).catch((err) => {
|
|
40
140
|
const status = typeof err?.status === "number" ? err.status : 500;
|
package/dist-cjs/express.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAkD,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"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"}
|
package/dist-cjs/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
|
+
const DEFAULT_MAX_IMAGE_BYTES = 15 * 1024 * 1024;
|
|
4
5
|
export function timingSafeEqual(a, b) {
|
|
5
6
|
const abuf = Buffer.from(a);
|
|
6
7
|
const bbuf = Buffer.from(b);
|
|
@@ -9,24 +10,59 @@ export function timingSafeEqual(a, b) {
|
|
|
9
10
|
return crypto.timingSafeEqual(abuf, bbuf);
|
|
10
11
|
}
|
|
11
12
|
export function assertApiKey(headerValue, expected) {
|
|
12
|
-
|
|
13
|
+
const candidate = headerValue?.trim();
|
|
14
|
+
const configured = expected.trim();
|
|
15
|
+
if (!candidate || !configured || !timingSafeEqual(candidate, configured)) {
|
|
13
16
|
const err = new Error("Unauthorized");
|
|
14
17
|
// @ts-expect-error custom status
|
|
15
18
|
err.status = 401;
|
|
16
19
|
throw err;
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
|
-
export function makeThumbSaver(imageDir) {
|
|
22
|
+
export function makeThumbSaver(imageDir, maxImageBytes = DEFAULT_MAX_IMAGE_BYTES) {
|
|
20
23
|
return async (base, data) => {
|
|
21
24
|
if (!imageDir || !data)
|
|
22
25
|
return undefined;
|
|
23
|
-
|
|
26
|
+
if (!Number.isSafeInteger(maxImageBytes) || maxImageBytes <= 0) {
|
|
27
|
+
throw httpError(500, "maxImageBytes must be a positive safe integer");
|
|
28
|
+
}
|
|
29
|
+
const encoded = normalizeJpegBase64(data);
|
|
30
|
+
const estimatedBytes = Math.floor((encoded.length * 3) / 4);
|
|
31
|
+
if (estimatedBytes > maxImageBytes) {
|
|
32
|
+
throw httpError(413, `Thumbnail exceeds the ${maxImageBytes}-byte limit`);
|
|
33
|
+
}
|
|
34
|
+
const bytes = Buffer.from(encoded, "base64");
|
|
35
|
+
if (bytes.length === 0 || bytes.length > maxImageBytes) {
|
|
36
|
+
throw httpError(413, `Thumbnail exceeds the ${maxImageBytes}-byte limit`);
|
|
37
|
+
}
|
|
38
|
+
if (bytes.length < 3 || bytes[0] !== 0xff || bytes[1] !== 0xd8 || bytes[2] !== 0xff) {
|
|
39
|
+
throw httpError(400, "thumbImageData is not a valid JPEG image");
|
|
40
|
+
}
|
|
24
41
|
const safeName = (base || crypto.randomUUID()).replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
25
|
-
const fileName = `${safeName}.
|
|
42
|
+
const fileName = `${safeName}.jpg`;
|
|
26
43
|
await fs.mkdir(imageDir, { recursive: true });
|
|
27
44
|
const full = path.join(imageDir, fileName);
|
|
28
45
|
await fs.writeFile(full, bytes);
|
|
29
46
|
return full;
|
|
30
47
|
};
|
|
31
48
|
}
|
|
49
|
+
function normalizeJpegBase64(data) {
|
|
50
|
+
const trimmed = data.trim();
|
|
51
|
+
const dataUrl = /^data:image\/(?:jpeg|jpg);base64,/i;
|
|
52
|
+
if (/^data:/i.test(trimmed) && !dataUrl.test(trimmed)) {
|
|
53
|
+
throw httpError(400, "thumbImageData must be a JPEG data URL");
|
|
54
|
+
}
|
|
55
|
+
const encoded = trimmed.replace(dataUrl, "").replace(/\s+/g, "");
|
|
56
|
+
if (encoded.length === 0
|
|
57
|
+
|| encoded.length % 4 === 1
|
|
58
|
+
|| !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded)) {
|
|
59
|
+
throw httpError(400, "thumbImageData is not valid base64");
|
|
60
|
+
}
|
|
61
|
+
return encoded;
|
|
62
|
+
}
|
|
63
|
+
function httpError(status, message) {
|
|
64
|
+
const error = new Error(message);
|
|
65
|
+
error.status = status;
|
|
66
|
+
return error;
|
|
67
|
+
}
|
|
32
68
|
//# sourceMappingURL=utils.js.map
|
package/dist-cjs/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7B,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC9C,OAAO,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAA+B,EAAE,QAAgB;IAC5E,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monok/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Monok server integration helpers (Express router + linkback client)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
19
|
"dist-cjs",
|
|
20
|
+
"AGENTS.md",
|
|
20
21
|
"README.md",
|
|
21
22
|
"LICENSE"
|
|
22
23
|
],
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"sideEffects": false,
|
|
27
28
|
"scripts": {
|
|
28
29
|
"build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json",
|
|
30
|
+
"test": "npm run build && node --test tests/*.test.mjs",
|
|
29
31
|
"dev": "tsc -w -p tsconfig.json",
|
|
30
32
|
"clean": "rimraf dist dist-cjs",
|
|
31
33
|
"prepublishOnly": "npm run build"
|