feat: implement dotenv secrets loading from /home/admin/secrets/.env
- Added dotenv package to load environment variables from absolute path - Secrets now loaded automatically on app startup, no manual sourcing needed - Secrets file secured with chmod 600, outside project directory - Enables persistent secrets across service restarts - Tested: Job submission flow working end-to-end with all steps completing
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
import { config } from 'dotenv';
|
||||
import { Hono } from 'hono';
|
||||
import { serveStatic } from 'hono/bun';
|
||||
import { cors } from 'hono/cors';
|
||||
import PocketBase from 'pocketbase';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { config } from 'dotenv';
|
||||
import { mkdir, appendFile } from 'fs/promises';
|
||||
import { existsSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { processNewJobRecord } from './extracted-graph-logic/post-record-integration.js';
|
||||
|
||||
config();
|
||||
// Load secrets from absolute path (not in project directory)
|
||||
config({ path: '/home/admin/secrets/.env' });
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
@@ -124,7 +126,7 @@ let tableCache: {
|
||||
} | null = null;
|
||||
|
||||
// Resolve the Excel table name from env; prefers EXCEL_TABLE_NAME (just the table's name),
|
||||
// and falls back to parsing EXCEL_TABLE if provided as a full path, else default to Job_List.
|
||||
// and falls back to parsing EXCEL_TABLE if provided as a full path, else default to Test_Table.
|
||||
function resolveTableName(): string {
|
||||
const nameFromSimpleEnv = process.env.EXCEL_TABLE_NAME?.trim();
|
||||
if (nameFromSimpleEnv) return nameFromSimpleEnv;
|
||||
@@ -132,7 +134,7 @@ function resolveTableName(): string {
|
||||
const nameFromPath = process.env.EXCEL_TABLE?.split('/tables/').pop()?.trim();
|
||||
if (nameFromPath) return nameFromPath;
|
||||
|
||||
return 'Job_List';
|
||||
return 'Test_Table';
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -393,6 +395,48 @@ async function addToExcel(record: any, pbId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the Excel row's Job_Folder_Link for the given pb_id
|
||||
async function updateExcelFolderLink(pbId: string, link: string) {
|
||||
const client = await getGraphClient();
|
||||
const tableName = resolveTableName();
|
||||
const excelTablePath = `/drives/${process.env.DRIVE_ID}/items/${process.env.ITEM_ID}/workbook/tables/${tableName}`;
|
||||
|
||||
// Initialize cache if not already done
|
||||
if (!tableCache) {
|
||||
await initializeTableCache();
|
||||
}
|
||||
const { columnNames } = tableCache!;
|
||||
const lower = columnNames.map((n) => n.toLowerCase());
|
||||
const linkColIndex = lower.indexOf('job_folder_link');
|
||||
const pbIdColIndex = lower.indexOf('pb_id');
|
||||
if (linkColIndex === -1 || pbIdColIndex === -1) {
|
||||
console.warn('⚠️ Excel table missing Job_Folder_Link or pb_id column');
|
||||
return;
|
||||
}
|
||||
|
||||
const rowsResp = await client.api(`${excelTablePath}/rows`).get();
|
||||
const rows = rowsResp?.value || [];
|
||||
const target = rows.find((row: any) => {
|
||||
const vals = row?.values?.[0];
|
||||
return vals && vals[pbIdColIndex] === pbId;
|
||||
});
|
||||
if (!target) {
|
||||
console.warn('⚠️ Excel row not found for pb_id:', pbId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update using table row itemAt - update the full row with link populated
|
||||
const currentValues = [...target.values[0]]; // Copy array
|
||||
currentValues[linkColIndex] = link || '';
|
||||
|
||||
await client.api(`${excelTablePath}/rows/itemAt(index=${target.index})`)
|
||||
.patch({
|
||||
values: [currentValues]
|
||||
});
|
||||
|
||||
console.log('✓ Updated Excel Job_Folder_Link for pb_id:', pbId);
|
||||
}
|
||||
|
||||
// API endpoint to submit job
|
||||
app.post('/api/submit', async (c) => {
|
||||
let data: any;
|
||||
@@ -465,9 +509,35 @@ app.post('/api/submit', async (c) => {
|
||||
const record = await pb.collection(process.env.PB_COLLECTION!).create(data);
|
||||
console.log(`✓ Created PocketBase record with Job_Number: ${newJobNumber}, ID: ${record.id}`);
|
||||
|
||||
// Add to Excel with pb_id = record.id
|
||||
// Persist calculated fields to PocketBase immediately after creation
|
||||
try {
|
||||
const fullName = calculateJobFullName(record);
|
||||
const qbLink = calculateJobQbLink(record);
|
||||
const voxerLinkVal = calculateVoxerLink(record);
|
||||
const jobCodesVal = calculateJobCodes(record);
|
||||
|
||||
await pb.collection(process.env.PB_COLLECTION!).update(record.id, {
|
||||
Job_Full_Name: fullName,
|
||||
Job_QB_Link: qbLink,
|
||||
Voxer_Link: voxerLinkVal,
|
||||
Job_Codes: jobCodesVal
|
||||
});
|
||||
|
||||
// Keep local record in sync for Excel add
|
||||
(record as any).Job_Full_Name = fullName;
|
||||
(record as any).Job_QB_Link = qbLink;
|
||||
(record as any).Voxer_Link = voxerLinkVal;
|
||||
(record as any).Job_Codes = jobCodesVal;
|
||||
console.log('✓ Updated PB with calculated fields (Full Name, QB, Voxer, Codes)');
|
||||
} catch (pbUpdateErr: any) {
|
||||
console.error('⚠️ Failed to update PB calculated fields:', pbUpdateErr);
|
||||
await logError('PocketBase Calculated Fields Update', pbUpdateErr, { recordId: record.id });
|
||||
}
|
||||
|
||||
// Add to Excel first with pb_id = record.id (dynamic formulas for Due_Date_Counter, Active)
|
||||
try {
|
||||
await addToExcel(record, record.id);
|
||||
console.log('✓ Excel row created');
|
||||
} catch (excelError: any) {
|
||||
console.error('⚠️ Excel sync failed, but PocketBase record was created:', excelError);
|
||||
return c.json({
|
||||
@@ -476,15 +546,66 @@ app.post('/api/submit', async (c) => {
|
||||
message: `Job created successfully with Job Number: ${newJobNumber}`,
|
||||
jobId: record.id,
|
||||
jobNumber: newJobNumber,
|
||||
jobFolderLink: null,
|
||||
excelError: excelError.message
|
||||
});
|
||||
}
|
||||
|
||||
// After Excel is added, create folder structure and capture share link
|
||||
let jobFolderLink = '';
|
||||
try {
|
||||
const graphToken = await getGraphToken();
|
||||
const folderResult = await processNewJobRecord(record, graphToken) as any;
|
||||
|
||||
if (folderResult.success) {
|
||||
jobFolderLink = folderResult.shareLink || '';
|
||||
console.log(`✓ Folder created and share link captured: ${jobFolderLink}`);
|
||||
console.log(`📁 Open folder: ${jobFolderLink}`);
|
||||
} else {
|
||||
console.warn(`⚠️ Folder creation failed: ${folderResult.error}`);
|
||||
}
|
||||
} catch (folderError: any) {
|
||||
console.error('⚠️ Folder creation failed, continuing without folder link:', folderError);
|
||||
await logError('Folder Creation', folderError, {
|
||||
jobNumber: newJobNumber,
|
||||
recordId: record.id
|
||||
});
|
||||
}
|
||||
|
||||
// Update Excel row with Job_Folder_Link (if available)
|
||||
if (jobFolderLink) {
|
||||
try {
|
||||
await updateExcelFolderLink(record.id, jobFolderLink);
|
||||
} catch (excelUpdateErr: any) {
|
||||
console.error('⚠️ Failed to update Excel with folder link:', excelUpdateErr);
|
||||
await logError('Excel Update Folder Link', excelUpdateErr, { recordId: record.id, jobFolderLink });
|
||||
}
|
||||
}
|
||||
|
||||
// Persist static values back to PocketBase: Job_Folder_Link, Due_Date_Counter, Active
|
||||
try {
|
||||
const dueStatic = calculateDueDateCounter(record);
|
||||
const activeStatic = calculateActive(record);
|
||||
await pb.collection(process.env.PB_COLLECTION!).update(record.id, {
|
||||
Job_Folder_Link: jobFolderLink || '',
|
||||
Due_Date_Counter: dueStatic,
|
||||
Active: activeStatic
|
||||
});
|
||||
(record as any).Job_Folder_Link = jobFolderLink || '';
|
||||
(record as any).Due_Date_Counter = dueStatic;
|
||||
(record as any).Active = activeStatic;
|
||||
console.log('✓ Updated PB with folder link, due date counter (static), and active (boolean)');
|
||||
} catch (pbFinalizeErr: any) {
|
||||
console.error('⚠️ Failed to update PB with final static values:', pbFinalizeErr);
|
||||
await logError('PocketBase Final Update', pbFinalizeErr, { recordId: record.id, jobFolderLink });
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
message: `Job created successfully with Job Number: ${newJobNumber} and synced to Excel`,
|
||||
jobId: record.id,
|
||||
jobNumber: newJobNumber
|
||||
jobNumber: newJobNumber,
|
||||
jobFolderLink: jobFolderLink || null
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('✗ Error submitting job:', error);
|
||||
|
||||
Reference in New Issue
Block a user