mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
* feat: remember listing delete preference Persist soft/hard choice and skip-confirm in user settings.
231 lines
6.7 KiB
JavaScript
231 lines
6.7 KiB
JavaScript
/*
|
|
* Copyright (c) 2026 by Christian Kellner.
|
|
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
|
|
*/
|
|
|
|
/**
|
|
* Lightweight dev mock server on port 9998.
|
|
* Vite proxies /api to this. Run with: node tools/devMock.js
|
|
*/
|
|
|
|
import http from 'node:http';
|
|
const now = Date.now();
|
|
|
|
const users = [{ id: 1, username: 'admin', isAdmin: true, lastLogin: now, numberOfJobs: 2, mcpToken: 'tok_abc123' }];
|
|
|
|
const jobs = [
|
|
{
|
|
id: 'job1',
|
|
name: 'Munich Apartments',
|
|
enabled: true,
|
|
running: false,
|
|
blacklist: [],
|
|
provider: [
|
|
{
|
|
id: 'immoscout',
|
|
name: 'ImmobilienScout24',
|
|
url: 'https://www.immobilienscout24.de/Suche/S-T/Wohnung-Miete/Bayern/Muenchen',
|
|
},
|
|
],
|
|
notificationAdapter: [],
|
|
specFilter: { maxPrice: 1500, minSize: 50 },
|
|
numberOfFoundListings: 2,
|
|
isOnlyShared: false,
|
|
},
|
|
{
|
|
id: 'job2',
|
|
name: 'Berlin Rentals',
|
|
enabled: true,
|
|
running: false,
|
|
blacklist: ['keller', 'EG'],
|
|
provider: [{ id: 'immo', name: 'Immowelt', url: 'https://www.immowelt.de/suche/berlin/wohnungen/mieten' }],
|
|
notificationAdapter: [],
|
|
specFilter: {},
|
|
numberOfFoundListings: 2,
|
|
isOnlyShared: false,
|
|
},
|
|
];
|
|
|
|
const listings = [
|
|
{
|
|
id: 'l1',
|
|
title: '3-Zimmer-Wohnung in Schwabing',
|
|
price: 1350,
|
|
address: 'Leopoldstr. 42, München',
|
|
provider: 'ImmobilienScout24',
|
|
createdAt: now - 3600000,
|
|
created_at: now - 3600000,
|
|
image_url: null,
|
|
link: 'https://example.com/l1',
|
|
is_active: true,
|
|
isWatched: 0,
|
|
jobId: 'job1',
|
|
job_name: 'Munich Apartments',
|
|
size: 72,
|
|
rooms: 3,
|
|
description: 'Schöne 3-Zimmer-Wohnung in bester Lage in Schwabing. Balkon, Parkett, moderne Küche.',
|
|
latitude: 48.1598,
|
|
longitude: 11.5876,
|
|
},
|
|
{
|
|
id: 'l2',
|
|
title: 'Helle 2-Zimmer near Ostbahnhof',
|
|
price: 980,
|
|
address: 'Rosenheimer Str. 15, München',
|
|
provider: 'ImmobilienScout24',
|
|
createdAt: now - 7200000,
|
|
created_at: now - 7200000,
|
|
image_url: null,
|
|
link: 'https://example.com/l2',
|
|
is_active: true,
|
|
isWatched: 1,
|
|
jobId: 'job1',
|
|
job_name: 'Munich Apartments',
|
|
size: 55,
|
|
rooms: 2,
|
|
description: 'Helle 2-Zimmer-Wohnung nahe Ostbahnhof. Ruhige Lage, gute Anbindung.',
|
|
latitude: 48.1285,
|
|
longitude: 11.6005,
|
|
},
|
|
{
|
|
id: 'l3',
|
|
title: 'Altbau in Prenzlauer Berg',
|
|
price: 1100,
|
|
address: 'Kastanienallee 28, Berlin',
|
|
provider: 'Immowelt',
|
|
createdAt: now - 86400000,
|
|
created_at: now - 86400000,
|
|
image_url: null,
|
|
link: 'https://example.com/l3',
|
|
is_active: false,
|
|
isWatched: 0,
|
|
jobId: 'job2',
|
|
job_name: 'Berlin Rentals',
|
|
size: 65,
|
|
rooms: 2,
|
|
description: 'Charmante Altbauwohnung in Prenzlauer Berg. Hohe Decken, Stuck, Holzdielen.',
|
|
latitude: 52.5397,
|
|
longitude: 13.4098,
|
|
},
|
|
{
|
|
id: 'l4',
|
|
title: '4-Zimmer Neubau Mitte',
|
|
price: 2200,
|
|
address: 'Karl-Liebknecht-Str. 5, Berlin',
|
|
provider: 'Immowelt',
|
|
createdAt: now - 172800000,
|
|
created_at: now - 172800000,
|
|
image_url: null,
|
|
link: 'https://example.com/l4',
|
|
is_active: true,
|
|
isWatched: 1,
|
|
jobId: 'job2',
|
|
job_name: 'Berlin Rentals',
|
|
size: 95,
|
|
rooms: 4,
|
|
description: 'Moderner Neubau im Herzen von Berlin Mitte. Fußbodenheizung, Aufzug, Tiefgarage.',
|
|
latitude: 52.5219,
|
|
longitude: 13.4132,
|
|
},
|
|
];
|
|
|
|
const dashboard = {
|
|
general: { interval: 30, lastRun: now - 1800000, nextRun: now + 1800000 },
|
|
kpis: { totalJobs: 2, totalListings: 4, numberOfActiveListings: 3, medianPriceOfListings: 1225 },
|
|
pie: [
|
|
{ type: 'ImmobilienScout24', value: 50 },
|
|
{ type: 'Immowelt', value: 50 },
|
|
],
|
|
};
|
|
|
|
const routes = {
|
|
'GET /api/login/user': { userId: 1, username: 'admin', isAdmin: true },
|
|
'GET /api/admin/users': users,
|
|
'GET /api/jobs/provider': [
|
|
{ id: 'immoscout', name: 'ImmobilienScout24', baseUrl: 'https://www.immobilienscout24.de' },
|
|
{ id: 'immo', name: 'Immowelt', baseUrl: 'https://www.immowelt.de' },
|
|
],
|
|
'GET /api/jobs': jobs,
|
|
'GET /api/jobs/shareableUserList': [],
|
|
'GET /api/jobs/notificationAdapter': [],
|
|
'GET /api/admin/generalSettings': { demoMode: false, analyticsEnabled: true, interval: 30 },
|
|
'GET /api/user/settings': {},
|
|
'GET /api/version': { newVersion: null },
|
|
'GET /api/tracking/trackingPois': [],
|
|
'GET /api/dashboard': dashboard,
|
|
'GET /api/demo': { demoMode: false },
|
|
'POST /api/user/settings/news-hash': {},
|
|
'POST /api/user/settings/listing-deletion-preference': {},
|
|
};
|
|
|
|
const server = http.createServer((req, res) => {
|
|
const origin = req.headers.origin || 'http://localhost:5175';
|
|
const path = req.url.split('?')[0];
|
|
const key = req.method + ' ' + path;
|
|
|
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
|
|
if (req.method === 'OPTIONS') {
|
|
res.writeHead(200);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
if (path === '/api/jobs/events') {
|
|
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });
|
|
res.write(': connected\n\n');
|
|
const interval = setInterval(() => res.write(': ping\n\n'), 15000);
|
|
req.on('close', () => clearInterval(interval));
|
|
return;
|
|
}
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
const userMatch = path.match(/^\/api\/admin\/users\/(\d+)$/);
|
|
if (req.method === 'GET' && userMatch) {
|
|
const user = users.find((u) => u.id === parseInt(userMatch[1]));
|
|
res.writeHead(user ? 200 : 404);
|
|
res.end(JSON.stringify(user || { message: 'Not found' }));
|
|
return;
|
|
}
|
|
|
|
const listingMatch = path.match(/^\/api\/listings\/([^/]+)$/);
|
|
if (
|
|
req.method === 'GET' &&
|
|
listingMatch &&
|
|
!path.includes('/table') &&
|
|
!path.includes('/map') &&
|
|
!path.includes('/watch')
|
|
) {
|
|
const listing = listings.find((l) => l.id === listingMatch[1]);
|
|
res.writeHead(listing ? 200 : 404);
|
|
res.end(JSON.stringify(listing || { message: 'Not found' }));
|
|
return;
|
|
}
|
|
|
|
if (path.startsWith('/api/jobs/data')) {
|
|
res.writeHead(200);
|
|
res.end(JSON.stringify({ result: jobs, totalNumber: jobs.length, page: 1 }));
|
|
return;
|
|
}
|
|
if (path.startsWith('/api/listings/table')) {
|
|
res.writeHead(200);
|
|
res.end(JSON.stringify({ result: listings, totalNumber: listings.length, page: 1 }));
|
|
return;
|
|
}
|
|
if (path.startsWith('/api/listings/map')) {
|
|
res.writeHead(200);
|
|
res.end(JSON.stringify({ listings: listings.filter((l) => l.is_active), maxPrice: 2200 }));
|
|
return;
|
|
}
|
|
|
|
const data = routes[key];
|
|
res.writeHead(200);
|
|
res.end(JSON.stringify(data !== undefined ? data : {}));
|
|
});
|
|
|
|
server.listen(9998, () => console.warn('Dev mock ready on :9998'));
|