diff --git a/index.js b/index.js index 706a3a5..640038d 100755 --- a/index.js +++ b/index.js @@ -8,7 +8,6 @@ import { checkIfConfigIsAccessible, getProviders, refreshConfig } from './lib/ut import * as similarityCache from './lib/services/similarity-check/similarityCache.js'; import { runMigrations } from './lib/services/storage/migrations/migrate.js'; import { ensureDemoUserExists, ensureAdminUserExists } from './lib/services/storage/userStorage.js'; -import { cleanupDemoAtMidnight } from './lib/services/crons/demoCleanup-cron.js'; import { initTrackerCron } from './lib/services/crons/tracker-cron.js'; import logger from './lib/services/logger.js'; import { initActiveCheckerCron } from './lib/services/crons/listing-alive-cron.js'; @@ -54,7 +53,6 @@ await import('./lib/api/api.js'); if (settings.demoMode) { logger.info('Running in demo mode'); - cleanupDemoAtMidnight(); } ensureAdminUserExists(); diff --git a/lib/api/routes/userSettingsRoute.js b/lib/api/routes/userSettingsRoute.js index ba82749..38bb293 100644 --- a/lib/api/routes/userSettingsRoute.js +++ b/lib/api/routes/userSettingsRoute.js @@ -5,14 +5,15 @@ import restana from 'restana'; import SqliteConnection from '../../services/storage/SqliteConnection.js'; -import { upsertSettings } from '../../services/storage/settingsStorage.js'; +import { getSettings, upsertSettings } from '../../services/storage/settingsStorage.js'; +import { resetGeocoordinatesAndDistanceForUser } from '../../services/storage/listingsStorage.js'; import { geocodeAddress } from '../../services/geocoding/geoCodingService.js'; import { autocompleteAddress } from '../../services/geocoding/autocompleteService.js'; -import { calculateDistanceForUser } from '../../services/geocoding/distanceService.js'; import { fromJson } from '../../utils.js'; import { trackFeature } from '../../services/tracking/Tracker.js'; import { FEATURES } from '../../features.js'; import logger from '../../services/logger.js'; +import { runGeoCordTask } from '../../services/crons/geocoding-cron.js'; const service = restana(); const userSettingsRouter = service.newRouter(); @@ -43,6 +44,12 @@ userSettingsRouter.get('/autocomplete', async (req, res) => { userSettingsRouter.post('/home-address', async (req, res) => { const userId = req.session.currentUser; const { home_address } = req.body; + const settings = await getSettings(); + + if (settings.demoMode) { + res.send(new Error('In demo mode, it is not allowed to change the home address.')); + return; + } try { if (home_address) { @@ -50,7 +57,9 @@ userSettingsRouter.post('/home-address', async (req, res) => { const coords = await geocodeAddress(home_address); if (coords && coords.lat !== -1) { upsertSettings({ home_address: { address: home_address, coords } }, userId); - calculateDistanceForUser(userId); + resetGeocoordinatesAndDistanceForUser(userId); + //we do NOT wait for this to finish, as we don't want to block the response + runGeoCordTask(); res.send({ success: true, coords }); } else { res.statusCode = 400; diff --git a/lib/services/crons/demoCleanup-cron.js b/lib/services/crons/demoCleanup-cron.js deleted file mode 100644 index 0e69182..0000000 --- a/lib/services/crons/demoCleanup-cron.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2026 by Christian Kellner. - * Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause - */ - -import { removeJobsByUserId } from '../storage/jobStorage.js'; -import { getUsers } from '../storage/userStorage.js'; -import logger from '../logger.js'; -import cron from 'node-cron'; -import { getSettings } from '../storage/settingsStorage.js'; - -/** - * if we are running in demo environment, we have to cleanup the db files (specifically the jobs table) - */ -export function cleanupDemoAtMidnight() { - cron.schedule('0 0 * * *', cleanup); -} - -async function cleanup() { - const settings = await getSettings(); - if (settings.demoMode) { - const demoUser = getUsers(false).find((user) => user.username === 'demo'); - if (demoUser == null) { - logger.error('Demo user not found, cannot remove Jobs'); - return Promise.resolve(); - } - removeJobsByUserId(demoUser.id); - } -} diff --git a/lib/services/crons/geocoding-cron.js b/lib/services/crons/geocoding-cron.js index 1e67f2e..d6bd2e3 100644 --- a/lib/services/crons/geocoding-cron.js +++ b/lib/services/crons/geocoding-cron.js @@ -9,7 +9,7 @@ import { geocodeAddress, isGeocodingPaused } from '../geocoding/geoCodingService import { getJobs } from '../storage/jobStorage.js'; import { calculateDistanceForJob } from '../geocoding/distanceService.js'; -async function runTask() { +export async function runGeoCordTask() { const listings = getListingsToGeocode(); if (listings.length > 0) { for (const listing of listings) { @@ -33,7 +33,7 @@ async function runTask() { export async function initGeocodingCron() { // run directly on start - await runTask(); + await runGeoCordTask(); // then every 6 hours - cron.schedule('0 */6 * * *', runTask); + cron.schedule('0 */6 * * *', runGeoCordTask); } diff --git a/lib/services/storage/listingsStorage.js b/lib/services/storage/listingsStorage.js index 07b50d8..0a211ce 100755 --- a/lib/services/storage/listingsStorage.js +++ b/lib/services/storage/listingsStorage.js @@ -592,3 +592,23 @@ export const getListingById = (id, userId = null, isAdmin = false) => { )[0] || null ); }; + +/** + * Resets geocoordinates and distance for all listings related to a user. + * + * @param {string} userId + * @returns {void} + */ +export const resetGeocoordinatesAndDistanceForUser = (userId) => { + SqliteConnection.execute( + `UPDATE listings + SET latitude = NULL, + longitude = NULL, + distance_to_destination = NULL + WHERE job_id IN ( + SELECT id FROM jobs j + WHERE j.user_id = @userId + )`, + { userId }, + ); +}; diff --git a/lib/services/storage/userStorage.js b/lib/services/storage/userStorage.js index 6a26b79..38ff1ce 100644 --- a/lib/services/storage/userStorage.js +++ b/lib/services/storage/userStorage.js @@ -7,6 +7,7 @@ import * as hasher from '../security/hash.js'; import { nanoid } from 'nanoid'; import SqliteConnection from './SqliteConnection.js'; import { getSettings } from './settingsStorage.js'; +import { inDevMode } from '../../utils.js'; /** * Get all users. @@ -137,8 +138,12 @@ export const removeUser = (userId) => { export const ensureDemoUserExists = async () => { const settings = await getSettings(); if (!settings.demoMode) { - // Remove demo user (and cascade delete their jobs/listings) - SqliteConnection.execute(`DELETE FROM users WHERE username = 'demo'`); + if (!inDevMode()) { + // Remove demo user (and cascade delete their jobs/listings) + SqliteConnection.execute(`DELETE + FROM users + WHERE username = 'demo'`); + } return; } // Ensure demo user exists when demo mode is on diff --git a/package.json b/package.json index bda93fa..b93ff4d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fredy", - "version": "19.3.0", + "version": "19.3.1", "description": "[F]ind [R]eal [E]states [d]amn eas[y].", "scripts": { "prepare": "husky", diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 1e65c2b..8cf0725 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -124,14 +124,7 @@ export default function FredyApp() { } /> - - - - } - /> + } /> } description="Avg. Price of listings" /> diff --git a/ui/src/views/userSettings/UserSettings.jsx b/ui/src/views/userSettings/UserSettings.jsx index 4ca57fb..b35d1cd 100644 --- a/ui/src/views/userSettings/UserSettings.jsx +++ b/ui/src/views/userSettings/UserSettings.jsx @@ -33,7 +33,9 @@ const UserSettings = () => { if (response.status === 200) { setCoords(response.json.coords); await actions.userSettings.getUserSettings(); - Toast.success('Settings saved successfully'); + Toast.success( + 'Settings saved successfully. We will now start calculating distances for you. This may take a while and runs in the background.', + ); } else { Toast.error(response.json.error || 'Failed to save settings'); }