Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,5 @@ flowchart TB
```

**Deltas from current β†’ target:** delete the mock; frontend talks only to the real backend via `apiClient`; real auth; real database behind a repository interface; the 1580-line `index.ts` split into domain modules; business logic and secrets (answer keys, PII) removed from the frontend.

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "STD_14349",
"student_name": "Amanpreet Singh",
"enrolled_class": 3,
"test_date": "2026-07-30",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "807",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "STD_51683",
"student_name": "Shreya Sharma ",
"enrolled_class": 3,
"test_date": "2026-07-29",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "80",
"confidence": 0.95
}
}
}
14 changes: 14 additions & 0 deletions backend/ai-services/student_responses/class_3/phrase_1/s31.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s31",
"student_name": "Vihaan Joshi",
"enrolled_class": 3,
"test_date": "2026-07-30",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "801",
"confidence": 0.95
}
}
}
14 changes: 14 additions & 0 deletions backend/ai-services/student_responses/class_3/phrase_1/s6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s6",
"student_name": "Sandeep Kumar",
"enrolled_class": 3,
"test_date": "2026-08-08",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "801",
"confidence": 0.95
}
}
}
14 changes: 14 additions & 0 deletions backend/ai-services/student_responses/class_3/phrase_1/s7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s7",
"student_name": "Sneha Sharma",
"enrolled_class": 3,
"test_date": "2026-07-28",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "801",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s_new_14",
"student_name": "Rohini Patil",
"enrolled_class": 3,
"test_date": "2026-08-08",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "807",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s_new_3",
"student_name": "Ananya Mishra",
"enrolled_class": 3,
"test_date": "2026-07-27",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "801",
"confidence": 0.95
}
}
}
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s19",
"student_name": "Rohan Das",
"enrolled_class": 3,
"test_date": "2026-07-20",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "809",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s31",
"student_name": "Vihaan Joshi",
"enrolled_class": 3,
"test_date": "2026-07-20",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "809",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s7",
"student_name": "Sneha Sharma",
"enrolled_class": 3,
"test_date": "2026-07-19",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "81",
"confidence": 0.95
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"student_id": "s8",
"student_name": "Rajesh Saini",
"enrolled_class": 3,
"test_date": "2026-07-19",
"phrase": "phrase_1",
"exam_id": "C3_WORKSHEET_PHRASE_1",
"answers": {
"Q1": {
"answer": "80",
"confidence": 0.95
}
}
}
2 changes: 1 addition & 1 deletion backend/fln-backend/scripts/debug-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const APP_URL = pathToFileURL(path.join(__dirname, '..', 'app', 'index.html')).h
);

console.log('Loading', APP_URL, '...');
await page.goto(APP_URL, { waitUntil: 'networkidle0', timeout: 60000 }).catch((e) =>
await page.goto(APP_URL, { waitUntil: 'load', timeout: 15000 }).catch((e) =>
console.log('[goto error]', e.message)
);

Expand Down
7 changes: 7 additions & 0 deletions backend/fln-backend/server.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
const express = require('express');
const path = require('path');
const originalPort = process.env.PORT;
try {
require('dotenv').config({ path: path.resolve(__dirname, '..', '..', '.env') });
} catch (e) {
// ignore if dotenv is not present or fails
}
process.env.PORT = originalPort || '4000';
const { closeBrowser } = require('./services/renderWorksheet');
const generateRoutes = require('./routes/generate');
const downloadRoutes = require('./routes/download');
Expand Down
8 changes: 5 additions & 3 deletions backend/fln-backend/services/batchProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@ async function processBatch(roster, existingBatchId) {
updateStatus(batchId, { total: jobs.length, message: `Rendering ${jobs.length} file(s)...` });

// Render pool: each worker owns its own page for the whole run.
const pages = await Promise.all(
Array.from({ length: Math.min(CONCURRENCY, Math.max(1, jobs.length)) }, () => createRenderPage())
);
const pages = [];
const pagesCount = Math.min(CONCURRENCY, Math.max(1, jobs.length));
for (let i = 0; i < pagesCount; i++) {
pages.push(await createRenderPage());
}

let processed = 0;
const errors = [];
Expand Down
7 changes: 5 additions & 2 deletions backend/fln-backend/services/renderWorksheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ let browserPromise = null;
/** Launch (once) and reuse a single headless Chromium instance for the process lifetime. */
function getBrowser() {
if (!browserPromise) {
const executablePath = process.env.CHROME_EXECUTABLE_PATH || undefined;
browserPromise = puppeteer.launch({
headless: 'new',
executablePath,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu'
'--disable-gpu',
'--allow-file-access-from-files'
]
});
}
Expand Down Expand Up @@ -97,7 +100,7 @@ async function createRenderPage() {
}
});

await page.goto(APP_URL, { waitUntil: 'networkidle0', timeout: 60000 });
await page.goto(APP_URL, { waitUntil: 'load', timeout: 15000 });
await page.waitForFunction(REQUIRED_GLOBALS_CHECK, { timeout: 30000 });

return page;
Expand Down
23 changes: 23 additions & 0 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import express from 'express';
import cors from 'cors';
import remediationRoutes from './routes/remediation.routes';
import blueprintRoutes from './routes/blueprint.routes';

const app = express();

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/api/health', (_req, res) => {
res.status(200).json({
success: true,
message: 'Server is running',
data: null
});
});

app.use('/api/remediation', remediationRoutes);
app.use('/api/blueprints', blueprintRoutes);

export default app;
39 changes: 25 additions & 14 deletions backend/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,31 @@ import puppeteer, { Browser } from 'puppeteer';
//
// If the browser can't start (not installed, or missing system libraries), we
// throw one clear, actionable error instead of a raw Puppeteer stack trace.
let browserPromise: Promise<Browser> | null = null;

export async function launchBrowser(): Promise<Browser> {
const executablePath = process.env.CHROME_EXECUTABLE_PATH || undefined;
try {
return await puppeteer.launch({
headless: true,
executablePath,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
} catch (err: any) {
throw new Error(
'Failed to launch Chrome for PDF generation. Install the browser with ' +
'`npx puppeteer browsers install chrome` (and its system libraries), or set ' +
'CHROME_EXECUTABLE_PATH to a valid Chrome binary. Original error: ' +
(err?.message || String(err)),
);
if (browserPromise) {
return browserPromise;
}

const executablePath = process.env.CHROME_EXECUTABLE_PATH || undefined;
browserPromise = (async () => {
try {
return await puppeteer.launch({
headless: true,
executablePath,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--allow-file-access-from-files'],
});
} catch (err: any) {
browserPromise = null;
throw new Error(
'Failed to launch Chrome for PDF generation. Install the browser with ' +
'`npx puppeteer browsers install chrome` (and its system libraries), or set ' +
'CHROME_EXECUTABLE_PATH to a valid Chrome binary. Original error: ' +
(err?.message || String(err)),
);
}
})();

return browserPromise;
}
28 changes: 28 additions & 0 deletions backend/src/config/database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import mongoose from 'mongoose';
import { env } from './environment';

export async function connectDatabase(): Promise<void> {
mongoose.connection.on('connected', () => {
const { host, port, name } = mongoose.connection;
console.log(`βœ… MongoDB Connected β€” Host: ${host}, Port: ${port}, Database: ${name}`);
});

mongoose.connection.on('error', (err) => {
console.error('❌ MongoDB runtime error:', err.message);
});

mongoose.connection.on('disconnected', () => {
console.warn('⚠️ MongoDB disconnected');
});

try {
await mongoose.connect(env.mongodbUri, {
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});
} catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error';
console.error('❌ MongoDB connection failed:', message);
console.warn('⚠️ Server will start without database. Only non-DB routes will work.');
}
}
Loading