ani-web 1.5.8
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.
Potentially problematic release.
This version of ani-web might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/client/dist/assets/AnimeInfo-C7DQp7Oo.js +1 -0
- package/client/dist/assets/AnimeInfo-Sb3YiXHJ.css +1 -0
- package/client/dist/assets/AnimeInfoPage-DJA7AJQ8.js +2 -0
- package/client/dist/assets/Button-Fq9KaUOg.css +1 -0
- package/client/dist/assets/Button-o0l9V_NG.js +1 -0
- package/client/dist/assets/ErrorMessage-Ddf2zmRx.js +1 -0
- package/client/dist/assets/ErrorMessage-FOxXyZC9.css +1 -0
- package/client/dist/assets/Home-CKHJA97j.css +1 -0
- package/client/dist/assets/Home-Dey0azy1.js +1 -0
- package/client/dist/assets/Insights-BSRcCkDs.css +1 -0
- package/client/dist/assets/Insights-CogjPOd_.js +1 -0
- package/client/dist/assets/MAL-CYArH4yf.js +1 -0
- package/client/dist/assets/MAL-DeQNXXPx.css +1 -0
- package/client/dist/assets/Player-BWFN9gud.js +9 -0
- package/client/dist/assets/Player-CBCYW7uG.css +1 -0
- package/client/dist/assets/PlayerSettings-BgStUrrP.css +1 -0
- package/client/dist/assets/PlayerSettings-rWZuATQf.js +1 -0
- package/client/dist/assets/RemoveConfirmationModal-BBiogSdf.css +1 -0
- package/client/dist/assets/RemoveConfirmationModal-CLYqyGOv.js +1 -0
- package/client/dist/assets/Search-DZAWgKwq.js +1 -0
- package/client/dist/assets/Search-lWsVQ0Ke.css +1 -0
- package/client/dist/assets/Settings-Bv9fX-x3.css +1 -0
- package/client/dist/assets/Settings-DyisJGeD.js +1 -0
- package/client/dist/assets/ToggleSwitch-CLnWnAuY.js +1 -0
- package/client/dist/assets/ToggleSwitch-DInRb7iM.css +1 -0
- package/client/dist/assets/Watchlist-2dVYksxq.css +1 -0
- package/client/dist/assets/Watchlist-CuqJISI3.js +1 -0
- package/client/dist/assets/hls.light-DcbkToIY.js +27 -0
- package/client/dist/assets/index-BK_Zaqaw.css +1 -0
- package/client/dist/assets/index-CHVF4D4L.js +178 -0
- package/client/dist/assets/useAnimeInfoData-Cr58brCY.js +1 -0
- package/client/dist/assets/useIsMobile-gHo4t6g6.js +1 -0
- package/client/dist/assets/vendor-DdbgYKo4.js +3 -0
- package/client/dist/favicon.ico +0 -0
- package/client/dist/index.html +35 -0
- package/client/dist/logo.png +0 -0
- package/client/dist/placeholder.svg +4 -0
- package/client/dist/robots.txt +3 -0
- package/client/package.json +54 -0
- package/orchestrator.js +302 -0
- package/package.json +69 -0
- package/server/dist/config.js +86 -0
- package/server/dist/constants.json +1359 -0
- package/server/dist/controllers/auth.controller.js +213 -0
- package/server/dist/controllers/data.controller.js +126 -0
- package/server/dist/controllers/insights.controller.js +125 -0
- package/server/dist/controllers/proxy.controller.js +235 -0
- package/server/dist/controllers/settings.controller.js +147 -0
- package/server/dist/controllers/watchlist.controller.js +499 -0
- package/server/dist/db.js +231 -0
- package/server/dist/github-sync.js +279 -0
- package/server/dist/google.js +274 -0
- package/server/dist/logger.js +21 -0
- package/server/dist/providers/123anime.provider.js +229 -0
- package/server/dist/providers/allanime.provider.js +773 -0
- package/server/dist/providers/animepahe.provider.js +313 -0
- package/server/dist/providers/animeya.provider.js +799 -0
- package/server/dist/providers/provider.interface.js +2 -0
- package/server/dist/rclone.js +126 -0
- package/server/dist/repositories/insights.repository.js +30 -0
- package/server/dist/repositories/notifications.repository.js +22 -0
- package/server/dist/repositories/settings.repository.js +13 -0
- package/server/dist/repositories/shows-meta.repository.js +39 -0
- package/server/dist/repositories/watched-episodes.repository.js +60 -0
- package/server/dist/repositories/watchlist.repository.js +49 -0
- package/server/dist/routes/auth.routes.js +23 -0
- package/server/dist/routes/data.routes.js +43 -0
- package/server/dist/routes/insights.routes.js +11 -0
- package/server/dist/routes/proxy.routes.js +13 -0
- package/server/dist/routes/settings.routes.js +26 -0
- package/server/dist/routes/watchlist.routes.js +26 -0
- package/server/dist/server.js +179 -0
- package/server/dist/sync-config.js +28 -0
- package/server/dist/sync.js +383 -0
- package/server/dist/utils/db-utils.js +36 -0
- package/server/dist/utils/env.utils.js +70 -0
- package/server/package.json +54 -0
package/orchestrator.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process')
|
|
3
|
+
const readline = require('readline')
|
|
4
|
+
const http = require('http')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const axios = require('axios')
|
|
8
|
+
|
|
9
|
+
const mode = process.argv[2] || 'prod'
|
|
10
|
+
const isWin = os.platform() === 'win32'
|
|
11
|
+
const npmCmd = isWin ? 'npm.cmd' : 'npm'
|
|
12
|
+
|
|
13
|
+
const colors = {
|
|
14
|
+
reset: '\x1b[0m',
|
|
15
|
+
server: '\x1b[36m',
|
|
16
|
+
client: '\x1b[32m',
|
|
17
|
+
system: '\x1b[33m',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (mode === '--version' || mode === '-v') {
|
|
21
|
+
const pkg = require('./package.json')
|
|
22
|
+
console.log(`ani-web version ${pkg.version}`)
|
|
23
|
+
process.exit(0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function checkForUpdates() {
|
|
27
|
+
if (process.argv.includes('--no-update') || mode === 'dev') return
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const npmGlobalPrefix = require('child_process')
|
|
31
|
+
.execSync('npm config get prefix', { encoding: 'utf8' })
|
|
32
|
+
.trim()
|
|
33
|
+
const scriptPath = path.resolve(__dirname)
|
|
34
|
+
const isGlobalInstall = scriptPath.includes(npmGlobalPrefix)
|
|
35
|
+
|
|
36
|
+
if (!isGlobalInstall) return
|
|
37
|
+
|
|
38
|
+
const pkg = require('./package.json')
|
|
39
|
+
const current = pkg.version
|
|
40
|
+
|
|
41
|
+
const { data } = await axios.get('https://registry.npmjs.org/ani-web/latest', {
|
|
42
|
+
timeout: 3000,
|
|
43
|
+
headers: { 'User-Agent': 'ani-web-cli' },
|
|
44
|
+
})
|
|
45
|
+
const latest = data.version
|
|
46
|
+
|
|
47
|
+
if (current !== latest) {
|
|
48
|
+
console.log(
|
|
49
|
+
`\n${colors.system}[Update]${colors.reset} ` +
|
|
50
|
+
`New version ${colors.client}${latest}${colors.reset} available (current: ${current})`
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if (process.stdin.isTTY) {
|
|
54
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
55
|
+
const answer = await new Promise((resolve) => {
|
|
56
|
+
rl.question(
|
|
57
|
+
`${colors.system}[Update]${colors.reset} Would you like to perform a clean install now? (y/N) `,
|
|
58
|
+
(ans) => {
|
|
59
|
+
rl.close()
|
|
60
|
+
resolve(ans.toLowerCase())
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
if (answer === 'y' || answer === 'yes') {
|
|
66
|
+
console.log(`${colors.system}[Update]${colors.reset} Updating ani-web...`)
|
|
67
|
+
try {
|
|
68
|
+
require('child_process').execSync(`${npmCmd} install -g ani-web@latest`, {
|
|
69
|
+
stdio: 'inherit',
|
|
70
|
+
})
|
|
71
|
+
console.log(
|
|
72
|
+
`\n${colors.system}[Update]${colors.reset} Update successful! Please restart ani-web to apply changes.`
|
|
73
|
+
)
|
|
74
|
+
process.exit(0)
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`\n${colors.system}[Update]${colors.reset} Update failed: ${err.message}`)
|
|
77
|
+
if (!isWin) {
|
|
78
|
+
console.log(
|
|
79
|
+
`${colors.system}[Update]${colors.reset} Hint: You might need to run with sudo:`
|
|
80
|
+
)
|
|
81
|
+
console.log(
|
|
82
|
+
`${colors.system}[Update]${colors.reset} ${colors.client}sudo ani-web${colors.reset}\n`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
console.log(
|
|
86
|
+
`${colors.system}[Update]${colors.reset} Continuing with current version...\n`
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
console.log(
|
|
91
|
+
`${colors.system}[Update]${colors.reset} Continuing with version ${current}...\n`
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
if (process.stdin.isTTY) process.stdin.resume()
|
|
95
|
+
} else {
|
|
96
|
+
console.log(
|
|
97
|
+
`${colors.system}[Update]${colors.reset} Run: npm install -g ani-web to update.\n`
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Silently ignore network/registry errors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const SERVER_DIR = path.join(__dirname, 'server')
|
|
107
|
+
const CLIENT_DIR = path.join(__dirname, 'client')
|
|
108
|
+
|
|
109
|
+
let syncSpinner = null
|
|
110
|
+
let syncMessage = ''
|
|
111
|
+
let syncDots = 0
|
|
112
|
+
|
|
113
|
+
const startSpinner = (msg) => {
|
|
114
|
+
syncMessage = msg
|
|
115
|
+
syncDots = 0
|
|
116
|
+
process.stdout.write(`${colors.system}[System]${colors.reset} ${msg}`)
|
|
117
|
+
syncSpinner = setInterval(() => {
|
|
118
|
+
syncDots = (syncDots + 1) % 4
|
|
119
|
+
process.stdout.write(
|
|
120
|
+
`\r${colors.system}[System]${colors.reset} ${msg}${'.'.repeat(syncDots)}${' '.repeat(3 - syncDots)}`
|
|
121
|
+
)
|
|
122
|
+
}, 400)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const stopSpinner = () => {
|
|
126
|
+
if (syncSpinner) {
|
|
127
|
+
clearInterval(syncSpinner)
|
|
128
|
+
syncSpinner = null
|
|
129
|
+
process.stdout.write('\n')
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const log = (prefix, color, data) => {
|
|
134
|
+
const str = data.toString()
|
|
135
|
+
|
|
136
|
+
if (str.includes('[SYNC_START]')) {
|
|
137
|
+
const parts = str.split('[SYNC_START]')
|
|
138
|
+
if (parts[1]) startSpinner(parts[1].split('\n')[0].trim())
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if (str.includes('[SYNC_END]')) {
|
|
142
|
+
stopSpinner()
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (str.includes('[SERVER_EXIT]')) {
|
|
147
|
+
stopSpinner()
|
|
148
|
+
console.log(
|
|
149
|
+
`${colors.system}[System]${colors.reset} Server sync complete. Shutting down cleanly.`
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if (isWin) {
|
|
153
|
+
if (serverProcess) spawn('taskkill', ['/pid', serverProcess.pid, '/f', '/t'], { shell: true })
|
|
154
|
+
if (clientProcess) spawn('taskkill', ['/pid', clientProcess.pid, '/f', '/t'], { shell: true })
|
|
155
|
+
} else {
|
|
156
|
+
if (serverProcess) {
|
|
157
|
+
serverProcess.kill('SIGTERM')
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
if (serverProcess.connected || !serverProcess.killed) serverProcess.kill('SIGKILL')
|
|
160
|
+
}, 5000)
|
|
161
|
+
}
|
|
162
|
+
if (clientProcess) {
|
|
163
|
+
clientProcess.kill('SIGTERM')
|
|
164
|
+
setTimeout(() => {
|
|
165
|
+
if (clientProcess.connected || !clientProcess.killed) clientProcess.kill('SIGKILL')
|
|
166
|
+
}, 5000)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
setTimeout(() => process.exit(0), 5500)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const lines = str.split('\n').filter((line) => line.trim() !== '')
|
|
174
|
+
if (lines.length === 0) return
|
|
175
|
+
|
|
176
|
+
if (syncSpinner) {
|
|
177
|
+
process.stdout.write('\r\x1b[K')
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
console.log(`${color}[${prefix}]${colors.reset} ${line}`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
process.stdout.write(
|
|
183
|
+
`${colors.system}[System]${colors.reset} ${syncMessage}${'.'.repeat(syncDots)}${' '.repeat(3 - syncDots)}`
|
|
184
|
+
)
|
|
185
|
+
} else {
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
console.log(`${color}[${prefix}]${colors.reset} ${line}`)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const spawnOpts = (cwd) => ({ stdio: 'pipe', shell: isWin, cwd })
|
|
193
|
+
let serverProcess, clientProcess
|
|
194
|
+
let isShuttingDown = false
|
|
195
|
+
|
|
196
|
+
async function main() {
|
|
197
|
+
console.log(
|
|
198
|
+
`${colors.system}[System]${colors.reset} Starting ani-web in ${mode.toUpperCase()} mode...`
|
|
199
|
+
)
|
|
200
|
+
console.log(
|
|
201
|
+
`${colors.system}[System]${colors.reset} Press 'q' or 'Ctrl+C' to cleanly exit and sync data.\n`
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if (mode === 'dev') {
|
|
205
|
+
serverProcess = spawn(npmCmd, ['run', 'dev'], spawnOpts(SERVER_DIR))
|
|
206
|
+
clientProcess = spawn(npmCmd, ['run', 'dev'], spawnOpts(CLIENT_DIR))
|
|
207
|
+
} else {
|
|
208
|
+
const serverPath = path.join(SERVER_DIR, 'dist', 'server.js')
|
|
209
|
+
serverProcess = spawn('node', ['--max-old-space-size=256', serverPath], spawnOpts(SERVER_DIR))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (serverProcess) {
|
|
213
|
+
serverProcess.stdout.on('data', (data) => log('Server', colors.server, data))
|
|
214
|
+
serverProcess.stderr.on('data', (data) => log('Server', colors.server, data))
|
|
215
|
+
serverProcess.on('exit', (code) => {
|
|
216
|
+
if (!isShuttingDown) {
|
|
217
|
+
log('System', colors.system, `Server crashed or exited prematurely.`)
|
|
218
|
+
process.exit(code || 0)
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (clientProcess) {
|
|
224
|
+
clientProcess.stdout.on('data', (data) => log('Client', colors.client, data))
|
|
225
|
+
clientProcess.stderr.on('data', (data) => log('Client', colors.client, data))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (process.stdin.isTTY) {
|
|
229
|
+
process.stdin.resume()
|
|
230
|
+
readline.emitKeypressEvents(process.stdin)
|
|
231
|
+
process.stdin.setRawMode(true)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
process.stdin.on('keypress', (str, key) => {
|
|
235
|
+
if (key && (key.name === 'q' || (key.ctrl && key.name === 'c'))) {
|
|
236
|
+
shutdown()
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const shutdown = () => {
|
|
242
|
+
if (isShuttingDown) return
|
|
243
|
+
isShuttingDown = true
|
|
244
|
+
console.log(`\n${colors.system}[System]${colors.reset} Initiating clean shutdown...`)
|
|
245
|
+
|
|
246
|
+
if (clientProcess) {
|
|
247
|
+
if (isWin) spawn('taskkill', ['/pid', clientProcess.pid, '/f', '/t'], { shell: true })
|
|
248
|
+
else {
|
|
249
|
+
clientProcess.kill('SIGTERM')
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
if (clientProcess.connected || !clientProcess.killed) clientProcess.kill('SIGKILL')
|
|
252
|
+
}, 5000)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const req = http.request({
|
|
257
|
+
hostname: '127.0.0.1',
|
|
258
|
+
port: 3000,
|
|
259
|
+
path: '/api/internal/shutdown',
|
|
260
|
+
method: 'POST',
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
req.on('error', () => {
|
|
264
|
+
console.log(`${colors.system}[System]${colors.reset} Server unreachable, forcing exit.`)
|
|
265
|
+
if (isWin && serverProcess)
|
|
266
|
+
spawn('taskkill', ['/pid', serverProcess.pid, '/f', '/t'], { shell: true })
|
|
267
|
+
else if (serverProcess) {
|
|
268
|
+
serverProcess.kill('SIGTERM')
|
|
269
|
+
setTimeout(() => {
|
|
270
|
+
if (serverProcess.connected || !serverProcess.killed) serverProcess.kill('SIGKILL')
|
|
271
|
+
process.exit(0)
|
|
272
|
+
}, 5000)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
process.exit(0)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
req.end()
|
|
279
|
+
|
|
280
|
+
setTimeout(() => {
|
|
281
|
+
console.log(`${colors.system}[System]${colors.reset} Force exiting after timeout.`)
|
|
282
|
+
if (isWin && serverProcess)
|
|
283
|
+
spawn('taskkill', ['/pid', serverProcess.pid, '/f', '/t'], { shell: true })
|
|
284
|
+
else if (serverProcess) {
|
|
285
|
+
serverProcess.kill('SIGTERM')
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
if (serverProcess.connected || !serverProcess.killed) serverProcess.kill('SIGKILL')
|
|
288
|
+
process.exit(1)
|
|
289
|
+
}, 5000)
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
process.exit(1)
|
|
293
|
+
}, 15000)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
process.on('SIGINT', () => {
|
|
297
|
+
shutdown()
|
|
298
|
+
})
|
|
299
|
+
;(async () => {
|
|
300
|
+
await checkForUpdates()
|
|
301
|
+
main()
|
|
302
|
+
})()
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ani-web",
|
|
3
|
+
"version": "1.5.8",
|
|
4
|
+
"description": "A web application for watching anime.",
|
|
5
|
+
"main": "orchestrator.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ani-web": "orchestrator.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22.5.0"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"setup": "npm install --prefix client && npm install --prefix server",
|
|
14
|
+
"build": "npm run build --prefix client && npm run build --prefix server",
|
|
15
|
+
"start": "node orchestrator.js prod",
|
|
16
|
+
"dev": "node orchestrator.js dev",
|
|
17
|
+
"client": "npm start --prefix client",
|
|
18
|
+
"server": "npm start --prefix server",
|
|
19
|
+
"preview": "node orchestrator.js prod",
|
|
20
|
+
"lighthouse": "npm run lighthouse --prefix client",
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
+
"lint": "npm run lint --prefix client && npm run lint --prefix server",
|
|
23
|
+
"format": "prettier --write .",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"update-version": "node update-version.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"orchestrator.js",
|
|
29
|
+
"client/dist",
|
|
30
|
+
"server/dist",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"README.md",
|
|
33
|
+
"package.json"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"anime",
|
|
37
|
+
"react",
|
|
38
|
+
"express",
|
|
39
|
+
"typescript",
|
|
40
|
+
"streaming",
|
|
41
|
+
"cli"
|
|
42
|
+
],
|
|
43
|
+
"author": "",
|
|
44
|
+
"license": "ISC",
|
|
45
|
+
"type": "commonjs",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"axios": "^1.15.1",
|
|
48
|
+
"axios-retry": "^4.5.0",
|
|
49
|
+
"cheerio": "^1.2.0",
|
|
50
|
+
"chokidar": "^5.0.0",
|
|
51
|
+
"compression": "^1.8.1",
|
|
52
|
+
"cors": "^2.8.5",
|
|
53
|
+
"crypto-js": "^4.2.0",
|
|
54
|
+
"dotenv": "^17.4.2",
|
|
55
|
+
"express": "^5.1.0",
|
|
56
|
+
"multer": "^2.0.2",
|
|
57
|
+
"node-cache": "^5.1.2",
|
|
58
|
+
"pino": "^10.3.1",
|
|
59
|
+
"pino-pretty": "^13.1.1",
|
|
60
|
+
"xml2js": "^0.6.2"
|
|
61
|
+
},
|
|
62
|
+
"overrides": {
|
|
63
|
+
"encoding-sniffer": "^1.0.2"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"eslint": "^10.2.1",
|
|
67
|
+
"prettier": "^3.8.3"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CONFIG = exports.SERVER_ROOT = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
11
|
+
exports.SERVER_ROOT = path_1.default.resolve(__dirname, '..');
|
|
12
|
+
const PACKAGE_ROOT = path_1.default.resolve(exports.SERVER_ROOT, '..');
|
|
13
|
+
function resolveDataRoot() {
|
|
14
|
+
if (process.platform === 'win32' && process.env.APPDATA) {
|
|
15
|
+
return path_1.default.join(process.env.APPDATA, 'ani-web');
|
|
16
|
+
}
|
|
17
|
+
if (process.platform === 'darwin') {
|
|
18
|
+
return path_1.default.join(os_1.default.homedir(), 'Library', 'Application Support', 'ani-web');
|
|
19
|
+
}
|
|
20
|
+
if (process.env.XDG_DATA_HOME) {
|
|
21
|
+
return path_1.default.join(process.env.XDG_DATA_HOME, 'ani-web');
|
|
22
|
+
}
|
|
23
|
+
return path_1.default.join(os_1.default.homedir(), '.local', 'share', 'ani-web');
|
|
24
|
+
}
|
|
25
|
+
function moveFileIfNeeded(sourcePath, destinationPath) {
|
|
26
|
+
if (!fs_1.default.existsSync(sourcePath) || fs_1.default.existsSync(destinationPath)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
fs_1.default.renameSync(sourcePath, destinationPath);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
fs_1.default.copyFileSync(sourcePath, destinationPath);
|
|
34
|
+
fs_1.default.unlinkSync(sourcePath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function migrateLegacyData(packageServerRoot, dataRoot) {
|
|
38
|
+
const legacyFiles = [
|
|
39
|
+
'.env',
|
|
40
|
+
'google_tokens.json',
|
|
41
|
+
'sync_manifest.json',
|
|
42
|
+
'sync_manifest.dev.json',
|
|
43
|
+
'anime.db',
|
|
44
|
+
'anime.db-shm',
|
|
45
|
+
'anime.db-wal',
|
|
46
|
+
'anime.dev.db',
|
|
47
|
+
'anime.dev.db-shm',
|
|
48
|
+
'anime.dev.db-wal',
|
|
49
|
+
];
|
|
50
|
+
fs_1.default.mkdirSync(dataRoot, { recursive: true });
|
|
51
|
+
for (const filename of legacyFiles) {
|
|
52
|
+
moveFileIfNeeded(path_1.default.join(packageServerRoot, filename), path_1.default.join(dataRoot, filename));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const DATA_ROOT = resolveDataRoot();
|
|
56
|
+
const ENV_PATH = path_1.default.join(DATA_ROOT, '.env');
|
|
57
|
+
migrateLegacyData(exports.SERVER_ROOT, DATA_ROOT);
|
|
58
|
+
dotenv_1.default.config({ path: ENV_PATH });
|
|
59
|
+
const IS_DEV = process.argv.includes('--dev');
|
|
60
|
+
const PORT = 3000;
|
|
61
|
+
const GOOGLE_REDIRECT_URI = IS_DEV
|
|
62
|
+
? 'http://localhost:5173/api/auth/google/callback'
|
|
63
|
+
: `http://localhost:${PORT}/api/auth/google/callback`;
|
|
64
|
+
exports.CONFIG = {
|
|
65
|
+
ROOT: DATA_ROOT,
|
|
66
|
+
SERVER_ROOT: exports.SERVER_ROOT,
|
|
67
|
+
PACKAGE_ROOT,
|
|
68
|
+
ENV_PATH,
|
|
69
|
+
TOKEN_PATH: path_1.default.join(DATA_ROOT, 'google_tokens.json'),
|
|
70
|
+
LOCAL_MANIFEST_PATH: path_1.default.join(DATA_ROOT, IS_DEV ? 'sync_manifest.dev.json' : 'sync_manifest.json'),
|
|
71
|
+
DB_NAME_PROD: 'anime.db',
|
|
72
|
+
DB_NAME_DEV: 'anime.dev.db',
|
|
73
|
+
REMOTE_FOLDER_PROD: 'aniweb_db',
|
|
74
|
+
REMOTE_FOLDER_DEV: 'aniweb_dev_db',
|
|
75
|
+
MANIFEST_FILENAME: IS_DEV ? 'sync_manifest.dev.json' : 'sync_manifest.json',
|
|
76
|
+
GOOGLE_SCOPES: [
|
|
77
|
+
'https://www.googleapis.com/auth/drive',
|
|
78
|
+
'https://www.googleapis.com/auth/userinfo.profile',
|
|
79
|
+
],
|
|
80
|
+
IS_DEV,
|
|
81
|
+
PORT,
|
|
82
|
+
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
|
83
|
+
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
|
84
|
+
GOOGLE_REDIRECT_URI: GOOGLE_REDIRECT_URI,
|
|
85
|
+
RCLONE_REMOTE: process.env.RCLONE_REMOTE,
|
|
86
|
+
};
|