9drive 1.0.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/LICENSE +201 -0
- package/README.md +389 -0
- package/backend/dist/app.js +113 -0
- package/backend/dist/config/env.js +75 -0
- package/backend/dist/config/prisma.js +30 -0
- package/backend/dist/middleware/api-key.middleware.js +38 -0
- package/backend/dist/middleware/auth.middleware.js +21 -0
- package/backend/dist/middleware/error.middleware.js +7 -0
- package/backend/dist/modules/api-keys/api-key.routes.js +90 -0
- package/backend/dist/modules/audit-logs/audit-log.routes.js +20 -0
- package/backend/dist/modules/auth/auth.routes.js +201 -0
- package/backend/dist/modules/cdn/cdn.routes.js +105 -0
- package/backend/dist/modules/connected-accounts/connected-account.routes.js +313 -0
- package/backend/dist/modules/files/file.routes.js +1013 -0
- package/backend/dist/modules/files/stream-file.js +10 -0
- package/backend/dist/modules/files/stream-google-file.js +79 -0
- package/backend/dist/modules/folders/folder.routes.js +546 -0
- package/backend/dist/modules/google/google.service.js +485 -0
- package/backend/dist/modules/invites/invite.routes.js +127 -0
- package/backend/dist/modules/provider-configs/provider-config.routes.js +53 -0
- package/backend/dist/modules/public/public.routes.js +44 -0
- package/backend/dist/modules/public-api/public-api.routes.js +8 -0
- package/backend/dist/modules/s3/s3.service.js +104 -0
- package/backend/dist/modules/storage/storage.routes.js +108 -0
- package/backend/dist/modules/system/system.routes.js +663 -0
- package/backend/dist/modules/uploads/upload.routes.js +569 -0
- package/backend/dist/scripts/debug-query.js +19 -0
- package/backend/dist/scripts/seed-google-config-if-present.js +49 -0
- package/backend/dist/scripts/seed-google-config.js +40 -0
- package/backend/dist/scripts/test-api-upload.js +37 -0
- package/backend/dist/scripts/test-external-upload.js +181 -0
- package/backend/dist/scripts/test-s3-connection.js +33 -0
- package/backend/dist/server.js +7 -0
- package/backend/dist/utils/audit.js +20 -0
- package/backend/dist/utils/crypto.js +33 -0
- package/backend/dist/utils/jwt.js +15 -0
- package/backend/dist/utils/password.js +14 -0
- package/backend/prisma/generated/sqlite-client/client.d.ts +1 -0
- package/backend/prisma/generated/sqlite-client/client.js +5 -0
- package/backend/prisma/generated/sqlite-client/default.d.ts +1 -0
- package/backend/prisma/generated/sqlite-client/default.js +5 -0
- package/backend/prisma/generated/sqlite-client/edge.d.ts +1 -0
- package/backend/prisma/generated/sqlite-client/edge.js +436 -0
- package/backend/prisma/generated/sqlite-client/index-browser.js +422 -0
- package/backend/prisma/generated/sqlite-client/index.d.ts +35178 -0
- package/backend/prisma/generated/sqlite-client/index.js +457 -0
- package/backend/prisma/generated/sqlite-client/package.json +183 -0
- package/backend/prisma/generated/sqlite-client/query_engine-windows.dll.node +0 -0
- package/backend/prisma/generated/sqlite-client/query_engine_bg.js +2 -0
- package/backend/prisma/generated/sqlite-client/query_engine_bg.wasm +0 -0
- package/backend/prisma/generated/sqlite-client/runtime/edge-esm.js +35 -0
- package/backend/prisma/generated/sqlite-client/runtime/edge.js +35 -0
- package/backend/prisma/generated/sqlite-client/runtime/index-browser.d.ts +370 -0
- package/backend/prisma/generated/sqlite-client/runtime/index-browser.js +17 -0
- package/backend/prisma/generated/sqlite-client/runtime/library.d.ts +3982 -0
- package/backend/prisma/generated/sqlite-client/runtime/library.js +147 -0
- package/backend/prisma/generated/sqlite-client/runtime/react-native.js +84 -0
- package/backend/prisma/generated/sqlite-client/runtime/wasm-compiler-edge.js +85 -0
- package/backend/prisma/generated/sqlite-client/runtime/wasm-engine-edge.js +38 -0
- package/backend/prisma/generated/sqlite-client/schema.prisma +354 -0
- package/backend/prisma/generated/sqlite-client/wasm-edge-light-loader.mjs +5 -0
- package/backend/prisma/generated/sqlite-client/wasm-worker-loader.mjs +5 -0
- package/backend/prisma/generated/sqlite-client/wasm.d.ts +1 -0
- package/backend/prisma/generated/sqlite-client/wasm.js +443 -0
- package/backend/prisma/migrations/20260604084325_init/migration.sql +191 -0
- package/backend/prisma/migrations/20260604090020_global_provider_config/migration.sql +8 -0
- package/backend/prisma/migrations/20260604091957_virtual_folders/migration.sql +32 -0
- package/backend/prisma/migrations/20260604095418_nested_folders/migration.sql +8 -0
- package/backend/prisma/migrations/20260604095705_file_shares/migration.sql +22 -0
- package/backend/prisma/migrations/20260604100802_file_preview_tokens/migration.sql +20 -0
- package/backend/prisma/migrations/20260604120000_add_file_share_token/migration.sql +3 -0
- package/backend/prisma/migrations/20260604130000_workspace_invites/migration.sql +17 -0
- package/backend/prisma/migrations/20260604133000_invite_targets/migration.sql +9 -0
- package/backend/prisma/migrations/20260605090000_query_indexes/migration.sql +12 -0
- package/backend/prisma/migrations/20260605100000_folder_icons/migration.sql +1 -0
- package/backend/prisma/migrations/20260605110000_google_auth_handoff/migration.sql +22 -0
- package/backend/prisma/migrations/20260611120000_s3_storage_support/migration.sql +31 -0
- package/backend/prisma/migrations/20260611130000_upload_routing_policy/migration.sql +14 -0
- package/backend/prisma/migrations/20260611140000_api_keys/migration.sql +21 -0
- package/backend/prisma/migrations/20260630030000_upload_session_resumable_fields/migration.sql +9 -0
- package/backend/prisma/migrations/migration_lock.toml +3 -0
- package/backend/prisma/schema.prisma +353 -0
- package/backend/prisma/schema.sqlite.prisma +354 -0
- package/backend/public/assets/index-B4ifIM_G.js +135 -0
- package/backend/public/assets/index-DwOPZrmt.css +2 -0
- package/backend/public/assets/ort-wasm-simd-threaded.jsep-D5Jk56-t.wasm +0 -0
- package/backend/public/assets/ort.bundle.min-CpXgll2e.js +2746 -0
- package/backend/public/assets/ort.bundle.min-OfoG_cy9.mjs +2817 -0
- package/backend/public/assets/ort.webgpu.bundle.min-C1vdBqT7.js +2746 -0
- package/backend/public/assets/ort.webgpu.bundle.min-CEayb2S6.mjs +2817 -0
- package/backend/public/assets/pdf.worker.min-Dswkl-cV.mjs +29 -0
- package/backend/public/favicon.ico +74 -0
- package/backend/public/favicon.svg +74 -0
- package/backend/public/icons.svg +24 -0
- package/backend/public/index.html +34 -0
- package/backend/public/manifest.webmanifest +1 -0
- package/backend/public/maskable-icon.svg +22 -0
- package/backend/public/pwa-192x192.svg +22 -0
- package/backend/public/pwa-512x512.svg +22 -0
- package/backend/public/registerSW.js +1 -0
- package/backend/public/sw.js +1 -0
- package/backend/public/workbox-9c191d2f.js +1 -0
- package/bin/9drive.js +320 -0
- package/package.json +60 -0
package/bin/9drive.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 9Drive CLI Launcher
|
|
5
|
+
* Zero-config Local-First Storage Cloud Server
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs')
|
|
9
|
+
const path = require('path')
|
|
10
|
+
const os = require('os')
|
|
11
|
+
const crypto = require('crypto')
|
|
12
|
+
const { spawn, execSync } = require('child_process')
|
|
13
|
+
|
|
14
|
+
// Version from root package.json
|
|
15
|
+
let version = '1.0.0'
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'))
|
|
18
|
+
if (pkg.version) version = pkg.version
|
|
19
|
+
} catch {}
|
|
20
|
+
|
|
21
|
+
const BANNER = `
|
|
22
|
+
\x1b[36m ___ ___ _ \x1b[0m
|
|
23
|
+
\x1b[36m / _ \\ | \\ _ _(_)_ _____ \x1b[0m
|
|
24
|
+
\x1b[36m | (_) || |) | '_| \\ V / -_) \x1b[0m
|
|
25
|
+
\x1b[36m \\__\\_\\|___/|_| |_|\\_/\\___| \x1b[33mv${version}\x1b[0m
|
|
26
|
+
\x1b[90m Cloud Storage & Universal API Gateway\x1b[0m
|
|
27
|
+
`
|
|
28
|
+
|
|
29
|
+
function printHelp() {
|
|
30
|
+
console.log(BANNER)
|
|
31
|
+
console.log(`
|
|
32
|
+
Usage:
|
|
33
|
+
9drive [command] [options]
|
|
34
|
+
|
|
35
|
+
Commands:
|
|
36
|
+
start Start the 9Drive local server (default)
|
|
37
|
+
status Show 9Drive data directory, database, and configuration
|
|
38
|
+
backup Create an instant backup of the local database
|
|
39
|
+
open Open the 9Drive Web UI in your default browser
|
|
40
|
+
|
|
41
|
+
Options:
|
|
42
|
+
--port <num> Port number to run 9Drive on (default: 9999 or from config)
|
|
43
|
+
--host <str> Host to bind server (default: localhost)
|
|
44
|
+
--db <url> Custom database URL (SQLite file:... or MySQL mysql://...)
|
|
45
|
+
--no-open Do not open the browser automatically upon launch
|
|
46
|
+
--help, -h Show this help message
|
|
47
|
+
--version, -v Show version number
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
npx 9drive
|
|
51
|
+
npx 9drive start --port 8080
|
|
52
|
+
npx 9drive status
|
|
53
|
+
npx 9drive backup
|
|
54
|
+
`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Parse command line arguments
|
|
58
|
+
const args = process.argv.slice(2)
|
|
59
|
+
let command = 'start'
|
|
60
|
+
let portArg = null
|
|
61
|
+
let dbArg = null
|
|
62
|
+
let noOpen = false
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < args.length; i++) {
|
|
65
|
+
const arg = args[i]
|
|
66
|
+
if (arg === '--help' || arg === '-h') {
|
|
67
|
+
printHelp()
|
|
68
|
+
process.exit(0)
|
|
69
|
+
}
|
|
70
|
+
if (arg === '--version' || arg === '-v') {
|
|
71
|
+
console.log(`9Drive v${version}`)
|
|
72
|
+
process.exit(0)
|
|
73
|
+
}
|
|
74
|
+
if (arg === '--port') {
|
|
75
|
+
portArg = Number(args[++i])
|
|
76
|
+
} else if (arg === '--db') {
|
|
77
|
+
dbArg = args[++i]
|
|
78
|
+
} else if (arg === '--no-open') {
|
|
79
|
+
noOpen = true
|
|
80
|
+
} else if (!arg.startsWith('-') && i === 0) {
|
|
81
|
+
command = arg
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Setup user data directory: ~/.9drive/
|
|
86
|
+
const homeDir = os.homedir()
|
|
87
|
+
const dataDir = process.env.NINEDRIVE_DATA_DIR || path.join(homeDir, '.9drive')
|
|
88
|
+
const backupsDir = path.join(dataDir, 'backups')
|
|
89
|
+
const storageDir = path.join(dataDir, 'storage')
|
|
90
|
+
const configPath = path.join(dataDir, 'config.json')
|
|
91
|
+
const sqliteDbPath = path.join(dataDir, '9drive.db')
|
|
92
|
+
|
|
93
|
+
function ensureDirectories() {
|
|
94
|
+
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
|
|
95
|
+
if (!fs.existsSync(backupsDir)) fs.mkdirSync(backupsDir, { recursive: true })
|
|
96
|
+
if (!fs.existsSync(storageDir)) fs.mkdirSync(storageDir, { recursive: true })
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function loadOrCreateConfig() {
|
|
100
|
+
ensureDirectories()
|
|
101
|
+
let config = {}
|
|
102
|
+
if (fs.existsSync(configPath)) {
|
|
103
|
+
try {
|
|
104
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const defaultPort = portArg || config.APP_PORT || 9999
|
|
109
|
+
const defaultDatabaseUrl = dbArg || config.DATABASE_URL || `file:${sqliteDbPath.replace(/\\/g, '/')}`
|
|
110
|
+
const defaultFrontendUrl = config.FRONTEND_URL || `http://localhost:${defaultPort}`
|
|
111
|
+
const defaultJwtSecret = config.JWT_ACCESS_SECRET || crypto.randomBytes(32).toString('hex')
|
|
112
|
+
const defaultEncryptionKey = config.TOKEN_ENCRYPTION_KEY || crypto.randomBytes(32).toString('hex')
|
|
113
|
+
|
|
114
|
+
const updatedConfig = {
|
|
115
|
+
APP_PORT: defaultPort,
|
|
116
|
+
DATABASE_URL: defaultDatabaseUrl,
|
|
117
|
+
FRONTEND_URL: defaultFrontendUrl,
|
|
118
|
+
JWT_ACCESS_SECRET: defaultJwtSecret,
|
|
119
|
+
TOKEN_ENCRYPTION_KEY: defaultEncryptionKey,
|
|
120
|
+
MAX_UPLOAD_BYTES: config.MAX_UPLOAD_BYTES || 5 * 1024 * 1024 * 1024,
|
|
121
|
+
...config,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (portArg) updatedConfig.APP_PORT = portArg
|
|
125
|
+
if (dbArg) updatedConfig.DATABASE_URL = dbArg
|
|
126
|
+
|
|
127
|
+
fs.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2))
|
|
128
|
+
return updatedConfig
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function openBrowser(url) {
|
|
132
|
+
const start =
|
|
133
|
+
process.platform === 'darwin'
|
|
134
|
+
? 'open'
|
|
135
|
+
: process.platform === 'win32'
|
|
136
|
+
? 'start'
|
|
137
|
+
: 'xdg-open'
|
|
138
|
+
try {
|
|
139
|
+
execSync(`${start} ${url}`, { stdio: 'ignore' })
|
|
140
|
+
} catch {}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function handleStatus() {
|
|
144
|
+
ensureDirectories()
|
|
145
|
+
const config = loadOrCreateConfig()
|
|
146
|
+
console.log(BANNER)
|
|
147
|
+
console.log('\x1b[32m[9Drive Status]\x1b[0m')
|
|
148
|
+
console.log(` Version: v${version}`)
|
|
149
|
+
console.log(` Data Directory: ${dataDir}`)
|
|
150
|
+
console.log(` Config File: ${configPath}`)
|
|
151
|
+
console.log(` Database URL: ${config.DATABASE_URL}`)
|
|
152
|
+
console.log(` Configured Port: ${config.APP_PORT}`)
|
|
153
|
+
|
|
154
|
+
if (fs.existsSync(sqliteDbPath)) {
|
|
155
|
+
const stats = fs.statSync(sqliteDbPath)
|
|
156
|
+
console.log(` SQLite DB Size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`)
|
|
157
|
+
} else {
|
|
158
|
+
console.log(` SQLite DB Size: Not initialized yet`)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const backups = fs.existsSync(backupsDir) ? fs.readdirSync(backupsDir) : []
|
|
162
|
+
console.log(` Total Backups: ${backups.length} snapshot(s)`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function handleBackup() {
|
|
166
|
+
ensureDirectories()
|
|
167
|
+
if (!fs.existsSync(sqliteDbPath)) {
|
|
168
|
+
console.log('\x1b[33m[!] No SQLite database found to backup yet.\x1b[0m')
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
172
|
+
const destPath = path.join(backupsDir, `9drive-backup-${timestamp}.db`)
|
|
173
|
+
fs.copyFileSync(sqliteDbPath, destPath)
|
|
174
|
+
console.log(`\x1b[32m[✓] Database backup created:\x1b[0m ${destPath}`)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function handleStart() {
|
|
178
|
+
ensureDirectories()
|
|
179
|
+
const config = loadOrCreateConfig()
|
|
180
|
+
|
|
181
|
+
console.log(BANNER)
|
|
182
|
+
console.log(` \x1b[32m[✓]\x1b[0m Data directory: \x1b[90m${dataDir}\x1b[0m`)
|
|
183
|
+
|
|
184
|
+
const isSqlite = config.DATABASE_URL.startsWith('file:') || config.DATABASE_URL.startsWith('sqlite:')
|
|
185
|
+
|
|
186
|
+
// Auto-backup database on startup if existing SQLite database found
|
|
187
|
+
if (isSqlite && fs.existsSync(sqliteDbPath)) {
|
|
188
|
+
try {
|
|
189
|
+
const stats = fs.statSync(sqliteDbPath)
|
|
190
|
+
if (stats.size > 0) {
|
|
191
|
+
const timestamp = new Date().toISOString().slice(0, 10)
|
|
192
|
+
const autoBackup = path.join(backupsDir, `auto-backup-${timestamp}.db`)
|
|
193
|
+
fs.copyFileSync(sqliteDbPath, autoBackup)
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Resolve backend root and prisma
|
|
199
|
+
const backendDir = path.resolve(__dirname, '../backend')
|
|
200
|
+
const rootDir = path.resolve(__dirname, '..')
|
|
201
|
+
|
|
202
|
+
// Sync SQLite schema automatically if in SQLite mode
|
|
203
|
+
if (isSqlite) {
|
|
204
|
+
const sqliteSchemaPath = path.join(backendDir, 'prisma/schema.sqlite.prisma')
|
|
205
|
+
const sqliteClientIndex = path.join(backendDir, 'prisma/generated/sqlite-client/index.js')
|
|
206
|
+
const prismaCli = [
|
|
207
|
+
path.join(rootDir, 'node_modules/prisma/build/index.js'),
|
|
208
|
+
path.join(backendDir, 'node_modules/prisma/build/index.js'),
|
|
209
|
+
].find((p) => fs.existsSync(p)) || null
|
|
210
|
+
|
|
211
|
+
if (fs.existsSync(sqliteSchemaPath) && prismaCli) {
|
|
212
|
+
if (!fs.existsSync(sqliteClientIndex)) {
|
|
213
|
+
process.stdout.write(' [⧗] Generating SQLite database client... ')
|
|
214
|
+
try {
|
|
215
|
+
execSync(`node "${prismaCli}" generate --schema="${sqliteSchemaPath}"`, {
|
|
216
|
+
stdio: 'ignore',
|
|
217
|
+
env: { ...process.env, DATABASE_URL: config.DATABASE_URL },
|
|
218
|
+
})
|
|
219
|
+
console.log('\x1b[32m[Ready]\x1b[0m')
|
|
220
|
+
} catch {
|
|
221
|
+
console.log('\x1b[33m[Notice: Generator skipped]\x1b[0m')
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
process.stdout.write(' [⧗] Checking database schema... ')
|
|
226
|
+
try {
|
|
227
|
+
execSync(
|
|
228
|
+
`node "${prismaCli}" db push --schema="${sqliteSchemaPath}" --skip-generate`,
|
|
229
|
+
{
|
|
230
|
+
stdio: 'ignore',
|
|
231
|
+
env: {
|
|
232
|
+
...process.env,
|
|
233
|
+
DATABASE_URL: config.DATABASE_URL,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
console.log('\x1b[32m[Ready]\x1b[0m')
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.log('\x1b[33m[Notice: Using existing schema]\x1b[0m')
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Locate server entry file (dist/server.js or src/server.ts)
|
|
245
|
+
let serverEntry = path.join(backendDir, 'dist/server.js')
|
|
246
|
+
let useTsx = false
|
|
247
|
+
|
|
248
|
+
if (!fs.existsSync(serverEntry)) {
|
|
249
|
+
serverEntry = path.join(backendDir, 'src/server.ts')
|
|
250
|
+
useTsx = true
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const serverUrl = `http://localhost:${config.APP_PORT}`
|
|
254
|
+
console.log(` \x1b[32m[✓]\x1b[0m Web Dashboard: \x1b[36m\x1b[4m${serverUrl}\x1b[0m`)
|
|
255
|
+
console.log(` \x1b[90m[i] Press Ctrl+C to stop the server.\x1b[0m\n`)
|
|
256
|
+
|
|
257
|
+
const nodePath = [
|
|
258
|
+
path.join(backendDir, 'node_modules'),
|
|
259
|
+
path.join(rootDir, 'node_modules'),
|
|
260
|
+
process.env.NODE_PATH || '',
|
|
261
|
+
].filter(Boolean).join(path.delimiter)
|
|
262
|
+
|
|
263
|
+
const envVars = {
|
|
264
|
+
...process.env,
|
|
265
|
+
NODE_PATH: nodePath,
|
|
266
|
+
NINEDRIVE_CLI: 'true',
|
|
267
|
+
NINEDRIVE_DATA_DIR: dataDir,
|
|
268
|
+
APP_PORT: String(config.APP_PORT),
|
|
269
|
+
DATABASE_URL: config.DATABASE_URL,
|
|
270
|
+
FRONTEND_URL: config.FRONTEND_URL || serverUrl,
|
|
271
|
+
JWT_ACCESS_SECRET: config.JWT_ACCESS_SECRET,
|
|
272
|
+
TOKEN_ENCRYPTION_KEY: config.TOKEN_ENCRYPTION_KEY,
|
|
273
|
+
SERVE_FRONTEND: 'true',
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const runner = useTsx
|
|
277
|
+
? path.join(backendDir, 'node_modules/tsx/dist/cli.mjs')
|
|
278
|
+
: null
|
|
279
|
+
|
|
280
|
+
const child = runner
|
|
281
|
+
? spawn('node', [runner, serverEntry], {
|
|
282
|
+
cwd: backendDir,
|
|
283
|
+
env: envVars,
|
|
284
|
+
stdio: 'inherit',
|
|
285
|
+
})
|
|
286
|
+
: spawn('node', [serverEntry], {
|
|
287
|
+
cwd: backendDir,
|
|
288
|
+
env: envVars,
|
|
289
|
+
stdio: 'inherit',
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Open browser after short delay
|
|
293
|
+
if (!noOpen) {
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
openBrowser(serverUrl)
|
|
296
|
+
}, 1500)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
child.on('exit', (code) => {
|
|
300
|
+
process.exit(code || 0)
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Route commands
|
|
305
|
+
switch (command) {
|
|
306
|
+
case 'status':
|
|
307
|
+
handleStatus()
|
|
308
|
+
break
|
|
309
|
+
case 'backup':
|
|
310
|
+
handleBackup()
|
|
311
|
+
break
|
|
312
|
+
case 'open':
|
|
313
|
+
const conf = loadOrCreateConfig()
|
|
314
|
+
openBrowser(`http://localhost:${conf.APP_PORT}`)
|
|
315
|
+
break
|
|
316
|
+
case 'start':
|
|
317
|
+
default:
|
|
318
|
+
handleStart()
|
|
319
|
+
break
|
|
320
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "9drive",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "9Drive - Personal Cloud Storage & Universal API Gateway",
|
|
5
|
+
"main": "bin/9drive.js",
|
|
6
|
+
"bin": "bin/9drive.js",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"cloud-storage",
|
|
9
|
+
"s3",
|
|
10
|
+
"google-drive",
|
|
11
|
+
"storage",
|
|
12
|
+
"self-hosted",
|
|
13
|
+
"local-first",
|
|
14
|
+
"drive",
|
|
15
|
+
"npx"
|
|
16
|
+
],
|
|
17
|
+
"author": "Ninedrive",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/ninedrive/9drive.git"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"backend/dist",
|
|
26
|
+
"backend/public",
|
|
27
|
+
"backend/prisma",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"start": "node bin/9drive.js",
|
|
32
|
+
"dev": "concurrently -n \"backend,frontend\" -c \"blue,green\" \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"",
|
|
33
|
+
"dev:backend": "npm run dev --prefix backend",
|
|
34
|
+
"dev:frontend": "npm run dev --prefix frontend",
|
|
35
|
+
"install:all": "npm install --prefix backend && npm install --prefix frontend",
|
|
36
|
+
"build": "npm run build --prefix backend && npm run build --prefix frontend",
|
|
37
|
+
"build:sqlite": "npm run prisma:generate:sqlite --prefix backend",
|
|
38
|
+
"bundle:cli": "node scripts/bundle-cli.js",
|
|
39
|
+
"prepack": "node scripts/bundle-cli.js"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@aws-sdk/client-s3": "^3.954.0",
|
|
43
|
+
"@aws-sdk/lib-storage": "^3.954.0",
|
|
44
|
+
"@prisma/client": "^6.19.3",
|
|
45
|
+
"archiver": "^8.0.0",
|
|
46
|
+
"argon2": "^0.44.0",
|
|
47
|
+
"busboy": "^1.6.0",
|
|
48
|
+
"cors": "^2.8.6",
|
|
49
|
+
"dotenv": "^17.4.2",
|
|
50
|
+
"express": "^5.2.1",
|
|
51
|
+
"googleapis": "^173.0.0",
|
|
52
|
+
"jsonwebtoken": "^9.0.3",
|
|
53
|
+
"prisma": "^6.19.3",
|
|
54
|
+
"undici": "^7.27.1",
|
|
55
|
+
"zod": "^4.4.3"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"concurrently": "^9.1.2"
|
|
59
|
+
}
|
|
60
|
+
}
|