alpha
This commit is contained in:
Generated
+1
-1
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cors": "^2.8.5",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"ioredis": "^5.6.1",
|
||||
|
||||
+2
-2
@@ -9,11 +9,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cors": "^2.8.5",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"ioredis": "^5.6.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^3.12.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-15
@@ -7,6 +7,7 @@ import { pool } from "./db.js";
|
||||
import {
|
||||
createWideTrendTable,
|
||||
ensureIspMetadata,
|
||||
LOCAL_POINT_LIMIT,
|
||||
fetchLatestValues,
|
||||
fetchTrendSeries,
|
||||
getAvailableIsps,
|
||||
@@ -39,18 +40,24 @@ const allowedOrigins = config.corsOrigin
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin(origin, callback) {
|
||||
if (!origin || allowedOrigins.includes("*") || allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
return;
|
||||
}
|
||||
const corsOptions = {
|
||||
origin(origin, callback) {
|
||||
if (!origin || allowedOrigins.includes("*") || allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error(`Origin not allowed: ${origin}`));
|
||||
},
|
||||
credentials: true,
|
||||
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization"],
|
||||
};
|
||||
|
||||
app.use(cors(corsOptions));
|
||||
app.options(/.*/, cors(corsOptions));
|
||||
|
||||
|
||||
callback(new Error("Origin not allowed."));
|
||||
},
|
||||
})
|
||||
);
|
||||
app.use(express.json({ limit: "50mb" }));
|
||||
|
||||
function serializeUser(row) {
|
||||
@@ -370,6 +377,21 @@ app.get("/api/auth/me", authenticateToken, wrap(async (req, res) => {
|
||||
return res.json({ user: await serializeUserWithPermissions(rows[0]) });
|
||||
}));
|
||||
|
||||
async function resolveIspPointCount(isp) {
|
||||
const safeIsp = sanitizeIspName(isp);
|
||||
const [rows] = await pool.query(
|
||||
`
|
||||
SELECT COUNT(*) AS pointCount
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ?
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME LIKE 'Wert%'
|
||||
`,
|
||||
[config.dbName, safeIsp]
|
||||
);
|
||||
return Number(rows[0]?.pointCount || 0) || (safeIsp.startsWith("trend_") ? LOCAL_POINT_LIMIT : 200);
|
||||
}
|
||||
|
||||
app.get("/api/isps", authenticateToken, wrap(async (_req, res) => {
|
||||
const isps = await getAvailableIsps(pool, config.dbName);
|
||||
const response = await Promise.all(
|
||||
@@ -387,7 +409,8 @@ app.get("/api/isps", authenticateToken, wrap(async (_req, res) => {
|
||||
}));
|
||||
|
||||
app.get("/api/isps/:isp/aliases", authenticateToken, wrap(async (req, res) => {
|
||||
const metadata = await ensureIspMetadata(redis, req.params.isp);
|
||||
const pointCount = await resolveIspPointCount(req.params.isp);
|
||||
const metadata = await ensureIspMetadata(redis, req.params.isp, pointCount);
|
||||
res.json(metadata);
|
||||
}));
|
||||
|
||||
@@ -397,6 +420,7 @@ app.put(
|
||||
requirePermission("edit_aliases"),
|
||||
wrap(async (req, res) => {
|
||||
const { displayName, description, points } = req.body || {};
|
||||
const pointCount = await resolveIspPointCount(req.params.isp);
|
||||
const updated = await updateIspMetadata(redis, req.params.isp, (metadata) => {
|
||||
if (typeof displayName === "string") {
|
||||
metadata.displayName = displayName.trim() || metadata.displayName;
|
||||
@@ -424,7 +448,7 @@ app.put(
|
||||
}
|
||||
|
||||
return metadata;
|
||||
});
|
||||
}, pointCount);
|
||||
|
||||
res.json(updated);
|
||||
})
|
||||
@@ -437,7 +461,7 @@ app.post("/api/trend-tables", authenticateToken, requirePermission("manage_sourc
|
||||
const metadata = await updateIspMetadata(redis, safeName, (current) => ({
|
||||
...current,
|
||||
displayName: displayName || current.displayName,
|
||||
}));
|
||||
}), LOCAL_POINT_LIMIT);
|
||||
res.status(201).json({ tableName: safeName, metadata });
|
||||
}));
|
||||
|
||||
@@ -458,7 +482,7 @@ app.post(
|
||||
|
||||
const normalized = [];
|
||||
for (const isp of targets) {
|
||||
const metadata = await ensureIspMetadata(redis, isp.tableName, isp.pointCount || 200);
|
||||
const metadata = await ensureIspMetadata(redis, isp.tableName, isp.pointCount || (isp.tableName.startsWith("trend_") ? LOCAL_POINT_LIMIT : 200));
|
||||
normalized.push({ isp: isp.tableName, metadata });
|
||||
}
|
||||
|
||||
|
||||
+37
-8
@@ -1,4 +1,7 @@
|
||||
const META_PREFIX = "meta:isp:";
|
||||
export const LEGACY_POINT_LIMIT = 200;
|
||||
export const LOCAL_POINT_LIMIT = 2000;
|
||||
|
||||
|
||||
export function buildPointKey(isp, pointIndex) {
|
||||
return `${isp}:${pointIndex}`;
|
||||
@@ -17,8 +20,8 @@ export function sanitizeIspName(value) {
|
||||
export function sanitizePointIndex(value) {
|
||||
const pointIndex = Number(value);
|
||||
|
||||
if (!Number.isInteger(pointIndex) || pointIndex < 1 || pointIndex > 200) {
|
||||
throw new Error("Point index must be between 1 and 200.");
|
||||
if (!Number.isInteger(pointIndex) || pointIndex < 1 || pointIndex > LOCAL_POINT_LIMIT) {
|
||||
throw new Error(`Point index must be between 1 and ${LOCAL_POINT_LIMIT}.`);
|
||||
}
|
||||
|
||||
return pointIndex;
|
||||
@@ -48,7 +51,7 @@ export function parseTrendValue(rawValue, factor = 1) {
|
||||
function buildValueColumns() {
|
||||
const columns = [];
|
||||
|
||||
for (let index = 1; index <= 200; index += 1) {
|
||||
for (let index = 1; index <= LOCAL_POINT_LIMIT; index += 1) {
|
||||
columns.push('`Wert' + index + '` TEXT NULL');
|
||||
}
|
||||
|
||||
@@ -136,6 +139,31 @@ function createDefaultPoint(index) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function ensureWideTrendColumns(pool, tableName) {
|
||||
const [columns] = await pool.query(
|
||||
`
|
||||
SELECT COLUMN_NAME AS columnName
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = ?
|
||||
AND COLUMN_NAME LIKE 'Wert%'
|
||||
`,
|
||||
[tableName]
|
||||
);
|
||||
const existing = new Set(columns.map((column) => column.columnName));
|
||||
const missing = [];
|
||||
for (let index = 1; index <= LOCAL_POINT_LIMIT; index += 1) {
|
||||
const columnName = `Wert${index}`;
|
||||
if (!existing.has(columnName)) {
|
||||
missing.push(`ADD COLUMN \`${columnName}\` TEXT NULL`);
|
||||
}
|
||||
}
|
||||
for (let index = 0; index < missing.length; index += 50) {
|
||||
await pool.query(`ALTER TABLE \`${tableName}\` ${missing.slice(index, index + 50).join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createWideTrendTable(pool, tableName) {
|
||||
const safeTable = sanitizeIspName(tableName);
|
||||
const sql = `
|
||||
@@ -149,6 +177,7 @@ export async function createWideTrendTable(pool, tableName) {
|
||||
`;
|
||||
|
||||
await pool.query(sql);
|
||||
await ensureWideTrendColumns(pool, safeTable);
|
||||
return safeTable;
|
||||
}
|
||||
|
||||
@@ -197,7 +226,7 @@ export async function getAvailableIsps(pool, dbName) {
|
||||
});
|
||||
}
|
||||
|
||||
export function createDefaultMetadata(isp, pointCount = 200) {
|
||||
export function createDefaultMetadata(isp, pointCount = LEGACY_POINT_LIMIT) {
|
||||
const points = {};
|
||||
|
||||
for (let index = 1; index <= pointCount; index += 1) {
|
||||
@@ -212,7 +241,7 @@ export function createDefaultMetadata(isp, pointCount = 200) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function ensureIspMetadata(redis, isp, pointCount = 200) {
|
||||
export async function ensureIspMetadata(redis, isp, pointCount = LEGACY_POINT_LIMIT) {
|
||||
const safeIsp = sanitizeIspName(isp);
|
||||
const key = `${META_PREFIX}${safeIsp}`;
|
||||
const existing = await redis.get(key);
|
||||
@@ -285,8 +314,8 @@ export async function ensureIspMetadata(redis, isp, pointCount = 200) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
export async function updateIspMetadata(redis, isp, updater) {
|
||||
const metadata = await ensureIspMetadata(redis, isp);
|
||||
export async function updateIspMetadata(redis, isp, updater, pointCount = LEGACY_POINT_LIMIT) {
|
||||
const metadata = await ensureIspMetadata(redis, isp, pointCount);
|
||||
const updated = updater(JSON.parse(JSON.stringify(metadata)));
|
||||
await redis.set(`${META_PREFIX}${sanitizeIspName(isp)}`, JSON.stringify(updated));
|
||||
return updated;
|
||||
@@ -309,7 +338,7 @@ export async function getPointDescriptors(redis, points) {
|
||||
const descriptors = [];
|
||||
|
||||
for (const [isp, pointIndexes] of grouped.entries()) {
|
||||
const metadata = await ensureIspMetadata(redis, isp);
|
||||
const metadata = await ensureIspMetadata(redis, isp, LOCAL_POINT_LIMIT);
|
||||
|
||||
pointIndexes.forEach((pointIndex) => {
|
||||
const pointMeta = metadata.points[String(pointIndex)];
|
||||
|
||||
Reference in New Issue
Block a user