@commentbox/server-worker 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +9 -2
- package/migrations/0001_init.sql +54 -0
- package/package.json +58 -3
- package/src/d1.ts +18 -0
- package/src/index.ts +6 -0
- package/src/widget.d.ts +4 -0
- package/src/worker.ts +51 -0
- package/wrangler.toml +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Solutions Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @commentbox/server-worker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[CommentBox](https://commentbox.dev) server for Cloudflare Workers + D1. Usually scaffolded for you:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @commentbox/cli init --origin https://your-site.com --provision
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Manual use: `export { default } from '@commentbox/server-worker'` as your Worker entry, with a D1 binding `DB`,
|
|
10
|
+
the migration in `migrations/` and the `[[rules]]` Text rule from `wrangler.toml`. MIT licensed.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
-- CommentBox schema (SQLite / Cloudflare D1). Applied by both servers on start or via wrangler d1 migrations apply.
|
|
2
|
+
CREATE TABLE IF NOT EXISTS comments (
|
|
3
|
+
id TEXT PRIMARY KEY,
|
|
4
|
+
created_at TEXT NOT NULL,
|
|
5
|
+
updated_at TEXT NOT NULL,
|
|
6
|
+
status TEXT NOT NULL DEFAULT 'new',
|
|
7
|
+
type TEXT NOT NULL,
|
|
8
|
+
text TEXT NOT NULL,
|
|
9
|
+
name TEXT NOT NULL DEFAULT '',
|
|
10
|
+
page_url TEXT NOT NULL,
|
|
11
|
+
pathname TEXT NOT NULL,
|
|
12
|
+
selector TEXT,
|
|
13
|
+
tag_name TEXT,
|
|
14
|
+
element_text TEXT,
|
|
15
|
+
rect TEXT,
|
|
16
|
+
scroll_y REAL NOT NULL DEFAULT 0,
|
|
17
|
+
viewport_width INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
viewport_height INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
device_pixel_ratio REAL NOT NULL DEFAULT 1,
|
|
20
|
+
user_agent TEXT NOT NULL DEFAULT '',
|
|
21
|
+
screenshot_id TEXT,
|
|
22
|
+
meta TEXT,
|
|
23
|
+
note TEXT
|
|
24
|
+
);
|
|
25
|
+
CREATE INDEX IF NOT EXISTS comments_created_at ON comments (created_at);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS comments_status ON comments (status);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS screenshots (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
mime TEXT NOT NULL,
|
|
31
|
+
data TEXT NOT NULL,
|
|
32
|
+
created_at TEXT NOT NULL
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS deliveries (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
comment_id TEXT,
|
|
38
|
+
event TEXT NOT NULL,
|
|
39
|
+
url TEXT NOT NULL,
|
|
40
|
+
body TEXT NOT NULL,
|
|
41
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
42
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
43
|
+
next_attempt_at INTEGER NOT NULL,
|
|
44
|
+
last_status INTEGER,
|
|
45
|
+
last_error TEXT,
|
|
46
|
+
created_at TEXT NOT NULL,
|
|
47
|
+
updated_at TEXT NOT NULL
|
|
48
|
+
);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS deliveries_due ON deliveries (status, next_attempt_at);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
52
|
+
key TEXT PRIMARY KEY,
|
|
53
|
+
value TEXT NOT NULL
|
|
54
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commentbox/server-worker",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "CommentBox server for Cloudflare Workers + D1.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"commentbox",
|
|
7
|
+
"cloudflare-workers",
|
|
8
|
+
"d1",
|
|
9
|
+
"feedback",
|
|
10
|
+
"webhook"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Solutions Lab (https://solutionslab.net)",
|
|
14
|
+
"homepage": "https://commentbox.dev",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/loek-solutionslab/commentbox/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/loek-solutionslab/commentbox.git",
|
|
21
|
+
"directory": "packages/server-worker"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./src/index.ts",
|
|
25
|
+
"types": "./src/index.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./src/index.ts",
|
|
28
|
+
"./worker": "./src/worker.ts",
|
|
29
|
+
"./migrations/0001_init.sql": "./migrations/0001_init.sql",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"README.md",
|
|
35
|
+
"migrations",
|
|
36
|
+
"src",
|
|
37
|
+
"wrangler.toml"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@commentbox/widget": "0.1.2",
|
|
47
|
+
"@commentbox/core": "0.1.2"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@cloudflare/workers-types": "^4.20241011.0",
|
|
51
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
52
|
+
"better-sqlite3": "^11.5.0",
|
|
53
|
+
"wrangler": "^3.80.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "node build.mjs",
|
|
57
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
58
|
+
"dev": "wrangler dev",
|
|
59
|
+
"db:migrate:local": "wrangler d1 migrations apply commentbox --local"
|
|
60
|
+
}
|
|
6
61
|
}
|
package/src/d1.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SqlDriver } from '@commentbox/core'
|
|
2
|
+
|
|
3
|
+
/** Adapts a Cloudflare D1 binding to the core SqlDriver. */
|
|
4
|
+
export function d1Driver(db: D1Database): SqlDriver {
|
|
5
|
+
return {
|
|
6
|
+
async run(sql, params = []) {
|
|
7
|
+
const res = await db.prepare(sql).bind(...params).run()
|
|
8
|
+
return { changes: res.meta.changes ?? 0 }
|
|
9
|
+
},
|
|
10
|
+
async all(sql, params = []) {
|
|
11
|
+
const res = await db.prepare(sql).bind(...params).all<Record<string, unknown>>()
|
|
12
|
+
return res.results
|
|
13
|
+
},
|
|
14
|
+
async exec(statements) {
|
|
15
|
+
await db.batch(statements.map((s) => db.prepare(s)))
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/index.ts
ADDED
package/src/widget.d.ts
ADDED
package/src/worker.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { configFromEnv, createHandler, createSqlStore, processDeliveries } from '@commentbox/core'
|
|
2
|
+
|
|
3
|
+
import { d1Driver } from './d1.js'
|
|
4
|
+
|
|
5
|
+
export type Env = {
|
|
6
|
+
DB: D1Database
|
|
7
|
+
ALLOWED_ORIGINS?: string
|
|
8
|
+
PUBLIC_URL?: string
|
|
9
|
+
PROJECT_NAME?: string
|
|
10
|
+
WIDGET_ENABLED?: string
|
|
11
|
+
RATE_LIMIT_PER_HOUR?: string
|
|
12
|
+
WEBHOOK_URL?: string
|
|
13
|
+
WEBHOOK_MAX_ATTEMPTS?: string
|
|
14
|
+
WEBHOOK_INTEGRATION?: string
|
|
15
|
+
// Opt-in install count (off by default; DO_NOT_TRACK=1 always disables). See docs/telemetry.md.
|
|
16
|
+
COMMENTBOX_TELEMETRY?: string
|
|
17
|
+
COMMENTBOX_TELEMETRY_URL?: string
|
|
18
|
+
DO_NOT_TRACK?: string
|
|
19
|
+
// Secrets (wrangler secret put …):
|
|
20
|
+
ADMIN_TOKEN?: string
|
|
21
|
+
WEBHOOK_SECRET?: string
|
|
22
|
+
WEBHOOK_BEARER_TOKEN?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Worker entry, without the widget import so tests can run it against a fake D1. */
|
|
26
|
+
export function createWorker(widgetJs?: string) {
|
|
27
|
+
// One handler per isolate (env is stable per isolate): keeps the schema check and rate limiter warm.
|
|
28
|
+
const cache = new WeakMap<Env, { handle: ReturnType<typeof createHandler>; store: ReturnType<typeof createSqlStore>; ready: Promise<void> | null }>()
|
|
29
|
+
const get = (env: Env) => {
|
|
30
|
+
let hit = cache.get(env)
|
|
31
|
+
if (!hit) {
|
|
32
|
+
const store = createSqlStore(d1Driver(env.DB))
|
|
33
|
+
hit = { store, handle: createHandler({ store, config: configFromEnv(env), widgetJs }), ready: null }
|
|
34
|
+
cache.set(env, hit)
|
|
35
|
+
}
|
|
36
|
+
return hit
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
41
|
+
return get(env).handle(req, { waitUntil: (p) => ctx.waitUntil(p) })
|
|
42
|
+
},
|
|
43
|
+
/** Cron trigger (every minute): sends webhook retries that are due. */
|
|
44
|
+
async scheduled(_event: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
|
|
45
|
+
const hit = get(env)
|
|
46
|
+
hit.ready ??= hit.store.init()
|
|
47
|
+
await hit.ready
|
|
48
|
+
ctx.waitUntil(processDeliveries(hit.store, configFromEnv(env)))
|
|
49
|
+
},
|
|
50
|
+
} satisfies ExportedHandler<Env>
|
|
51
|
+
}
|
package/wrangler.toml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# CommentBox on Cloudflare Workers + D1.
|
|
2
|
+
# 1. npx wrangler d1 create commentbox -> paste the database_id below
|
|
3
|
+
# 2. npx wrangler d1 migrations apply commentbox --remote
|
|
4
|
+
# 3. npx wrangler secret put ADMIN_TOKEN / WEBHOOK_SECRET (never commit secrets)
|
|
5
|
+
# 4. npx wrangler deploy
|
|
6
|
+
# `npx @commentbox/cli init --origin https://your-site.com --provision` runs these steps for you.
|
|
7
|
+
name = "commentbox"
|
|
8
|
+
main = "src/index.ts"
|
|
9
|
+
compatibility_date = "2024-10-01"
|
|
10
|
+
|
|
11
|
+
[vars]
|
|
12
|
+
# Comma-separated origins allowed to post comments (no trailing slash).
|
|
13
|
+
ALLOWED_ORIGINS = "http://localhost:3000"
|
|
14
|
+
PROJECT_NAME = "my-site"
|
|
15
|
+
# Hard switch; the soft switch is PUT /v1/settings {"enabled": false} (or `npx @commentbox/cli toggle off`).
|
|
16
|
+
WIDGET_ENABLED = "true"
|
|
17
|
+
RATE_LIMIT_PER_HOUR = "20"
|
|
18
|
+
# Where every new comment is POSTed (your Grok Bot or any agent endpoint). Empty = no webhook.
|
|
19
|
+
WEBHOOK_URL = ""
|
|
20
|
+
WEBHOOK_MAX_ATTEMPTS = "6"
|
|
21
|
+
# "grok-bot" or "webhook": what WEBHOOK_URL points at.
|
|
22
|
+
WEBHOOK_INTEGRATION = "webhook"
|
|
23
|
+
# Opt-in anonymous install count, OFF by default. "1" sends one ping per verified webhook registration
|
|
24
|
+
# (random install id, version, integration, event kind; nothing else). DO_NOT_TRACK = "1" always wins. See docs/telemetry.md.
|
|
25
|
+
COMMENTBOX_TELEMETRY = "0"
|
|
26
|
+
# Set when the Worker runs behind a custom domain, e.g. "https://comments.example.com".
|
|
27
|
+
PUBLIC_URL = ""
|
|
28
|
+
|
|
29
|
+
[[d1_databases]]
|
|
30
|
+
binding = "DB"
|
|
31
|
+
database_name = "commentbox"
|
|
32
|
+
database_id = "00000000-0000-0000-0000-000000000000"
|
|
33
|
+
migrations_dir = "migrations"
|
|
34
|
+
|
|
35
|
+
# Retries for failed webhook deliveries.
|
|
36
|
+
[triggers]
|
|
37
|
+
crons = ["* * * * *"]
|
|
38
|
+
|
|
39
|
+
[[rules]]
|
|
40
|
+
type = "Text"
|
|
41
|
+
globs = ["**/*.global.js"]
|
|
42
|
+
fallthrough = true
|