( req: NextRequest, deps: FreebuffSessionDeps, )
| 220 | |
| 221 | /** POST /api/v1/freebuff/session — join queue / take over as this instance. */ |
| 222 | export async function postFreebuffSession( |
| 223 | req: NextRequest, |
| 224 | deps: FreebuffSessionDeps, |
| 225 | ): Promise<NextResponse> { |
| 226 | const auth = await resolveUser(req, deps) |
| 227 | if ('error' in auth) return auth.error |
| 228 | |
| 229 | const countryAccess = await getCountryAccess(auth.userId, req, deps) |
| 230 | logCountryAccess('POST', auth.userId, countryAccess, deps) |
| 231 | if (shouldHardBlockFreeModeAccess(countryAccess)) { |
| 232 | await endSessionForHardBlock(auth, deps) |
| 233 | return hardBlockedResponse(countryAccess) |
| 234 | } |
| 235 | const accessTier = getFreeModeAccessTier(countryAccess) |
| 236 | |
| 237 | const requestedModel = req.headers.get(FREEBUFF_MODEL_HEADER) ?? '' |
| 238 | |
| 239 | try { |
| 240 | const state = await requestSession({ |
| 241 | userId: auth.userId, |
| 242 | userEmail: auth.userEmail, |
| 243 | userBanned: auth.userBanned, |
| 244 | model: requestedModel, |
| 245 | accessTier, |
| 246 | countryAccess: toSessionCountryAccess(countryAccess), |
| 247 | deps: deps.sessionDeps, |
| 248 | }) |
| 249 | // model_locked / model_unavailable are 409 so they're distinguishable |
| 250 | // from normal queued/active responses on the client. banned is a 403 |
| 251 | // (terminal, mirrors country_blocked) so older CLIs that don't know the |
| 252 | // status fall into their `!resp.ok` error path and back off instead of |
| 253 | // tight-polling on the unrecognized 200 body. rate_limited uses 429 for |
| 254 | // the same reason as banned — older CLIs back off, newer CLIs parse the |
| 255 | // structured body. |
| 256 | const status = |
| 257 | state.status === 'model_locked' || state.status === 'model_unavailable' |
| 258 | ? 409 |
| 259 | : state.status === 'banned' |
| 260 | ? 403 |
| 261 | : state.status === 'rate_limited' |
| 262 | ? 429 |
| 263 | : 200 |
| 264 | return NextResponse.json(state, { status }) |
| 265 | } catch (error) { |
| 266 | return serverError(deps, 'POST', auth.userId, error) |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | /** GET /api/v1/freebuff/session — read current state without mutation. The |
| 271 | * caller's instance id (via X-Freebuff-Instance-Id) is used to detect |
no test coverage detected