Spaces:
Sleeping
Sleeping
File size: 5,161 Bytes
9179e11 | 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 | 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;
// Only /internal routes require the admin secret. Keep this scoped so unknown
// public paths return 404 instead of noisy "admin secret mismatch" warnings.
router.use('/internal', requireAdminSecret);
/**
* POST /internal/jobs/:id/run
* Manually trigger a job run. Stub implementation — validates the job exists
* and returns 202 Accepted. Actual worker invocation is handled elsewhere.
*/
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 });
// TODO: Integrate with worker dispatch
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' });
}
});
/**
* POST /internal/accounts/:id/quota-check
* Manually trigger a quota check for a specific account.
* The check is started in the background after the account is leased, so the
* dashboard request is not held open for a full browser session.
*/
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;
|