| import { Router, Request, Response } from 'express'; |
| import { config } from '../../config'; |
| import { |
| claimSpecificAccountForManualQuota, |
| getTurnitinAccountById, |
| } from '../../db/accounts'; |
| import { requireAdminSecret } from '../middleware/admin-secret'; |
| import { getJobById } from '../../db/jobs'; |
| import { runQuotaCheckForAccount } from '../../cron/quota-check'; |
| import { logger } from '../../utils/logger'; |
|
|
| const router = Router(); |
| const MAX_MANUAL_QUOTA_CHECKS_PER_WORKER = 1; |
| let activeManualQuotaChecks = 0; |
|
|
| |
| |
| router.use('/internal', requireAdminSecret); |
|
|
| |
| |
| |
| |
| |
| router.post('/internal/jobs/:id/run', async (req: Request, res: Response): Promise<void> => { |
| const id = String(req.params.id); |
|
|
| try { |
| const job = await getJobById(id); |
| if (!job) { |
| res.status(404).json({ error: 'Job not found' }); |
| return; |
| } |
|
|
| logger.info('Manual job run requested', { jobId: id }); |
|
|
| |
| res.status(202).json({ message: 'Job run accepted', jobId: id }); |
| } catch (err) { |
| logger.error('Internal job run error', { |
| jobId: id, |
| error: err instanceof Error ? err.message : String(err), |
| }); |
| res.status(500).json({ error: 'Internal server error' }); |
| } |
| }); |
|
|
| |
| |
| |
| |
| |
| |
| router.post( |
| '/internal/accounts/:id/quota-check', |
| async (req: Request, res: Response): Promise<void> => { |
| const id = String(req.params.id); |
| let handedOffToBackground = false; |
|
|
| try { |
| logger.info('Manual quota check requested', { accountId: id }); |
|
|
| if (activeManualQuotaChecks >= MAX_MANUAL_QUOTA_CHECKS_PER_WORKER) { |
| res.status(429).json({ |
| error: 'This worker is already running a manual quota check. Try another worker or wait briefly.', |
| }); |
| return; |
| } |
|
|
| activeManualQuotaChecks += 1; |
|
|
| const existingAccount = await getTurnitinAccountById(id); |
| if (!existingAccount) { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| res.status(404).json({ error: 'Turnitin account not found' }); |
| return; |
| } |
|
|
| if (existingAccount.turnitin_status === 'disabled') { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| res.status(409).json({ error: 'This account is disabled and cannot be checked.' }); |
| return; |
| } |
|
|
| if (existingAccount.turnitin_status === 'login_failed') { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| res.status(409).json({ |
| error: 'This account is marked login_failed. Fix the credential before checking quota.', |
| }); |
| return; |
| } |
|
|
| if (existingAccount.turnitin_pool_key === 'legacy_carta' || existingAccount.turnitin_pool_key === 'modern_one') { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| res.status(409).json({ |
| error: |
| 'Manual quota check supports reusable modern Turnitin accounts only. Legacy and modern_one pools are finalized after submission.', |
| }); |
| return; |
| } |
|
|
| const leaseOwner = `${config.workerId}:manual-quota-check`; |
| const claimedAccount = await claimSpecificAccountForManualQuota(id, leaseOwner); |
| if (!claimedAccount) { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| res.status(409).json({ |
| error: 'This account is currently leased by another worker or is not ready for checking.', |
| }); |
| return; |
| } |
|
|
| handedOffToBackground = true; |
| void runQuotaCheckForAccount(claimedAccount, { |
| source: 'manual', |
| fallbackStatus: existingAccount.turnitin_status || 'available', |
| }) |
| .catch((err: unknown) => { |
| logger.error('Manual quota check background task failed', { |
| accountId: id, |
| error: err instanceof Error ? err.message : String(err), |
| }); |
| }) |
| .finally(() => { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| }); |
|
|
| res.status(202).json({ |
| message: 'Quota check started', |
| accountId: id, |
| workerId: config.workerId, |
| }); |
| } catch (err: unknown) { |
| logger.error('Manual quota check request failed', { |
| accountId: id, |
| error: err instanceof Error ? err.message : String(err), |
| }); |
| if (!handedOffToBackground) { |
| activeManualQuotaChecks = Math.max(0, activeManualQuotaChecks - 1); |
| } |
| res.status(500).json({ error: 'Failed to start quota check' }); |
| } |
| }, |
| ); |
|
|
| export default router; |
|
|