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>
42 lines
1.7 KiB
TypeScript
42 lines
1.7 KiB
TypeScript
import User from '#models/user'
|
|
import db from '@adonisjs/lucid/services/db'
|
|
import { signupValidator } from '#validators/user'
|
|
import type { HttpContext } from '@adonisjs/core/http'
|
|
import UserTransformer from '#transformers/user_transformer'
|
|
import AccessService from '#services/access_service'
|
|
|
|
class InviteGone extends Error {}
|
|
|
|
export default class NewAccountController {
|
|
async store({ request, response, serialize }: HttpContext) {
|
|
const { fullName, email, password, invite } = await request.validateUsing(signupValidator)
|
|
if (AccessService.signup === 'closed') return response.forbidden({ error: 'Signup is closed' })
|
|
if (AccessService.signup === 'invite' && !invite) return response.forbidden({ error: 'An invite code is needed to sign up' })
|
|
|
|
// The user and their invite together: an invite that's gone rolls the signup back.
|
|
const user = await db
|
|
.transaction(async (trx) => {
|
|
const created = await User.create({ fullName, email, password }, { client: trx })
|
|
if (AccessService.signup !== 'invite') return created
|
|
const gives = await AccessService.redeem(invite!, created.id, trx)
|
|
if (!gives) throw new InviteGone()
|
|
if (gives.tier) {
|
|
AccessService.grantTier(created, gives.tier, gives.tier_days)
|
|
await created.useTransaction(trx).save()
|
|
}
|
|
return created
|
|
})
|
|
.catch((error) => {
|
|
if (error instanceof InviteGone) return null
|
|
throw error
|
|
})
|
|
if (!user) return response.forbidden({ error: 'Unknown or used invite code' })
|
|
const token = await User.accessTokens.create(user)
|
|
|
|
return serialize({
|
|
user: UserTransformer.transform(user),
|
|
token: token.value!.release(),
|
|
})
|
|
}
|
|
}
|