{"version":2,"sessionId":"4f975fee-1714-4d0d-ae43-e289ce8dde95","initialFileContents":[["file:///c%3A/Servers/job-apply-express/prisma/schema.prisma","747443c"],["file:///c%3A/Servers/job-apply-express/controller.js","dda2c0c"]],"timeline":{"checkpoints":[{"checkpointId":"876c9ff0-2def-4dcd-bb46-6e543f49eeaf","epoch":0,"label":"Initial State","description":"Starting point before any edits"},{"checkpointId":"f297bc4c-40b9-4e78-b0ee-87ae41837306","requestId":"request_0ebedb46-06b7-4eab-a274-be3a70b09fb3","epoch":1,"label":"Request request_0ebedb46-06b7-4eab-a274-be3a70b09fb3"},{"checkpointId":"c09a4e85-5b4f-48a7-b02d-95c5b2bafff5","requestId":"request_2abd0f14-c3fe-45f9-838d-8f1ea79b09a9","epoch":2,"label":"Request request_2abd0f14-c3fe-45f9-838d-8f1ea79b09a9"},{"checkpointId":"182f60a8-db3c-43d1-baa4-405a6948e93e","requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","epoch":3,"label":"Request request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80"},{"checkpointId":"9840e5e4-5656-4e19-af14-b1dad73efc2c","requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","undoStopId":"0163baee-0030-40a3-865f-a1732bcf77db","epoch":4,"label":"Request request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80 - Stop 0163baee-0030-40a3-865f-a1732bcf77db"},{"checkpointId":"8ac6a9a7-b00c-4156-98ca-9b3b35aae53c","requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","undoStopId":"3e2ec50f-f67a-4c59-bcb3-0ea9196b9ba6","epoch":7,"label":"Request request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80 - Stop 3e2ec50f-f67a-4c59-bcb3-0ea9196b9ba6"},{"checkpointId":"01459634-217f-41a5-9b58-d814e78f7d09","requestId":"request_536cac45-cb3d-49a1-be19-f19126b4b75c","epoch":9,"label":"Request request_536cac45-cb3d-49a1-be19-f19126b4b75c"},{"checkpointId":"4918e05b-be74-407c-b642-ae77e2aa60fa","requestId":"request_536cac45-cb3d-49a1-be19-f19126b4b75c","undoStopId":"a40e2e4c-e7ad-469a-9676-5283e037facc","epoch":10,"label":"Request request_536cac45-cb3d-49a1-be19-f19126b4b75c - Stop a40e2e4c-e7ad-469a-9676-5283e037facc"},{"checkpointId":"c0122ebc-0999-4b69-9de7-7d67fd26af6e","requestId":"request_e598acc8-299f-4518-b0c7-516287d739ea","epoch":13,"label":"Request request_e598acc8-299f-4518-b0c7-516287d739ea"}],"currentEpoch":14,"fileBaselines":[["file:///c%3A/Servers/job-apply-express/prisma/schema.prisma::request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80",{"uri":{"$mid":1,"fsPath":"c:\\Servers\\job-apply-express\\prisma\\schema.prisma","_sep":1,"external":"file:///c%3A/Servers/job-apply-express/prisma/schema.prisma","path":"/C:/Servers/job-apply-express/prisma/schema.prisma","scheme":"file"},"requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","content":"generator client {\r\n  provider = \"prisma-client-js\"\r\n  output   = \"../generated/prisma\"\r\n}\r\n\r\ndatasource db {\r\n  provider = \"mysql\"\r\n  url      = env(\"DATABASE_URL\")\r\n}\r\n\r\nmodel applications {\r\n  id                   Int      @id @default(autoincrement())\r\n  profile_id           Int\r\n  company_name         String   @db.VarChar(255)\r\n  job_title            String   @db.VarChar(255)\r\n  url                  String   @db.VarChar(1000)\r\n  salary               String?  @db.VarChar(255)\r\n  job_description      String?  @db.Text\r\n  conflicts            Int?     @default(0) @db.TinyInt\r\n  onsite_required      Int?     @default(0) @db.TinyInt\r\n  clearance_required   Int?     @default(0) @db.TinyInt\r\n  cert_required        Int?     @default(0) @db.TinyInt\r\n  resume_filename      String   @db.VarChar(255)\r\n  description_filename String   @db.VarChar(255)\r\n  created_date         DateTime @default(now()) @db.DateTime(0)\r\n  profile              profiles @relation(fields: [profile_id], references: [id])\r\n\r\n  @@index([profile_id], map: \"applications_profile_id_fkey\")\r\n}\r\n\r\nmodel profiles {\r\n  id                Int            @id @default(autoincrement())\r\n  profile_uuid      String         @db.VarChar(255)\r\n  firstname         String?        @db.VarChar(255)\r\n  lastname          String?        @db.VarChar(255)\r\n  openaiapi_key     String?        @db.VarChar(255)\r\n  prompt            String?        @db.Text\r\n  filename_template String         @db.VarChar(255)\r\n  template_file     String         @db.VarChar(255)\r\n  back_days         Int?           @default(100)\r\n  created_date      DateTime       @default(now()) @db.DateTime(0)\r\n  applications      applications[]\r\n  assignments       assignments[]\r\n}\r\n\r\nmodel users {\r\n  id       Int    @id @default(autoincrement())\r\n  email    String @unique\r\n  password String\r\n  allow    Boolean @default(false)\r\n  assignments assignments[]\r\n}\r\n\r\nmodel assignments {\r\n  id         Int      @id @default(autoincrement())\r\n  user_id    Int\r\n  profile_id Int\r\n\r\n  user    users    @relation(fields: [user_id], references: [id])\r\n  profile profiles @relation(fields: [profile_id], references: [id])\r\n\r\n  @@index([user_id], map: \"assignments_user_id_fkey\")\r\n  @@index([profile_id], map: \"assignments_profile_id_fkey\")\r\n}\r\n\r\n","epoch":5,"telemetryInfo":{}}],["file:///c%3A/Servers/job-apply-express/controller.js::request_536cac45-cb3d-49a1-be19-f19126b4b75c",{"uri":{"$mid":1,"fsPath":"c:\\Servers\\job-apply-express\\controller.js","_sep":1,"external":"file:///c%3A/Servers/job-apply-express/controller.js","path":"/C:/Servers/job-apply-express/controller.js","scheme":"file"},"requestId":"request_536cac45-cb3d-49a1-be19-f19126b4b75c","content":"const { PrismaClient } = require('./generated/prisma');\r\nconst { generateResume, log } = require('./services');\r\nconst jwt = require('jsonwebtoken');\r\nconst bcrypt = require('bcryptjs');\r\n\r\nconst prisma = new PrismaClient()\r\nrequire('dotenv').config();\r\nconst SECRET = process.env.SECRET;\r\n\r\nprisma.$connect()\r\n    .then(() => log('success', 'Prisma client connected successfully'))\r\n    .catch((error) => {\r\n        log('error', 'Failed to connect Prisma client:', error);\r\n        process.exit(1);\r\n    });\r\n\r\nexports.auth = async (req, res, next) => {\r\n    const token = req.headers.authorization?.split(' ')[1];\r\n    if (!token) return res.sendStatus(401);\r\n    try {\r\n        const decoded = jwt.verify(token, SECRET);\r\n        const user = await prisma.users.findUnique({ where: { email: decoded.email } });\r\n        if (!user || user.allow !== true) return res.sendStatus(403);\r\n        req.user = user;\r\n        next();\r\n    } catch {\r\n        res.sendStatus(403);\r\n    }\r\n};\r\n\r\nexports.getCompanyNames = async (req, res) => {\r\n    const profileKey = req.header('profileKey');\r\n    if (!profileKey) {\r\n        log('error', 'Missing profileKey header');\r\n        return res.status(400).json({ message: 'Missing profileKey header' });\r\n    }\r\n\r\n    try {\r\n        // Find the profile by profile_uuid\r\n        const profile = await prisma.profiles.findFirst({\r\n            where: { profile_uuid: profileKey }\r\n        });\r\n\r\n        if (!profile) {\r\n            log('error', 'Profile not found for profileKey:', profileKey);\r\n            return res.status(404).json({ message: 'Profile not found' });\r\n        }\r\n\r\n        const backDays = profile.back_days || 100;\r\n        const backDay = new Date();\r\n        if (backDays < 0) {\r\n            backDay.setDate(2000, 0, 1); // January 1, 2000\r\n        } else {\r\n            backDay.setDate(backDay.getDate() - backDays);\r\n        }\r\n\r\n        const companies = await prisma.applications.findMany({\r\n            where: {\r\n                profile_id: profile.id,\r\n                created_date: {\r\n                    gte: backDay\r\n                }\r\n            },\r\n            select: { company_name: true },\r\n            distinct: ['company_name']\r\n        });\r\n\r\n        const companyNames = companies.map(c => c.company_name);\r\n\r\n        res.json({\r\n            profileName: `${profile.firstname} ${profile.lastname}`,\r\n            companyNames\r\n        });\r\n        log('success', 'Fetched company names for profile:', profileKey);\r\n    } catch (error) {\r\n        res.status(500).json({ message: 'Internal server error' });\r\n        log('error', 'Error fetching company names:', error);\r\n    }\r\n};\r\n\r\nexports.requestResume = async (req, res) => {\r\n    const profileKey = req.header('profileKey');\r\n    if (!profileKey) {\r\n        log('error', 'Missing profileKey header');\r\n        return res.status(400).json({ message: 'Missing profileKey header' });\r\n    }\r\n\r\n    try {\r\n        // Find the profile by profile_uuid\r\n        const profile = await prisma.profiles.findFirst({\r\n            where: { profile_uuid: profileKey }\r\n        });\r\n\r\n        if (!profile) {\r\n            log('error', 'Profile not found for profileKey:', profileKey);\r\n            return res.status(404).json({ message: 'Profile not found' });\r\n        }\r\n\r\n        if (req.body.company_name) {\r\n            const backDays = profile.back_days || 100;\r\n            const backDay = new Date();\r\n            if (backDays < 0) {\r\n                backDay.setDate(2000, 0, 1); // January 1, 2000\r\n            } else {\r\n                backDay.setDate(backDay.getDate() - backDays);\r\n            }\r\n\r\n            const existingApplication = await prisma.applications.findFirst({\r\n                where: {\r\n                    profile_id: profile.id,\r\n                    company_name: req.body.company_name,\r\n                    created_date: {\r\n                        gte: backDay\r\n                    }\r\n                }\r\n            });\r\n\r\n            if (existingApplication) {\r\n                const message = `Application for \"${req.body.company_name}\" exists in last ${backDays} days`;\r\n                log('warning', message);\r\n                return res.status(409).json({\r\n                    status: 'warning',\r\n                    message: message\r\n                });\r\n            }\r\n        }\r\n\r\n        const jd = (req.body.jobTitle ? ('Job Title: ' + req.body.jobTitle + '\\n\\n') : '') +\r\n            (req.body.company_name ? ('Company Name: ' + req.body.company_name + '\\n\\n') : '') +\r\n            (req.body.jobDetails ? ('Job Details: ' + req.body.jobDetails + '\\n\\n') : '') +\r\n            (req.body.url ? ('Job Url: ' + req.body.url + '\\n\\n') : '') +\r\n            'Job Description: ' + req.body.jobDescription + '\\n\\n';\r\n\r\n        const result = await generateResume({ ...req.body, profile_id: profile.id, ...profile, jobDescription: jd });\r\n        // if (result.status === 'warning') {\r\n        //     log('warning', result.message);\r\n        //     return res.status(409).json(result);\r\n        // }\r\n        const { filename, resumeData } = result;\r\n        let status, message;\r\n\r\n        if (resumeData.conflicts || !resumeData.fullyRemoteRole || resumeData.clearanceRequired || resumeData.certificationRequired) {\r\n            message = `${resumeData.company_name}\\n` +\r\n                `No-Conflict : ${resumeData.conflicts ? '❌' : '✅'}\\n` +\r\n                `Full-Remote : ${resumeData.fullyRemoteRole ? '✅' : '❌'}\\n` +\r\n                `No-Clearance : ${resumeData.clearanceRequired ? '❌' : '✅'}\\n` +\r\n                `No-Certification : ${resumeData.certificationRequired ? '❌' : '✅'}\\n`\r\n            status = 'warning';\r\n        } else {\r\n            message = 'Resume generated successfully';\r\n            status = 'success';\r\n        }\r\n\r\n        res.json({\r\n            status, message, filename, resumeData,\r\n            resumeUrl: `/${profile.id}/cv/${filename}.docx`,\r\n            jdUrl: `/${profile.id}/jd/${filename}.txt`,\r\n        });\r\n\r\n        try {\r\n            // Save application to DB\r\n            const app = await prisma.applications.create({\r\n                data: {\r\n                    profile_id: profile.id,\r\n                    company_name: resumeData.company_name,\r\n                    job_title: resumeData.roleTitle,\r\n                    url: req.body.url || '',\r\n                    salary: resumeData.salary || null,\r\n                    job_description: jd,\r\n                    conflicts: resumeData.conflicts ? 1 : 0,\r\n                    onsite_required: resumeData.fullyRemoteRole === false ? 1 : 0,\r\n                    clearance_required: resumeData.clearanceRequired ? 1 : 0,\r\n                    cert_required: resumeData.certificationRequired ? 1 : 0,\r\n                    resume_filename: filename + '.docx',\r\n                    description_filename: filename + '.txt'\r\n                }\r\n            })\r\n            log('success', 'Application saved:', app.id);\r\n        } catch (error) {\r\n            log('error', 'Error saving application to DB:', error);\r\n        }\r\n    } catch (error) {\r\n        log('error', 'Error processing request:', error);\r\n        return res.status(500).json({ message: error.message || 'Internal server error' });\r\n    }\r\n}\r\n\r\nexports.register = async (req, res) => {\r\n    const { email, password } = req.body;\r\n    const existing = await prisma.users.findUnique({ where: { email } });\r\n    if (existing) return res.status(400).json({ message: 'User exists' });\r\n\r\n    const hash = await bcrypt.hash(password, 10);\r\n    await prisma.users.create({ data: { email, password: hash } });\r\n    res.json({ message: 'Registration successful' });\r\n};\r\n\r\nexports.login = async (req, res) => {\r\n    const { email, password } = req.body;\r\n    const user = await prisma.users.findUnique({ where: { email } });\r\n    if (!user) return res.status(400).json({ message: 'No user' });\r\n\r\n    if (user.allow !== true) {\r\n        return res.status(403).json({ message: 'User not allowed' });\r\n    }\r\n\r\n    const valid = await bcrypt.compare(password, user.password);\r\n    if (!valid) return res.status(401).json({ message: 'Wrong password' });\r\n\r\n    const token = jwt.sign({ email: user.email }, SECRET);\r\n    res.json({ token });\r\n};\r\n\r\nexports.getProfiles = async (req, res) => {\r\n    const profiles = await prisma.profiles.findMany({\r\n        where: {\r\n            assignments: {\r\n                some: { user_id: req.user?.id }\r\n            }\r\n        },\r\n        select: { id: true, firstname: true, lastname: true, profile_uuid: true },\r\n        orderBy: { firstname: 'asc' },\r\n    });\r\n    res.json({ profiles });\r\n};\r\n\r\nexports.getApplications = async (req, res) => {\r\n    const { profile_id, back_days = 100 } = req.query;\r\n    let filters = {};\r\n    if (profile_id) filters.profile_id = Number(profile_id);\r\n    if (back_days) {\r\n        const date = new Date();\r\n        date.setDate(date.getDate() - Number(back_days));\r\n        filters.created_date = { gte: date };\r\n    }\r\n\r\n    const applications = await prisma.applications.findMany({\r\n        where: filters,\r\n        select: {\r\n            id: true,\r\n            company_name: true,\r\n            job_title: true,\r\n            url: true,\r\n            salary: true,\r\n            conflicts: true,\r\n            onsite_required: true,\r\n            clearance_required: true,\r\n            cert_required: true,\r\n            resume_filename: true,\r\n            description_filename: true,\r\n            created_date: true,\r\n            profile_id: true,\r\n        },\r\n        orderBy: { created_date: 'desc' }\r\n    });\r\n\r\n    res.json({ applications });\r\n}","epoch":11,"telemetryInfo":{}}]],"operations":[{"type":"textEdit","uri":{"$mid":1,"fsPath":"c:\\Servers\\job-apply-express\\prisma\\schema.prisma","_sep":1,"external":"file:///c%3A/Servers/job-apply-express/prisma/schema.prisma","path":"/C:/Servers/job-apply-express/prisma/schema.prisma","scheme":"file"},"requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","epoch":6,"edits":[{"text":"model applications {\r\n  id                   Int      @id @default(autoincrement())\r\n  profile_id           Int\r\n  applied_by           Int?\r\n  company_name         String   @db.VarChar(255)\r\n  job_title            String   @db.VarChar(255)\r\n  url                  String   @db.VarChar(1000)\r\n  salary               String?  @db.VarChar(255)\r\n  job_description      String?  @db.Text\r\n  conflicts            Int?     @default(0) @db.TinyInt\r\n  onsite_required      Int?     @default(0) @db.TinyInt\r\n  clearance_required   Int?     @default(0) @db.TinyInt\r\n  cert_required        Int?     @default(0) @db.TinyInt\r\n  resume_filename      String   @db.VarChar(255)\r\n  description_filename String   @db.VarChar(255)\r\n  created_date         DateTime @default(now()) @db.DateTime(0)\r\n  profile              profiles @relation(fields: [profile_id], references: [id])\r\n  user                 users?   @relation(fields: [applied_by], references: [id])\r\n\r\n  @@index([profile_id], map: \"applications_profile_id_fkey\")\r\n  @@index([applied_by], map: \"applications_applied_by_fkey\")\r\n}","range":{"startLineNumber":11,"startColumn":1,"endLineNumber":29,"endColumn":2}}]},{"type":"textEdit","uri":{"$mid":1,"fsPath":"c:\\Servers\\job-apply-express\\prisma\\schema.prisma","_sep":1,"external":"file:///c%3A/Servers/job-apply-express/prisma/schema.prisma","path":"/C:/Servers/job-apply-express/prisma/schema.prisma","scheme":"file"},"requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","epoch":8,"edits":[{"text":"model users {\r\n  id           Int            @id @default(autoincrement())\r\n  email        String         @unique\r\n  password     String\r\n  allow        Boolean        @default(false)\r\n  assignments  assignments[]\r\n  applications applications[]\r\n}","range":{"startLineNumber":49,"startColumn":1,"endLineNumber":55,"endColumn":2}}]},{"type":"textEdit","uri":{"$mid":1,"fsPath":"c:\\Servers\\job-apply-express\\controller.js","_sep":1,"external":"file:///c%3A/Servers/job-apply-express/controller.js","path":"/C:/Servers/job-apply-express/controller.js","scheme":"file"},"requestId":"request_536cac45-cb3d-49a1-be19-f19126b4b75c","epoch":12,"edits":[{"text":"        try {\r\n            // Save application to DB\r\n            const app = await prisma.applications.create({\r\n                data: {\r\n                    profile_id: profile.id,\r\n                    applied_by: req.user?.id || null,\r\n                    company_name: resumeData.company_name,\r\n                    job_title: resumeData.roleTitle,\r\n                    url: req.body.url || '',\r\n                    salary: resumeData.salary || null,\r\n                    job_description: jd,\r\n                    conflicts: resumeData.conflicts ? 1 : 0,\r\n                    onsite_required: resumeData.fullyRemoteRole === false ? 1 : 0,\r\n                    clearance_required: resumeData.clearanceRequired ? 1 : 0,\r\n                    cert_required: resumeData.certificationRequired ? 1 : 0,\r\n                    resume_filename: filename + '.docx',\r\n                    description_filename: filename + '.txt'\r\n                }\r\n            })\r\n            log('success', 'Application saved:', app.id);\r\n        } catch (error) {\r\n            log('error', 'Error saving application to DB:', error);\r\n        }","range":{"startLineNumber":160,"startColumn":1,"endLineNumber":181,"endColumn":10}}]}],"epochCounter":14},"recentSnapshot":{"entries":[{"resource":"file:///c%3A/Servers/job-apply-express/prisma/schema.prisma","languageId":"plaintext","originalHash":"8b27576","currentHash":"8b27576","state":1,"snapshotUri":"chat-editing-snapshot-text-model:/c%3A/Servers/job-apply-express/prisma/schema.prisma?%7B%22sessionId%22%3A%224f975fee-1714-4d0d-ae43-e289ce8dde95%22%2C%22requestId%22%3A%22%22%2C%22undoStop%22%3A%22%22%7D","telemetryInfo":{"requestId":"request_989c3253-1fe1-41ad-b8d6-9b1c7d307a80","agentId":"github.copilot.editsAgent","modelId":"copilot/claude-sonnet-4.5","modeId":"agent"}},{"resource":"file:///c%3A/Servers/job-apply-express/controller.js","languageId":"javascript","originalHash":"49c1a53","currentHash":"49c1a53","state":1,"snapshotUri":"chat-editing-snapshot-text-model:/c%3A/Servers/job-apply-express/controller.js?%7B%22sessionId%22%3A%224f975fee-1714-4d0d-ae43-e289ce8dde95%22%2C%22requestId%22%3A%22%22%2C%22undoStop%22%3A%22%22%7D","telemetryInfo":{"requestId":"request_536cac45-cb3d-49a1-be19-f19126b4b75c","agentId":"github.copilot.editsAgent","modelId":"copilot/claude-sonnet-4.5","modeId":"agent"}}]}}