Skip to content

Commit 98bd699

Browse files
Yerazeclaude
andauthored
fix: add missing routePositions column to PG/MySQL traceroutes baseline (#2390)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ede0711 commit 98bd699

File tree

5 files changed

+689
-37
lines changed

5 files changed

+689
-37
lines changed

src/server/migrations/001_v37_baseline.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,25 @@ export const migration = {
481481
db.exec(`
482482
CREATE TABLE IF NOT EXISTS user_map_preferences (
483483
id INTEGER PRIMARY KEY AUTOINCREMENT,
484-
userId INTEGER NOT NULL,
484+
user_id INTEGER NOT NULL,
485485
centerLat REAL,
486486
centerLng REAL,
487487
zoom REAL,
488488
selectedLayer TEXT,
489+
map_tileset TEXT,
490+
show_paths INTEGER DEFAULT 0,
491+
show_neighbor_info INTEGER DEFAULT 0,
492+
show_route INTEGER DEFAULT 1,
493+
show_motion INTEGER DEFAULT 1,
494+
show_mqtt_nodes INTEGER DEFAULT 1,
495+
show_meshcore_nodes INTEGER DEFAULT 1,
496+
show_animations INTEGER DEFAULT 0,
497+
show_accuracy_regions INTEGER DEFAULT 0,
498+
show_estimated_positions INTEGER DEFAULT 0,
499+
position_history_hours INTEGER,
489500
createdAt INTEGER NOT NULL,
490501
updatedAt INTEGER NOT NULL,
491-
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
502+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
492503
)
493504
`);
494505

@@ -900,6 +911,7 @@ export async function runMigration001Postgres(client: PoolClient): Promise<void>
900911
"routeBack" TEXT,
901912
"snrTowards" TEXT,
902913
"snrBack" TEXT,
914+
"routePositions" TEXT,
903915
timestamp BIGINT NOT NULL,
904916
"createdAt" BIGINT NOT NULL
905917
);
@@ -912,6 +924,10 @@ export async function runMigration001Postgres(client: PoolClient): Promise<void>
912924
"toNodeId" TEXT NOT NULL,
913925
"distanceKm" REAL NOT NULL,
914926
"isRecordHolder" BOOLEAN DEFAULT false,
927+
"fromLatitude" DOUBLE PRECISION,
928+
"fromLongitude" DOUBLE PRECISION,
929+
"toLatitude" DOUBLE PRECISION,
930+
"toLongitude" DOUBLE PRECISION,
915931
timestamp BIGINT NOT NULL,
916932
"createdAt" BIGINT NOT NULL
917933
);
@@ -1521,6 +1537,7 @@ export async function runMigration001Mysql(pool: MySQLPool): Promise<void> {
15211537
routeBack TEXT,
15221538
snrTowards TEXT,
15231539
snrBack TEXT,
1540+
routePositions TEXT,
15241541
timestamp BIGINT NOT NULL,
15251542
createdAt BIGINT NOT NULL,
15261543
INDEX idx_traceroutes_from_to (fromNodeNum, toNodeNum),
@@ -1535,6 +1552,10 @@ export async function runMigration001Mysql(pool: MySQLPool): Promise<void> {
15351552
toNodeId VARCHAR(32) NOT NULL,
15361553
distanceKm DOUBLE NOT NULL,
15371554
isRecordHolder BOOLEAN DEFAULT false,
1555+
fromLatitude DOUBLE,
1556+
fromLongitude DOUBLE,
1557+
toLatitude DOUBLE,
1558+
toLongitude DOUBLE,
15381559
timestamp BIGINT NOT NULL,
15391560
createdAt BIGINT NOT NULL,
15401561
INDEX idx_route_segments_from_to (fromNodeNum, toNodeNum)

src/server/migrations/007_add_missing_map_preference_columns.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,42 @@ import { PoolClient } from 'pg';
1414
import { Pool as MySQLPool } from 'mysql2/promise';
1515
import { logger } from '../../utils/logger.js';
1616

17-
export function runMigration083Sqlite(_db: Database.Database): void {
18-
// SQLite already has all columns from migration 030 — nothing to do
19-
logger.debug('Migration 083: SQLite already has all map preference columns, skipping.');
17+
export function runMigration083Sqlite(db: Database.Database): void {
18+
// v3.7 baseline may not have these columns — add idempotently
19+
const columnsToAdd = [
20+
{ name: 'map_tileset', type: 'TEXT' },
21+
{ name: 'show_paths', type: 'INTEGER DEFAULT 0' },
22+
{ name: 'show_neighbor_info', type: 'INTEGER DEFAULT 0' },
23+
{ name: 'show_route', type: 'INTEGER DEFAULT 1' },
24+
{ name: 'show_motion', type: 'INTEGER DEFAULT 1' },
25+
{ name: 'show_mqtt_nodes', type: 'INTEGER DEFAULT 1' },
26+
{ name: 'show_meshcore_nodes', type: 'INTEGER DEFAULT 1' },
27+
{ name: 'show_animations', type: 'INTEGER DEFAULT 0' },
28+
{ name: 'show_accuracy_regions', type: 'INTEGER DEFAULT 0' },
29+
{ name: 'show_estimated_positions', type: 'INTEGER DEFAULT 0' },
30+
{ name: 'position_history_hours', type: 'INTEGER' },
31+
];
32+
33+
for (const col of columnsToAdd) {
34+
try {
35+
db.exec(`ALTER TABLE user_map_preferences ADD COLUMN ${col.name} ${col.type}`);
36+
logger.debug(`✅ Added ${col.name} column to user_map_preferences`);
37+
} catch {
38+
// Column already exists — ignore
39+
}
40+
}
41+
42+
// Also ensure user_id column exists (baseline may have userId instead)
43+
try {
44+
db.exec(`ALTER TABLE user_map_preferences ADD COLUMN user_id INTEGER REFERENCES users(id) ON DELETE CASCADE`);
45+
// If we added user_id, populate from userId
46+
db.exec(`UPDATE user_map_preferences SET user_id = userId WHERE user_id IS NULL`);
47+
logger.debug('✅ Added user_id column to user_map_preferences');
48+
} catch {
49+
// Column already exists — ignore
50+
}
51+
52+
logger.debug('Migration 083: SQLite map preference columns ensured.');
2053
}
2154

2255
export async function runMigration083Postgres(client: PoolClient): Promise<void> {

src/server/models/User.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -395,38 +395,31 @@ export class UserModel {
395395
* Get user's map preferences
396396
*/
397397
getMapPreferences(userId: number): Record<string, any> | null {
398+
// Detect which user ID column exists (userId vs user_id depending on migration state)
399+
const columns = this.db.prepare("PRAGMA table_info(user_map_preferences)").all() as any[];
400+
const userIdCol = columns.find((c: any) => c.name === 'user_id') ? 'user_id' : 'userId';
401+
398402
const stmt = this.db.prepare(`
399-
SELECT
400-
map_tileset as mapTileset,
401-
show_paths as showPaths,
402-
show_neighbor_info as showNeighborInfo,
403-
show_route as showRoute,
404-
show_motion as showMotion,
405-
show_mqtt_nodes as showMqttNodes,
406-
show_meshcore_nodes as showMeshCoreNodes,
407-
show_animations as showAnimations,
408-
show_accuracy_regions as showAccuracyRegions,
409-
show_estimated_positions as showEstimatedPositions,
410-
position_history_hours as positionHistoryHours
411-
FROM user_map_preferences
412-
WHERE user_id = ?
403+
SELECT * FROM user_map_preferences
404+
WHERE ${userIdCol} = ?
405+
LIMIT 1
413406
`);
414407

415408
const row = stmt.get(userId) as any;
416409
if (!row) return null;
417410

418411
return {
419-
mapTileset: row.mapTileset || null,
420-
showPaths: Boolean(row.showPaths),
421-
showNeighborInfo: Boolean(row.showNeighborInfo),
422-
showRoute: Boolean(row.showRoute),
423-
showMotion: Boolean(row.showMotion),
424-
showMqttNodes: Boolean(row.showMqttNodes),
425-
showMeshCoreNodes: Boolean(row.showMeshCoreNodes),
426-
showAnimations: Boolean(row.showAnimations),
427-
showAccuracyRegions: Boolean(row.showAccuracyRegions),
428-
showEstimatedPositions: Boolean(row.showEstimatedPositions),
429-
positionHistoryHours: row.positionHistoryHours ?? null,
412+
mapTileset: row.map_tileset || row.mapTileset || null,
413+
showPaths: Boolean(row.show_paths ?? row.showPaths ?? false),
414+
showNeighborInfo: Boolean(row.show_neighbor_info ?? row.showNeighborInfo ?? false),
415+
showRoute: Boolean(row.show_route ?? row.showRoute ?? true),
416+
showMotion: Boolean(row.show_motion ?? row.showMotion ?? true),
417+
showMqttNodes: Boolean(row.show_mqtt_nodes ?? row.showMqttNodes ?? true),
418+
showMeshCoreNodes: Boolean(row.show_meshcore_nodes ?? row.showMeshCoreNodes ?? true),
419+
showAnimations: Boolean(row.show_animations ?? row.showAnimations ?? false),
420+
showAccuracyRegions: Boolean(row.show_accuracy_regions ?? row.showAccuracyRegions ?? false),
421+
showEstimatedPositions: Boolean(row.show_estimated_positions ?? row.showEstimatedPositions ?? false),
422+
positionHistoryHours: row.position_history_hours ?? row.positionHistoryHours ?? null,
430423
};
431424
}
432425

src/server/services/systemBackupService.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,11 @@ class SystemBackupService {
361361
*/
362362
async listBackups(): Promise<SystemBackupInfo[]> {
363363
try {
364+
const dbType = databaseService.drizzleDbType;
365+
const col = (name: string) => dbType === 'postgres' ? `"${name}"` : name;
366+
364367
const rows = await this.queryRows(`
365-
SELECT dirname, timestamp, type, size, table_count, meshmonitor_version, schema_version
368+
SELECT ${col('backupPath')}, timestamp, ${col('backupType')}, ${col('totalSize')}, ${col('tableCount')}, ${col('appVersion')}, ${col('schemaVersion')}
366369
FROM system_backup_history
367370
ORDER BY timestamp DESC
368371
`);
@@ -371,14 +374,14 @@ class SystemBackupService {
371374
// PostgreSQL returns bigint as strings, so we need to parse them
372375
const timestampNum = typeof row.timestamp === 'string' ? parseInt(row.timestamp, 10) : row.timestamp;
373376
return {
374-
dirname: row.dirname,
377+
dirname: row.backupPath,
375378
timestamp: new Date(timestampNum).toISOString(),
376379
timestampUnix: timestampNum,
377-
type: row.type,
378-
size: typeof row.size === 'string' ? parseInt(row.size, 10) : row.size,
379-
tableCount: row.table_count,
380-
meshmonitorVersion: row.meshmonitor_version,
381-
schemaVersion: row.schema_version
380+
type: row.backupType,
381+
size: typeof row.totalSize === 'string' ? parseInt(row.totalSize, 10) : row.totalSize,
382+
tableCount: row.tableCount,
383+
meshmonitorVersion: row.appVersion,
384+
schemaVersion: row.schemaVersion
382385
};
383386
});
384387
} catch (error) {

0 commit comments

Comments
 (0)