refactor(ts): day 3 batch 6 — final 8 routes renamed

admin.ts adminConfig.ts adminMilestones.ts
  auth.ts oidc.ts
  learningHub.ts learningAI.ts learningAdmin.ts

These were the largest files (auth alone is ~600 lines). Minimum-
viable conversion: extension change + spot-fix the few places where
TypeScript's default inference caught genuine `unknown` escapes
from fetch().json() — patched with `: any` type annotations so the
compile passes. Day 5 strict-mode pass will replace those `any`s
with proper response type narrowing.

Fixes in this batch:
- adminConfig.ts: sttResp var shadowing (two declarations of same
  name with different types); renamed to sttRespFetch / sttRespAxios
- auth.ts: turnstileData + tsData from fetch(...).json() now `: any`
- auth.ts: resp object for password change / reset now typed
  { success: boolean; message?: string; passwordWarning?: string }
- learningAI.ts: parseInt(questionCount) cast through `any`

All 29 of 29 routes now .ts. Backend progress: 30/54 files
migrated (server.ts + 29 routes). Remaining: 2 middleware + 20
utils + 2 db files. Day 4 handles those.

tsc --noEmit green (EXIT 0).
This commit is contained in:
Daniel 2026-04-23 19:48:56 +02:00
parent acdd9313d0
commit 3d597d61a8
8 changed files with 15 additions and 15 deletions

View file

@ -780,22 +780,22 @@ router.post('/config/stt/test', async function(req, res) {
form.append('model', sttModel);
var fetchHeaders = {};
if (process.env.LITELLM_API_KEY) fetchHeaders['Authorization'] = 'Bearer ' + process.env.LITELLM_API_KEY;
var sttResp = await fetch(gatewayUrl('/audio/transcriptions'), {
var sttRespFetch: any = await fetch(gatewayUrl('/audio/transcriptions'), {
method: 'POST', headers: fetchHeaders, body: form
}).then(function(r) { return r.json().then(function(d) { return { data: d }; }); });
text = sttResp.data && sttResp.data.text ? sttResp.data.text : '';
}).then(function(r) { return r.json().then(function(d: any) { return { data: d }; }); });
text = sttRespFetch.data && sttRespFetch.data.text ? sttRespFetch.data.text : '';
} else {
var headers = { 'Content-Type': 'application/json' };
var headers: any = { 'Content-Type': 'application/json' };
if (process.env.LITELLM_API_KEY) headers['Authorization'] = 'Bearer ' + process.env.LITELLM_API_KEY;
var sttResp = await axios.post(gatewayUrl('/chat/completions'), {
var sttRespAxios: any = await axios.post(gatewayUrl('/chat/completions'), {
model: sttModel,
messages: [{ role: 'user', content: [
{ type: 'input_audio', input_audio: { data: audioBase64, format: mime.split('/')[1] || 'webm' } },
{ type: 'text', text: 'Transcribe this audio. Output the spoken words only.' }
]}]
}, { headers: headers, timeout: 60000 });
if (sttResp.data && sttResp.data.choices && sttResp.data.choices[0]) {
text = (sttResp.data.choices[0].message && sttResp.data.choices[0].message.content) || '';
if (sttRespAxios.data && sttRespAxios.data.choices && sttRespAxios.data.choices[0]) {
text = (sttRespAxios.data.choices[0].message && sttRespAxios.data.choices[0].message.content) || '';
}
}
} else if (provider === 'openai') {

View file

@ -193,7 +193,7 @@ router.post('/register', async (req, res) => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ secret: process.env.TURNSTILE_SECRET_KEY, response: turnstileToken, remoteip: req.ip })
});
var turnstileData = await turnstileRes.json();
var turnstileData: any = await turnstileRes.json();
if (!turnstileData.success) {
console.error('[Auth] Turnstile verification failed:', turnstileData['error-codes']);
return res.status(400).json({ error: 'Bot verification failed. Please try again.' });
@ -309,7 +309,7 @@ router.post('/login', async (req, res) => {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ secret: process.env.TURNSTILE_SECRET_KEY, response: turnstileToken, remoteip: req.ip })
});
var tsData = await tsRes.json();
var tsData: any = await tsRes.json();
if (!tsData.success) return res.status(400).json({ error: 'Verification failed. Please try again.' });
}
@ -556,7 +556,7 @@ router.post('/forgot-password', async (req, res) => {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ secret: process.env.TURNSTILE_SECRET_KEY, response: turnstileToken, remoteip: req.ip })
});
var tsData = await tsRes.json();
var tsData: any = await tsRes.json();
if (!tsData.success) return res.status(400).json({ error: 'Verification failed. Please try again.' });
}
@ -601,10 +601,10 @@ router.post('/reset-password', async (req, res) => {
await db.run('UPDATE users SET password = ?, reset_token = NULL, reset_expires = NULL WHERE id = ?', [hash, user.id]);
// Force logout — destroy all sessions for this user
try { await db.run('DELETE FROM user_sessions WHERE user_id = ?', [user.id]); } catch (e) { /* best effort */ }
var resp = { success: true };
var resp: { success: boolean; passwordWarning?: string } = { success: true };
if (pwnedCount > 0) resp.passwordWarning = 'This password has appeared in ' + pwnedCount.toLocaleString() + ' data breaches. Consider changing it to something unique.';
res.json(resp);
} catch (err) { console.error('[Auth] Reset password error:', err.message); res.status(500).json({ error: 'Password reset failed' }); }
} catch (err: any) { console.error('[Auth] Reset password error:', err.message); res.status(500).json({ error: 'Password reset failed' }); }
});
// Logout — destroy session and clear cookie
@ -656,10 +656,10 @@ router.post('/change-password', authMiddleware, async (req, res) => {
logger.audit(req.user.id, 'password_changed', 'Password changed', req, { category: 'auth' });
notifyPasswordChanged(req.user.id);
var resp = { success: true, message: 'Password changed. All other sessions have been logged out.' };
var resp: { success: boolean; message: string; passwordWarning?: string } = { success: true, message: 'Password changed. All other sessions have been logged out.' };
if (pwnedCount > 0) resp.passwordWarning = 'This password has appeared in ' + pwnedCount.toLocaleString() + ' data breaches. Consider choosing a different one.';
res.json(resp);
} catch (err) { console.error('[Auth] Change password error:', err.message); res.status(500).json({ error: 'Password change failed' }); }
} catch (err: any) { console.error('[Auth] Change password error:', err.message); res.status(500).json({ error: 'Password change failed' }); }
});
// Get current user

View file

@ -310,7 +310,7 @@ router.post('/ai-generate', upload.array('files', 10), async function(req, res)
var raw = result.content.trim();
// Strip any leading text before the first { or --- (models sometimes add preamble)
if (contentType !== 'presentation' || parseInt(questionCount) > 0) {
if (contentType !== 'presentation' || parseInt(questionCount as any) > 0) {
var jsonStart = raw.indexOf('{');
if (jsonStart > 0) raw = raw.substring(jsonStart);
}