login
This commit is contained in:
@ -8,112 +8,137 @@
|
||||
{
|
||||
"id": "swing-residential",
|
||||
"name": "Residential Swing Gate Operator",
|
||||
"model": "SG-1000",
|
||||
"model": "LA500",
|
||||
"category": "swing",
|
||||
"description": "12V DC swing gate operator for residential gates up to 14 ft",
|
||||
"description": "24V DC Swing Gate Linear Operator ",
|
||||
"basePrice": 2495,
|
||||
"image": "swing",
|
||||
"imageFile": "liftmaster-la500-bundle.jpg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-swing", "name": "Operator Unit (SG-1000)", "qty": 2, "unitPrice": 1247.50 },
|
||||
{ "id": "bracket-kit", "name": "Gate Bracket Kit", "qty": 2, "unitPrice": 85 },
|
||||
{ "id": "photo-eye", "name": "Photo Eye Kit", "qty": 1, "unitPrice": 125 },
|
||||
{ "id": "hinges", "name": "Gate Hinges (Heavy Duty)", "qty": 4, "unitPrice": 45 }
|
||||
{ "id": "op-unit-swing", "name": "Operator Unit (LA500)", "qty": 2, "unitPrice": 1247.50 },
|
||||
{ "id": "mounting-post", "name": "Mounting Post", "qty": 1, "unitPrice": 85 },
|
||||
{ "id": "prep-cost", "name": "In House Prep Cost", "qty": 1, "unitPrice": 150 },
|
||||
{ "id": "shipping", "name": "Shipping Cost", "qty": 1, "unitPrice": 200 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "swing-commercial",
|
||||
"name": "Commercial Swing Gate Operator",
|
||||
"model": "SG-2000",
|
||||
"model": "CSW24UL",
|
||||
"category": "swing",
|
||||
"description": "24V DC heavy-duty swing operator for commercial gates up to 24 ft",
|
||||
"description": "24V DC Heavy Duty Swing Gate Operator",
|
||||
"basePrice": 3995,
|
||||
"image": "swing",
|
||||
"imageFile": "liftmaster-csw24.jpg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-swing-hd", "name": "Heavy Duty Operator Unit (SG-2000)", "qty": 2, "unitPrice": 1997.50 },
|
||||
{ "id": "bracket-kit-hd", "name": "Commercial Bracket Kit", "qty": 2, "unitPrice": 145 },
|
||||
{ "id": "photo-eye", "name": "Photo Eye Kit", "qty": 1, "unitPrice": 125 },
|
||||
{ "id": "hinges-hd", "name": "Commercial Gate Hinges", "qty": 4, "unitPrice": 75 },
|
||||
{ "id": "warning-light", "name": "Strobe Warning Light", "qty": 1, "unitPrice": 195 }
|
||||
{ "id": "op-unit-swing-hd", "name": "Heavy Duty Operator Unit (CSW24UL)", "qty": 2, "unitPrice": 1997.50 },
|
||||
{ "id": "mount-pad", "name": "Mounting Pad", "qty": 2, "unitPrice": 145 },
|
||||
{ "id": "prep-cost", "name": "In House Prep Cost", "qty": 1, "unitPrice": 150 },
|
||||
{ "id": "shipping", "name": "Shipping Cost", "qty": 1, "unitPrice": 200 },
|
||||
{ "id": "mounting-post", "name": "Mounting Post", "qty": 2, "unitPrice": 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "slide-residential",
|
||||
"name": "Light Commercial Slide Gate Operator",
|
||||
"model": "SL-1500",
|
||||
"model": "CSL24UL",
|
||||
"category": "slide",
|
||||
"description": "12V DC slide operator for residential and light commercial gates up to 20 ft",
|
||||
"description": "24V DC Light Duty Slide Gate Operator",
|
||||
"basePrice": 2495,
|
||||
"image": "slide",
|
||||
"imageFile": "liftmaster-csl24.jpg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-slide-lc", "name": "Slide Operator Unit (SL-1500)", "qty": 1, "unitPrice": 2495 },
|
||||
{ "id": "rack-kit-lc", "name": "Slide Gate Rack Kit (10ft sections)", "qty": 2, "unitPrice": 175 },
|
||||
{ "id": "photo-eye", "name": "Photo Eye Kit", "qty": 1, "unitPrice": 125 }
|
||||
{ "id": "op-unit-slide-lc", "name": "Slide Operator Unit (CSL24UL)", "qty": 1, "unitPrice": 2495 },
|
||||
{ "id": "mount-pad", "name": "Mounting Pad", "qty": 2, "unitPrice": 145 },
|
||||
{ "id": "prep-cost", "name": "In House Prep Cost", "qty": 1, "unitPrice": 150 },
|
||||
{ "id": "shipping", "name": "Shipping Cost", "qty": 1, "unitPrice": 200 },
|
||||
{ "id": "mounting-post", "name": "Mounting Post", "qty": 2, "unitPrice": 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "slide-commercial",
|
||||
"name": "Commercial Slide Gate Operator",
|
||||
"model": "SL-3000",
|
||||
"model": "IHSL24UL",
|
||||
"category": "slide",
|
||||
"description": "24V DC slide gate operator for commercial gates up to 40 ft",
|
||||
"description": "24V DC Commercial Gate Operator",
|
||||
"basePrice": 3895,
|
||||
"image": "slide",
|
||||
"imageFile": "liftmaster-ihsl24ul.jpg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-slide", "name": "Heavy Duty Operator Unit (SL-3000)", "qty": 1, "unitPrice": 3895 },
|
||||
{ "id": "rack-kit", "name": "Slide Gate Rack Kit (10ft sections)", "qty": 4, "unitPrice": 175 },
|
||||
{ "id": "photo-eye", "name": "Photo Eye Kit", "qty": 1, "unitPrice": 125 },
|
||||
{ "id": "warning-light", "name": "Strobe Warning Light", "qty": 1, "unitPrice": 195 },
|
||||
{ "id": "sensor-loop", "name": "Vehicle Loop Sensor", "qty": 1, "unitPrice": 285 }
|
||||
{ "id": "op-unit-slide", "name": "Slide Operator Unit (IHSL24UL)", "qty": 1, "unitPrice": 3895 },
|
||||
{ "id": "prep-cost", "name": "In House Prep Cost", "qty": 1, "unitPrice": 150 },
|
||||
{ "id": "shipping", "name": "Shipping Cost", "qty": 1, "unitPrice": 200 },
|
||||
{ "id": "mounting-post", "name": "Mounting Post", "qty": 2, "unitPrice": 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "barrier-parking",
|
||||
"name": "Parking Barrier Gate Operator",
|
||||
"model": "BG-200",
|
||||
"name": "Barrier Gate Operator",
|
||||
"model": "MAT",
|
||||
"category": "barrier",
|
||||
"description": "120V AC barrier gate for parking lots and access control up to 20 ft",
|
||||
"description": "Mega Arm Tower Barrier Gate Operator",
|
||||
"basePrice": 3195,
|
||||
"image": "barrier",
|
||||
"imageFile": "Mat.jpeg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-barrier", "name": "Barrier Arm Assembly (BG-200)", "qty": 1, "unitPrice": 3195 },
|
||||
{ "id": "barrier-arm", "name": "Barrier Arm (8ft Aluminum)", "qty": 1, "unitPrice": 395 },
|
||||
{ "id": "loop-detector", "name": "Loop Detector Card", "qty": 1, "unitPrice": 345 },
|
||||
{ "id": "warning-light", "name": "LED Warning Light", "qty": 2, "unitPrice": 145 },
|
||||
{ "id": "signage", "name": "Gate Signage Kit", "qty": 1, "unitPrice": 120 }
|
||||
{ "id": "op-unit-barrier", "name": "Mega Arm Tower Barrier Gate Operator(MAT)", "qty": 1, "unitPrice": 3195 },
|
||||
{ "id": "barrier-arm", "name": "Barrier Arm (8ft Aluminum)", "qty": 1, "unitPrice": 395 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "barrier-heavy",
|
||||
"name": "Heavy Duty Barrier Gate Operator",
|
||||
"model": "BG-500",
|
||||
"name": "Techna Barrier Gate Operator",
|
||||
"model": "CBG24DC",
|
||||
"category": "barrier",
|
||||
"description": "230V AC heavy-duty barrier gate for industrial sites up to 30 ft",
|
||||
"description": "Commercial Barrier Gate operator",
|
||||
"basePrice": 5495,
|
||||
"image": "barrier",
|
||||
"imageFile": "techno.jpeg",
|
||||
"requiredParts": [
|
||||
{ "id": "op-unit-barrier-hd", "name": "Heavy Duty Barrier Assembly (BG-500)", "qty": 1, "unitPrice": 5495 },
|
||||
{ "id": "barrier-arm-hd", "name": "Barrier Arm (16ft Steel)", "qty": 1, "unitPrice": 695 },
|
||||
{ "id": "loop-detector", "name": "Loop Detector Card", "qty": 2, "unitPrice": 345 },
|
||||
{ "id": "warning-light-hd", "name": "LED Warning Light Kit", "qty": 2, "unitPrice": 195 },
|
||||
{ "id": "signage", "name": "Gate Signage Kit", "qty": 1, "unitPrice": 120 },
|
||||
{ "id": "safety-edge", "name": "Safety Edge Sensor", "qty": 1, "unitPrice": 295 }
|
||||
{ "id": "op-unit-barrier-hd", "name": "Techna Barrier Gate Operator (CBG24DC)", "qty": 1, "unitPrice": 5495 },
|
||||
{ "id": "barrier-arm-hd", "name": "Barrier Arm (16ft Steel)", "qty": 1, "unitPrice": 695 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tilt",
|
||||
"name": "Tilt Gate Operator",
|
||||
"model": "",
|
||||
"category": "tilt",
|
||||
"description": "Enter tilt gate operator details below",
|
||||
"basePrice": 0,
|
||||
"image": "tilt",
|
||||
"imageFile": "",
|
||||
"isCustom": true,
|
||||
"requiredParts": []
|
||||
}
|
||||
],
|
||||
"groundLoopStyles": [
|
||||
{ "id": "saw-cut", "name": "Saw-Cut Installation", "description": "Loop cut into existing pavement and sealed flush", "additionalCost": 0 },
|
||||
{ "id": "pave-over", "name": "Pave-Over Installation", "description": "Surface-mount loop installed on top of pavement with protective casing", "additionalCost": 195 }
|
||||
],
|
||||
"loopDetectors": [
|
||||
{ "id": "single", "name": "Single-Channel Loop Detector", "description": "Detects vehicle presence over the loop wire", "price": 250 }
|
||||
],
|
||||
"groundLoopSizes": [
|
||||
{ "id": "4x8", "name": "4' x 8'", "description": "Standard loop size for residential applications", "additionalCost": 0 },
|
||||
{ "id": "6x12", "name": "6' x 12'", "description": "Larger loop size for commercial or wide driveways", "additionalCost": 175 }
|
||||
],
|
||||
"groundLoopTypes": [
|
||||
{ "id": "interrupt", "name": "Interrupt Loop", "description": "Primary vehicle detection loop for gate activation", "price": 425 },
|
||||
{ "id": "shadow", "name": "Shadow Loop", "description": "Secondary safety loop located behind gate to prevent closure on vehicle", "price": 375 },
|
||||
{ "id": "exit", "name": "Exit Loop", "description": "Free exit loop on departure side for automatic exit detection", "price": 375 }
|
||||
],
|
||||
"remoteButtonOptions": [
|
||||
{ "id": "1", "name": "1-Button Remote", "price": 95 },
|
||||
{ "id": "2", "name": "2-Button Remote", "price": 125 },
|
||||
{ "id": "3", "name": "3-Button Remote", "price": 165 },
|
||||
{ "id": "4", "name": "4-Button Remote", "price": 195 }
|
||||
],
|
||||
"accessControl": [
|
||||
{ "id": "keypad", "name": "Digital Keypad", "model": "KP-200", "description": "Weatherproof digital keypad with backlit keys, 500 user codes", "price": 295 },
|
||||
{ "id": "card-reader", "name": "Proximity Card Reader", "model": "PR-500", "description": "125kHz proximity card reader with 1000 card capacity", "price": 445 },
|
||||
{ "id": "intercom", "name": "Audio/Video Intercom", "model": "AV-700", "description": "2-wire audio/video intercom with color camera", "price": 895 },
|
||||
{ "id": "remote-kit", "name": "Remote Control Kit", "model": "RC-4", "description": "4-button remote control with 2 remotes, rolling code", "price": 195 },
|
||||
{ "id": "remote-kit", "name": "Remote Control", "model": "", "description": "Remote controls with rolling code technology", "price": 0 },
|
||||
{ "id": "solar-kit", "name": "Solar Panel Kit", "model": "SP-100", "description": "100W solar panel with charge controller and battery", "price": 695 },
|
||||
{ "id": "gsm-controller", "name": "GSM Cellular Controller", "model": "GSM-4G", "description": "4G cellular controller with app, no phone line needed", "price": 595 }
|
||||
]
|
||||
|
||||
@ -1,35 +1,98 @@
|
||||
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
|
||||
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const session = require('express-session');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
app.use(cors());
|
||||
app.use(cors({
|
||||
origin: process.env.NODE_ENV === 'production' ? false : 'http://localhost:5173',
|
||||
credentials: true,
|
||||
}));
|
||||
app.use(express.json());
|
||||
|
||||
app.use(session({
|
||||
secret: process.env.APP_SESSION_SECRET || 'dev-secret-change-in-production',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax',
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
},
|
||||
}));
|
||||
|
||||
const pricingData = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, 'data', 'pricing.json'), 'utf-8')
|
||||
);
|
||||
|
||||
app.get('/api/pricing', (req, res) => {
|
||||
// Auth middleware
|
||||
function requireAuth(req, res, next) {
|
||||
if (req.session && req.session.authenticated) {
|
||||
return next();
|
||||
}
|
||||
res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
// Login
|
||||
app.post('/api/login', (req, res) => {
|
||||
const { password } = req.body;
|
||||
if (password === process.env.APP_PASSWORD) {
|
||||
req.session.authenticated = true;
|
||||
return res.json({ success: true });
|
||||
}
|
||||
res.status(401).json({ error: 'Invalid password' });
|
||||
});
|
||||
|
||||
// Logout
|
||||
app.post('/api/logout', (req, res) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to logout' });
|
||||
}
|
||||
res.clearCookie('connect.sid');
|
||||
res.json({ success: true });
|
||||
});
|
||||
});
|
||||
|
||||
// Check auth status
|
||||
app.get('/api/auth/status', (req, res) => {
|
||||
res.json({ authenticated: !!(req.session && req.session.authenticated) });
|
||||
});
|
||||
|
||||
// Protected API routes
|
||||
app.get('/api/pricing', requireAuth, (req, res) => {
|
||||
res.json(pricingData);
|
||||
});
|
||||
|
||||
app.get('/api/pricing/:category', (req, res) => {
|
||||
app.get('/api/pricing/:category', requireAuth, (req, res) => {
|
||||
const { category } = req.params;
|
||||
const validCategories = ['operators', 'groundLoopStyles', 'groundLoopTypes', 'accessControl'];
|
||||
const validCategories = ['operators', 'groundLoopStyles', 'groundLoopSizes', 'loopDetectors', 'groundLoopTypes', 'accessControl', 'remoteButtonOptions'];
|
||||
if (validCategories.includes(category)) {
|
||||
return res.json(pricingData[category]);
|
||||
}
|
||||
res.status(400).json({ error: `Invalid category. Use: ${validCategories.join(', ')}` });
|
||||
});
|
||||
|
||||
// Serve static files with auth protection
|
||||
const clientDist = path.join(__dirname, '..', 'client', 'dist');
|
||||
if (fs.existsSync(clientDist)) {
|
||||
app.use(express.static(clientDist));
|
||||
app.get('*', (req, res) => {
|
||||
// Allow unauthenticated access to login page assets and auth endpoints
|
||||
app.use('/assets', express.static(path.join(clientDist, 'assets')));
|
||||
|
||||
// Login page is always accessible
|
||||
app.get('/login', (req, res) => {
|
||||
res.sendFile(path.join(clientDist, 'index.html'));
|
||||
});
|
||||
|
||||
// All other routes require auth
|
||||
app.get('*', requireAuth, (req, res) => {
|
||||
res.sendFile(path.join(clientDist, 'index.html'));
|
||||
});
|
||||
}
|
||||
@ -47,5 +110,6 @@ function getLocalIP() {
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
const ip = getLocalIP();
|
||||
console.log(`\n Local: http://localhost:${PORT}`);
|
||||
console.log(` Network: http://${ip}:${PORT}\n`);
|
||||
console.log(` Network: http://${ip}:${PORT}`);
|
||||
console.log(` Login: http://localhost:${PORT}/login\n`);
|
||||
});
|
||||
|
||||
69
server/package-lock.json
generated
69
server/package-lock.json
generated
@ -9,7 +9,9 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2"
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
@ -174,6 +176,18 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.4.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
|
||||
"integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@ -294,6 +308,29 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz",
|
||||
"integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "~0.7.2",
|
||||
"cookie-signature": "~1.0.7",
|
||||
"debug": "~2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.1.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "~5.2.1",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
||||
@ -576,6 +613,15 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@ -619,6 +665,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
@ -823,6 +878,18 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"random-bytes": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2"
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.19.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user