File size: 5,249 Bytes
1906404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee58798
 
 
 
1906404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import express from 'express';
import cors from 'cors';
import 'dotenv/config';

// Import route modules
import authRoutes from './routes/auth.js';
import interviewRoutes from './routes/interview.js';
import atsRoutes from './routes/ats.js';
import resumeRoutes from './routes/resume.js';
import companyRoutes from './routes/company.js';
import dashboardRoutes from './routes/dashboard.js';

// Initialize Express App
const app = express();
const port = process.env.PORT || 3001;

// ── Middleware ──
app.use(cors({
  origin: '*',
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
}));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('<h1>PlaceMate AI API Server is Running</h1><p>Health check at /api/health</p>');
});

// ── Health Check ──
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// ── API Routes ──
app.use('/api/auth', authRoutes);
app.use('/api/interview', interviewRoutes);
app.use('/api/ats', atsRoutes);
app.use('/api/resume', resumeRoutes);
app.use('/api/company', companyRoutes);
app.use('/api/dashboard', dashboardRoutes);

// ── Legacy Backward-Compatible Routes ──
// These keep the existing frontend interview components working
// (they call /api/chat, /api/dsa/*, /api/resume/generate directly)
import { chatInterview } from './services/aiService.js';
import Groq from 'groq-sdk';

const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });

app.post('/api/chat', async (req, res) => {
  try {
    const { history } = req.body;
    if (!history) return res.status(400).json({ error: 'Chat history is required.' });
    const message = await chatInterview(history);
    res.json({ message });
  } catch (error) {
    console.error('Error in /api/chat:', error);
    res.status(500).json({ error: 'Failed to get response from AI model.' });
  }
});

app.post('/api/dsa/question', async (req, res) => {
  try {
    const { difficulty } = req.body;
    const difficultyLevel = difficulty === 'intern' ? 'medium' : 'medium-hard';
    const response = await groq.chat.completions.create({
      model: 'llama-3.3-70b-versatile',
      messages: [
        { role: 'system', content: 'You are a technical interviewer. Respond with valid JSON only.' },
        { role: 'user', content: `Generate a single data structures and algorithms coding problem suitable for a ${difficultyLevel} level interview.\n\nReturn ONLY a JSON object:\n{\n  "title": "Problem Title",\n  "description": "Detailed problem description with examples, constraints, and input/output format",\n  "difficulty": "${difficultyLevel}"\n}` }
      ],
      max_tokens: 2048,
      temperature: 0.7,
    });
    const text = response.choices[0]?.message?.content || '';
    const jsonMatch = text.match(/\{[\s\S]*\}/);
    if (jsonMatch) {
      res.json({ question: JSON.parse(jsonMatch[0]) });
    } else {
      res.json({ question: { title: 'Two Sum', description: 'Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.', difficulty: difficultyLevel } });
    }
  } catch (error) {
    console.error('Error in /api/dsa/question:', error);
    res.status(500).json({ error: 'Failed to generate DSA question.' });
  }
});

app.post('/api/dsa/evaluate', async (req, res) => {
  try {
    const { question, code, language, explanation } = req.body;
    if (!question || !code || !language || !explanation) return res.status(400).json({ error: 'All fields are required.' });
    const response = await groq.chat.completions.create({
      model: 'llama-3.3-70b-versatile',
      messages: [
        { role: 'system', content: 'You are a technical interviewer. Respond with valid JSON only.' },
        { role: 'user', content: `Evaluate this DSA solution:\n\nProblem: ${question.title}\nDescription: ${question.description}\nCode (${language}): ${code}\nExplanation: ${explanation}\n\nReturn ONLY JSON: { "score": <0-100>, "strengths": [...], "weaknesses": [...], "advice": [...] }` }
      ],
      max_tokens: 2048,
      temperature: 0.7,
    });
    const text = response.choices[0]?.message?.content || '';
    const jsonMatch = text.match(/\{[\s\S]*\}/);
    if (jsonMatch) {
      res.json({ feedback: JSON.parse(jsonMatch[0]) });
    } else {
      res.status(500).json({ error: 'Failed to parse feedback.' });
    }
  } catch (error) {
    console.error('Error in /api/dsa/evaluate:', error);
    res.status(500).json({ error: 'Failed to evaluate DSA solution.' });
  }
});

// ── 404 Handler ──
app.use('/api/*', (req, res) => {
  res.status(404).json({ error: `Route ${req.method} ${req.originalUrl} not found.` });
});

// ── Global Error Handler ──
app.use((err, req, res, next) => {
  console.error('Unhandled error:', err);
  res.status(500).json({ error: 'Internal server error.' });
});

// ── Start Server ──
app.listen(port, () => {
  console.log(`\nπŸš€ PlaceMate API Server running on http://localhost:${port}`);
  console.log(`   Health check: http://localhost:${port}/api/health`);
  console.log(`   Routes: auth, interview, ats, resume, company, dashboard\n`);
});