cross-seed 6.13.6 → 7.0.0-10
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/dist/Result.d.ts +27 -0
- package/dist/action.d.ts +34 -0
- package/dist/action.js +2 -1
- package/dist/action.js.map +1 -1
- package/dist/arr.d.ts +31 -0
- package/dist/arr.js +107 -39
- package/dist/arr.js.map +1 -1
- package/dist/auth.d.ts +3 -0
- package/dist/auth.js +9 -6
- package/dist/auth.js.map +1 -1
- package/dist/clients/Deluge.d.ts +153 -0
- package/dist/clients/Deluge.js +8 -7
- package/dist/clients/Deluge.js.map +1 -1
- package/dist/clients/QBittorrent.d.ts +218 -0
- package/dist/clients/QBittorrent.js +3 -2
- package/dist/clients/QBittorrent.js.map +1 -1
- package/dist/clients/RTorrent.d.ts +43 -0
- package/dist/clients/RTorrent.js +8 -4
- package/dist/clients/RTorrent.js.map +1 -1
- package/dist/clients/TorrentClient.d.ts +108 -0
- package/dist/clients/TorrentClient.js +137 -67
- package/dist/clients/TorrentClient.js.map +1 -1
- package/dist/clients/Transmission.d.ts +43 -0
- package/dist/clients/Transmission.js +4 -3
- package/dist/clients/Transmission.js.map +1 -1
- package/dist/cmd.d.ts +2 -0
- package/dist/cmd.js +42 -110
- package/dist/cmd.js.map +1 -1
- package/dist/configSchema.d.ts +1 -0
- package/dist/configSchema.js +1 -666
- package/dist/configSchema.js.map +1 -1
- package/dist/configuration.d.ts +63 -0
- package/dist/configuration.js +263 -24
- package/dist/configuration.js.map +1 -1
- package/dist/constants.d.ts +108 -0
- package/dist/constants.js +2 -32
- package/dist/constants.js.map +1 -1
- package/dist/dataFiles.d.ts +8 -0
- package/dist/dataFiles.js +21 -6
- package/dist/dataFiles.js.map +1 -1
- package/dist/db.d.ts +14 -0
- package/dist/db.js +76 -5
- package/dist/db.js.map +1 -1
- package/dist/dbConfig.d.ts +4 -0
- package/dist/dbConfig.js +67 -0
- package/dist/dbConfig.js.map +1 -0
- package/dist/decide.d.ts +25 -0
- package/dist/decide.js +4 -4
- package/dist/decide.js.map +1 -1
- package/dist/diagnostics/db.d.ts +21 -0
- package/dist/diagnostics/db.js +107 -0
- package/dist/diagnostics/db.js.map +1 -0
- package/dist/diff.d.ts +1 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +0 -9
- package/dist/errors.js.map +1 -1
- package/dist/indexers.d.ts +105 -0
- package/dist/indexers.js +82 -14
- package/dist/indexers.js.map +1 -1
- package/dist/inject.d.ts +2 -0
- package/dist/jobs.d.ts +29 -0
- package/dist/jobs.js +14 -9
- package/dist/jobs.js.map +1 -1
- package/dist/logger.d.ts +29 -0
- package/dist/logger.js +18 -4
- package/dist/logger.js.map +1 -1
- package/dist/migrations/00-initialSchema.d.ts +9 -0
- package/dist/migrations/01-jobs.d.ts +9 -0
- package/dist/migrations/02-timestamps.d.ts +9 -0
- package/dist/migrations/03-rateLimits.d.ts +9 -0
- package/dist/migrations/04-auth.d.ts +9 -0
- package/dist/migrations/04-auth.js +1 -1
- package/dist/migrations/04-auth.js.map +1 -1
- package/dist/migrations/05-caps.d.ts +9 -0
- package/dist/migrations/06-uniqueDecisions.d.ts +9 -0
- package/dist/migrations/07-limits.d.ts +9 -0
- package/dist/migrations/08-rss.d.ts +9 -0
- package/dist/migrations/09-clientAndDataSearchees.d.ts +9 -0
- package/dist/migrations/10-indexerNameAudioBookCaps.d.ts +9 -0
- package/dist/migrations/11-trackers.d.ts +9 -0
- package/dist/migrations/12-user-auth.d.ts +9 -0
- package/dist/migrations/12-user-auth.js +22 -0
- package/dist/migrations/12-user-auth.js.map +1 -0
- package/dist/migrations/13-settings.d.ts +9 -0
- package/dist/migrations/13-settings.js +23 -0
- package/dist/migrations/13-settings.js.map +1 -0
- package/dist/migrations/14-indexer-enabled-flag.d.ts +9 -0
- package/dist/migrations/14-indexer-enabled-flag.js +12 -0
- package/dist/migrations/14-indexer-enabled-flag.js.map +1 -0
- package/dist/migrations/15-remove-url-unique-constraint.d.ts +9 -0
- package/dist/migrations/15-remove-url-unique-constraint.js +14 -0
- package/dist/migrations/15-remove-url-unique-constraint.js.map +1 -0
- package/dist/migrations/16-prune-inactive-indexers.d.ts +9 -0
- package/dist/migrations/16-prune-inactive-indexers.js +17 -0
- package/dist/migrations/16-prune-inactive-indexers.js.map +1 -0
- package/dist/migrations/migrations.d.ts +13 -0
- package/dist/migrations/migrations.js +10 -0
- package/dist/migrations/migrations.js.map +1 -1
- package/dist/parseTorrent.d.ts +53 -0
- package/dist/pipeline.d.ts +41 -0
- package/dist/pipeline.js +64 -13
- package/dist/pipeline.js.map +1 -1
- package/dist/preFilter.d.ts +25 -0
- package/dist/preFilter.js +15 -15
- package/dist/preFilter.js.map +1 -1
- package/dist/problems/linking.d.ts +2 -0
- package/dist/problems/linking.js +80 -0
- package/dist/problems/linking.js.map +1 -0
- package/dist/problems/path.d.ts +22 -0
- package/dist/problems/path.js +96 -0
- package/dist/problems/path.js.map +1 -0
- package/dist/problems.d.ts +13 -0
- package/dist/problems.js +48 -0
- package/dist/problems.js.map +1 -0
- package/dist/pushNotifier.d.ts +19 -0
- package/dist/routes/baseApi.d.ts +2 -0
- package/dist/routes/baseApi.js +354 -0
- package/dist/routes/baseApi.js.map +1 -0
- package/dist/routes/indexerApi.d.ts +6 -0
- package/dist/routes/indexerApi.js +165 -0
- package/dist/routes/indexerApi.js.map +1 -0
- package/dist/routes/staticFrontendPlugin.d.ts +4 -0
- package/dist/routes/staticFrontendPlugin.js +61 -0
- package/dist/routes/staticFrontendPlugin.js.map +1 -0
- package/dist/runtimeConfig.d.ts +6 -0
- package/dist/runtimeConfig.js +17 -1
- package/dist/runtimeConfig.js.map +1 -1
- package/dist/searchee.d.ts +108 -0
- package/dist/searchee.js +38 -6
- package/dist/searchee.js.map +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.js +38 -429
- package/dist/server.js.map +1 -1
- package/dist/services/indexerService.d.ts +96 -0
- package/dist/services/indexerService.js +287 -0
- package/dist/services/indexerService.js.map +1 -0
- package/dist/sessionCookies.d.ts +5 -0
- package/dist/sessionCookies.js +27 -0
- package/dist/sessionCookies.js.map +1 -0
- package/dist/startup.d.ts +25 -0
- package/dist/startup.js +105 -151
- package/dist/startup.js.map +1 -1
- package/dist/torrent.d.ts +69 -0
- package/dist/torrent.js +17 -13
- package/dist/torrent.js.map +1 -1
- package/dist/torznab.d.ts +60 -0
- package/dist/torznab.js +14 -89
- package/dist/torznab.js.map +1 -1
- package/dist/trpc/fastifyAdapter.d.ts +2 -0
- package/dist/trpc/fastifyAdapter.js +9 -0
- package/dist/trpc/fastifyAdapter.js.map +1 -0
- package/dist/trpc/index.d.ts +49 -0
- package/dist/trpc/index.js +53 -0
- package/dist/trpc/index.js.map +1 -0
- package/dist/trpc/routers/auth.d.ts +43 -0
- package/dist/trpc/routers/auth.js +116 -0
- package/dist/trpc/routers/auth.js.map +1 -0
- package/dist/trpc/routers/clients.d.ts +21 -0
- package/dist/trpc/routers/clients.js +65 -0
- package/dist/trpc/routers/clients.js.map +1 -0
- package/dist/trpc/routers/health.d.ts +17 -0
- package/dist/trpc/routers/health.js +24 -0
- package/dist/trpc/routers/health.js.map +1 -0
- package/dist/trpc/routers/index.d.ts +394 -0
- package/dist/trpc/routers/index.js +23 -0
- package/dist/trpc/routers/index.js.map +1 -0
- package/dist/trpc/routers/indexers.d.ts +75 -0
- package/dist/trpc/routers/indexers.js +79 -0
- package/dist/trpc/routers/indexers.js.map +1 -0
- package/dist/trpc/routers/jobs.d.ts +33 -0
- package/dist/trpc/routers/jobs.js +84 -0
- package/dist/trpc/routers/jobs.js.map +1 -0
- package/dist/trpc/routers/logs.d.ts +27 -0
- package/dist/trpc/routers/logs.js +91 -0
- package/dist/trpc/routers/logs.js.map +1 -0
- package/dist/trpc/routers/searchees.d.ts +51 -0
- package/dist/trpc/routers/searchees.js +156 -0
- package/dist/trpc/routers/searchees.js.map +1 -0
- package/dist/trpc/routers/settings.d.ts +83 -0
- package/dist/trpc/routers/settings.js +92 -0
- package/dist/trpc/routers/settings.js.map +1 -0
- package/dist/trpc/routers/stats.d.ts +42 -0
- package/dist/trpc/routers/stats.js +102 -0
- package/dist/trpc/routers/stats.js.map +1 -0
- package/dist/userAuth.d.ts +21 -0
- package/dist/userAuth.js +86 -0
- package/dist/userAuth.js.map +1 -0
- package/dist/utils/authUtils.d.ts +10 -0
- package/dist/utils/authUtils.js +24 -0
- package/dist/utils/authUtils.js.map +1 -0
- package/dist/utils/logWatcher.d.ts +28 -0
- package/dist/utils/logWatcher.js +229 -0
- package/dist/utils/logWatcher.js.map +1 -0
- package/dist/utils/object.d.ts +1 -0
- package/dist/utils/object.js +4 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils.d.ts +172 -0
- package/dist/utils.js +61 -50
- package/dist/utils.js.map +1 -1
- package/dist/webui/assets/FieldInfo-sRlPRNSK.js +1 -0
- package/dist/webui/assets/Page-B68mlTwU.js +1 -0
- package/dist/webui/assets/array-field-BCFMrvoU.js +1 -0
- package/dist/webui/assets/badge-C5YCxEzP.js +1 -0
- package/dist/webui/assets/check-NQsw6yBl.js +1 -0
- package/dist/webui/assets/chevron-down-8PGvFYxV.js +1 -0
- package/dist/webui/assets/clients-DnVpwApe.js +1 -0
- package/dist/webui/assets/connect-wMg2zyz6.js +1 -0
- package/dist/webui/assets/debug-BrjwiEi2.js +1 -0
- package/dist/webui/assets/directories-CHpJCWNR.js +1 -0
- package/dist/webui/assets/duration-field-DIkKt3iw.js +1 -0
- package/dist/webui/assets/general-uZrUIxbI.js +1 -0
- package/dist/webui/assets/health-_MuvAyjo.js +1 -0
- package/dist/webui/assets/index-B41DM2T5.css +1 -0
- package/dist/webui/assets/index-BBzHsn7u.js +1 -0
- package/dist/webui/assets/index-Ncy0-Qo7.js +54 -0
- package/dist/webui/assets/index-pKWy6v1P.js +1 -0
- package/dist/webui/assets/jobs-B8eat0YU.js +1 -0
- package/dist/webui/assets/library-BB0jQ8zn.js +1 -0
- package/dist/webui/assets/loader-circle-Bz67bJa3.js +1 -0
- package/dist/webui/assets/logs-CeP28848.js +1 -0
- package/dist/webui/assets/search-BRBIrqaX.js +1 -0
- package/dist/webui/assets/select-GZr6C6eZ.js +1 -0
- package/dist/webui/assets/select-field-CvT0SYk8.js +1 -0
- package/dist/webui/assets/settings-0ZdYY8g_.js +1 -0
- package/dist/webui/assets/submit-button-D7DKHqAq.js +1 -0
- package/dist/webui/assets/switch-BeMrf8sh.js +1 -0
- package/dist/webui/assets/switch-field-qMXHRKhx.js +1 -0
- package/dist/webui/assets/table-qEFWauuw.js +1 -0
- package/dist/webui/assets/test-tube-DhD6uWdp.js +1 -0
- package/dist/webui/assets/text-field-ZnKHDUks.js +1 -0
- package/dist/webui/assets/time-BM9K_Fbp.js +1 -0
- package/dist/webui/assets/trackers-BjJuAdX3.js +7 -0
- package/dist/webui/assets/use-form-validation-context-D2oA54L_.js +1 -0
- package/dist/webui/assets/use-settings-form-submit-CXwtE1sI.js +2 -0
- package/dist/webui/assets/useQuery-DD10sbzn.js +1 -0
- package/dist/webui/index.html +13 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.d.ts +261 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.js +53 -0
- package/node_modules/@cross-seed/shared/dist/configSchema.js.map +1 -0
- package/node_modules/@cross-seed/shared/dist/constants.d.ts +122 -0
- package/node_modules/@cross-seed/shared/dist/constants.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/constants.js +127 -0
- package/node_modules/@cross-seed/shared/dist/constants.js.map +1 -0
- package/node_modules/@cross-seed/shared/dist/tsconfig.tsbuildinfo +1 -0
- package/node_modules/@cross-seed/shared/dist/utils.d.ts +9 -0
- package/node_modules/@cross-seed/shared/dist/utils.d.ts.map +1 -0
- package/node_modules/@cross-seed/shared/dist/utils.js +20 -0
- package/node_modules/@cross-seed/shared/dist/utils.js.map +1 -0
- package/node_modules/@cross-seed/shared/package.json +24 -0
- package/package.json +25 -42
- package/LICENSE +0 -201
- package/README.md +0 -34
- package/dist/config.template.cjs +0 -353
- package/dist/config.template.cjs.map +0 -1
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { db } from "../db.js";
|
|
3
|
+
import { Label, logger } from "../logger.js";
|
|
4
|
+
import { assembleUrl } from "../torznab.js";
|
|
5
|
+
import { USER_AGENT } from "../constants.js";
|
|
6
|
+
import { getAllIndexers } from "../indexers.js";
|
|
7
|
+
import { resultOf, resultOfErr } from "../Result.js";
|
|
8
|
+
import ms from "ms";
|
|
9
|
+
// Helper function to deserialize raw database row (snake_case columns) to Indexer
|
|
10
|
+
function deserializeRawRow(rawRow) {
|
|
11
|
+
const row = rawRow;
|
|
12
|
+
return {
|
|
13
|
+
id: row.id,
|
|
14
|
+
name: row.name,
|
|
15
|
+
url: row.url,
|
|
16
|
+
apikey: row.apikey,
|
|
17
|
+
enabled: Boolean(row.enabled),
|
|
18
|
+
status: row.status,
|
|
19
|
+
retryAfter: row.retry_after,
|
|
20
|
+
searchCap: Boolean(row.search_cap),
|
|
21
|
+
tvSearchCap: Boolean(row.tv_search_cap),
|
|
22
|
+
movieSearchCap: Boolean(row.movie_search_cap),
|
|
23
|
+
musicSearchCap: Boolean(row.music_search_cap),
|
|
24
|
+
audioSearchCap: Boolean(row.audio_search_cap),
|
|
25
|
+
bookSearchCap: Boolean(row.book_search_cap),
|
|
26
|
+
tvIdCaps: row.tv_id_caps ? JSON.parse(row.tv_id_caps) : null,
|
|
27
|
+
movieIdCaps: row.movie_id_caps
|
|
28
|
+
? JSON.parse(row.movie_id_caps)
|
|
29
|
+
: null,
|
|
30
|
+
categories: row.cat_caps ? JSON.parse(row.cat_caps) : null,
|
|
31
|
+
limits: row.limits_caps ? JSON.parse(row.limits_caps) : null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Validation schemas
|
|
35
|
+
export const indexerCreateSchema = z.object({
|
|
36
|
+
name: z.string().min(1).optional(),
|
|
37
|
+
url: z.string().url(),
|
|
38
|
+
apikey: z.string().min(1),
|
|
39
|
+
enabled: z.boolean().default(true),
|
|
40
|
+
});
|
|
41
|
+
export const indexerUpdateSchema = z.object({
|
|
42
|
+
id: z.number().int().positive(),
|
|
43
|
+
name: z.string().min(1).optional().nullable(),
|
|
44
|
+
url: z.string().url().optional(),
|
|
45
|
+
apikey: z.string().min(1).optional(),
|
|
46
|
+
enabled: z.boolean().optional(),
|
|
47
|
+
});
|
|
48
|
+
export const indexerTestSchema = z.object({
|
|
49
|
+
url: z.string().url(),
|
|
50
|
+
apikey: z.string().min(1),
|
|
51
|
+
});
|
|
52
|
+
export async function testIndexerConnection(url, apikey, name) {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(assembleUrl(url, apikey, { t: "caps" }), {
|
|
55
|
+
headers: { "User-Agent": USER_AGENT },
|
|
56
|
+
signal: AbortSignal.timeout(ms("5 seconds")),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
if (response.status === 401) {
|
|
60
|
+
return resultOfErr({
|
|
61
|
+
code: "AUTH_FAILED",
|
|
62
|
+
message: "Authentication failed - check API key",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else if (response.status === 429) {
|
|
66
|
+
return resultOfErr({
|
|
67
|
+
code: "RATE_LIMITED",
|
|
68
|
+
message: "Rate limited by indexer",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return resultOfErr({
|
|
73
|
+
code: "CONNECTION_FAILED",
|
|
74
|
+
message: "Connection failed",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
logger.verbose({
|
|
79
|
+
label: Label.TORZNAB,
|
|
80
|
+
message: `Test connection successful for: ${name}`,
|
|
81
|
+
});
|
|
82
|
+
return resultOf({
|
|
83
|
+
success: true,
|
|
84
|
+
message: "Connection successful",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
const message = error.message;
|
|
89
|
+
logger.warn({
|
|
90
|
+
label: Label.TORZNAB,
|
|
91
|
+
message: `Test connection failed for ${name}: ${message}`,
|
|
92
|
+
});
|
|
93
|
+
// Handle timeout specifically - AbortSignal.timeout() throws TimeoutError (DOMException)
|
|
94
|
+
if (error.name === "TimeoutError" || error.name === "AbortError") {
|
|
95
|
+
return resultOfErr({
|
|
96
|
+
code: "TIMEOUT",
|
|
97
|
+
message: "Connection timed out",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return resultOfErr({
|
|
101
|
+
code: "CONNECTION_FAILED",
|
|
102
|
+
message: "Connection failed",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export async function createIndexer(input) {
|
|
107
|
+
// Create new indexer only (no upsert)
|
|
108
|
+
const [result] = await db("indexer")
|
|
109
|
+
.insert({
|
|
110
|
+
name: input.name || null,
|
|
111
|
+
url: input.url,
|
|
112
|
+
apikey: input.apikey,
|
|
113
|
+
trackers: null,
|
|
114
|
+
enabled: input.enabled,
|
|
115
|
+
status: null,
|
|
116
|
+
retry_after: null,
|
|
117
|
+
search_cap: null,
|
|
118
|
+
tv_search_cap: null,
|
|
119
|
+
movie_search_cap: null,
|
|
120
|
+
music_search_cap: null,
|
|
121
|
+
audio_search_cap: null,
|
|
122
|
+
book_search_cap: null,
|
|
123
|
+
tv_id_caps: null,
|
|
124
|
+
movie_id_caps: null,
|
|
125
|
+
cat_caps: null,
|
|
126
|
+
limits_caps: null,
|
|
127
|
+
})
|
|
128
|
+
.returning("id");
|
|
129
|
+
// Query the created record
|
|
130
|
+
const rawRow = await db("indexer").where({ id: result.id }).first();
|
|
131
|
+
const indexer = deserializeRawRow(rawRow);
|
|
132
|
+
logger.verbose({
|
|
133
|
+
label: Label.TORZNAB,
|
|
134
|
+
message: `Created indexer: ${input.name || input.url}`,
|
|
135
|
+
});
|
|
136
|
+
return indexer;
|
|
137
|
+
}
|
|
138
|
+
export async function updateIndexer(input) {
|
|
139
|
+
const { id, ...updates } = input;
|
|
140
|
+
// Prepare update object
|
|
141
|
+
const updateData = {
|
|
142
|
+
...(updates.name !== undefined && { name: updates.name }),
|
|
143
|
+
...(updates.url !== undefined && { url: updates.url }),
|
|
144
|
+
...(updates.apikey !== undefined && { apikey: updates.apikey }),
|
|
145
|
+
...(updates.enabled !== undefined && { enabled: updates.enabled }),
|
|
146
|
+
};
|
|
147
|
+
// Atomic update with existence check
|
|
148
|
+
const updateCount = await db("indexer").where({ id }).update(updateData);
|
|
149
|
+
if (updateCount === 0) {
|
|
150
|
+
return resultOfErr({
|
|
151
|
+
code: "INDEXER_NOT_FOUND",
|
|
152
|
+
message: `Indexer with ID ${id} not found`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Query the updated record
|
|
156
|
+
const updatedRawRow = await db("indexer").where({ id }).first();
|
|
157
|
+
const updatedIndexer = deserializeRawRow(updatedRawRow);
|
|
158
|
+
logger.verbose({
|
|
159
|
+
label: Label.TORZNAB,
|
|
160
|
+
message: `Updated indexer: ${updatedIndexer.name || updatedIndexer.url}`,
|
|
161
|
+
});
|
|
162
|
+
return resultOf(updatedIndexer);
|
|
163
|
+
}
|
|
164
|
+
export async function deleteIndexer(id) {
|
|
165
|
+
return db.transaction(async (trx) => {
|
|
166
|
+
const rawRow = await trx("indexer").where({ id }).first();
|
|
167
|
+
if (!rawRow) {
|
|
168
|
+
return resultOfErr({
|
|
169
|
+
code: "INDEXER_NOT_FOUND",
|
|
170
|
+
message: `Indexer with ID ${id} not found`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const indexer = deserializeRawRow(rawRow);
|
|
174
|
+
await trx("timestamp").where({ indexer_id: id }).del();
|
|
175
|
+
await trx("rss").where({ indexer_id: id }).del();
|
|
176
|
+
await trx("indexer").where({ id }).del();
|
|
177
|
+
logger.verbose({
|
|
178
|
+
label: Label.TORZNAB,
|
|
179
|
+
message: `Deleted indexer: ${indexer.name || indexer.url}`,
|
|
180
|
+
});
|
|
181
|
+
return resultOf({ success: true, indexer });
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
export async function getIndexerById(id) {
|
|
185
|
+
const rawRow = await db("indexer").where({ id }).first();
|
|
186
|
+
if (!rawRow) {
|
|
187
|
+
return resultOfErr({
|
|
188
|
+
code: "INDEXER_NOT_FOUND",
|
|
189
|
+
message: `Indexer with ID ${id} not found`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return resultOf(deserializeRawRow(rawRow));
|
|
193
|
+
}
|
|
194
|
+
// TODO: This is a thin wrapper around getAllIndexers from ../indexers.js
|
|
195
|
+
// Consider consolidating duplicate functionality between indexerService and indexers modules
|
|
196
|
+
export async function listAllIndexers() {
|
|
197
|
+
return getAllIndexers();
|
|
198
|
+
}
|
|
199
|
+
export async function mergeDisabledIndexer(sourceId, targetId) {
|
|
200
|
+
if (sourceId === targetId) {
|
|
201
|
+
return resultOfErr({
|
|
202
|
+
code: "SAME_INDEXER",
|
|
203
|
+
message: "Source and target indexers must be different",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return db.transaction(async (trx) => {
|
|
207
|
+
const sourceIndexer = await trx("indexer")
|
|
208
|
+
.select("id", "enabled")
|
|
209
|
+
.where({ id: sourceId })
|
|
210
|
+
.first();
|
|
211
|
+
if (!sourceIndexer) {
|
|
212
|
+
return resultOfErr({
|
|
213
|
+
code: "SOURCE_NOT_FOUND",
|
|
214
|
+
message: `Disabled indexer with ID ${sourceId} not found`,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (sourceIndexer.enabled) {
|
|
218
|
+
return resultOfErr({
|
|
219
|
+
code: "INVALID_SOURCE_STATE",
|
|
220
|
+
message: "Source indexer must be disabled before merging",
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const targetIndexer = await trx("indexer")
|
|
224
|
+
.select("id", "enabled")
|
|
225
|
+
.where({ id: targetId })
|
|
226
|
+
.first();
|
|
227
|
+
if (!targetIndexer) {
|
|
228
|
+
return resultOfErr({
|
|
229
|
+
code: "TARGET_NOT_FOUND",
|
|
230
|
+
message: `Target indexer with ID ${targetId} not found`,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (!targetIndexer.enabled) {
|
|
234
|
+
return resultOfErr({
|
|
235
|
+
code: "INVALID_TARGET_STATE",
|
|
236
|
+
message: "Target indexer must be enabled",
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const sourceTimestamps = await trx("timestamp")
|
|
240
|
+
.where({ indexer_id: sourceId })
|
|
241
|
+
.select("searchee_id as searcheeId", "first_searched as firstSearched", "last_searched as lastSearched");
|
|
242
|
+
if (sourceTimestamps.length > 0) {
|
|
243
|
+
const rows = sourceTimestamps.map((row) => ({
|
|
244
|
+
indexer_id: targetId,
|
|
245
|
+
searchee_id: row.searcheeId,
|
|
246
|
+
first_searched: row.firstSearched,
|
|
247
|
+
last_searched: row.lastSearched,
|
|
248
|
+
}));
|
|
249
|
+
await trx("timestamp")
|
|
250
|
+
.insert(rows)
|
|
251
|
+
.onConflict(["searchee_id", "indexer_id"])
|
|
252
|
+
.merge({
|
|
253
|
+
first_searched: trx.raw("MIN(timestamp.first_searched, excluded.first_searched)"),
|
|
254
|
+
last_searched: trx.raw("MAX(timestamp.last_searched, excluded.last_searched)"),
|
|
255
|
+
});
|
|
256
|
+
await trx("timestamp").where({ indexer_id: sourceId }).del();
|
|
257
|
+
logger.verbose({
|
|
258
|
+
label: Label.TORZNAB,
|
|
259
|
+
message: `Merged ${sourceTimestamps.length} timestamp rows from indexer ${sourceId} into ${targetId}`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
await trx("rss").where({ indexer_id: sourceId }).del();
|
|
263
|
+
const deleted = await trx("indexer").where({ id: sourceId }).del();
|
|
264
|
+
if (deleted) {
|
|
265
|
+
logger.verbose({
|
|
266
|
+
label: Label.TORZNAB,
|
|
267
|
+
message: `Deleted disabled indexer ${sourceId} after merge into ${targetId}`,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return resultOf({
|
|
271
|
+
mergedCount: sourceTimestamps.length,
|
|
272
|
+
deleted: Boolean(deleted),
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
export async function testExistingIndexer(id) {
|
|
277
|
+
const indexerResult = await getIndexerById(id);
|
|
278
|
+
if (indexerResult.isErr()) {
|
|
279
|
+
return indexerResult;
|
|
280
|
+
}
|
|
281
|
+
const indexer = indexerResult.unwrap();
|
|
282
|
+
return testIndexerConnection(indexer.url, indexer.apikey, indexer.name || indexer.url);
|
|
283
|
+
}
|
|
284
|
+
export async function testNewIndexer(input) {
|
|
285
|
+
return testIndexerConnection(input.url, input.apikey, input.url);
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=indexerService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexerService.js","sourceRoot":"","sources":["../../src/services/indexerService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
export declare function setSessionCookie(reply: FastifyReply, sessionId: string): void;
|
|
3
|
+
export declare function clearSessionCookie(reply: FastifyReply): void;
|
|
4
|
+
export declare function getSessionCookie(request: FastifyRequest): string | undefined;
|
|
5
|
+
export declare function validateAuth(request: FastifyRequest): Promise<boolean>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { validateSession } from "./userAuth.js";
|
|
2
|
+
const COOKIE_NAME = "cross-seed-session";
|
|
3
|
+
const COOKIE_OPTIONS = {
|
|
4
|
+
httpOnly: true,
|
|
5
|
+
secure: process.env.NODE_ENV === "production", // Only set secure in production
|
|
6
|
+
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days in milliseconds (match session expiry)
|
|
7
|
+
path: "/",
|
|
8
|
+
sameSite: "lax",
|
|
9
|
+
};
|
|
10
|
+
export function setSessionCookie(reply, sessionId) {
|
|
11
|
+
void reply.setCookie(COOKIE_NAME, sessionId, COOKIE_OPTIONS);
|
|
12
|
+
}
|
|
13
|
+
export function clearSessionCookie(reply) {
|
|
14
|
+
void reply.clearCookie(COOKIE_NAME, { path: "/" });
|
|
15
|
+
}
|
|
16
|
+
export function getSessionCookie(request) {
|
|
17
|
+
return request.cookies[COOKIE_NAME];
|
|
18
|
+
}
|
|
19
|
+
export async function validateAuth(request) {
|
|
20
|
+
const sessionId = getSessionCookie(request);
|
|
21
|
+
if (!sessionId) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const user = await validateSession(sessionId);
|
|
25
|
+
return !!user;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sessionCookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionCookies.js","sourceRoot":"","sources":["../src/sessionCookies.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,MAAM,cAAc,GAAG;IACtB,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,gCAAgC;IAC/E,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,iDAAiD;IACnF,IAAI,EAAE,GAAG;IACT,QAAQ,EAAE,KAAc;CACxB,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,KAAmB,EAAE,SAAiB;IACtE,KAAK,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACrD,KAAK,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAuB;IACvD,OAAO,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAuB;IACzD,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE5C,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAC9C,OAAO,CAAC,CAAC,IAAI,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { RuntimeConfig } from "./runtimeConfig.js";
|
|
2
|
+
import { Awaitable } from "./utils.js";
|
|
3
|
+
export declare function exitGracefully(): Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* validates and sets RuntimeConfig
|
|
6
|
+
* @return (the number of errors Zod encountered in the configuration)
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* starts singletons, then runs the callback, then cleans up
|
|
10
|
+
* @param entrypoint
|
|
11
|
+
*/
|
|
12
|
+
type CommanderActionCb = (options: Record<string, unknown>) => void | Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Initializes only the database, runs the callback, then cleans up
|
|
15
|
+
*/
|
|
16
|
+
export declare function withMinimalRuntime<T extends (...args: unknown[]) => Awaitable<string | void>>(entrypoint: T, { migrate }?: {
|
|
17
|
+
migrate?: boolean | undefined;
|
|
18
|
+
}): (...args: Parameters<T>) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Initializes the full runtime, runs the callback, then cleans up
|
|
21
|
+
* @param entrypoint
|
|
22
|
+
*/
|
|
23
|
+
export declare function withFullRuntime(entrypoint: (runtimeConfig: RuntimeConfig) => Promise<void>): CommanderActionCb;
|
|
24
|
+
export declare function restartCrossSeed(): Promise<void>;
|
|
25
|
+
export {};
|
package/dist/startup.js
CHANGED
|
@@ -1,170 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { getClients, instantiateDownloadClients, } from "./clients/TorrentClient.js";
|
|
7
|
-
import { customizeErrorMessage, VALIDATION_SCHEMA } from "./configSchema.js";
|
|
8
|
-
import { NEWLINE_INDENT, PROGRAM_NAME, PROGRAM_VERSION } from "./constants.js";
|
|
1
|
+
import { mkdir } from "fs/promises";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { resetApiKey } from "./auth.js";
|
|
4
|
+
import { instantiateDownloadClients } from "./clients/TorrentClient.js";
|
|
5
|
+
import { createAppDirHierarchy, getDefaultRuntimeConfig, getFileConfig, stripDefaults, transformFileConfig, } from "./configuration.js";
|
|
9
6
|
import { db } from "./db.js";
|
|
10
|
-
import {
|
|
11
|
-
import { initializeLogger, Label, logger } from "./logger.js";
|
|
7
|
+
import { getDbConfig, setDbConfig } from "./dbConfig.js";
|
|
8
|
+
import { exitOnCrossSeedErrors, initializeLogger, Label, logger, } from "./logger.js";
|
|
12
9
|
import { initializePushNotifier } from "./pushNotifier.js";
|
|
13
10
|
import { getRuntimeConfig, setRuntimeConfig, } from "./runtimeConfig.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import { notExists } from "./utils.js";
|
|
12
|
+
import { getLogWatcher } from "./utils/logWatcher.js";
|
|
13
|
+
import { omitUndefined } from "./utils/object.js";
|
|
16
14
|
export async function exitGracefully() {
|
|
17
15
|
await db.destroy();
|
|
18
16
|
process.exit();
|
|
19
17
|
}
|
|
20
18
|
process.on("SIGINT", exitGracefully);
|
|
21
19
|
process.on("SIGTERM", exitGracefully);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const { dataDirs, injectDir, linkDirs, outputDir, torrentDir } = getRuntimeConfig();
|
|
28
|
-
const READ_ONLY = constants.R_OK;
|
|
29
|
-
const READ_AND_WRITE = constants.R_OK | constants.W_OK;
|
|
30
|
-
let pathFailure = 0;
|
|
31
|
-
const linkDev = [];
|
|
32
|
-
const dataDev = [];
|
|
33
|
-
if (torrentDir && !(await verifyDir(torrentDir, "torrentDir", READ_ONLY))) {
|
|
34
|
-
pathFailure++;
|
|
35
|
-
}
|
|
36
|
-
if (await notExists(outputDir)) {
|
|
37
|
-
logger.info(`Creating outputDir: ${outputDir}`);
|
|
38
|
-
await mkdir(outputDir, { recursive: true });
|
|
39
|
-
}
|
|
40
|
-
if (!(await verifyDir(outputDir, "outputDir", READ_AND_WRITE))) {
|
|
41
|
-
pathFailure++;
|
|
20
|
+
async function ensureConfiguredDirectories() {
|
|
21
|
+
const { outputDir, linkDirs = [] } = getRuntimeConfig();
|
|
22
|
+
const directories = [];
|
|
23
|
+
if (outputDir) {
|
|
24
|
+
directories.push({ path: outputDir, label: "outputDir" });
|
|
42
25
|
}
|
|
43
26
|
for (const [index, linkDir] of linkDirs.entries()) {
|
|
44
|
-
|
|
45
|
-
if (await notExists(linkDir)) {
|
|
46
|
-
logger.info(`Creating ${linkDirName}: ${linkDir}`);
|
|
47
|
-
await mkdir(linkDir, { recursive: true });
|
|
48
|
-
}
|
|
49
|
-
if (await verifyDir(linkDir, linkDirName, READ_AND_WRITE)) {
|
|
50
|
-
linkDev.push({ path: linkDir, dev: (await stat(linkDir)).dev });
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
pathFailure++;
|
|
54
|
-
}
|
|
27
|
+
directories.push({ path: linkDir, label: `linkDir${index}` });
|
|
55
28
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for (const [index, dataDir] of dataDirs.entries()) {
|
|
60
|
-
const dataDirName = `dataDir${index}`;
|
|
61
|
-
if (await verifyDir(dataDir, dataDirName, READ_ONLY)) {
|
|
62
|
-
dataDev.push({ path: dataDir, dev: (await stat(dataDir)).dev });
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
pathFailure++;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (dataDev.length) {
|
|
69
|
-
logger.verbose(`Storage device for each dataDir: ${inspect(dataDev)}`);
|
|
70
|
-
}
|
|
71
|
-
if (injectDir) {
|
|
72
|
-
if (!(await verifyDir(injectDir, "injectDir", READ_AND_WRITE))) {
|
|
73
|
-
pathFailure++;
|
|
29
|
+
for (const { path, label } of directories) {
|
|
30
|
+
if (!(await notExists(path))) {
|
|
31
|
+
continue;
|
|
74
32
|
}
|
|
75
|
-
}
|
|
76
|
-
if (linkDirs.length) {
|
|
77
|
-
for (const [index, dataDir] of dataDirs.entries()) {
|
|
78
|
-
const dataDirName = `dataDir${index}`;
|
|
79
|
-
try {
|
|
80
|
-
const res = await testLinking(dataDir, `${dataDirName}Src.cross-seed`, `${dataDirName}Dest.cross-seed`);
|
|
81
|
-
if (!res) {
|
|
82
|
-
logger.error("Failed to link from dataDirs to linkDirs.");
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch (e) {
|
|
86
|
-
logger.error(e);
|
|
87
|
-
logger.error("Failed to link from dataDirs to linkDirs.");
|
|
88
|
-
pathFailure++;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (pathFailure) {
|
|
93
|
-
throw new CrossSeedError(`\tYour configuration is invalid, please see the ${pathFailure > 1 ? "errors" : "error"} above for details.`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async function retry(cb, numRetries, delayMs) {
|
|
97
|
-
const retries = Math.max(numRetries, 0);
|
|
98
|
-
let lastError = new Error("Retry failed");
|
|
99
|
-
for (let i = 0; i <= retries; i++) {
|
|
100
33
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
catch (e) {
|
|
104
|
-
const retryMsg = i < retries ? `, retrying in ${delayMs / 1000}s` : "";
|
|
105
|
-
logger.error(`Attempt ${i + 1}/${retries + 1} failed${retryMsg}: ${e.message}`);
|
|
106
|
-
logger.debug(e);
|
|
107
|
-
lastError = e;
|
|
108
|
-
if (i >= retries)
|
|
109
|
-
break;
|
|
110
|
-
await wait(delayMs);
|
|
34
|
+
logger.info(`Creating ${label}: ${path}`);
|
|
35
|
+
await mkdir(path, { recursive: true });
|
|
111
36
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
instantiateDownloadClients();
|
|
118
|
-
const validateClientConfig = () => mapAsync(getClients(), (client) => client.validateConfig());
|
|
119
|
-
const errors = (await Promise.allSettled([
|
|
120
|
-
retry(validateTorznabUrls, 5, ms("1 minute")),
|
|
121
|
-
retry(validateUArrLs, 5, ms("1 minute")),
|
|
122
|
-
retry(validateClientConfig, 5, ms("1 minute")),
|
|
123
|
-
])).filter((p) => p.status === "rejected");
|
|
124
|
-
if (errors.length) {
|
|
125
|
-
throw new CrossSeedError(`\tYour configuration is invalid, please see the ${errors.length > 1 ? "errors" : "error"} above for details.`);
|
|
126
|
-
}
|
|
127
|
-
logger.verbose({
|
|
128
|
-
label: Label.CONFIGDUMP,
|
|
129
|
-
message: inspect(getRuntimeConfig()),
|
|
130
|
-
});
|
|
131
|
-
logger.info("Your configuration is valid!");
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* validates and sets RuntimeConfig
|
|
135
|
-
* @return (the number of errors Zod encountered in the configuration)
|
|
136
|
-
*/
|
|
137
|
-
export function parseRuntimeConfigAndLogErrors(options) {
|
|
138
|
-
logger.info(`${PROGRAM_NAME} v${PROGRAM_VERSION}`);
|
|
139
|
-
logger.info("Validating your configuration...");
|
|
140
|
-
let parsedOptions;
|
|
141
|
-
try {
|
|
142
|
-
parsedOptions = VALIDATION_SCHEMA.parse(options, {
|
|
143
|
-
errorMap: customizeErrorMessage,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
catch (error) {
|
|
147
|
-
logger.verbose({
|
|
148
|
-
label: Label.CONFIGDUMP,
|
|
149
|
-
message: inspect(options),
|
|
150
|
-
});
|
|
151
|
-
if ("errors" in error && Array.isArray(error.errors)) {
|
|
152
|
-
error.errors.forEach(({ path, message }) => {
|
|
153
|
-
const urlPath = path[0];
|
|
154
|
-
const optionLine = path.length === 2
|
|
155
|
-
? `${path[0]} (position #${path[1] + 1})`
|
|
156
|
-
: path;
|
|
157
|
-
logger.error(`${path.length > 0
|
|
158
|
-
? `Option: ${optionLine}`
|
|
159
|
-
: "Configuration:"}${NEWLINE_INDENT}${message}${NEWLINE_INDENT}(https://www.cross-seed.org/docs/basics/options${urlPath ? `#${urlPath.toLowerCase()}` : ""})\n`);
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
39
|
+
logger.error({
|
|
40
|
+
label: Label.SERVER,
|
|
41
|
+
message: `Failed to create ${label} at ${path}: ${message}`,
|
|
160
42
|
});
|
|
161
|
-
if (error.errors.length > 0) {
|
|
162
|
-
throw new CrossSeedError(`Your configuration is invalid, please see the ${error.errors.length > 1 ? "errors" : "error"} above for details.`);
|
|
163
|
-
}
|
|
164
43
|
}
|
|
165
|
-
throw error;
|
|
166
44
|
}
|
|
167
|
-
return parsedOptions;
|
|
168
45
|
}
|
|
169
46
|
/**
|
|
170
47
|
* Initializes only the database, runs the callback, then cleans up
|
|
@@ -186,18 +63,95 @@ export function withMinimalRuntime(entrypoint, { migrate = true } = {}) {
|
|
|
186
63
|
}
|
|
187
64
|
};
|
|
188
65
|
}
|
|
66
|
+
async function applyExistingApiKey(config) {
|
|
67
|
+
try {
|
|
68
|
+
const existingApiKey = await db("settings").select("apikey").first();
|
|
69
|
+
if (existingApiKey?.apikey && !config.apiKey) {
|
|
70
|
+
config.apiKey = existingApiKey.apikey;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// best-effort only
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function determineRuntimeConfig(rawOptions) {
|
|
78
|
+
const cliOptions = omitUndefined(rawOptions);
|
|
79
|
+
// first, try to load from database (existing user happy path)
|
|
80
|
+
let dbOverrides;
|
|
81
|
+
try {
|
|
82
|
+
dbOverrides = await getDbConfig();
|
|
83
|
+
}
|
|
84
|
+
catch (dbError) {
|
|
85
|
+
logger.debug("Unable to load configuration from database", dbError);
|
|
86
|
+
}
|
|
87
|
+
if (dbOverrides !== undefined) {
|
|
88
|
+
return {
|
|
89
|
+
...getDefaultRuntimeConfig(),
|
|
90
|
+
...dbOverrides,
|
|
91
|
+
...cliOptions,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// then, try to migrate from file config (v6 to v7 upgrade path)
|
|
95
|
+
try {
|
|
96
|
+
const fileConfig = await getFileConfig();
|
|
97
|
+
if (fileConfig) {
|
|
98
|
+
const transformedFileConfig = transformFileConfig(fileConfig);
|
|
99
|
+
const runtimeFromFile = {
|
|
100
|
+
...getDefaultRuntimeConfig(),
|
|
101
|
+
...transformedFileConfig,
|
|
102
|
+
};
|
|
103
|
+
await applyExistingApiKey(runtimeFromFile);
|
|
104
|
+
await setDbConfig(runtimeFromFile);
|
|
105
|
+
const resolvedOverrides = stripDefaults(runtimeFromFile);
|
|
106
|
+
logger.info("Migrated file config to database");
|
|
107
|
+
return {
|
|
108
|
+
...getDefaultRuntimeConfig(),
|
|
109
|
+
...resolvedOverrides,
|
|
110
|
+
...cliOptions,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (migrationError) {
|
|
115
|
+
logger.error(new Error("Failed to import configuration file, falling back to defaults", { cause: migrationError }));
|
|
116
|
+
}
|
|
117
|
+
// finally, fall back to defaults (new user happy path or migration failure)
|
|
118
|
+
const defaultRuntime = getDefaultRuntimeConfig();
|
|
119
|
+
await setDbConfig(defaultRuntime);
|
|
120
|
+
await resetApiKey();
|
|
121
|
+
logger.info("Created initial database config from defaults");
|
|
122
|
+
const resolvedOverrides = stripDefaults(defaultRuntime);
|
|
123
|
+
return {
|
|
124
|
+
...getDefaultRuntimeConfig(),
|
|
125
|
+
...resolvedOverrides,
|
|
126
|
+
...cliOptions,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
189
129
|
/**
|
|
190
130
|
* Initializes the full runtime, runs the callback, then cleans up
|
|
191
131
|
* @param entrypoint
|
|
192
132
|
*/
|
|
193
133
|
export function withFullRuntime(entrypoint) {
|
|
194
134
|
return withMinimalRuntime(async (options) => {
|
|
135
|
+
createAppDirHierarchy();
|
|
195
136
|
initializeLogger(options);
|
|
196
|
-
const runtimeConfig =
|
|
137
|
+
const runtimeConfig = await determineRuntimeConfig(options);
|
|
197
138
|
setRuntimeConfig(runtimeConfig);
|
|
198
139
|
initializePushNotifier();
|
|
199
|
-
|
|
140
|
+
getLogWatcher();
|
|
141
|
+
await ensureConfiguredDirectories();
|
|
142
|
+
instantiateDownloadClients();
|
|
200
143
|
await entrypoint(runtimeConfig);
|
|
201
144
|
});
|
|
202
145
|
}
|
|
146
|
+
export async function restartCrossSeed() {
|
|
147
|
+
logger.info("Restarting cross-seed");
|
|
148
|
+
process.on("exit", () => {
|
|
149
|
+
spawn(process.argv[0], process.argv.slice(1), {
|
|
150
|
+
cwd: process.cwd(),
|
|
151
|
+
stdio: "inherit",
|
|
152
|
+
detached: false,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
await exitGracefully();
|
|
156
|
+
}
|
|
203
157
|
//# sourceMappingURL=startup.js.map
|