forked from jordanlambrecht/tracker-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlimits.ts
More file actions
153 lines (135 loc) · 7.68 KB
/
Copy pathlimits.ts
File metadata and controls
153 lines (135 loc) · 7.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// src/lib/limits.ts
// ─── Auth ─────────────────────────────────────────────────────────────────────
export const PASSWORD_MIN = 8
export const PASSWORD_MAX = 128
export const USERNAME_MIN = 3
export const USERNAME_MAX = 100
export const TOTP_TOKEN_MAX = 2048
/** Letters, digits, underscore, hyphen, dot, space. */
const USERNAME_PATTERN = /^[\w\-. ]+$/
// Not exported: callers narrow on `.ok` off the return value, and an exported
// type nobody imports is exactly what knip flags.
type UsernameValidation = { ok: true; username: string } | { ok: false; error: string }
/**
* The one definition of a valid login username, in the order initial setup has
* always applied it: trim FIRST, then bound the length, then the character class.
*
* Order matters. Checking the length of the raw string lets `" a "` through a
* `>= 3` test and then stores the trimmed `"a"`. A username setup itself would
* have rejected. Every surface that accepts a username has to agree with setup,
* or the login form ends up demanding a value that could never have been typed
* into the form that created the account.
*
* Deliberately pure, and deliberately free of `next/server`: this module is
* imported by client components (i.e. the settings Account form), so anything
* server-only in here would be dragged into the browser bundle. Route handlers
* wrap the returned `error` in their own NextResponse.
*/
export function validateUsername(raw: unknown): UsernameValidation {
if (typeof raw !== "string" || !raw.trim()) {
return { ok: false, error: "Username is required" }
}
const username = raw.trim()
if (username.length < USERNAME_MIN || username.length > USERNAME_MAX) {
return {
ok: false,
error: `Username must be between ${USERNAME_MIN} and ${USERNAME_MAX} characters`,
}
}
if (!USERNAME_PATTERN.test(username)) {
return {
ok: false,
error: "Username may only contain letters, numbers, underscores, hyphens, dots, and spaces",
}
}
return { ok: true, username }
}
// ─── Network ──────────────────────────────────────────────────────────────────
export const PORT_MIN = 1
export const PORT_MAX = 65535
// ─── Tracker ──────────────────────────────────────────────────────────────────
export const TRACKER_NAME_MAX = 100
export const TRACKER_URL_MAX = 500
export const TRACKER_TOKEN_MAX = 500
/** Cap for platforms whose "token" is a JSON credential blob (cookies, UA, etc). */
export const LARGE_TOKEN_MAX = 5000
/**
* Platforms that authenticate with a JSON credential blob rather than a plain
* API key. These need the larger cap. An IPTorrents cookie header alone can
* run past 1 KB. Add new blob-credential platforms here and every route picks
* it up.
*/
const LARGE_TOKEN_PLATFORMS: ReadonlySet<string> = new Set([
"avistaz",
"iptorrents",
"torrentleech",
])
export function maxTokenLengthFor(platform: string | null | undefined): number {
return platform && LARGE_TOKEN_PLATFORMS.has(platform) ? LARGE_TOKEN_MAX : TRACKER_TOKEN_MAX
}
export const TRACKER_TAG_MAX = 100
export const TRACKER_NOTES_MAX = 2000
export const TRACKER_ROLE_NAME_MAX = 255
// ─── Polling & Retention ──────────────────────────────────────────────────────
export const POLL_INTERVAL_MIN = 15
export const POLL_INTERVAL_MAX = 1440
export const POLL_INTERVAL_DEFAULT = 60
export const POLL_MANUAL_COOLDOWN_MS = 10_000
export const CLIENT_POLL_INTERVAL_MIN = 60
export const CLIENT_POLL_INTERVAL_MAX = 86400
export const CLIENT_POLL_INTERVAL_DEFAULT = 300
export const SNAPSHOT_RETENTION_MIN = 7
export const SNAPSHOT_RETENTION_MAX = 3650
export const SNAPSHOT_RETENTION_DEFAULT = 90
export const SNAPSHOT_QUERY_MAX = 3650
export const FLEET_SNAPSHOT_QUERY_MAX = 365
// ─── Session & Lockout ────────────────────────────────────────────────────────
export const SESSION_TIMEOUT_MIN = 1
export const SESSION_TIMEOUT_MAX = 525960
export const LOCKOUT_THRESHOLD_MIN = 1
export const LOCKOUT_THRESHOLD_MAX = 99
export const LOCKOUT_THRESHOLD_DEFAULT = 5
export const LOCKOUT_DURATION_MIN = 1
export const LOCKOUT_DURATION_MAX = 1440
export const LOCKOUT_DURATION_DEFAULT = 15
// ─── Backup ───────────────────────────────────────────────────────────────────
export const BACKUP_RETENTION_MIN = 1
export const BACKUP_RETENTION_MAX = 365
export const BACKUP_PASSWORD_MAX = 128
// ─── String Lengths ───────────────────────────────────────────────────────────
export const SHORT_NAME_MAX = 100
export const HOST_MAX = 255
export const CREDENTIAL_MAX = 255
export const LONG_STRING_MAX = 500
export const EMOJI_MAX = 10
export const ALERT_KEY_MAX = 255
export const ALERT_TYPE_MAX = 30
export const SORT_ORDER_MAX = 9999
export const COLOR_STRING_MAX = 20
export const BATCH_MEMBERS_MAX = 500
// ─── Download Clients ─────────────────────────────────────────────────────────
export const CROSS_SEED_TAGS_MAX = 50
export const CROSS_SEED_TAG_MAX = 100
// ─── File Sizes ───────────────────────────────────────────────────────────────
export const UPLOAD_IMAGE_MAX_BYTES = 32 * 1024 * 1024
export const BACKUP_RESTORE_MAX_BYTES = 50 * 1024 * 1024
export const AVATAR_FETCH_MAX_BYTES = 5 * 1024 * 1024
export const MOUSEHOLE_BODY_MAX_BYTES = 256
// ─── Image Hosting ────────────────────────────────────────────────────────────
export const IMAGE_EXPIRATION_MIN = 60
export const IMAGE_EXPIRATION_MAX = 15_552_000
// ─── Notifications ────────────────────────────────────────────────────────────
export const NOTIFICATION_COOLDOWN_MAX_MS = 300_000
export const VIP_EXPIRING_DAYS_DEFAULT = 7
export const UNSATISFIED_LIMIT_PCT_DEFAULT = 80
export const RATIO_DROP_DELTA_DEFAULT = 0.1
export const BUFFER_MILESTONE_DEFAULT_BYTES = 10_737_418_240
// ─── Misc ─────────────────────────────────────────────────────────────────────
export const QUICKLINK_SLUGS_MAX = 100
export const QUICKLINK_SLUG_MAX = 200
export const REORDER_IDS_MAX = 500
export const EVENTS_LIMIT_DEFAULT = 50
export const EVENTS_LIMIT_CAP = 1000
// ─── Time ─────────────────────────────────────────────────────────────────────
export const MS_PER_DAY = 24 * 60 * 60 * 1000
export const ADAPTER_FETCH_TIMEOUT_MS = 15_000