ice/src/models/Location.ts
Claude Code 30fdd72cc5 Add coordinate validation and ESLint integration
- Add explicit latitude/longitude validation in location submissions
- Implement ESLint with TypeScript support and flat config
- Auto-fix 621 formatting issues across codebase
- Add comprehensive tests for coordinate validation
- Update documentation with lint scripts and validation rules
- Maintain 128 passing tests with enhanced security

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-05 22:12:37 -04:00

162 lines
No EOL
4.9 KiB
TypeScript

import { Database } from 'sqlite3';
import { Location as LocationInterface } from '../types';
export interface LocationCreateInput {
address: string;
latitude?: number | undefined;
longitude?: number | undefined;
description?: string | undefined;
}
export interface LocationUpdateInput {
address?: string | undefined;
latitude?: number | undefined;
longitude?: number | undefined;
description?: string | undefined;
}
export interface DatabaseResult {
changes: number;
}
export interface LocationCreatedResult {
id: number;
address: string;
latitude?: number | undefined;
longitude?: number | undefined;
description?: string | undefined;
}
class Location {
private db: Database;
constructor(db: Database) {
this.db = db;
}
async getActive(hoursThreshold: number = 48): Promise<LocationInterface[]> {
const cutoffTime = new Date(Date.now() - hoursThreshold * 60 * 60 * 1000).toISOString();
return new Promise((resolve, reject) => {
this.db.all(
'SELECT * FROM locations WHERE created_at > ? OR persistent = 1 ORDER BY created_at DESC',
[cutoffTime],
(err: Error | null, rows: LocationInterface[]) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
async getAll(): Promise<LocationInterface[]> {
return new Promise((resolve, reject) => {
this.db.all(
'SELECT id, address, description, latitude, longitude, persistent, created_at FROM locations ORDER BY created_at DESC',
[],
(err: Error | null, rows: LocationInterface[]) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
async create(location: LocationCreateInput): Promise<LocationCreatedResult> {
const { address, latitude, longitude, description } = location;
return new Promise((resolve, reject) => {
this.db.run(
'INSERT INTO locations (address, latitude, longitude, description) VALUES (?, ?, ?, ?)',
[address, latitude, longitude, description],
function(this: { lastID: number }, err: Error | null) {
if (err) return reject(err);
resolve({ id: this.lastID, ...location });
}
);
});
}
async update(id: number, location: LocationUpdateInput): Promise<DatabaseResult> {
const { address, latitude, longitude, description } = location;
return new Promise((resolve, reject) => {
this.db.run(
'UPDATE locations SET address = ?, latitude = ?, longitude = ?, description = ? WHERE id = ?',
[address, latitude, longitude, description, id],
function(this: { changes: number }, err: Error | null) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async togglePersistent(id: number, persistent: boolean): Promise<DatabaseResult> {
return new Promise((resolve, reject) => {
this.db.run(
'UPDATE locations SET persistent = ? WHERE id = ?',
[persistent ? 1 : 0, id],
function(this: { changes: number }, err: Error | null) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async delete(id: number): Promise<DatabaseResult> {
return new Promise((resolve, reject) => {
this.db.run(
'DELETE FROM locations WHERE id = ?',
[id],
function(this: { changes: number }, err: Error | null) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async cleanupExpired(hoursThreshold: number = 48): Promise<DatabaseResult> {
const cutoffTime = new Date(Date.now() - hoursThreshold * 60 * 60 * 1000).toISOString();
return new Promise((resolve, reject) => {
this.db.run(
'DELETE FROM locations WHERE created_at < ? AND persistent = 0',
[cutoffTime],
function(this: { changes: number }, err: Error | null) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async initializeTable(): Promise<void> {
return new Promise((resolve, reject) => {
this.db.serialize(() => {
this.db.run(`
CREATE TABLE IF NOT EXISTS locations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
address TEXT NOT NULL,
latitude REAL,
longitude REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`, (err: Error | null) => {
if (err) return reject(err);
this.db.run(`
ALTER TABLE locations ADD COLUMN persistent INTEGER DEFAULT 0
`, (err: Error | null) => {
if (err && !err.message.includes('duplicate column name')) {
return reject(err);
}
resolve();
});
});
});
});
}
}
export default Location;