changes
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"argon2": "^0.44.0",
|
||||
"chalk": "^5.6.2",
|
||||
"cors": "^2.8.5",
|
||||
"csv-parser": "^3.2.0",
|
||||
@@ -19,6 +20,7 @@
|
||||
"http-proxy": "^1.18.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"moment": "^2.30.1",
|
||||
"ws": "^8.18.3"
|
||||
"ws": "^8.18.3",
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
149
server/db.js
149
server/db.js
@@ -1,149 +0,0 @@
|
||||
import chalk from 'chalk'
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { pathToFileURL } from 'url';
|
||||
import parse from "csv-parser"
|
||||
import Node from "../model/Node.js"
|
||||
|
||||
export default class Database {
|
||||
#nodes;
|
||||
#edges;
|
||||
#labels = {}
|
||||
|
||||
constructor() {
|
||||
this.getData()
|
||||
}
|
||||
|
||||
async getData() {
|
||||
const dbData = await fs.readFile(path.join(process.cwd(), 'db/db.json'), 'utf8');
|
||||
let dbJson;
|
||||
try {
|
||||
dbJson = JSON.parse(dbData);
|
||||
} catch {
|
||||
dbJson = []
|
||||
}
|
||||
this.#nodes = dbJson["nodes"];
|
||||
this.#edges = dbJson["edges"];
|
||||
|
||||
console.log(chalk.yellow("DB established."))
|
||||
Object.preventExtensions(this);
|
||||
}
|
||||
|
||||
// superKey = "nodes" || "edges"
|
||||
async writeData(superKey, key, value) {
|
||||
const dbData = await fs.readFile(path.join(process.cwd(), 'db/db.json'), 'utf8');
|
||||
let dbJson;
|
||||
try {
|
||||
dbJson = JSON.parse(dbData);
|
||||
} catch {
|
||||
dbJson = []
|
||||
}
|
||||
|
||||
dbJson[superKey][key] = value;
|
||||
|
||||
await fs.writeFile(path.join(process.cwd(), 'db/db.json'), JSON.stringify(dbJson, null, 2), 'utf8')
|
||||
}
|
||||
|
||||
async getLabelModels() {
|
||||
const labelHandlers = {};
|
||||
const labelDir = path.join(process.cwd(), 'src/model/labels');
|
||||
const files = await fs.readdir(labelDir);
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.js')) continue;
|
||||
|
||||
const label = path.basename(file, '.js');
|
||||
const modulePath = path.join(labelDir, file);
|
||||
const module = await import(pathToFileURL(modulePath).href);
|
||||
labelHandlers[label] = module.default;
|
||||
|
||||
if (!this.#labels[label]) {
|
||||
this.#labels[label] = [];
|
||||
}
|
||||
}
|
||||
return labelHandlers
|
||||
}
|
||||
|
||||
generateUserID() {
|
||||
let id = this.#labels["User"].length + 1;
|
||||
while (this.get.user(`user-${id}`)) {
|
||||
id++;
|
||||
}
|
||||
return `user-${id}`; // O(1) most of the time
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
return { nodes: this.#nodes }
|
||||
}
|
||||
|
||||
async loadCSV(csvString) {
|
||||
if (!csvString || typeof csvString !== 'string') {
|
||||
throw new Error('loadCSVAsync: input must be a non-empty string');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const records = [];
|
||||
|
||||
const parser = parse({
|
||||
columns: true,
|
||||
skip_empty_lines: true,
|
||||
trim: true,
|
||||
relax_column_count: true,
|
||||
bom: true
|
||||
});
|
||||
|
||||
parser.on('readable', () => {
|
||||
let row;
|
||||
while ((row = parser.read()) !== null) {
|
||||
records.push(this.cleanRow(row));
|
||||
}
|
||||
});
|
||||
|
||||
parser.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
parser.on('end', () => {
|
||||
resolve(records);
|
||||
});
|
||||
|
||||
// Feed the CSV string
|
||||
parser.write(csvString);
|
||||
parser.end();
|
||||
});
|
||||
}
|
||||
|
||||
cleanRow(row) {
|
||||
const cleaned = {};
|
||||
|
||||
for (let [key, value] of Object.entries(row)) {
|
||||
// 1. Clean header
|
||||
key = key
|
||||
.replace(/ -MyData/g, '')
|
||||
.replace(/[^a-zA-Z0-9]/g, ' ')
|
||||
.trim()
|
||||
.replace(/\s+(.)/g, (_, c) => c.toUpperCase())
|
||||
.replace(/^(.)/, c => c.toLowerCase());
|
||||
|
||||
// 2. Empty → null
|
||||
if (value === '' || value == null) {
|
||||
cleaned[key] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. Type conversion
|
||||
if (key === 'registrationStatus') {
|
||||
cleaned[key] = value === 'True' ? true : value === 'False' ? false : null;
|
||||
}
|
||||
else if (['confidences', 'personId', 'uid', 'stateVoterId'].includes(key)) {
|
||||
const num = parseFloat(value);
|
||||
cleaned[key] = isNaN(num) ? null : num;
|
||||
}
|
||||
else {
|
||||
cleaned[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,28 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { broadcast } from './ws.js';
|
||||
|
||||
const CSV_PATH = path.join(process.cwd(), 'db/logs.csv')
|
||||
|
||||
function appendToCSV(row) {
|
||||
const exists = fs.existsSync(CSV_PATH)
|
||||
|
||||
const header = 'name,latitude,longitude,timestamp\n'
|
||||
const line = `${row.name},${row.latitude},${row.longitude},${row.timestamp}\n`
|
||||
|
||||
if (!exists) {
|
||||
fs.appendFileSync(CSV_PATH, header + line, 'utf8')
|
||||
} else {
|
||||
fs.appendFileSync(CSV_PATH, line, 'utf8')
|
||||
}
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
updateLocation(req, res) {
|
||||
const { name, latitude, longitude, timestamp } = req.body;
|
||||
console.log(`Received location: (${name}, ${latitude}, ${longitude}) at ${timestamp}`);
|
||||
broadcast("update-location", { name, latitude, longitude, timestamp });
|
||||
appendToCSV({ name, latitude, longitude, timestamp })
|
||||
res.json({ success: true });
|
||||
},
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import chalk from 'chalk'
|
||||
import moment from 'moment'
|
||||
import path from 'path';
|
||||
import { initWebSocket } from './ws.js'
|
||||
import "./util.js"
|
||||
|
||||
import Database from "./db.js"
|
||||
import AuthHandler from './auth.js';
|
||||
import handlers from "./handlers.js";
|
||||
|
||||
@@ -116,7 +116,6 @@ class Server {
|
||||
}
|
||||
|
||||
constructor() {
|
||||
global.db = new Database()
|
||||
this.auth = new AuthHandler()
|
||||
const app = express();
|
||||
app.use(cors({ origin: '*' }));
|
||||
@@ -131,7 +130,7 @@ class Server {
|
||||
|
||||
const server = http.createServer(app);
|
||||
initWebSocket(server);
|
||||
const PORT = 3008;
|
||||
const PORT = 3009;
|
||||
server.listen(PORT, () => {
|
||||
console.log("\n")
|
||||
console.log(chalk.yellow("**************Blockcatcher*************"))
|
||||
|
||||
17
server/util.js
Normal file
17
server/util.js
Normal file
@@ -0,0 +1,17 @@
|
||||
global.currentTime = function () {
|
||||
const now = new Date();
|
||||
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(now.getDate()).padStart(2, "0");
|
||||
const year = now.getFullYear();
|
||||
|
||||
let hours = now.getHours();
|
||||
const ampm = hours >= 12 ? "pm" : "am";
|
||||
hours = hours % 12 || 12; // convert to 12-hour format
|
||||
|
||||
const minutes = String(now.getMinutes()).padStart(2, "0");
|
||||
const seconds = String(now.getSeconds()).padStart(2, "0");
|
||||
const ms = String(now.getMilliseconds()).padStart(4, "0"); // 4-digit like "5838"
|
||||
|
||||
return `${month}.${day}.${year}-${hours}:${minutes}:${seconds}${ms}${ampm}`;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import "./LocationList.js"
|
||||
import "./Sidebar.js"
|
||||
import "./MainPanel.js"
|
||||
import "./components/LocationList.js"
|
||||
import "./components/Sidebar.js"
|
||||
import "./components/MainPanel.js"
|
||||
|
||||
class Home extends Shadow {
|
||||
render() {
|
||||
@@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Blockcatcher</title>
|
||||
<title>Blockcatcher Admin</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="/_/icons/logo.svg">
|
||||
<link rel="icon" href="/_/icons/runner.svg">
|
||||
<link rel="stylesheet" href="/_/code/styles.css">
|
||||
<script src="/_/code/quill.js"></script>
|
||||
<script type="module" src="/index.js"></script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ConnectionHandler from "./ws/Connection.js"
|
||||
window.ConnectionHandler = new ConnectionHandler()
|
||||
|
||||
import "./components/Home.js"
|
||||
import "./Home.js"
|
||||
Home()
|
||||
@@ -10,7 +10,7 @@ class Connection {
|
||||
|
||||
init() {
|
||||
if(window.location.hostname === "localhost") {
|
||||
this.ws = new WebSocket("ws://" + "localhost:3008")
|
||||
this.ws = new WebSocket("ws://" + "localhost:3009")
|
||||
} else {
|
||||
this.ws = new WebSocket("wss://" + window.location.hostname + window.location.pathname)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user