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.
- package/README.md +39 -0
- package/bin/cli.ts +185 -0
- package/package.json +54 -0
- package/src/admin/assets.ts +134 -0
- package/src/admin/serve.ts +447 -0
- package/src/admin-ui/App.vue +71 -0
- package/src/admin-ui/api/client.ts +179 -0
- package/src/admin-ui/components/Icon.vue +85 -0
- package/src/admin-ui/components/Sidebar.vue +275 -0
- package/src/admin-ui/components/StatCard.vue +40 -0
- package/src/admin-ui/index.html +48 -0
- package/src/admin-ui/main.ts +25 -0
- package/src/admin-ui/pages/ApiKeys.vue +198 -0
- package/src/admin-ui/pages/AppDashboard.vue +274 -0
- package/src/admin-ui/pages/Applications.vue +123 -0
- package/src/admin-ui/pages/Backups.vue +140 -0
- package/src/admin-ui/pages/Dashboard.vue +293 -0
- package/src/admin-ui/pages/DataBrowser.vue +310 -0
- package/src/admin-ui/pages/Docs.vue +40 -0
- package/src/admin-ui/pages/FileBrowser.vue +587 -0
- package/src/admin-ui/pages/HookEditor.vue +258 -0
- package/src/admin-ui/pages/LogViewer.vue +109 -0
- package/src/admin-ui/pages/Login.vue +229 -0
- package/src/admin-ui/pages/SchemaBuilder.vue +501 -0
- package/src/admin-ui/pages/Settings.vue +251 -0
- package/src/admin-ui/pages/Setup.vue +275 -0
- package/src/admin-ui/router.ts +169 -0
- package/src/admin-ui/stores/application.ts +68 -0
- package/src/admin-ui/stores/auth.ts +109 -0
- package/src/admin-ui/style.css +530 -0
- package/src/admin-ui/tsconfig.json +21 -0
- package/src/admin-ui/version.ts +3 -0
- package/src/admin-ui/vite.config.ts +35 -0
- package/src/api/admin-config.ts +596 -0
- package/src/api/admin.ts +8 -0
- package/src/api/applications.ts +148 -0
- package/src/api/auth.ts +690 -0
- package/src/api/backups.ts +173 -0
- package/src/api/batch.ts +135 -0
- package/src/api/collections.ts +354 -0
- package/src/api/files.ts +801 -0
- package/src/api/health.ts +48 -0
- package/src/api/helpers.ts +86 -0
- package/src/api/middleware/auth.ts +202 -0
- package/src/api/middleware/cors.ts +37 -0
- package/src/api/middleware/logging.ts +27 -0
- package/src/api/middleware/ratelimit.ts +105 -0
- package/src/api/middleware/rls.ts +37 -0
- package/src/api/records.ts +521 -0
- package/src/api/router.ts +294 -0
- package/src/app.ts +617 -0
- package/src/auth/apikey.ts +73 -0
- package/src/auth/jwt.ts +210 -0
- package/src/auth/oauth2.ts +144 -0
- package/src/auth/password.ts +68 -0
- package/src/backup/manager.ts +227 -0
- package/src/config/yaml.ts +78 -0
- package/src/config.ts +240 -0
- package/src/db/application-migrations.ts +112 -0
- package/src/db/application-schema.ts +297 -0
- package/src/db/migrations.ts +54 -0
- package/src/db/pool.ts +143 -0
- package/src/db/system-schema.ts +149 -0
- package/src/hooks/api.ts +157 -0
- package/src/hooks/loader.ts +263 -0
- package/src/hooks/runner.ts +76 -0
- package/src/index.ts +17 -0
- package/src/logging/logger.ts +131 -0
- package/src/realtime/broadcast.ts +132 -0
- package/src/realtime/subscriptions.ts +124 -0
- package/src/security/rls-engine.ts +160 -0
- package/src/security/sanitizer.ts +72 -0
- package/src/storage/interface.ts +48 -0
- package/src/storage/local.ts +189 -0
- package/src/storage/mime-types.ts +32 -0
- package/src/storage/s3.ts +213 -0
- package/src/sync/changelog.ts +247 -0
- package/src/sync/engine.ts +159 -0
- package/src/sync/lamport.ts +48 -0
- package/src/sync/protocol.ts +234 -0
- 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
|
+
}
|