Accounts with limits: a Newznab key and a tier per user, daily API and NZB counts, invites
ACCESS=accounts (config/access.ts; open stays the default and behaves as before): Newznab and the catalog's releases routes name a user (apikey or the web interface's token), whose tier sets searches and NZBs per UTC day (Newznab 500/501, the catalog 429) and how far back they see (102/403). Counted in one upsert per use, so the last one can't be taken twice; caps, stats and refused requests aren't counted. Signup takes an invite (SIGNUP), which may give a tier; ace access:invite, access:tier (extends a running tier), access:suspend. The web interface signs in and shows the day's usage and the Newznab key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c55918350e
commit
1322e42fb7
23 files changed
+1023
-39
No files matched your search
@@ -2,4 +2,4 @@
|
||||
* Newznab API formatting, independent of AdonisJS. From voyager 1 (its CONCEPT.md D23).
|
||||
*/
|
||||
export { CATEGORIES, categoryLabel, expandCategories, parentCategory } from './categories.js'
|
||||
export { capsXml, errorXml, resultsXml, type NewznabItem, type NewznabServer } from './xml.js'
|
||||
export { capsXml, errorXml, resultsXml, type ApiLimits, type NewznabItem, type NewznabServer } from './xml.js'
|
||||
+24
-4
@@ -27,7 +27,7 @@ export interface NewznabServer {
|
||||
apiKey?: string
|
||||
}
|
||||
|
||||
/** Newznab error codes: 100 credentials, 200/201 parameters, 202 function, 300 item. */
|
||||
/** Newznab error codes: 100 credentials, 101 suspended, 102 privileges, 200/201 parameters, 202 function, 300 item, 500/501 request/download limit. */
|
||||
/** Spotnet UBB markup (or the HTML of old spots) as plain text: [br] as line breaks, other tags dropped. */
|
||||
export function ubbToText(ubb: string): string {
|
||||
return ubb
|
||||
@@ -44,11 +44,23 @@ export function errorXml(code: number, description: string): string {
|
||||
.end({ prettyPrint: true })
|
||||
}
|
||||
|
||||
export function capsXml(server: NewznabServer, limits: { max: number; default: number }): string {
|
||||
/** A caller's day so far and its limits (null: none), as nZEDb-style indexers report them. */
|
||||
export interface ApiLimits {
|
||||
apiCurrent: number
|
||||
apiMax: number | null
|
||||
grabCurrent: number
|
||||
grabMax: number | null
|
||||
}
|
||||
|
||||
export function capsXml(
|
||||
server: NewznabServer,
|
||||
limits: { max: number; default: number },
|
||||
registration = { available: false, open: false }
|
||||
): string {
|
||||
const caps = create({ version: '1.0', encoding: 'UTF-8' }).ele('caps')
|
||||
caps.ele('server', { version: '1.0', title: server.title, strapline: 'Usenet indexer', url: `${server.baseUrl}/` })
|
||||
caps.ele('limits', { max: String(limits.max), default: String(limits.default) })
|
||||
caps.ele('registration', { available: 'no', open: 'no' })
|
||||
caps.ele('registration', { available: registration.available ? 'yes' : 'no', open: registration.open ? 'yes' : 'no' })
|
||||
const searching = caps.ele('searching')
|
||||
searching.ele('search', { available: 'yes', supportedParams: 'q' })
|
||||
searching.ele('tv-search', { available: 'yes', supportedParams: 'q,season,ep' })
|
||||
@@ -63,7 +75,7 @@ export function capsXml(server: NewznabServer, limits: { max: number; default: n
|
||||
return caps.end({ prettyPrint: true })
|
||||
}
|
||||
|
||||
export function resultsXml(server: NewznabServer, items: NewznabItem[], offset: number, total: number): string {
|
||||
export function resultsXml(server: NewznabServer, items: NewznabItem[], offset: number, total: number, limits?: ApiLimits): string {
|
||||
const rss = create({ version: '1.0', encoding: 'UTF-8' }).ele('rss', {
|
||||
'version': '2.0',
|
||||
'xmlns:atom': 'http://www.w3.org/2005/Atom',
|
||||
@@ -74,6 +86,14 @@ export function resultsXml(server: NewznabServer, items: NewznabItem[], offset:
|
||||
channel.ele('description').txt(`${server.title} API results`)
|
||||
channel.ele('link').txt(`${server.baseUrl}/`)
|
||||
channel.ele('newznab:response', { offset: String(offset), total: String(total) })
|
||||
if (limits) {
|
||||
channel.ele('newznab:apilimits', {
|
||||
apicurrent: String(limits.apiCurrent),
|
||||
...(limits.apiMax !== null && { apimax: String(limits.apiMax) }),
|
||||
grabcurrent: String(limits.grabCurrent),
|
||||
...(limits.grabMax !== null && { grabmax: String(limits.grabMax) }),
|
||||
})
|
||||
}
|
||||
|
||||
const key = server.apiKey ? `&apikey=${encodeURIComponent(server.apiKey)}` : ''
|
||||
for (const item of items) {
|
||||
|
||||
Reference in new issue
Block a user