EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
index.js
Go to the documentation of this file.
1// ======================================================================
2// Backend API Entry — Digital Ocean App Platform Version
3// v1.0.3 - Add manual sync API endpoint
4// ======================================================================
5
6const express = require("express");
7const cors = require("cors");
8const cookieParser = require("cookie-parser");
9const path = require("path");
10require("dotenv").config();
11
12const app = express();
13
14// CORS
15// ----------------------------------------------------
16const allowedOrigins = [
17 "http://localhost:5173",
18 "http://localhost:5174",
19 "http://192.168.0.188:5173",
20 "http://192.168.122.1:5173",
21 "https://demo.everydayoffice.au",
22 "https://everydayoffice.au",
23 "https://everydaytech.au",
24 "https://www.everydaytech.au",
25 "https://dashboard.everydaytech.au",
26 "https://api.everydaytech.au",
27 "https://rmm.everydaytech.au",
28 "https://psa.everydaytech.au",
29 "https://rmm-psa-dashboard-5jyun.ondigitalocean.app",
30 "https://rmm-psa-backend-t9f7k.ondigitalocean.app",
31];
32
33app.use(cors({
34 origin: (origin, callback) => {
35 // Allow requests with no origin (mobile apps, curl, etc.)
36 if (!origin) return callback(null, true);
37
38 // Check if origin is in allowed list
39 if (allowedOrigins.includes(origin)) return callback(null, true);
40
41 // Allow any subdomain of everydaytech.au (safe regex test)
42 try {
43 if (/^https:\/\/([a-z0-9-]+\.)?everydaytech\.au$/i.test(origin)) {
44 return callback(null, true);
45 }
46
47 // Allow any subdomain of everydayoffice.au
48 if (/^https:\/\/([a-z0-9-]+\.)?everydayoffice\.au$/i.test(origin)) {
49 return callback(null, true);
50 }
51 } catch (err) {
52 console.error('CORS regex error:', err);
53 }
54
55 console.warn(`CORS blocked origin: ${origin}`);
56 return callback(new Error("Not allowed by CORS"));
57 },
58 credentials: true,
59}));
60
61app.use(cookieParser());
62app.use(express.json());
63
64// =========================================
65// ROOT ROUTES
66// =========================================
67app.get("/", (req, res) => {
68 res.json({
69 name: "RMM PSA Backend API",
70 version: "1.0.0",
71 status: "online",
72 endpoints: {
73 health: "/api/health",
74 docs: "https://github.com/Independent-Business-Group/rmm-psa-backend"
75 }
76 });
77});
78
79app.get("/api", (req, res) => {
80 res.json({
81 message: "RMM PSA API",
82 endpoints: [
83 "/api/health",
84 "/api/auth/login",
85 "/api/tickets",
86 "/api/customers",
87 "/api/agents",
88 "/api/monitoring"
89 ]
90 });
91});
92
93// =========================================
94// PUBLIC BUILDER REDIRECTS (DEPRECATED - KEPT FOR BACKWARD COMPATIBILITY)
95// =========================================
96// Old agents may still reference these URLs
97// Redirect to download status page
98app.get("/builder/dl/:os", (req, res) => {
99 const job = req.query.job;
100 if (!job) return res.status(410).json({ error: "Builder service discontinued. Agent v1 no longer supported. Please use Agent v3 (MeshCentral)." });
101 return res.status(410).json({ error: "Builder service discontinued. Agent v1 no longer supported. Please use Agent v3 (MeshCentral)." });
102});
103
104app.get("/builder/status", (req, res) => {
105 const job = req.query.job;
106 if (!job) return res.status(410).json({ error: "Builder service discontinued. Agent v1 no longer supported. Please use Agent v3 (MeshCentral)." });
107 return res.status(410).json({ error: "Builder service discontinued. Agent v1 no longer supported. Please use Agent v3 (MeshCentral)." });
108});
109
110
111// ----------------------------------------------------
112// Auth Bootstrap
113// ----------------------------------------------------
114try {
115 const { ensureAuthExtras } = require("./services/initAuth");
116 ensureAuthExtras().catch(e => console.error("Auth bootstrap failed:", e));
117} catch (e) {
118 console.error("initAuth load failed:", e);
119}
120
121
122// ----------------------------------------------------
123// Middleware
124// ----------------------------------------------------
125const authenticateToken = require("./middleware/auth");
126const { setTenantContext } = require("./middleware/tenant");
127
128
129// ----------------------------------------------------
130// CORE API ROUTES
131/**
132 *
133 * @param path
134 * @param mod
135 */
136function debugRoute(path, mod) {
137 const type = typeof mod;
138 const isRouter = mod && mod.stack && Array.isArray(mod.stack);
139 console.log(`[DEBUG] Mounting ${path}: type=${type}, isRouter=${isRouter}`);
140}
141
142const routes = [
143 ["/api", require("./routes/ai")],
144 ["/api", require("./routes/downloadJob")],
145 ["/api/download", require("./routes/download")],
146 ["/api/users", require("./routes/users")],
147 ["/api/tickets", require("./routes/tickets")],
148 ["/api/customers", require("./routes/customers")],
149 ["/api/invoices", require("./routes/invoices")],
150 ["/api/invoices", require("./routes/invoice-payments")], // Stripe payment processing for invoices
151 ["/api/invoices", require("./routes/invoice-pdf")], // PDF generation for invoices
152 ["/api/invoices", require("./routes/refunds")], // Invoice refunds (tenant admin only)
153 ["/api/public", require("./routes/public-invoice-payment")], // Public invoice payment (no auth - token-based)
154 ["/api/public/pay", require("./routes/invoice-pdf")], // Public invoice PDF (no auth)
155 ["/api/stripe/webhook", require("./routes/stripe-webhook")], // Stripe webhooks (no auth - signature verified)
156 ["/api/stripe", require("./routes/stripe-connect")], // Stripe Connect dashboard integration (authenticated)
157 ["/api/notifications", require("./routes/notifications")],
158 ["/api/slack", require("./routes/slack")],
159 ["/api/documents", require("./routes/documents")],
160 ["/api/monitoring", require("./routes/monitoring")],
161 ["/api/reports", require("./routes/reports")],
162 ["/api/settings", require("./routes/settings")],
163 ["/api/auth", require("./routes/auth")],
164 ["/api/products", require("./routes/products")],
165 ["/api/purchase-orders", require("./routes/purchaseOrders")],
166 ["/api/contracts", require("./routes/contracts")],
167 ["/api/domains", require("./routes/domains")],
168 ["/api/domain-requests", require("./routes/domainRequests")],
169 ["/api/cloudflare-dns", require("./routes/cloudflare-dns")], // Cloudflare DNS Management
170 ["/api/office365", require("./routes/office365")], // Office 365 / Pax8 Integration
171 ["/api/hosting", require("./routes/hosting")], // DigitalOcean Hosting Management
172 ["/api/wordpress", require("./routes/wordpress")], // WordPress Site Creation
173 ["/api/customers", require("./routes/customerMerge")], // Customer Merge Tool
174 ["/api/dashboard", require("./routes/dashboard")],
175 ["/api/tenants", require("./routes/tenants")],
176 ["/api/tenants", require("./routes/tenantIcon")],
177 ["/api/integrations", require("./routes/integrations")],
178 // ["/api/plugins", require("./routes/plugins").router], // Disabled: requires agent directory not in backend-only deployment
179 ["/api/rds", require("./routes/rds")],
180 ["/api/scripts", require("./routes/scripts")],
181 ["/api/email", require("./routes/emailAutomation")],
182 ["/api/email", require("./routes/emailTracking")], // Email tracking pixel and analytics
183 ["/api/orders", require("./routes/orders")],
184 ["/api/github", require("./routes/github-webhook").router],
185 // Agent routes (grouped) - order matters: specific routes before general ones
186 ["/api/agent/v2", require("./routes/agentV2")],
187 ["/api/agent", require("./routes/agentVersion")],
188 ["/api/agent", require("./routes/agentManifest")],
189 ["/api/agent", require("./routes/agent")],
190 ["/api/agent/config", require("./routes/config")],
191 // ["/api/agent/plugins", require("./routes/plugins").router], // Disabled: requires agent directory not in backend-only deployment
192 ["/api/agent/commands", require("./routes/commands")],
193 ["/api/agent/contracts", require("./routes/contracts")],
194 ["/api/agent/cert", require("./routes/agentCert")],
195 ["/api/agent/update", require("./routes/agentUpdate")],
196 ["/api/agent/files", require("./routes/agentFiles")],
197 // ["/api/releases", require("./routes/agent-download")], // DEPRECATED: Agent v2 downloads directly from GitHub (public repo)
198];
199for (const [path, mod] of routes) {
200 debugRoute(path, mod);
201 app.use(path, mod);
202}
203// ...existing code...
204
205
206// ----------------------------------------------------
207// BUILDER LAYER (Correct Placement + Crash Protection)
208// ----------------------------------------------------
209// DEPRECATED: Builder service no longer used (Agent v1 discontinued)
210// Agent v2 is deprecated, Agent v3 uses MeshCentral's native agent
211// Kept for backward compatibility only
212try {
213 const downloadJobRoute = require("./routes/downloadJob");
214 app.use("/api", downloadJobRoute);
215 console.log("⚠️ Builder downloadJob route loaded (DEPRECATED - Agent v1 discontinued)");
216} catch (e) {
217 console.error("❌ Failed loading /routes/downloadJob:", e);
218}
219
220try {
221 const builderRoutes = require("./builder/builderRoutes");
222 app.use("/api/builder", builderRoutes);
223 console.log("⚠️ Builder routes loaded (DEPRECATED - Agent v1 discontinued)");
224} catch (e) {
225 console.error("❌ Failed loading builderRoutes:", e);
226}
227
228
229// ----------------------------------------------------
230// XERO PLUGIN ROUTES
231// ----------------------------------------------------
232const { xeroRouter } = require('./plugins/xero');
233app.use('/api/xero', xeroRouter);
234
235// ----------------------------------------------------
236// REMOTE ACCESS ROUTING
237// ----------------------------------------------------
238// Note: Guacamole and RustDesk routes removed - MeshCentral-only architecture
239
240// ----------------------------------------------------
241// MESHCENTRAL ROUTES
242// ----------------------------------------------------
243try {
244 const meshcentralRoutes = require('./routes/meshcentral');
245 app.use('/api/meshcentral', meshcentralRoutes);
246 console.log('✅ MeshCentral routes loaded');
247} catch (e) {
248 console.error('❌ Failed loading meshcentral routes:', e.message);
249 console.error('Stack:', e.stack);
250}
251
252// ----------------------------------------------------
253// HEALTH CHECK ROUTES (llama.cpp, Redis, DB)
254// ----------------------------------------------------
255try {
256 const healthRoutes = require('./routes/health');
257 app.use('/api', healthRoutes);
258 console.log('✅ Health check routes loaded');
259} catch (e) {
260 console.error('❌ Failed loading health routes:', e);
261}
262
263// ----------------------------------------------------
264// WEBHOOK ROUTES (MeshCentral events, etc.)
265// ----------------------------------------------------
266try {
267 const webhookRoutes = require('./routes/webhooks');
268 app.use('/api/webhooks', webhookRoutes);
269 console.log('✅ Webhook routes loaded');
270} catch (e) {
271 console.error('❌ Failed loading webhook routes:', e);
272}
273
274// ----------------------------------------------------
275// SYNC ROUTES (Manual sync triggers)
276// ----------------------------------------------------
277try {
278 const syncRoutes = require('./routes/sync');
279 app.use('/api/sync', syncRoutes);
280 console.log('✅ Sync routes loaded');
281} catch (e) {
282 console.error('❌ Failed loading sync routes:', e);
283}
284
285// ----------------------------------------------------
286// AGENT V3 ROUTES (MeshCentral's native agent - ACTIVE)
287// ----------------------------------------------------
288// Agent v3 = MeshCentral's white-labeled agent (no custom builds)
289// Provides download endpoints for Windows/Linux/macOS installers
290try {
291 const agentV3Routes = require('./routes/agent-v3');
292 app.use('/api/agent-v3', agentV3Routes);
293 console.log('✅ Agent v3 routes loaded (MeshCentral native agent)');
294} catch (e) {
295 console.error('❌ Failed loading agent-v3 routes:', e);
296}
297
298// ----------------------------------------------------
299// TENANT MESHCENTRAL SYNC (Auto-create device groups)
300// ----------------------------------------------------
301try {
302 const tenantSyncRoutes = require('./routes/tenant-meshcentral-sync');
303 app.use('/api/tenant-meshcentral-sync', tenantSyncRoutes);
304 console.log('✅ Tenant MeshCentral sync routes loaded');
305} catch (e) {
306 console.error('❌ Failed loading tenant-meshcentral-sync routes:', e);
307}
308
309// ----------------------------------------------------
310// DEBUG ENDPOINTS (Development/troubleshooting)
311// ----------------------------------------------------
312try {
313 const debugRoutes = require('./routes/debug');
314 app.use('/api/debug', debugRoutes);
315 console.log('✅ Debug routes loaded');
316} catch (e) {
317 console.error('❌ Failed loading debug routes:', e);
318}
319
320// ----------------------------------------------------
321// CUSTOMER PORTAL ROUTES (Customer self-service)
322// ----------------------------------------------------
323try {
324 const portalAuthRoutes = require('./routes/portalAuth');
325 app.use('/api/portal/auth', portalAuthRoutes);
326 console.log('✅ Customer portal auth routes loaded');
327} catch (e) {
328 console.error('❌ Failed loading portal auth routes:', e);
329}
330
331try {
332 const portalCustomerRoutes = require('./routes/portalCustomer');
333 app.use('/api/portal/customer', portalCustomerRoutes);
334 console.log('✅ Customer portal data routes loaded');
335} catch (e) {
336 console.error('❌ Failed loading portal customer routes:', e);
337}
338
339// ----------------------------------------------------
340// CUSTOMER PORTAL USER MANAGEMENT (Admin interface)
341// ----------------------------------------------------
342try {
343 const customerUsersRoutes = require('./routes/customerUsers');
344 app.use('/api/customer-users', customerUsersRoutes);
345 console.log('✅ Customer users admin routes loaded');
346} catch (e) {
347 console.error('❌ Failed loading customer users routes:', e);
348}
349
350// ----------------------------------------------------
351// STATIC FILES FOR AGENT DOWNLOADS
352// ----------------------------------------------------
353const publicPath = path.join(__dirname, 'public');
354app.use('/agent', express.static(publicPath, {
355 setHeaders: (res, filepath) => {
356 // Set proper content type for executables and scripts
357 if (filepath.endsWith('.exe')) {
358 res.setHeader('Content-Type', 'application/octet-stream');
359 res.setHeader('Content-Disposition', `attachment; filename="${path.basename(filepath)}"`);
360 } else if (filepath.endsWith('.sh')) {
361 res.setHeader('Content-Type', 'application/x-sh');
362 } else if (filepath.endsWith('.bat')) {
363 res.setHeader('Content-Type', 'application/x-msdos-program');
364 } else if (filepath.endsWith('.ps1')) {
365 res.setHeader('Content-Type', 'application/octet-stream');
366 }
367 // Prevent caching for downloads
368 if (filepath.includes('/downloads/')) {
369 res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
370 }
371 }
372}));
373console.log('✅ Static files mounted at /agent/ from', publicPath);
374
375
376
377// ----------------------------------------------------
378// TEST PAGES
379// ----------------------------------------------------
380// Guacamole test pages removed - MeshCentral-only architecture
381
382// ----------------------------------------------------
383// HEALTH CHECK
384// ----------------------------------------------------
385app.get("/api/health", (req, res) => res.status(200).send("OK"));
386
387
388// ----------------------------------------------------
389// WEBSOCKETS
390// ----------------------------------------------------
391const http = require("http");
392const server = http.createServer(app);
393const wsManager = require("./services/websocketManager");
394
395wsManager.init(server);
396
397setInterval(() => {
398 const stats = wsManager.getStats();
399 console.log(
400 `[WS Monitor] Agents: ${stats.agents}, Dashboards: ${stats.dashboards}, Subscriptions: ${stats.subscriptions} @ ${new Date().toISOString()}`
401 );
402}, 30000);
403
404
405// ----------------------------------------------------
406// START SERVER
407// ----------------------------------------------------
408const PORT = process.env.PORT || 3000;
409
410// Start pluginBuilder Redis worker on backend startup - DISABLED (requires agent directory)
411// try {
412// const { spawn } = require('child_process');
413// const pluginBuilderPath = path.resolve(__dirname, 'services', 'pluginBuilder.js');
414// const pluginBuilder = spawn('node', [pluginBuilderPath], {
415// stdio: ['ignore', 'inherit', 'inherit'],
416// detached: true
417// });
418// console.log(`✅ pluginBuilder Redis worker started (PID: ${pluginBuilder.pid})`);
419// } catch (err) {
420// console.error('❌ Failed to start pluginBuilder worker:', err.message);
421// }
422
423// RustDesk sync service removed - MeshCentral-only architecture
424
425// Start agentDeleteWorker on backend startup
426try {
427 const { spawn } = require('child_process');
428 const agentDeleteWorkerPath = path.resolve(__dirname, 'services', 'agentDeleteWorker.js');
429 const agentDeleteWorker = spawn('node', [agentDeleteWorkerPath], {
430 stdio: ['ignore', 'inherit', 'inherit'],
431 detached: true
432 });
433 console.log(`✅ agentDeleteWorker started (PID: ${agentDeleteWorker.pid})`);
434} catch (err) {
435 console.error('❌ Failed to start agentDeleteWorker:', err.message);
436}
437
438// Start all other workers on backend startup
439const workerFiles = [
440 'emailPollWorker.js',
441 'emailToTicketWorker.js',
442 'bullmqWorker.js',
443 'aiWorker.js',
444 'contractBillingWorker.js',
445 'digitalOceanSyncWorker.js',
446 'domainSyncWorker.js',
447 'dnsRecordsSyncWorker.js',
448 'pax8SyncWorker.js',
449 'workers/agentMonitor.js',
450 'workers/meshcentralSync.js'
451 // '../neto-xero/workers/netoSyncWorker.js' // Disabled: not in backend-only deployment
452];
453
454// Start MeshCentral Device Auto-Sync (self-initializing)
455// This worker polls MeshCentral and auto-creates agents in our DB
456try {
457 require('./workers/meshcentral-device-sync');
458 console.log('✅ MeshCentral device auto-sync worker loaded');
459} catch (err) {
460 console.error('❌ Failed to load MeshCentral device sync worker:', err.message);
461}
462for (const file of workerFiles) {
463 try {
464 const { spawn } = require('child_process');
465 const workerPath = path.resolve(__dirname, file);
466 const worker = spawn('node', [workerPath], {
467 stdio: ['ignore', 'inherit', 'inherit'],
468 detached: true
469 });
470 console.log(`✅ ${file} started (PID: ${worker.pid})`);
471 } catch (err) {
472 console.error(`❌ Failed to start ${file}:`, err.message);
473 }
474}
475
476// ----------------------------------------------------
477// TENANT-MESHCENTRAL STARTUP SYNC
478// Auto-creates device groups for all tenants
479// ----------------------------------------------------
480try {
481 const { initStartupSync, runStartupSync } = require('./startup/tenant-meshcentral-sync');
482
483 // Run on startup
484 initStartupSync();
485 console.log('✅ Tenant-MeshCentral startup sync initialized');
486
487 // Periodic sync every hour to ensure consistency
488 const SYNC_INTERVAL = parseInt(process.env.TENANT_SYNC_INTERVAL || '3600000'); // 1 hour default
489 if (SYNC_INTERVAL > 0) {
490 setInterval(() => {
491 console.log('\n⏰ Running scheduled tenant-mesh sync...');
492 runStartupSync().catch(err => {
493 console.error('❌ Scheduled sync error:', err.message);
494 });
495 }, SYNC_INTERVAL);
496 console.log(`✅ Periodic sync scheduled every ${SYNC_INTERVAL / 60000} minutes`);
497 }
498} catch (err) {
499 console.error('❌ Failed to initialize tenant-meshcentral sync:', err.message);
500}
501
502server.listen(PORT, () => {
503 console.log(`✅ Backend API online (HTTP + WS) on port ${PORT}`);
504});