ice/models/Location.js
Claude Code a0fffcf4f0 Refactor architecture: Add models/services layer and refactor frontend
Major architectural improvements:
- Created models/services layer for better separation of concerns
  - Location model with async methods for database operations
  - ProfanityWord model for content moderation
  - DatabaseService for centralized database management
  - ProfanityFilterService refactored to use models
- Refactored frontend map implementations to share common code
  - MapBase class extracts 60-70% of duplicate functionality
  - Refactored implementations extend MapBase for specific features
  - Maintained unique geocoding capabilities per implementation
- Updated server.js to use new service architecture
- All routes now use async/await with models instead of raw queries
- Enhanced error handling and maintainability

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-05 19:21:51 -04:00

131 lines
No EOL
3.6 KiB
JavaScript

class Location {
constructor(db) {
this.db = db;
}
async getActive(hoursThreshold = 48) {
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, rows) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
async getAll() {
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, rows) => {
if (err) return reject(err);
resolve(rows);
}
);
});
}
async create(location) {
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(err) {
if (err) return reject(err);
resolve({ id: this.lastID, ...location });
}
);
});
}
async update(id, location) {
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(err) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async togglePersistent(id, persistent) {
return new Promise((resolve, reject) => {
this.db.run(
'UPDATE locations SET persistent = ? WHERE id = ?',
[persistent ? 1 : 0, id],
function(err) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async delete(id) {
return new Promise((resolve, reject) => {
this.db.run(
'DELETE FROM locations WHERE id = ?',
[id],
function(err) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async cleanupExpired(hoursThreshold = 48) {
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(err) {
if (err) return reject(err);
resolve({ changes: this.changes });
}
);
});
}
async initializeTable() {
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) => {
if (err) return reject(err);
this.db.run(`
ALTER TABLE locations ADD COLUMN persistent INTEGER DEFAULT 0
`, (err) => {
if (err && !err.message.includes('duplicate column name')) {
return reject(err);
}
resolve();
});
});
});
});
}
}
module.exports = Location;