const express = require('express'); const router = express.Router(); const { authenticateToken } = require('./auth'); const GroupDoc = require('../models/GroupDoc'); const fs = require('fs'); // Helpers to load Google auth via service account function loadServiceAccount() { if (process.env.GOOGLE_SERVICE_ACCOUNT_JSON) { try { const obj = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_JSON); if (obj && obj.private_key && typeof obj.private_key === 'string') { obj.private_key = obj.private_key.replace(/\\n/g, '\n'); } return obj; } catch (_) {} } const path = process.env.SA_JSON_PATH || process.env.GOOGLE_APPLICATION_CREDENTIALS || '/workspace/service-account.json'; try { const obj = JSON.parse(fs.readFileSync(path, 'utf-8')); if (obj && obj.private_key && typeof obj.private_key === 'string') { obj.private_key = obj.private_key.replace(/\\n/g, '\n'); } return obj; } catch (_) { return null; } } function parseFolderId(input) { if (!input) return null; const m = String(input).match(/folders\/([A-Za-z0-9_-]+)/); return m ? m[1] : input; } async function createDriveClient() { const { google } = require('googleapis'); const sa = loadServiceAccount(); if (!sa || !sa.client_email || !sa.private_key) { throw new Error('Service account credentials not configured'); } const scopes = ['https://www.googleapis.com/auth/drive']; const auth = new google.auth.JWT(sa.client_email, undefined, sa.private_key, scopes); await auth.authorize(); const drive = google.drive({ version: 'v3', auth }); return drive; } // List docs for a week router.get('/list', authenticateToken, async (req, res) => { try { const weekNumber = parseInt(req.query.weekNumber); if (!weekNumber) return res.status(400).json({ error: 'weekNumber is required' }); const docs = await GroupDoc.find({ weekNumber }).sort({ groupNumber: 1 }); res.json({ success: true, docs }); } catch (e) { res.status(500).json({ error: 'Failed to list docs' }); } }); // Create a blank Google Doc for a week + group if not exists router.post('/create', authenticateToken, async (req, res) => { try { const { weekNumber, groupNumber, docUrl, title: providedTitle } = req.body || {}; if (!weekNumber || !groupNumber) return res.status(400).json({ error: 'weekNumber and groupNumber are required' }); const wn = parseInt(weekNumber); const gn = parseInt(groupNumber); // Return existing if present let existing = await GroupDoc.findOne({ weekNumber: wn, groupNumber: gn }); if (existing) return res.json({ success: true, doc: existing }); // If a docUrl is provided, treat as manual link fallback if (docUrl) { const m = String(docUrl).match(/document\/d\/([A-Za-z0-9_-]+)/); const baseDocId = m ? m[1] : `external-${Date.now()}`; // Avoid unique conflicts on docId across weeks/groups by namespacing if needed let docId = baseDocId; const conflict = await GroupDoc.findOne({ docId }); if (conflict) { if (conflict.weekNumber === wn && conflict.groupNumber === gn) { return res.json({ success: true, doc: conflict }); } docId = `${baseDocId}-${wn}-${gn}`; } const payload = { weekNumber: wn, groupNumber: gn, docId, docUrl, title: providedTitle || `Week ${wn} - Group ${gn} Translation` }; try { const savedManual = await GroupDoc.create(payload); return res.status(201).json({ success: true, doc: savedManual }); } catch (err) { // If unique error somehow still occurs, fall back to returning/creating a uniquely suffixed id if (err && err.code === 11000) { const fallbackId = `${baseDocId}-${wn}-${gn}-${Date.now()}`; const savedManual = await GroupDoc.create({ ...payload, docId: fallbackId }); return res.status(201).json({ success: true, doc: savedManual }); } throw err; } } // Auto-creation disabled per request return res.status(400).json({ error: 'Auto creation disabled. Please provide docUrl.' }); } catch (e) { console.error('Create group doc error:', e); res.status(500).json({ error: 'Failed to create doc', message: e?.message }); } }); // Fallback: save an existing Google Doc link for a week + group (no Drive API usage) router.post('/link', authenticateToken, async (req, res) => { try { const { weekNumber, groupNumber, docUrl, title } = req.body || {}; if (!weekNumber || !groupNumber || !docUrl) return res.status(400).json({ error: 'weekNumber, groupNumber and docUrl are required' }); const m = String(docUrl).match(/document\/d\/([A-Za-z0-9_-]+)/); const docId = m ? m[1] : `external-${Date.now()}`; // If exists, update URL/title let existing = await GroupDoc.findOne({ weekNumber: parseInt(weekNumber), groupNumber: parseInt(groupNumber) }); if (existing) { existing.docId = docId; existing.docUrl = docUrl; if (title) existing.title = title; await existing.save(); return res.json({ success: true, doc: existing }); } const saved = await GroupDoc.create({ weekNumber, groupNumber, docId, docUrl, title: title || `Week ${weekNumber} - Group ${groupNumber} Translation` }); res.status(201).json({ success: true, doc: saved }); } catch (e) { console.error('Link group doc error:', e); res.status(500).json({ error: 'Failed to save doc link', message: e?.message }); } }); // Delete a saved Google Doc link by id router.delete('/:id', authenticateToken, async (req, res) => { try { const id = req.params.id; if (!id) return res.status(400).json({ error: 'id is required' }); const found = await GroupDoc.findById(id); if (!found) return res.status(404).json({ error: 'Doc not found' }); await GroupDoc.deleteOne({ _id: id }); return res.json({ success: true, removed: id }); } catch (e) { console.error('Delete group doc error:', e); res.status(500).json({ error: 'Failed to delete doc', message: e?.message }); } }); module.exports = router;