@riligar/elysia-backup 1.8.2 → 1.10.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 +25 -5
- package/package.json +2 -3
- package/src/core/config.js +3 -1
- package/src/index.js +13 -7
package/README.md
CHANGED
|
@@ -41,7 +41,6 @@ const app = new Elysia()
|
|
|
41
41
|
.use(
|
|
42
42
|
r2Backup({
|
|
43
43
|
sourceDir: './data',
|
|
44
|
-
configPath: './backup-config.json',
|
|
45
44
|
})
|
|
46
45
|
)
|
|
47
46
|
.listen(3000)
|
|
@@ -60,10 +59,10 @@ On first run, you'll be guided through an onboarding wizard to configure:
|
|
|
60
59
|
|
|
61
60
|
### Plugin Options
|
|
62
61
|
|
|
63
|
-
| Option | Type | Required | Description
|
|
64
|
-
| ------------ | ------ | -------- |
|
|
65
|
-
| `sourceDir` | string | ✅ | Local directory to backup
|
|
66
|
-
| `configPath` | string | ❌ | Path to save runtime config (default:
|
|
62
|
+
| Option | Type | Required | Description |
|
|
63
|
+
| ------------ | ------ | -------- | ------------------------------------------------------------------- |
|
|
64
|
+
| `sourceDir` | string | ✅ | Local directory to backup |
|
|
65
|
+
| `configPath` | string | ❌ | Path to save runtime config (default: same directory as `sourceDir`) |
|
|
67
66
|
|
|
68
67
|
### Runtime Configuration (via UI or backup-config.json)
|
|
69
68
|
|
|
@@ -119,6 +118,27 @@ The plugin adds the following routes under `/backup`:
|
|
|
119
118
|
| POST | `/backup/api/totp/verify` | Verify and enable 2FA |
|
|
120
119
|
| POST | `/backup/api/totp/disable` | Disable 2FA |
|
|
121
120
|
|
|
121
|
+
## ☁️ Cloud Deployment (Fly.io, Railway, etc.)
|
|
122
|
+
|
|
123
|
+
For cloud platforms with ephemeral file systems, mount a persistent volume at your `sourceDir` path. The configuration file will be automatically saved alongside your data:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import { Elysia } from 'elysia'
|
|
127
|
+
import { r2Backup } from '@riligar/elysia-backup'
|
|
128
|
+
|
|
129
|
+
const app = new Elysia()
|
|
130
|
+
.use(
|
|
131
|
+
r2Backup({
|
|
132
|
+
// Mount your persistent volume here
|
|
133
|
+
// Config will be saved at /data/backup-config.json
|
|
134
|
+
sourceDir: '/data',
|
|
135
|
+
})
|
|
136
|
+
)
|
|
137
|
+
.listen(3000)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This ensures both your backup data and configuration persist across deployments and restarts.
|
|
141
|
+
|
|
122
142
|
## 🔐 Security Features
|
|
123
143
|
|
|
124
144
|
### Session-Based Authentication
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riligar/elysia-backup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Elysia plugin for R2/S3 backup with a built-in UI dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -42,8 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"elysia": ">=1.0.0"
|
|
46
|
-
"@elysiajs/html": ">=1.0.0"
|
|
45
|
+
"elysia": ">=1.0.0"
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
49
48
|
"cron": "^3.5.0",
|
package/src/core/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs'
|
|
2
2
|
import { writeFile } from 'node:fs/promises'
|
|
3
|
+
import { dirname, join } from 'node:path'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Load configuration from a JSON file
|
|
@@ -43,7 +44,8 @@ export const saveConfig = async (configPath, config) => {
|
|
|
43
44
|
* @returns {Object} Configuration manager with get/set/save methods
|
|
44
45
|
*/
|
|
45
46
|
export const createConfigManager = initialConfig => {
|
|
46
|
-
|
|
47
|
+
// If configPath not specified, save config alongside sourceDir for cloud deployments
|
|
48
|
+
const configPath = initialConfig.configPath || join(dirname(initialConfig.sourceDir), 'backup-config.json')
|
|
47
49
|
const savedConfig = loadConfig(configPath)
|
|
48
50
|
|
|
49
51
|
let config = { ...initialConfig, ...savedConfig }
|
package/src/index.js
CHANGED
|
@@ -5,7 +5,12 @@ import { authenticator } from 'otplib'
|
|
|
5
5
|
import QRCode from 'qrcode'
|
|
6
6
|
import { writeFile } from 'node:fs/promises'
|
|
7
7
|
import { readFileSync, existsSync } from 'node:fs'
|
|
8
|
-
import {
|
|
8
|
+
import { dirname, join } from 'node:path'
|
|
9
|
+
// Helper to return HTML responses with proper Content-Type
|
|
10
|
+
const htmlResponse = content =>
|
|
11
|
+
new Response(content, {
|
|
12
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
13
|
+
})
|
|
9
14
|
|
|
10
15
|
// Import core modules
|
|
11
16
|
import { createSessionManager } from './core/session.js'
|
|
@@ -32,11 +37,12 @@ const sessionManager = createSessionManager()
|
|
|
32
37
|
* @param {string} [config.prefix] - Optional prefix for S3 keys (e.g. 'backups/')
|
|
33
38
|
* @param {string} [config.cronSchedule] - Cron schedule expression
|
|
34
39
|
* @param {boolean} [config.cronEnabled] - Whether the cron schedule is enabled
|
|
35
|
-
* @param {string} [config.configPath] - Path to save runtime configuration (default:
|
|
40
|
+
* @param {string} [config.configPath] - Path to save runtime configuration (default: same directory as sourceDir)
|
|
36
41
|
*/
|
|
37
42
|
export const r2Backup = initialConfig => app => {
|
|
38
43
|
// State to hold runtime configuration (allows UI updates)
|
|
39
|
-
|
|
44
|
+
// If configPath not specified, save config alongside sourceDir for cloud deployments
|
|
45
|
+
const configPath = initialConfig.configPath || join(dirname(initialConfig.sourceDir), 'backup-config.json')
|
|
40
46
|
|
|
41
47
|
// Load saved config if exists
|
|
42
48
|
let savedConfig = {}
|
|
@@ -76,7 +82,7 @@ export const r2Backup = initialConfig => app => {
|
|
|
76
82
|
// Setup initial cron schedule
|
|
77
83
|
scheduler.setup(config.cronSchedule, config.cronEnabled !== false)
|
|
78
84
|
|
|
79
|
-
return app.
|
|
85
|
+
return app.group('/backup', app => {
|
|
80
86
|
// Authentication Middleware
|
|
81
87
|
const authMiddleware = context => {
|
|
82
88
|
// Skip auth entirely if no valid config (needs onboarding)
|
|
@@ -138,7 +144,7 @@ export const r2Backup = initialConfig => app => {
|
|
|
138
144
|
return
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
return LoginPage({ totpEnabled: !!config.auth?.totpSecret })
|
|
147
|
+
return htmlResponse(LoginPage({ totpEnabled: !!config.auth?.totpSecret }))
|
|
142
148
|
})
|
|
143
149
|
|
|
144
150
|
// AUTH: Login Endpoint
|
|
@@ -452,7 +458,7 @@ export const r2Backup = initialConfig => app => {
|
|
|
452
458
|
set.headers['Location'] = '/backup'
|
|
453
459
|
return
|
|
454
460
|
}
|
|
455
|
-
return OnboardingPage({ sourceDir: config.sourceDir })
|
|
461
|
+
return htmlResponse(OnboardingPage({ sourceDir: config.sourceDir }))
|
|
456
462
|
})
|
|
457
463
|
|
|
458
464
|
// ONBOARDING: Save Initial Config
|
|
@@ -535,7 +541,7 @@ export const r2Backup = initialConfig => app => {
|
|
|
535
541
|
|
|
536
542
|
const jobStatus = scheduler.getStatus(config.cronEnabled)
|
|
537
543
|
const hasAuth = !!(config.auth && config.auth.username && config.auth.password)
|
|
538
|
-
return DashboardPage({ config, jobStatus, hasAuth })
|
|
544
|
+
return htmlResponse(DashboardPage({ config, jobStatus, hasAuth }))
|
|
539
545
|
})
|
|
540
546
|
)
|
|
541
547
|
})
|