boltstore 0.5.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.
Files changed (81) hide show
  1. package/README.md +39 -0
  2. package/bin/cli.ts +185 -0
  3. package/package.json +54 -0
  4. package/src/admin/assets.ts +134 -0
  5. package/src/admin/serve.ts +447 -0
  6. package/src/admin-ui/App.vue +71 -0
  7. package/src/admin-ui/api/client.ts +179 -0
  8. package/src/admin-ui/components/Icon.vue +85 -0
  9. package/src/admin-ui/components/Sidebar.vue +275 -0
  10. package/src/admin-ui/components/StatCard.vue +40 -0
  11. package/src/admin-ui/index.html +48 -0
  12. package/src/admin-ui/main.ts +25 -0
  13. package/src/admin-ui/pages/ApiKeys.vue +198 -0
  14. package/src/admin-ui/pages/AppDashboard.vue +274 -0
  15. package/src/admin-ui/pages/Applications.vue +123 -0
  16. package/src/admin-ui/pages/Backups.vue +140 -0
  17. package/src/admin-ui/pages/Dashboard.vue +293 -0
  18. package/src/admin-ui/pages/DataBrowser.vue +310 -0
  19. package/src/admin-ui/pages/Docs.vue +40 -0
  20. package/src/admin-ui/pages/FileBrowser.vue +587 -0
  21. package/src/admin-ui/pages/HookEditor.vue +258 -0
  22. package/src/admin-ui/pages/LogViewer.vue +109 -0
  23. package/src/admin-ui/pages/Login.vue +229 -0
  24. package/src/admin-ui/pages/SchemaBuilder.vue +501 -0
  25. package/src/admin-ui/pages/Settings.vue +251 -0
  26. package/src/admin-ui/pages/Setup.vue +275 -0
  27. package/src/admin-ui/router.ts +169 -0
  28. package/src/admin-ui/stores/application.ts +68 -0
  29. package/src/admin-ui/stores/auth.ts +109 -0
  30. package/src/admin-ui/style.css +530 -0
  31. package/src/admin-ui/tsconfig.json +21 -0
  32. package/src/admin-ui/version.ts +3 -0
  33. package/src/admin-ui/vite.config.ts +35 -0
  34. package/src/api/admin-config.ts +596 -0
  35. package/src/api/admin.ts +8 -0
  36. package/src/api/applications.ts +148 -0
  37. package/src/api/auth.ts +690 -0
  38. package/src/api/backups.ts +173 -0
  39. package/src/api/batch.ts +135 -0
  40. package/src/api/collections.ts +354 -0
  41. package/src/api/files.ts +801 -0
  42. package/src/api/health.ts +48 -0
  43. package/src/api/helpers.ts +86 -0
  44. package/src/api/middleware/auth.ts +202 -0
  45. package/src/api/middleware/cors.ts +37 -0
  46. package/src/api/middleware/logging.ts +27 -0
  47. package/src/api/middleware/ratelimit.ts +105 -0
  48. package/src/api/middleware/rls.ts +37 -0
  49. package/src/api/records.ts +521 -0
  50. package/src/api/router.ts +294 -0
  51. package/src/app.ts +617 -0
  52. package/src/auth/apikey.ts +73 -0
  53. package/src/auth/jwt.ts +210 -0
  54. package/src/auth/oauth2.ts +144 -0
  55. package/src/auth/password.ts +68 -0
  56. package/src/backup/manager.ts +227 -0
  57. package/src/config/yaml.ts +78 -0
  58. package/src/config.ts +240 -0
  59. package/src/db/application-migrations.ts +112 -0
  60. package/src/db/application-schema.ts +297 -0
  61. package/src/db/migrations.ts +54 -0
  62. package/src/db/pool.ts +143 -0
  63. package/src/db/system-schema.ts +149 -0
  64. package/src/hooks/api.ts +157 -0
  65. package/src/hooks/loader.ts +263 -0
  66. package/src/hooks/runner.ts +76 -0
  67. package/src/index.ts +17 -0
  68. package/src/logging/logger.ts +131 -0
  69. package/src/realtime/broadcast.ts +132 -0
  70. package/src/realtime/subscriptions.ts +124 -0
  71. package/src/security/rls-engine.ts +160 -0
  72. package/src/security/sanitizer.ts +72 -0
  73. package/src/storage/interface.ts +48 -0
  74. package/src/storage/local.ts +189 -0
  75. package/src/storage/mime-types.ts +32 -0
  76. package/src/storage/s3.ts +213 -0
  77. package/src/sync/changelog.ts +247 -0
  78. package/src/sync/engine.ts +159 -0
  79. package/src/sync/lamport.ts +48 -0
  80. package/src/sync/protocol.ts +234 -0
  81. package/src/version.ts +2 -0
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Boltstore
2
+
3
+ **Single-binary, self-hosted Backend-as-a-Service.**
4
+
5
+ Offline sync + S3-compatible storage + realtime + extensible JS hooks + Row-Level Security. Runs on a 512MB VPS. Built on **Bun**.
6
+
7
+ Zero production dependencies. Everything uses Bun built-ins: `bun:sqlite`, `Bun.serve()`, `Bun.password`, Web Crypto API.
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ bun install
13
+ bun run dev
14
+ ```
15
+
16
+ ## Packages
17
+
18
+ This repo is the main `boltstore` package (published on npm). The admin UI (Vue SPA) lives at `src/admin-ui/` and is built into the binary.
19
+
20
+ Companion packages published separately:
21
+ - **[@boltstore/utils](https://www.npmjs.com/package/@boltstore/utils)** — Shared types, validation, filter parser (used by client SDK)
22
+ - **[@boltstore/client](https://www.npmjs.com/package/@boltstore/client)** — TypeScript SDK for Bun, Node 18+, browsers
23
+
24
+ ## Features
25
+
26
+ - SQLite with dual-pool concurrency and WAL mode
27
+ - REST API auto-generated from collection schemas
28
+ - JWT + bcrypt + OAuth2 (Google, GitHub) + API keys
29
+ - Row-Level Security (SQL policy injection)
30
+ - WebSocket pub/sub with RLS-aware fan-out
31
+ - Offline sync (4 strategies, per-field override)
32
+ - LocalDisk + S3-compatible file storage
33
+ - Lifecycle hooks + custom routes/WS/CLI extensions
34
+ - Vue admin UI embedded in binary
35
+ - Standard SQLite backups + full-instance archives
36
+
37
+ ## License
38
+
39
+ MIT
package/bin/cli.ts ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env bun
2
+ // ── Boltstore CLI ──
3
+ // Usage: boltstore <command> [options]
4
+ //
5
+ // Commands:
6
+ // start Start the Boltstore server
7
+ // backup Create a backup (--full for full-instance)
8
+ // restore Restore from a backup archive
9
+ // version Print version
10
+
11
+ import { parseArgs } from "node:util";
12
+
13
+ const VERSION = "0.3.0";
14
+
15
+ function printHelp(): void {
16
+ console.log(`
17
+ Boltstore v${VERSION} — Self-hosted Backend-as-a-Service
18
+
19
+ Usage:
20
+ boltstore <command> [options]
21
+
22
+ Commands:
23
+ start Start the Boltstore server
24
+ backup Create a backup
25
+ --full Full-instance backup (all applications + uploads + config)
26
+ --application <id> Backup a specific application
27
+ restore --from <path> Restore from a backup archive
28
+ version Print version information
29
+
30
+ Options:
31
+ --port <n> Server port (default: 8090)
32
+ --host <host> Server hostname (default: 127.0.0.1)
33
+ --config <path> Path to config file (default: boltstore.yaml)
34
+ --data-dir <path> Data directory (default: data)
35
+ --log-level <lvl> Log level: debug, info, warn, error (default: info)
36
+
37
+ Examples:
38
+ boltstore start
39
+ boltstore start --port 3000
40
+ boltstore backup --full
41
+ boltstore restore --from data/backups/full-2026-06-09.zip
42
+ `);
43
+ }
44
+
45
+ async function main(): Promise<void> {
46
+ const { values, positionals } = parseArgs({
47
+ args: Bun.argv.slice(2),
48
+ options: {
49
+ port: { type: "string" },
50
+ host: { type: "string" },
51
+ config: { type: "string" },
52
+ "data-dir": { type: "string" },
53
+ "log-level": { type: "string" },
54
+ full: { type: "boolean" },
55
+ application: { type: "string" },
56
+ from: { type: "string" },
57
+ help: { type: "boolean", short: "h" },
58
+ },
59
+ strict: false,
60
+ allowPositionals: true,
61
+ });
62
+
63
+ const command = positionals[0] ?? "start";
64
+
65
+ if (values.help) {
66
+ printHelp();
67
+ return;
68
+ }
69
+
70
+ switch (command) {
71
+ case "version": {
72
+ console.log(`Boltstore v${VERSION}`);
73
+ return;
74
+ }
75
+
76
+ case "start": {
77
+ // Dynamic import so the config loader can apply CLI flags
78
+ const { createServer } = await import("../src/app");
79
+ const app = createServer({
80
+ ...(values.port ? { port: parseInt(values.port as string, 10) } : {}),
81
+ ...(values.host ? { host: values.host as string } : {}),
82
+ ...(values["data-dir"] ? { dataDir: values["data-dir"] as string } : {}),
83
+ });
84
+
85
+ // Graceful shutdown
86
+ process.on("SIGINT", () => { app.stop().then(() => process.exit(0)); });
87
+ process.on("SIGTERM", () => { app.stop().then(() => process.exit(0)); });
88
+
89
+ app.start();
90
+ return;
91
+ }
92
+
93
+ case "backup": {
94
+ const { createServer } = await import("../src/app");
95
+ const app = createServer();
96
+ const config = app.config;
97
+
98
+ console.log("📦 Creating backup...");
99
+
100
+ const { snapshotProjectDb, fullBackup } = await import("../src/backup/manager");
101
+ const backupConfig = {
102
+ backupDir: `${config.dataDir}/backups`,
103
+ dataDir: config.dataDir,
104
+ retentionCount: config.backup?.retentionCount ?? 48,
105
+ };
106
+
107
+ if (values.full) {
108
+ console.log(" Full-instance backup (all applications, uploads, hooks, config)");
109
+ const path = await fullBackup(backupConfig);
110
+ console.log(` ✅ Full backup created: ${path}`);
111
+ } else if (values.application) {
112
+ console.log(` Application: ${values.application}`);
113
+ const { Database } = await import("bun:sqlite");
114
+ const dbFile = `${config.dataDir}/application/${values.application}/data.db`;
115
+ const db = new Database(dbFile);
116
+ const path = await snapshotProjectDb(db, values.application as string, backupConfig);
117
+ db.close();
118
+ console.log(` ✅ Snapshot created: ${path}`);
119
+ } else {
120
+ console.log(" Per-application SQLite snapshots");
121
+ const { Database } = await import("bun:sqlite");
122
+ const { readdirSync, existsSync } = await import("node:fs");
123
+ const appsDir = `${config.dataDir}/application`;
124
+ if (existsSync(appsDir)) {
125
+ const appDirs = readdirSync(appsDir);
126
+ for (const appId of appDirs) {
127
+ const dbFile = `${appsDir}/${appId}/data.db`;
128
+ if (!existsSync(dbFile)) continue;
129
+ const db = new Database(dbFile);
130
+ const path = await snapshotProjectDb(db, appId, backupConfig);
131
+ db.close();
132
+ console.log(` ✅ ${appId}: ${path}`);
133
+ }
134
+ } else {
135
+ console.log(" No applications found.");
136
+ }
137
+ }
138
+ await app.stop();
139
+ return;
140
+ }
141
+
142
+ case "restore": {
143
+ if (!values.from) {
144
+ console.error("Error: --from <path> is required for restore");
145
+ console.error("Usage: boltstore restore --from <backup.zip>");
146
+ process.exit(1);
147
+ }
148
+ console.log(`📥 Restoring from ${values.from}...`);
149
+
150
+ const { createServer } = await import("../src/app");
151
+ const app = createServer();
152
+ const config = app.config;
153
+
154
+ const { restoreFromBackup } = await import("../src/backup/manager");
155
+ const backupConfig = {
156
+ backupDir: `${config.dataDir}/backups`,
157
+ dataDir: config.dataDir,
158
+ retentionCount: config.backup?.retentionCount ?? 48,
159
+ };
160
+
161
+ await restoreFromBackup(values.from as string, backupConfig);
162
+ console.log(" ✅ Restore complete");
163
+ await app.stop();
164
+ return;
165
+ }
166
+
167
+ default: {
168
+ // Try extension CLI commands
169
+ const { createServer } = await import("../src/app");
170
+ const app = createServer();
171
+ const extCmd = app.server ? undefined : undefined;
172
+ // We can't access extensions here easily since createServer returns an interface.
173
+ // Extensions should register their CLI commands via the registry.
174
+ // For now, print unknown command.
175
+ console.error(`Unknown command: ${command}`);
176
+ console.error("Run 'boltstore --help' for usage.");
177
+ process.exit(1);
178
+ }
179
+ }
180
+ }
181
+
182
+ main().catch((err) => {
183
+ console.error("Fatal error:", err);
184
+ process.exit(1);
185
+ });
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "boltstore",
3
+ "version": "0.5.0",
4
+ "description": "Boltstore — single-binary, self-hosted Backend-as-a-Service",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "bin": {
8
+ "boltstore": "./bin/cli.ts"
9
+ },
10
+ "exports": {
11
+ ".": "./src/index.ts",
12
+ "./config": "./src/config.ts",
13
+ "./app": "./src/app.ts"
14
+ },
15
+ "files": [
16
+ "src/",
17
+ "bin/"
18
+ ],
19
+ "scripts": {
20
+ "dev": "bun run scripts/dev.ts",
21
+ "build": "bun run scripts/build.ts",
22
+ "start": "bun run bin/cli.ts start",
23
+ "test": "bun test",
24
+ "check": "tsc --noEmit"
25
+ },
26
+ "dependencies": {
27
+ "@boltstore/utils": "^0.5.2"
28
+ },
29
+ "devDependencies": {
30
+ "@types/bun": "latest",
31
+ "@types/dompurify": "^3.2.0",
32
+ "@vitejs/plugin-vue": "^5.2.0",
33
+ "dompurify": "^3.4.8",
34
+ "pinia": "^2.3.0",
35
+ "typescript": "^5.7.0",
36
+ "vite": "^6.0.0",
37
+ "vue": "^3.5.0",
38
+ "vue-router": "^4.5.0",
39
+ "vue-tsc": "^2.2.0"
40
+ },
41
+ "keywords": [
42
+ "boltstore",
43
+ "baas",
44
+ "backend",
45
+ "sqlite",
46
+ "realtime",
47
+ "sync"
48
+ ],
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/boltstore/boltstore"
53
+ }
54
+ }