diff --git a/README.md b/README.md
index 93f088a53..0c7b4b49f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,199 @@
-# Buffer-7.0
-The themes for Buffer 7.0 are -
+#Team Name : AlgoX
+#Team Member
+Manasi Bhole
+Mahi Kala
+#Domain : Cybersecurity
-1. Enterprise Systems & Process Optimization
-2. GreenTech
-3. Cybersecurity and Digital Defense
-4. Open Innovation
+#Description of problem
+
+Video Link : https://drive.google.com/file/d/1KvEpx6jZH8djLlb5u_jpGuaZaTpfUbMu/view?usp=sharing
+# Smart-Cyber-Defense-System
+# 🛡️ SCDS — Smart Cybersecurity Detection System
+
+> A real-time, graph-based cybersecurity threat detection platform that combines intelligent password security analysis, network intrusion detection, and a fusion threat engine to automatically identify, score, and isolate suspicious users.
+
+---
+
+## 📌 Problem Statement
+
+In modern digital systems, cyberattacks such as brute-force login attempts, credential stuffing, and unauthorized geographic access are increasingly common and difficult to detect in real time. Traditional security systems rely on static rules and manual monitoring, which fail to adapt to evolving attack patterns.
+
+**SCDS** addresses this by building an intelligent, self-learning detection system that:
+- Evaluates password strength and vulnerability in real time
+- Monitors network activity for anomalous behavior using graph-based node modeling
+- Combines multiple risk signals into a unified threat score using a Fusion Threat Engine
+- Automatically isolates high-risk nodes and blocks further access
+- Learns and stores recurring attack patterns for future threat intelligence
+
+The system provides a live dashboard for security operators to monitor users, threat scores, suspicious nodes, and system logs — enabling proactive threat response rather than reactive incident handling.
+
+---
+
+## 🧠 Data Structures Used
+
+### 1. Graph (Nodes and Edges)
+- **Used in:** Network Intrusion Detection module
+- **Tables:** `nodes`, `edges`, `suspicious_nodes`
+- Each user session is represented as a **node** in the network graph. Connections between users (shared IPs, locations) are represented as **edges**.
+- Anomalous nodes (high failed logins, location jumps, excessive requests) are flagged and stored as suspicious nodes.
+- This graph structure allows the system to detect **lateral movement** and **clustered attack patterns** across multiple users.
+
+### 2. Hash Map (Dictionary / Key-Value Store)
+- **Used in:** Password Analyzer service (`passwordAnalyzer.js`)
+- A **dictionary array** (hash-mapped lookup) stores common weak passwords (`password`, `123456`, `admin`, etc.).
+- Password input is checked against this dictionary in O(1) average time using `.includes()` on the pre-loaded array.
+- The `attack_patterns` table functions as a persistent key-value store where the **pattern string is the key** and `occurrence_count` is the value, incremented on each match.
+
+### 3. Weighted Scoring Vector (Feature Vector)
+- **Used in:** Fusion Threat Engine (`fusionEngine.js`)
+- The system builds a **two-dimensional feature vector** `[passwordRisk, networkRisk]` for each user session.
+- A **weighted linear combination** is applied:
+ ```
+ ThreatScore = (passwordRisk × 0.4) + (networkRisk × 0.6)
+ ```
+- This vector-based scoring allows the system to combine heterogeneous risk signals into a single comparable scalar value for threshold-based decision making.
+
+### 4. Queue (FIFO Log Stream)
+- **Used in:** `logs` table and dashboard live feed
+- System events (registrations, scans, isolations) are appended in **insertion order** and retrieved in reverse-chronological order, simulating a FIFO log queue.
+- The dashboard displays the 15 most recent log entries, functioning as a **bounded queue** with automatic overflow trimming via SQL `LIMIT`.
+
+### 5. Threshold-Based Decision Tree
+- **Used in:** Fusion Engine + Isolation System
+- A simple **decision tree** structure governs isolation logic:
+ ```
+ ThreatScore < 40 → SAFE
+ 40 ≤ Score < 70 → SUSPICIOUS
+ Score ≥ 70 → ISOLATED → Block login + insert into isolated_nodes
+ ```
+- This tree is evaluated on every fusion call, with the outcome written to the `decisions` table.
+
+### 6. Relational Database Schema (Linked Tables)
+- **Used in:** MySQL (`scds_db`)
+- The 12-table schema forms a **relational graph** of foreign-key linked entities:
+ - `users` → `password_security`, `network_activity`, `nodes`, `fusion_scores`, `decisions`, `logs`
+ - `nodes` → `edges`, `suspicious_nodes`, `isolated_nodes`
+- Cascading deletes (`ON DELETE CASCADE`) maintain referential integrity across the graph when a user is removed.
+
+---
+
+## ⚙️ Tech Stack
+
+| Layer | Technology |
+|---|---|
+| Backend | Node.js, Express.js |
+| Frontend | HTML5, CSS3, Vanilla JavaScript |
+| Database | MySQL |
+| Charts | Chart.js |
+| Security | bcryptjs |
+| Environment | dotenv |
+| Dev Server | nodemon |
+
+---
+
+## 🗂️ Project Structure
+
+```
+scds/
+├── backend/
+│ ├── config/
+│ │ └── db.js # MySQL connection pool
+│ ├── routes/
+│ │ ├── auth.js # Register & login endpoints
+│ │ ├── password.js # Password analysis endpoint
+│ │ ├── network.js # Network scan endpoint
+│ │ ├── fusion.js # Threat fusion + isolation endpoint
+│ │ ├── dashboard.js # Dashboard stats endpoint
+│ │ └── patterns.js # Attack patterns endpoint
+│ ├── services/
+│ │ ├── passwordAnalyzer.js # Strength scoring, dictionary check
+│ │ ├── networkAnalyzer.js # Anomaly detection logic
+│ │ └── fusionEngine.js # Weighted threat score calculator
+│ └── server.js # Express app entry point
+├── frontend/
+│ ├── index.html # Main terminal (register/scan/analyze)
+│ ├── dashboard.html # Live monitoring dashboard
+│ ├── css/
+│ │ └── style.css # Dark cybersecurity theme
+│ └── js/
+│ ├── main.js # Terminal page logic
+│ └── dashboard.js # Dashboard charts and data
+├── .env # Environment variables (DB credentials)
+└── package.json # Node dependencies
+```
+
+---
+
+## 🚀 How to Run
+
+### Prerequisites
+- Node.js (v18+)
+- MySQL (v8+)
+- MySQL Workbench
+
+### Steps
+
+**1. Clone / set up the project folder**
+```bash
+cd scds
+npm install
+```
+
+**2. Configure environment**
+```
+# .env
+DB_HOST=localhost
+DB_USER=root
+DB_PASSWORD=your_mysql_password
+DB_NAME=scds_db
+PORT=3000
+```
+
+**3. Run the database schema in MySQL Workbench**
+- Open MySQL Workbench
+- Open a new query tab
+- Paste the full schema SQL and press `Ctrl + Shift + Enter`
+
+**4. Start the server**
+```bash
+npm run dev
+```
+
+**5. Open in browser**
+- Terminal: `http://localhost:3000`
+- Dashboard: `http://localhost:3000/dashboard`
+
+---
+
+## 🎯 Key Features
+
+- ✅ Real-time password strength scoring and dictionary attack detection
+- ✅ Brute-force cracking time estimation per password
+- ✅ Graph-based network node modeling with anomaly detection
+- ✅ Location jump detection (impossible travel alert)
+- ✅ Fusion Threat Engine with weighted multi-signal scoring
+- ✅ Automatic node isolation when threat score ≥ 70
+- ✅ Login blocking for isolated nodes
+- ✅ Self-learning attack pattern storage with occurrence tracking
+- ✅ Live dashboard with Chart.js visualizations (trend + distribution)
+- ✅ Severity-tagged system log feed
+- ✅ Dark cybersecurity UI with scanline effects and animated threat ring
+
+---
+
+## 🗃️ Database Tables
+
+| Table | Purpose |
+|---|---|
+| `users` | Stores registered user credentials |
+| `password_security` | Password strength scores and risk metrics |
+| `network_activity` | Raw network session data per user |
+| `nodes` | Graph nodes representing user sessions |
+| `edges` | Connections between nodes |
+| `suspicious_nodes` | Nodes flagged with anomaly reasons |
+| `attack_paths` | Full attack chain paths for forensics |
+| `fusion_scores` | Combined threat scores per user session |
+| `decisions` | Final SAFE / SUSPICIOUS / ISOLATED decisions |
+| `isolated_nodes` | Blocked nodes with isolation reason |
+| `logs` | Full system event log with severity |
+| `attack_patterns` | Self-learned patterns with occurrence count |
diff --git a/Team 38 - Smart Cyber Defense System/README.md b/Team 38 - Smart Cyber Defense System/README.md
new file mode 100644
index 000000000..7516733ec
--- /dev/null
+++ b/Team 38 - Smart Cyber Defense System/README.md
@@ -0,0 +1 @@
+Smart Cyber Defense System
diff --git a/backend/config/db.js b/backend/config/db.js
new file mode 100644
index 000000000..61e3f341a
--- /dev/null
+++ b/backend/config/db.js
@@ -0,0 +1,14 @@
+const mysql = require('mysql2');
+require('dotenv').config();
+
+const pool = mysql.createPool({
+ host: process.env.DB_HOST,
+ user: process.env.DB_USER,
+ password: process.env.DB_PASSWORD,
+ database: process.env.DB_NAME,
+ waitForConnections: true,
+ connectionLimit: 10,
+ queueLimit: 0
+});
+
+module.exports = pool.promise();
\ No newline at end of file
diff --git a/backend/routes/auth.js b/backend/routes/auth.js
new file mode 100644
index 000000000..ab882fa7d
--- /dev/null
+++ b/backend/routes/auth.js
@@ -0,0 +1,54 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+const bcrypt = require('bcryptjs');
+
+router.post('/register', async (req, res) => {
+ const { email, password } = req.body;
+ try {
+ const hashed = await bcrypt.hash(password, 10);
+ const [result] = await db.execute(
+ 'INSERT INTO users (email, password) VALUES (?, ?)',
+ [email, hashed]
+ );
+ res.json({ success: true, userId: result.insertId, message: 'User registered successfully' });
+ } catch (err) {
+ if (err.code === 'ER_DUP_ENTRY') {
+ return res.status(400).json({ success: false, message: 'Email already exists' });
+ }
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+router.post('/login', async (req, res) => {
+ const { email, password } = req.body;
+ try {
+ const [rows] = await db.execute('SELECT * FROM users WHERE email = ?', [email]);
+ if (rows.length === 0) return res.status(404).json({ success: false, message: 'User not found' });
+
+ const user = rows[0];
+ const valid = await bcrypt.compare(password, user.password);
+ if (!valid) return res.status(401).json({ success: false, message: 'Invalid password' });
+
+ const [isolated] = await db.execute(
+ `SELECT i.* FROM isolated_nodes i
+ JOIN nodes n ON i.node_id = n.id
+ WHERE n.user_id = ? ORDER BY i.isolated_at DESC LIMIT 1`,
+ [user.id]
+ );
+
+ if (isolated.length > 0) {
+ return res.status(403).json({
+ success: false,
+ blocked: true,
+ message: '🚨 Access Denied: Your node is ISOLATED due to high threat score.'
+ });
+ }
+
+ res.json({ success: true, userId: user.id, email: user.email });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/dashboard.js b/backend/routes/dashboard.js
new file mode 100644
index 000000000..72a435f2a
--- /dev/null
+++ b/backend/routes/dashboard.js
@@ -0,0 +1,45 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+
+router.get('/stats', async (req, res) => {
+ try {
+ const [[{ total_users }]] = await db.execute('SELECT COUNT(*) as total_users FROM users');
+ const [[{ isolated_count }]] = await db.execute('SELECT COUNT(*) as isolated_count FROM isolated_nodes');
+ const [[{ avg_threat }]] = await db.execute('SELECT AVG(total_threat_score) as avg_threat FROM fusion_scores');
+ const [[{ attack_count }]] = await db.execute('SELECT SUM(occurrence_count) as attack_count FROM attack_patterns');
+
+ const [recentUsers] = await db.execute(`
+ SELECT u.id, u.email, f.total_threat_score, f.password_risk, f.network_risk, d.status
+ FROM users u
+ LEFT JOIN fusion_scores f ON u.id = f.user_id
+ LEFT JOIN decisions d ON u.id = d.user_id
+ ORDER BY f.created_at DESC LIMIT 10
+ `);
+
+ const [suspiciousNodes] = await db.execute(`
+ SELECT sn.*, n.node_label, n.risk_status, u.email
+ FROM suspicious_nodes sn
+ JOIN nodes n ON sn.node_id = n.id
+ JOIN users u ON n.user_id = u.id
+ ORDER BY sn.detected_at DESC LIMIT 10
+ `);
+
+ const [attackPatterns] = await db.execute('SELECT * FROM attack_patterns ORDER BY occurrence_count DESC LIMIT 5');
+ const [logs] = await db.execute('SELECT l.*, u.email FROM logs l JOIN users u ON l.user_id = u.id ORDER BY l.created_at DESC LIMIT 15');
+ const [threatTrend] = await db.execute(`
+ SELECT DATE(created_at) as date, AVG(total_threat_score) as avg_score
+ FROM fusion_scores GROUP BY DATE(created_at) ORDER BY date DESC LIMIT 7
+ `);
+
+ res.json({
+ stats: { total_users, isolated_count, avg_threat: Math.round(avg_threat || 0), attack_count: attack_count || 0 },
+ recentUsers, suspiciousNodes, attackPatterns, logs,
+ threatTrend: threatTrend.reverse()
+ });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/fusion.js b/backend/routes/fusion.js
new file mode 100644
index 000000000..c6474c834
--- /dev/null
+++ b/backend/routes/fusion.js
@@ -0,0 +1,46 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+const { calculateFusionScore } = require('../services/fusionEngine');
+
+router.post('/evaluate', async (req, res) => {
+ const { userId, passwordRisk, networkRisk, nodeId } = req.body;
+ try {
+ const { total_threat_score, status } = calculateFusionScore(passwordRisk, networkRisk);
+
+ await db.execute(
+ 'INSERT INTO fusion_scores (user_id, password_risk, network_risk, total_threat_score) VALUES (?, ?, ?, ?)',
+ [userId, passwordRisk, networkRisk, total_threat_score]
+ );
+
+ await db.execute(
+ 'INSERT INTO decisions (user_id, threat_score, status) VALUES (?, ?, ?)',
+ [userId, total_threat_score, status]
+ );
+
+ if (status === 'ISOLATED') {
+ await db.execute(
+ 'INSERT INTO isolated_nodes (node_id, reason) VALUES (?, ?)',
+ [nodeId, `Threat score ${total_threat_score} exceeded isolation threshold of 70`]
+ );
+ await db.execute(
+ 'INSERT INTO logs (user_id, action, severity) VALUES (?, ?, ?)',
+ [userId, `Node ISOLATED - Threat Score: ${total_threat_score}`, 'CRITICAL']
+ );
+
+ const patternKey = `PWD:${passwordRisk > 60 ? 'HIGH' : 'LOW'}_NET:${networkRisk > 60 ? 'HIGH' : 'LOW'}`;
+ const [existing] = await db.execute('SELECT id FROM attack_patterns WHERE pattern = ?', [patternKey]);
+ if (existing.length > 0) {
+ await db.execute('UPDATE attack_patterns SET occurrence_count = occurrence_count + 1, last_seen = NOW() WHERE pattern = ?', [patternKey]);
+ } else {
+ await db.execute('INSERT INTO attack_patterns (pattern, occurrence_count) VALUES (?, 1)', [patternKey]);
+ }
+ }
+
+ res.json({ success: true, total_threat_score, status });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/network.js b/backend/routes/network.js
new file mode 100644
index 000000000..de3e6c4be
--- /dev/null
+++ b/backend/routes/network.js
@@ -0,0 +1,35 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+const { analyzeNetwork } = require('../services/networkAnalyzer');
+
+router.post('/analyze', async (req, res) => {
+ const { userId, ip_address, requests_per_minute, failed_logins, connection_degree, login_hour, current_location, previous_location, location_jump } = req.body;
+ try {
+ const { network_risk, anomalies } = analyzeNetwork({ requests_per_minute, failed_logins, connection_degree, login_hour, current_location, previous_location, location_jump });
+
+ await db.execute(
+ 'INSERT INTO network_activity (user_id, ip_address, requests_per_minute, failed_logins, connection_degree, login_hour, current_location, previous_location, location_jump) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ [userId, ip_address, requests_per_minute, failed_logins, connection_degree, login_hour, current_location, previous_location, location_jump ? 1 : 0]
+ );
+
+ const [nodeResult] = await db.execute(
+ 'INSERT INTO nodes (user_id, node_label, risk_status) VALUES (?, ?, ?)',
+ [userId, `Node-${userId}-${Date.now()}`, network_risk >= 70 ? 'HIGH' : network_risk >= 40 ? 'MEDIUM' : 'LOW']
+ );
+ const nodeId = nodeResult.insertId;
+
+ if (anomalies.length > 0) {
+ await db.execute(
+ 'INSERT INTO suspicious_nodes (node_id, reason, risk_score) VALUES (?, ?, ?)',
+ [nodeId, anomalies.join('; '), network_risk]
+ );
+ }
+
+ res.json({ success: true, network_risk, anomalies, nodeId });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/password.js b/backend/routes/password.js
new file mode 100644
index 000000000..c4db7b923
--- /dev/null
+++ b/backend/routes/password.js
@@ -0,0 +1,24 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+const { analyzePassword } = require('../services/passwordAnalyzer');
+
+router.post('/analyze', async (req, res) => {
+ const { userId, password } = req.body;
+ try {
+ const result = analyzePassword(password);
+ await db.execute(
+ 'INSERT INTO password_security (user_id, strength_score, dictionary_match, brute_force_time, password_risk) VALUES (?, ?, ?, ?, ?)',
+ [userId, result.strength_score, result.dictionary_match, result.brute_force_time, result.password_risk]
+ );
+ await db.execute(
+ 'INSERT INTO logs (user_id, action, severity) VALUES (?, ?, ?)',
+ [userId, `Password analyzed - Risk: ${result.password_risk}`, result.password_risk > 60 ? 'HIGH' : result.password_risk > 30 ? 'MEDIUM' : 'LOW']
+ );
+ res.json({ success: true, ...result });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/patterns.js b/backend/routes/patterns.js
new file mode 100644
index 000000000..17525e5f0
--- /dev/null
+++ b/backend/routes/patterns.js
@@ -0,0 +1,14 @@
+const express = require('express');
+const router = express.Router();
+const db = require('../config/db');
+
+router.get('/', async (req, res) => {
+ try {
+ const [patterns] = await db.execute('SELECT * FROM attack_patterns ORDER BY occurrence_count DESC');
+ res.json({ success: true, patterns });
+ } catch (err) {
+ res.status(500).json({ success: false, message: err.message });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/server.js b/backend/server.js
new file mode 100644
index 000000000..0a0f388f7
--- /dev/null
+++ b/backend/server.js
@@ -0,0 +1,22 @@
+const express = require('express');
+const cors = require('cors');
+const path = require('path');
+require('dotenv').config();
+
+const app = express();
+app.use(cors());
+app.use(express.json());
+app.use(express.static(path.join(__dirname, '../frontend')));
+
+app.use('/api/auth', require('./routes/auth'));
+app.use('/api/password', require('./routes/password'));
+app.use('/api/network', require('./routes/network'));
+app.use('/api/fusion', require('./routes/fusion'));
+app.use('/api/dashboard', require('./routes/dashboard'));
+app.use('/api/patterns', require('./routes/patterns'));
+
+app.get('/', (req, res) => res.sendFile(path.join(__dirname, '../frontend/index.html')));
+app.get('/dashboard', (req, res) => res.sendFile(path.join(__dirname, '../frontend/dashboard.html')));
+
+const PORT = process.env.PORT || 3000;
+app.listen(PORT, () => console.log(`🛡️ SCDS running on http://localhost:${PORT}`));
\ No newline at end of file
diff --git a/backend/services/fusionEngine.js b/backend/services/fusionEngine.js
new file mode 100644
index 000000000..e7e30bca8
--- /dev/null
+++ b/backend/services/fusionEngine.js
@@ -0,0 +1,9 @@
+function calculateFusionScore(passwordRisk, networkRisk) {
+ const total = Math.round((passwordRisk * 0.4) + (networkRisk * 0.6));
+ let status = 'SAFE';
+ if (total >= 70) status = 'ISOLATED';
+ else if (total >= 40) status = 'SUSPICIOUS';
+ return { total_threat_score: Math.min(total, 100), status };
+}
+
+module.exports = { calculateFusionScore };
\ No newline at end of file
diff --git a/backend/services/networkAnalyzer.js b/backend/services/networkAnalyzer.js
new file mode 100644
index 000000000..3e87053c4
--- /dev/null
+++ b/backend/services/networkAnalyzer.js
@@ -0,0 +1,42 @@
+function analyzeNetwork(activity) {
+ let riskScore = 0;
+ const anomalies = [];
+
+ if (activity.failed_logins >= 5) {
+ riskScore += 30;
+ anomalies.push('High failed login count');
+ } else if (activity.failed_logins >= 3) {
+ riskScore += 15;
+ anomalies.push('Moderate failed logins');
+ }
+
+ if (activity.requests_per_minute >= 100) {
+ riskScore += 25;
+ anomalies.push('High request rate - possible DDoS');
+ } else if (activity.requests_per_minute >= 50) {
+ riskScore += 10;
+ anomalies.push('Elevated request rate');
+ }
+
+ if (activity.location_jump) {
+ riskScore += 30;
+ anomalies.push(`Location jump: ${activity.previous_location} → ${activity.current_location}`);
+ }
+
+ if (activity.login_hour >= 0 && activity.login_hour <= 5) {
+ riskScore += 10;
+ anomalies.push('Unusual login hour (midnight-5AM)');
+ }
+
+ if (activity.connection_degree >= 15) {
+ riskScore += 5;
+ anomalies.push('High connection degree');
+ }
+
+ return {
+ network_risk: Math.min(riskScore, 100),
+ anomalies
+ };
+}
+
+module.exports = { analyzeNetwork };
\ No newline at end of file
diff --git a/backend/services/passwordAnalyzer.js b/backend/services/passwordAnalyzer.js
new file mode 100644
index 000000000..ec8001c9e
--- /dev/null
+++ b/backend/services/passwordAnalyzer.js
@@ -0,0 +1,53 @@
+const DICTIONARY = ['password', '123456', 'admin', 'qwerty', 'letmein', 'welcome', 'monkey', 'dragon', 'master', 'sunshine', 'abc123', 'iloveyou'];
+
+function analyzePassword(password) {
+ let score = 0;
+ const length = password.length;
+
+ if (length >= 8) score += 10;
+ if (length >= 12) score += 15;
+ if (length >= 16) score += 15;
+ if (/[A-Z]/.test(password)) score += 10;
+ if (/[a-z]/.test(password)) score += 10;
+ if (/[0-9]/.test(password)) score += 10;
+ if (/[^A-Za-z0-9]/.test(password)) score += 20;
+ if (length > 20) score += 10;
+
+ const dictionaryMatch = DICTIONARY.some(word => password.toLowerCase().includes(word));
+ if (dictionaryMatch) score = Math.max(0, score - 30);
+
+ const charsetSize = getCharsetSize(password);
+ const combinations = Math.pow(charsetSize, length);
+ const guessesPerSecond = 1e10;
+ const seconds = combinations / guessesPerSecond;
+ const bruteForceTime = formatTime(seconds);
+
+ const passwordRisk = Math.max(0, 100 - score);
+
+ return {
+ strength_score: Math.min(score, 100),
+ dictionary_match: dictionaryMatch,
+ brute_force_time: bruteForceTime,
+ password_risk: passwordRisk
+ };
+}
+
+function getCharsetSize(password) {
+ let size = 0;
+ if (/[a-z]/.test(password)) size += 26;
+ if (/[A-Z]/.test(password)) size += 26;
+ if (/[0-9]/.test(password)) size += 10;
+ if (/[^A-Za-z0-9]/.test(password)) size += 32;
+ return size || 26;
+}
+
+function formatTime(seconds) {
+ if (seconds < 60) return `${Math.round(seconds)} seconds`;
+ if (seconds < 3600) return `${Math.round(seconds / 60)} minutes`;
+ if (seconds < 86400) return `${Math.round(seconds / 3600)} hours`;
+ if (seconds < 31536000) return `${Math.round(seconds / 86400)} days`;
+ if (seconds < 3.154e9) return `${Math.round(seconds / 31536000)} years`;
+ return 'Centuries';
+}
+
+module.exports = { analyzePassword };
\ No newline at end of file
diff --git a/frontend/css/style.css b/frontend/css/style.css
new file mode 100644
index 000000000..6d670bc9d
--- /dev/null
+++ b/frontend/css/style.css
@@ -0,0 +1,642 @@
+@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Rajdhani:wght@300;400;500;600;700&family=Orbitron:wght@400;700;900&display=swap');
+
+:root {
+ --bg-primary: #020408;
+ --bg-secondary: #050c14;
+ --bg-card: #080f1a;
+ --bg-glass: rgba(0, 200, 255, 0.03);
+ --neon-cyan: #00c8ff;
+ --neon-green: #00ff88;
+ --neon-red: #ff2244;
+ --neon-orange: #ff6600;
+ --neon-purple: #9900ff;
+ --neon-yellow: #ffe000;
+ --text-primary: #c8e8ff;
+ --text-secondary: #5a8aaa;
+ --text-dim: #2a4a5a;
+ --border-glow: rgba(0, 200, 255, 0.15);
+ --border-bright: rgba(0, 200, 255, 0.4);
+ --font-mono: 'Share Tech Mono', monospace;
+ --font-display: 'Orbitron', monospace;
+ --font-body: 'Rajdhani', sans-serif;
+}
+
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+
+body {
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ min-height: 100vh;
+ overflow-x: hidden;
+}
+
+body::before {
+ content: '';
+ position: fixed;
+ inset: 0;
+ background:
+ radial-gradient(ellipse 80% 50% at 50% -20%, rgba(0,200,255,0.06) 0%, transparent 60%),
+ radial-gradient(ellipse 40% 30% at 80% 80%, rgba(0,255,136,0.04) 0%, transparent 50%),
+ linear-gradient(180deg, #020408 0%, #030a12 100%);
+ pointer-events: none;
+ z-index: 0;
+}
+
+/* SCANLINE EFFECT */
+body::after {
+ content: '';
+ position: fixed;
+ inset: 0;
+ background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.03) 2px, rgba(0,0,0,0.03) 4px);
+ pointer-events: none;
+ z-index: 1;
+ animation: scanlines 8s linear infinite;
+}
+
+@keyframes scanlines {
+ 0% { background-position: 0 0; }
+ 100% { background-position: 0 100px; }
+}
+
+/* ── HEADER ── */
+.header {
+ position: relative;
+ z-index: 10;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 2.5rem;
+ height: 70px;
+ border-bottom: 1px solid var(--border-glow);
+ background: rgba(2,4,8,0.95);
+ backdrop-filter: blur(20px);
+}
+
+.logo {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.logo-icon {
+ width: 36px;
+ height: 36px;
+ border: 1.5px solid var(--neon-cyan);
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ box-shadow: 0 0 15px rgba(0,200,255,0.3);
+ animation: pulse-border 2s ease-in-out infinite;
+}
+
+@keyframes pulse-border {
+ 0%, 100% { box-shadow: 0 0 10px rgba(0,200,255,0.3); }
+ 50% { box-shadow: 0 0 25px rgba(0,200,255,0.6); }
+}
+
+.logo-text {
+ font-family: var(--font-display);
+ font-size: 1.1rem;
+ font-weight: 700;
+ color: var(--neon-cyan);
+ letter-spacing: 0.15em;
+ text-shadow: 0 0 20px rgba(0,200,255,0.5);
+}
+
+.logo-sub {
+ font-size: 0.6rem;
+ color: var(--text-secondary);
+ letter-spacing: 0.2em;
+ font-family: var(--font-mono);
+ margin-top: -2px;
+}
+
+.nav-links {
+ display: flex;
+ gap: 2rem;
+ list-style: none;
+}
+
+.nav-links a {
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ text-decoration: none;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ transition: color 0.2s, text-shadow 0.2s;
+}
+
+.nav-links a:hover, .nav-links a.active {
+ color: var(--neon-cyan);
+ text-shadow: 0 0 10px rgba(0,200,255,0.5);
+}
+
+.status-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ color: var(--neon-green);
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--neon-green);
+ box-shadow: 0 0 10px var(--neon-green);
+ animation: blink 1.5s ease-in-out infinite;
+}
+
+@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
+
+/* ── MAIN LAYOUT ── */
+.main { position: relative; z-index: 2; }
+
+/* ── HERO ── */
+.hero {
+ text-align: center;
+ padding: 5rem 2rem 4rem;
+ position: relative;
+}
+
+.hero-badge {
+ display: inline-block;
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ letter-spacing: 0.3em;
+ color: var(--neon-cyan);
+ border: 1px solid var(--border-glow);
+ padding: 6px 20px;
+ border-radius: 2px;
+ margin-bottom: 2rem;
+ background: rgba(0,200,255,0.04);
+}
+
+.hero h1 {
+ font-family: var(--font-display);
+ font-size: clamp(2rem, 6vw, 4.5rem);
+ font-weight: 900;
+ line-height: 1.1;
+ color: #fff;
+ margin-bottom: 1.5rem;
+ letter-spacing: 0.05em;
+}
+
+.hero h1 span {
+ color: var(--neon-cyan);
+ text-shadow: 0 0 30px rgba(0,200,255,0.5);
+}
+
+.hero p {
+ font-size: 1.1rem;
+ color: var(--text-secondary);
+ max-width: 560px;
+ margin: 0 auto 3rem;
+ line-height: 1.7;
+ font-weight: 400;
+}
+
+/* ── GRID ── */
+.container { max-width: 1200px; margin: 0 auto; padding: 0 2rem; }
+
+.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin-bottom: 2rem; }
+.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
+
+/* ── CARDS ── */
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-glow);
+ border-radius: 4px;
+ padding: 2rem;
+ position: relative;
+ overflow: hidden;
+ transition: border-color 0.3s, box-shadow 0.3s;
+}
+
+.card::before {
+ content: '';
+ position: absolute;
+ top: 0; left: 0; right: 0;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, var(--neon-cyan), transparent);
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+.card:hover { border-color: var(--border-bright); box-shadow: 0 0 30px rgba(0,200,255,0.08); }
+.card:hover::before { opacity: 1; }
+
+.card-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 1.5rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid var(--border-glow);
+}
+
+.card-icon { font-size: 1.2rem; }
+
+.card-title {
+ font-family: var(--font-display);
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.2em;
+ color: var(--neon-cyan);
+ text-transform: uppercase;
+}
+
+.section-label {
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ letter-spacing: 0.25em;
+ color: var(--text-secondary);
+ text-transform: uppercase;
+ margin-bottom: 1.5rem;
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--border-glow);
+}
+
+/* ── FORM ELEMENTS ── */
+.form-group { margin-bottom: 1.2rem; }
+
+label {
+ display: block;
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ letter-spacing: 0.2em;
+ color: var(--text-secondary);
+ margin-bottom: 8px;
+ text-transform: uppercase;
+}
+
+input, select {
+ width: 100%;
+ background: rgba(0,200,255,0.03);
+ border: 1px solid var(--border-glow);
+ border-radius: 3px;
+ padding: 10px 14px;
+ color: var(--text-primary);
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ transition: border-color 0.2s, box-shadow 0.2s;
+ outline: none;
+ appearance: none;
+}
+
+input:focus, select:focus {
+ border-color: var(--neon-cyan);
+ box-shadow: 0 0 15px rgba(0,200,255,0.15);
+}
+
+input::placeholder { color: var(--text-dim); }
+
+/* ── BUTTONS ── */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 12px 28px;
+ font-family: var(--font-display);
+ font-size: 0.7rem;
+ font-weight: 700;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ border: none;
+ border-radius: 3px;
+ cursor: pointer;
+ transition: all 0.2s;
+ text-decoration: none;
+}
+
+.btn-primary {
+ background: var(--neon-cyan);
+ color: var(--bg-primary);
+ box-shadow: 0 0 20px rgba(0,200,255,0.3);
+}
+
+.btn-primary:hover {
+ background: #33d6ff;
+ box-shadow: 0 0 35px rgba(0,200,255,0.5);
+ transform: translateY(-1px);
+}
+
+.btn-danger {
+ background: var(--neon-red);
+ color: #fff;
+ box-shadow: 0 0 20px rgba(255,34,68,0.3);
+}
+
+.btn-outline {
+ background: transparent;
+ color: var(--neon-cyan);
+ border: 1px solid var(--border-bright);
+}
+
+.btn-outline:hover { background: rgba(0,200,255,0.08); }
+.btn-full { width: 100%; }
+
+/* ── STRENGTH BAR ── */
+.strength-bar-wrapper { margin: 1rem 0; }
+.strength-bar-track {
+ height: 6px;
+ background: rgba(255,255,255,0.05);
+ border-radius: 3px;
+ overflow: hidden;
+ margin-bottom: 6px;
+}
+.strength-bar-fill {
+ height: 100%;
+ border-radius: 3px;
+ transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1), background 0.3s;
+ width: 0%;
+}
+.strength-label {
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ color: var(--text-secondary);
+ display: flex;
+ justify-content: space-between;
+}
+
+/* ── METRICS ── */
+.metric-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 0;
+ border-bottom: 1px solid rgba(0,200,255,0.05);
+ font-size: 0.85rem;
+}
+
+.metric-row:last-child { border-bottom: none; }
+.metric-key { color: var(--text-secondary); font-family: var(--font-mono); font-size: 0.7rem; }
+.metric-val {
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+ color: var(--neon-cyan);
+ font-weight: 600;
+}
+
+/* ── BADGES ── */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: 3px 12px;
+ border-radius: 2px;
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+}
+
+.badge-safe { background: rgba(0,255,136,0.1); color: var(--neon-green); border: 1px solid rgba(0,255,136,0.2); }
+.badge-suspicious { background: rgba(255,102,0,0.1); color: var(--neon-orange); border: 1px solid rgba(255,102,0,0.2); }
+.badge-isolated { background: rgba(255,34,68,0.1); color: var(--neon-red); border: 1px solid rgba(255,34,68,0.2); }
+.badge-high { background: rgba(255,34,68,0.1); color: var(--neon-red); border: 1px solid rgba(255,34,68,0.2); }
+.badge-medium { background: rgba(255,102,0,0.1); color: var(--neon-orange); border: 1px solid rgba(255,102,0,0.2); }
+.badge-low { background: rgba(0,255,136,0.1); color: var(--neon-green); border: 1px solid rgba(0,255,136,0.2); }
+
+/* ── THREAT SCORE RING ── */
+.threat-ring {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 1.5rem 0;
+}
+
+.ring-container { position: relative; width: 140px; height: 140px; margin-bottom: 1rem; }
+.ring-svg { transform: rotate(-90deg); }
+.ring-track { fill: none; stroke: rgba(0,200,255,0.08); stroke-width: 8; }
+.ring-fill { fill: none; stroke-width: 8; stroke-linecap: round; transition: stroke-dashoffset 1s cubic-bezier(0.4, 0, 0.2, 1), stroke 0.5s; }
+.ring-text {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-family: var(--font-display);
+}
+.ring-number { font-size: 2.2rem; font-weight: 900; line-height: 1; }
+.ring-sub { font-size: 0.55rem; letter-spacing: 0.2em; color: var(--text-secondary); margin-top: 4px; }
+
+/* ── ALERT BOX ── */
+.alert {
+ padding: 12px 16px;
+ border-radius: 3px;
+ border-left: 3px solid;
+ font-family: var(--font-mono);
+ font-size: 0.78rem;
+ margin-bottom: 1rem;
+ line-height: 1.5;
+ display: none;
+}
+.alert.show { display: block; }
+.alert-danger { border-color: var(--neon-red); background: rgba(255,34,68,0.08); color: #ff8899; }
+.alert-success { border-color: var(--neon-green); background: rgba(0,255,136,0.08); color: #88ffcc; }
+.alert-warning { border-color: var(--neon-orange); background: rgba(255,102,0,0.08); color: #ffaa66; }
+
+/* ── TABS ── */
+.tabs { display: flex; gap: 0; margin-bottom: 2rem; border-bottom: 1px solid var(--border-glow); }
+.tab-btn {
+ padding: 10px 24px;
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ letter-spacing: 0.15em;
+ color: var(--text-secondary);
+ background: none;
+ border: none;
+ cursor: pointer;
+ border-bottom: 2px solid transparent;
+ margin-bottom: -1px;
+ transition: color 0.2s, border-color 0.2s;
+ text-transform: uppercase;
+}
+.tab-btn.active { color: var(--neon-cyan); border-bottom-color: var(--neon-cyan); }
+.tab-content { display: none; }
+.tab-content.active { display: block; }
+
+/* ── ANOMALY LIST ── */
+.anomaly-item {
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ padding: 10px 0;
+ border-bottom: 1px solid rgba(255,34,68,0.08);
+ font-size: 0.8rem;
+}
+.anomaly-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--neon-red); box-shadow: 0 0 6px var(--neon-red); flex-shrink: 0; margin-top: 5px; }
+
+/* ── TABLE ── */
+.cyber-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
+.cyber-table th {
+ font-family: var(--font-mono);
+ font-size: 0.6rem;
+ letter-spacing: 0.2em;
+ color: var(--text-secondary);
+ text-align: left;
+ padding: 10px 14px;
+ border-bottom: 1px solid var(--border-glow);
+ text-transform: uppercase;
+}
+.cyber-table td {
+ padding: 10px 14px;
+ border-bottom: 1px solid rgba(0,200,255,0.04);
+ font-family: var(--font-mono);
+ color: var(--text-primary);
+ font-size: 0.75rem;
+}
+.cyber-table tr:hover td { background: rgba(0,200,255,0.02); }
+
+/* ── STEP FLOW ── */
+.step-flow { display: flex; align-items: center; gap: 0; margin-bottom: 2rem; flex-wrap: wrap; }
+.step { display: flex; align-items: center; gap: 8px; }
+.step-num {
+ width: 28px; height: 28px;
+ border-radius: 50%;
+ border: 1.5px solid var(--border-glow);
+ display: flex; align-items: center; justify-content: center;
+ font-family: var(--font-display); font-size: 0.65rem; color: var(--text-secondary);
+ transition: all 0.3s;
+}
+.step.done .step-num { border-color: var(--neon-green); color: var(--neon-green); box-shadow: 0 0 10px rgba(0,255,136,0.3); }
+.step.active .step-num { border-color: var(--neon-cyan); color: var(--neon-cyan); box-shadow: 0 0 10px rgba(0,200,255,0.4); }
+.step-label { font-family: var(--font-mono); font-size: 0.6rem; color: var(--text-secondary); letter-spacing: 0.1em; }
+.step.active .step-label { color: var(--neon-cyan); }
+.step-arrow { color: var(--text-dim); font-size: 0.8rem; padding: 0 6px; }
+
+/* ── LOADING SPINNER ── */
+.spinner {
+ width: 20px; height: 20px;
+ border: 2px solid rgba(0,200,255,0.2);
+ border-top-color: var(--neon-cyan);
+ border-radius: 50%;
+ animation: spin 0.7s linear infinite;
+ display: none;
+}
+.spinner.show { display: inline-block; }
+@keyframes spin { to { transform: rotate(360deg); } }
+
+/* ── GLITCH TEXT EFFECT ── */
+.glitch {
+ position: relative;
+ animation: glitch 4s infinite;
+}
+@keyframes glitch {
+ 0%, 90%, 100% { text-shadow: 0 0 30px rgba(0,200,255,0.5); }
+ 92% { text-shadow: -2px 0 var(--neon-red), 2px 0 var(--neon-cyan); transform: translateX(1px); }
+ 94% { text-shadow: 2px 0 var(--neon-red), -2px 0 var(--neon-cyan); transform: translateX(-1px); }
+ 96% { text-shadow: 0 0 30px rgba(0,200,255,0.5); transform: translateX(0); }
+}
+
+/* ── TERMINAL OUTPUT ── */
+.terminal {
+ background: rgba(0,0,0,0.6);
+ border: 1px solid var(--border-glow);
+ border-radius: 3px;
+ padding: 1rem 1.2rem;
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ color: var(--neon-green);
+ min-height: 80px;
+ line-height: 1.8;
+ max-height: 160px;
+ overflow-y: auto;
+}
+.terminal .line::before { content: '> '; color: var(--neon-cyan); }
+
+/* ── FOOTER ── */
+.footer {
+ text-align: center;
+ padding: 3rem 2rem;
+ margin-top: 4rem;
+ border-top: 1px solid var(--border-glow);
+ font-family: var(--font-mono);
+ font-size: 0.65rem;
+ color: var(--text-dim);
+ letter-spacing: 0.15em;
+ position: relative;
+ z-index: 2;
+}
+
+/* ── DASHBOARD STATS ── */
+.stat-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-glow);
+ border-radius: 4px;
+ padding: 1.5rem;
+ position: relative;
+ overflow: hidden;
+}
+.stat-card::after {
+ content: '';
+ position: absolute;
+ bottom: 0; left: 0; right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, transparent, var(--accent), transparent);
+}
+.stat-num {
+ font-family: var(--font-display);
+ font-size: 2.5rem;
+ font-weight: 900;
+ line-height: 1;
+ margin: 0.5rem 0;
+}
+.stat-label { font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.2em; color: var(--text-secondary); text-transform: uppercase; }
+.stat-icon { font-size: 1.5rem; margin-bottom: 0.5rem; }
+
+/* ── CHART CONTAINER ── */
+.chart-box {
+ background: var(--bg-card);
+ border: 1px solid var(--border-glow);
+ border-radius: 4px;
+ padding: 1.5rem;
+ height: 280px;
+ position: relative;
+}
+.chart-title {
+ font-family: var(--font-display);
+ font-size: 0.65rem;
+ letter-spacing: 0.2em;
+ color: var(--text-secondary);
+ text-transform: uppercase;
+ margin-bottom: 1rem;
+}
+
+/* ── LOG ENTRIES ── */
+.log-entry {
+ display: flex;
+ gap: 12px;
+ padding: 8px 0;
+ border-bottom: 1px solid rgba(0,200,255,0.04);
+ font-family: var(--font-mono);
+ font-size: 0.7rem;
+ align-items: flex-start;
+}
+.log-sev { padding: 2px 8px; border-radius: 2px; font-size: 0.55rem; font-weight: 700; flex-shrink: 0; margin-top: 1px; }
+.log-sev.CRITICAL { background: rgba(255,34,68,0.15); color: var(--neon-red); }
+.log-sev.HIGH { background: rgba(255,102,0,0.15); color: var(--neon-orange); }
+.log-sev.MEDIUM { background: rgba(255,224,0,0.1); color: var(--neon-yellow); }
+.log-sev.LOW { background: rgba(0,255,136,0.1); color: var(--neon-green); }
+.log-action { color: var(--text-primary); flex: 1; }
+.log-time { color: var(--text-dim); font-size: 0.65rem; flex-shrink: 0; }
+
+/* ── RESPONSIVE ── */
+@media (max-width: 768px) {
+ .grid-2, .grid-3 { grid-template-columns: 1fr; }
+ .header { padding: 0 1.2rem; }
+ .nav-links { display: none; }
+ .hero { padding: 3rem 1.2rem; }
+ .container { padding: 0 1.2rem; }
+}
\ No newline at end of file
diff --git a/frontend/dashboard.html b/frontend/dashboard.html
new file mode 100644
index 000000000..ac88e5726
--- /dev/null
+++ b/frontend/dashboard.html
@@ -0,0 +1,141 @@
+
+
+
+
+
+ SCDS — Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
◈ REAL-TIME THREAT MONITORING DASHBOARD
+
+
+
+
+
+
+
🔴
+
—
+
ISOLATED NODES
+
+
+
⚡
+
—
+
AVG THREAT SCORE
+
+
+
🧠
+
—
+
ATTACK PATTERNS
+
+
+
+
+
+
+
◈ THREAT SCORE TREND (7 DAYS)
+
+
+
+
◈ RISK DISTRIBUTION
+
+
+
+
+
+
+
+
+
+
+
+
+ | EMAIL |
+ THREAT |
+ PWD RISK |
+ NET RISK |
+ STATUS |
+
+
+
+ | Loading... |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 000000000..a57cf03ff
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,248 @@
+
+
+
+
+
+ SCDS — Smart Cybersecurity Detection System
+
+
+
+
+
+
+
+
+ ◈ SCDS v2.1 — NEURAL THREAT ENGINE ACTIVE
+ SMART CYBER
DETECTION SYSTEM
+ Real-time threat analysis using graph-based network intrusion detection and intelligent password security scoring with fusion threat engine.
+
+
+
+
+
+
+
+
›
+
+
›
+
+
›
+
+
›
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Awaiting password scan...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Awaiting network scan...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/js/dashboard.js b/frontend/js/dashboard.js
new file mode 100644
index 000000000..94ba1519c
--- /dev/null
+++ b/frontend/js/dashboard.js
@@ -0,0 +1,133 @@
+const API = 'http://localhost:3000/api';
+
+Chart.defaults.color = '#5a8aaa';
+Chart.defaults.borderColor = 'rgba(0,200,255,0.08)';
+Chart.defaults.font.family = "'Share Tech Mono', monospace";
+
+async function loadDashboard() {
+ try {
+ const r = await fetch(`${API}/dashboard/stats`);
+ const d = await r.json();
+
+ // Stats
+ document.getElementById('st-users').textContent = d.stats.total_users;
+ document.getElementById('st-isolated').textContent = d.stats.isolated_count;
+ document.getElementById('st-threat').textContent = d.stats.avg_threat;
+ document.getElementById('st-attacks').textContent = d.stats.attack_count;
+
+ // Trend Chart
+ const trendCtx = document.getElementById('trendChart').getContext('2d');
+ new Chart(trendCtx, {
+ type: 'line',
+ data: {
+ labels: d.threatTrend.map(t => t.date),
+ datasets: [{
+ label: 'Avg Threat Score',
+ data: d.threatTrend.map(t => Math.round(t.avg_score)),
+ borderColor: '#00c8ff',
+ backgroundColor: 'rgba(0,200,255,0.05)',
+ borderWidth: 2,
+ pointBackgroundColor: '#00c8ff',
+ pointRadius: 4,
+ tension: 0.4,
+ fill: true
+ }]
+ },
+ options: {
+ responsive: true, maintainAspectRatio: false,
+ plugins: { legend: { display: false } },
+ scales: {
+ x: { grid: { color: 'rgba(0,200,255,0.05)' }, ticks: { font: { size: 10 } } },
+ y: { min: 0, max: 100, grid: { color: 'rgba(0,200,255,0.05)' }, ticks: { font: { size: 10 } } }
+ }
+ }
+ });
+
+ // Risk Distribution Chart
+ const safe = d.recentUsers.filter(u => u.status === 'SAFE').length;
+ const susp = d.recentUsers.filter(u => u.status === 'SUSPICIOUS').length;
+ const isol = d.recentUsers.filter(u => u.status === 'ISOLATED').length;
+ const other = d.recentUsers.length - safe - susp - isol;
+ const riskCtx = document.getElementById('riskChart').getContext('2d');
+ new Chart(riskCtx, {
+ type: 'doughnut',
+ data: {
+ labels: ['SAFE', 'SUSPICIOUS', 'ISOLATED', 'UNSCANNED'],
+ datasets: [{
+ data: [safe, susp, isol, other],
+ backgroundColor: ['rgba(0,255,136,0.7)', 'rgba(255,102,0,0.7)', 'rgba(255,34,68,0.7)', 'rgba(90,138,170,0.3)'],
+ borderColor: ['#00ff88','#ff6600','#ff2244','#2a4a5a'],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ responsive: true, maintainAspectRatio: false,
+ plugins: { legend: { position: 'right', labels: { font: { size: 10 }, padding: 12 } } },
+ cutout: '65%'
+ }
+ });
+
+ // Users table
+ const tbody = document.getElementById('users-tbody');
+ tbody.innerHTML = d.recentUsers.length ? d.recentUsers.map(u => {
+ const sc = u.total_threat_score ?? '—';
+ const cls = u.status === 'ISOLATED' ? 'badge-isolated' : u.status === 'SUSPICIOUS' ? 'badge-suspicious' : u.status === 'SAFE' ? 'badge-safe' : '';
+ return `
+ | ${u.email} |
+ ${sc} |
+ ${u.password_risk ?? '—'} |
+ ${u.network_risk ?? '—'} |
+ ${u.status ? `${u.status}` : '—'} |
+
`;
+ }).join('') : '| No threat data yet |
';
+
+ // Suspicious nodes
+ const snList = document.getElementById('suspicious-list');
+ snList.innerHTML = d.suspiciousNodes.length ? d.suspiciousNodes.map(n => `
+
+
+
+
${n.node_label}
+
${n.email} — Risk: ${n.risk_score}
+
${n.reason}
+
+
+ `).join('') : 'No suspicious nodes
';
+
+ // Attack patterns
+ const ptList = document.getElementById('patterns-list');
+ ptList.innerHTML = d.attackPatterns.length ? d.attackPatterns.map((p, i) => `
+
+
+
${p.pattern}
+
Last seen: ${new Date(p.last_seen).toLocaleString()}
+
+
+
${p.occurrence_count}
+
OCCURRENCES
+
+
+ `).join('') : 'No patterns learned yet
';
+
+ // Logs
+ const logsList = document.getElementById('logs-list');
+ logsList.innerHTML = d.logs.length ? d.logs.map(l => `
+
+ ${l.severity}
+ ${l.action} (${l.email})
+ ${new Date(l.created_at).toLocaleTimeString()}
+
+ `).join('') : 'No logs yet
';
+
+ } catch(e) {
+ console.error('Dashboard error:', e);
+ }
+}
+
+function threatColor(score) {
+ if (!score) return 'var(--text-secondary)';
+ return score >= 70 ? 'var(--neon-red)' : score >= 40 ? 'var(--neon-orange)' : 'var(--neon-green)';
+}
+
+loadDashboard();
+setInterval(loadDashboard, 30000); // auto-refresh every 30s
\ No newline at end of file
diff --git a/frontend/js/main.js b/frontend/js/main.js
new file mode 100644
index 000000000..c91e8b43d
--- /dev/null
+++ b/frontend/js/main.js
@@ -0,0 +1,228 @@
+const API = 'http://localhost:3000/api';
+let currentUserId = null;
+let currentPasswordRisk = null;
+let currentNetworkRisk = null;
+let currentNodeId = null;
+
+function switchTab(name) {
+ document.querySelectorAll('.tab-btn').forEach((b, i) => {
+ b.classList.toggle('active', ['register','login'][i] === name);
+ });
+ document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
+ document.getElementById('tab-' + name).classList.add('active');
+}
+
+function showAlert(id, msg, type) {
+ const el = document.getElementById(id);
+ el.className = `alert alert-${type} show`;
+ el.textContent = msg;
+}
+
+function hideAlert(id) {
+ document.getElementById(id).className = 'alert';
+}
+
+function setStep(n) {
+ for (let i = 1; i <= 5; i++) {
+ const s = document.getElementById('step' + i);
+ s.classList.remove('active', 'done');
+ if (i < n) s.classList.add('done');
+ else if (i === n) s.classList.add('active');
+ }
+}
+
+function previewStrength(pwd) {
+ if (!pwd) {
+ document.getElementById('previewBar').style.width = '0%';
+ document.getElementById('previewLabel').textContent = 'Awaiting input...';
+ document.getElementById('previewScore').textContent = '';
+ return;
+ }
+ let s = 0;
+ if (pwd.length >= 8) s += 10; if (pwd.length >= 12) s += 15; if (pwd.length >= 16) s += 15;
+ if (/[A-Z]/.test(pwd)) s += 10; if (/[a-z]/.test(pwd)) s += 10;
+ if (/[0-9]/.test(pwd)) s += 10; if (/[^A-Za-z0-9]/.test(pwd)) s += 20;
+ if (pwd.length > 20) s += 10;
+ s = Math.min(s, 100);
+ const bar = document.getElementById('previewBar');
+ bar.style.width = s + '%';
+ const color = s >= 70 ? 'var(--neon-green)' : s >= 40 ? 'var(--neon-orange)' : 'var(--neon-red)';
+ bar.style.background = color;
+ const labels = ['CRITICAL', 'WEAK', 'MODERATE', 'STRONG', 'EXCELLENT'];
+ const idx = Math.floor(s / 20);
+ document.getElementById('previewLabel').textContent = labels[Math.min(idx, 4)];
+ document.getElementById('previewLabel').style.color = color;
+ document.getElementById('previewScore').textContent = s + '/100';
+}
+
+async function registerUser() {
+ const email = document.getElementById('reg-email').value.trim();
+ const password = document.getElementById('reg-password').value;
+ if (!email || !password) return showAlert('reg-alert', 'Email and password are required.', 'warning');
+ hideAlert('reg-alert');
+ const sp = document.getElementById('reg-spinner');
+ sp.classList.add('show');
+
+ try {
+ const r = await fetch(`${API}/auth/register`, {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({ email, password })
+ });
+ const d = await r.json();
+ if (!d.success) return showAlert('reg-alert', d.message, 'danger');
+ currentUserId = d.userId;
+ showAlert('reg-alert', `✓ User registered (ID: ${d.userId}). Running password analysis...`, 'success');
+ setStep(2);
+ await analyzePassword(password);
+ } catch(e) {
+ showAlert('reg-alert', 'Connection failed: ' + e.message, 'danger');
+ } finally { sp.classList.remove('show'); }
+}
+
+async function analyzePassword(password) {
+ try {
+ const r = await fetch(`${API}/password/analyze`, {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({ userId: currentUserId, password })
+ });
+ const d = await r.json();
+ if (!d.success) return;
+ currentPasswordRisk = d.password_risk;
+ const color = d.strength_score >= 70 ? 'var(--neon-green)' : d.strength_score >= 40 ? 'var(--neon-orange)' : 'var(--neon-red)';
+ document.getElementById('pwd-result').innerHTML = `
+
+
+
+ STRENGTH: ${d.strength_score}/100
+ RISK: ${d.password_risk}/100
+
+
+ DICTIONARY MATCH
+ ${d.dictionary_match ? '⚠ DETECTED' : '✓ CLEAN'}
+ BRUTE FORCE TIME${d.brute_force_time}
+ PASSWORD RISK SCORE${d.password_risk}/100
+ `;
+ document.getElementById('network-section').style.display = 'grid';
+ setStep(3);
+ } catch(e) { console.error(e); }
+}
+
+async function analyzeNetwork() {
+ const sp = document.getElementById('net-spinner');
+ sp.classList.add('show');
+ const payload = {
+ userId: currentUserId,
+ ip_address: document.getElementById('ip').value,
+ requests_per_minute: parseInt(document.getElementById('rpm').value),
+ failed_logins: parseInt(document.getElementById('failed').value),
+ connection_degree: parseInt(document.getElementById('conn').value),
+ login_hour: parseInt(document.getElementById('hour').value),
+ location_jump: document.getElementById('locjump').value === 'true',
+ current_location: document.getElementById('curloc').value,
+ previous_location: document.getElementById('prevloc').value
+ };
+ try {
+ const r = await fetch(`${API}/network/analyze`, {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify(payload)
+ });
+ const d = await r.json();
+ if (!d.success) return;
+ currentNetworkRisk = d.network_risk;
+ currentNodeId = d.nodeId;
+ const color = d.network_risk >= 70 ? 'var(--neon-red)' : d.network_risk >= 40 ? 'var(--neon-orange)' : 'var(--neon-green)';
+ document.getElementById('net-result').innerHTML = `
+ NETWORK RISK SCORE
+ ${d.network_risk}/100
+ ANOMALIES DETECTED
+ ${d.anomalies.length}
+
+ ${d.anomalies.length > 0 ? d.anomalies.map(a => `
`).join('') : '
✓ No anomalies detected
'}
+
+ `;
+ setStep(4);
+ await runFusion();
+ } catch(e) { console.error(e); }
+ finally { sp.classList.remove('show'); }
+}
+
+async function runFusion() {
+ try {
+ const r = await fetch(`${API}/fusion/evaluate`, {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({
+ userId: currentUserId,
+ passwordRisk: currentPasswordRisk,
+ networkRisk: currentNetworkRisk,
+ nodeId: currentNodeId
+ })
+ });
+ const d = await r.json();
+ if (!d.success) return;
+
+ document.getElementById('fusion-section').style.display = 'block';
+
+ // Ring animation
+ const score = d.total_threat_score;
+ const circ = 351.86;
+ const offset = circ - (score / 100) * circ;
+ const ringColor = score >= 70 ? 'var(--neon-red)' : score >= 40 ? 'var(--neon-orange)' : 'var(--neon-green)';
+ const fill = document.getElementById('ringFill');
+ fill.style.stroke = ringColor;
+ setTimeout(() => { fill.style.strokeDashoffset = offset; }, 100);
+ document.getElementById('ringNum').textContent = score;
+ document.getElementById('ringNum').style.color = ringColor;
+
+ const statusClass = d.status === 'ISOLATED' ? 'badge-isolated' : d.status === 'SUSPICIOUS' ? 'badge-suspicious' : 'badge-safe';
+ document.getElementById('fusionBadge').innerHTML = `${d.status}
`;
+
+ document.getElementById('fusion-result').innerHTML = `
+ PASSWORD RISK (40%)${currentPasswordRisk}/100
+ NETWORK RISK (60%)${currentNetworkRisk}/100
+ FUSION SCORE${score}/100
+ DECISION${d.status}
+ `;
+
+ const terminal = document.getElementById('fusionTerminal');
+ const lines = [
+ `Fusion engine initialized...`,
+ `Password risk weight: 0.4 × ${currentPasswordRisk} = ${(currentPasswordRisk * 0.4).toFixed(1)}`,
+ `Network risk weight: 0.6 × ${currentNetworkRisk} = ${(currentNetworkRisk * 0.6).toFixed(1)}`,
+ `Total threat score: ${score}/100`,
+ d.status === 'ISOLATED' ? `⚠ THRESHOLD EXCEEDED — NODE ISOLATION TRIGGERED` :
+ d.status === 'SUSPICIOUS' ? `⚡ SUSPICIOUS ACTIVITY — MONITORING ELEVATED` :
+ `✓ THREAT WITHIN ACCEPTABLE RANGE — ACCESS GRANTED`
+ ];
+ terminal.innerHTML = '';
+ lines.forEach((l, i) => {
+ setTimeout(() => {
+ terminal.innerHTML += `${l}
`;
+ terminal.scrollTop = terminal.scrollHeight;
+ }, i * 300);
+ });
+
+ setStep(5);
+ } catch(e) { console.error(e); }
+}
+
+async function loginUser() {
+ const email = document.getElementById('login-email').value.trim();
+ const password = document.getElementById('login-password').value;
+ if (!email || !password) return showAlert('login-alert', 'Both fields required.', 'warning');
+ const sp = document.getElementById('login-spinner');
+ sp.classList.add('show');
+ try {
+ const r = await fetch(`${API}/auth/login`, {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify({ email, password })
+ });
+ const d = await r.json();
+ if (d.blocked) return showAlert('login-alert', d.message, 'danger');
+ if (!d.success) return showAlert('login-alert', d.message, 'danger');
+ showAlert('login-alert', `✓ Authentication successful. Welcome, ${d.email}`, 'success');
+ } catch(e) {
+ showAlert('login-alert', 'Connection error: ' + e.message, 'danger');
+ } finally { sp.classList.remove('show'); }
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 000000000..e020fb9d3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1388 @@
+{
+ "name": "scds",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "scds",
+ "version": "1.0.0",
+ "dependencies": {
+ "bcryptjs": "^2.4.3",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "mysql2": "^3.6.0"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~7.19.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/aws-ssl-profiles": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
+ "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "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",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
+ "license": "MIT"
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru.min": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz",
+ "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=1.30.0",
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wellwelwel"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/mysql2": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.0.tgz",
+ "integrity": "sha512-4jaJYBObj7FhD3lnZhqX1yDMuZN4mQNz+IolDySDXT7fbozMBpeGQNcuWXKUqo4ahkAEfkjUHPjnwuDI0/6VKw==",
+ "license": "MIT",
+ "dependencies": {
+ "aws-ssl-profiles": "^1.1.2",
+ "denque": "^2.1.0",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.7.2",
+ "long": "^5.3.2",
+ "lru.min": "^1.1.4",
+ "named-placeholders": "^1.1.6",
+ "sql-escaper": "^1.3.3"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 8"
+ }
+ },
+ "node_modules/mysql2/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
+ "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
+ "license": "MIT",
+ "dependencies": {
+ "lru.min": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
+ "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^10.2.1",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nodemon/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sql-escaper": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz",
+ "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=2.0.0",
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/mysqljs/sql-escaper?sponsor=1"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..6cd0d84f0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "scds",
+ "version": "1.0.0",
+ "description": "Smart Cybersecurity Detection System",
+ "main": "backend/server.js",
+ "scripts": {
+ "start": "node backend/server.js",
+ "dev": "nodemon backend/server.js"
+ },
+ "dependencies": {
+ "express": "^4.18.2",
+ "mysql2": "^3.6.0",
+ "bcryptjs": "^2.4.3",
+ "dotenv": "^16.3.1",
+ "cors": "^2.8.5"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+}
\ No newline at end of file