Add initial backend server
Этот коммит содержится в:
@@ -0,0 +1,78 @@
|
||||
import Fastify from 'fastify';
|
||||
import cors from '@fastify/cors';
|
||||
import rateLimit from '@fastify/rate-limit';
|
||||
import websocket from '@fastify/websocket';
|
||||
import { config } from './config.js';
|
||||
|
||||
const app = Fastify({
|
||||
logger: {
|
||||
transport: config.app.env === 'development' ? { target: 'pino-pretty' } : undefined
|
||||
},
|
||||
trustProxy: config.app.trustProxy
|
||||
});
|
||||
|
||||
await app.register(cors, {
|
||||
origin(origin, callback) {
|
||||
if (!origin) return callback(null, true);
|
||||
const allowed = new Set([config.cors.adminOrigin, config.cors.widgetOrigin]);
|
||||
callback(null, allowed.has(origin));
|
||||
},
|
||||
credentials: true
|
||||
});
|
||||
|
||||
await app.register(rateLimit, {
|
||||
max: 120,
|
||||
timeWindow: '1 minute'
|
||||
});
|
||||
|
||||
await app.register(websocket);
|
||||
|
||||
const clients = new Map();
|
||||
|
||||
app.get('/health', async () => ({
|
||||
ok: true,
|
||||
service: 'raspi-chat-backend',
|
||||
env: config.app.env
|
||||
}));
|
||||
|
||||
app.get('/api/config/public', async () => ({
|
||||
apiUrl: config.app.publicApiUrl,
|
||||
wsUrl: config.app.publicWsUrl
|
||||
}));
|
||||
|
||||
app.post('/api/widget/message', async (request) => {
|
||||
const body = request.body || {};
|
||||
const siteId = String(body.siteId || '');
|
||||
const visitorId = String(body.visitorId || '');
|
||||
const message = String(body.message || '').trim();
|
||||
|
||||
if (!siteId || !visitorId || !message) {
|
||||
return { ok: false, error: 'siteId, visitorId and message are required' };
|
||||
}
|
||||
|
||||
request.log.info({ siteId, visitorId }, 'Widget message received');
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
status: 'accepted',
|
||||
note: 'Telegram forwarding and persistence will be implemented next.'
|
||||
};
|
||||
});
|
||||
|
||||
app.get('/ws', { websocket: true }, (connection, request) => {
|
||||
const visitorId = request.query?.visitorId || crypto.randomUUID();
|
||||
clients.set(visitorId, connection);
|
||||
|
||||
connection.on('close', () => {
|
||||
clients.delete(visitorId);
|
||||
});
|
||||
|
||||
connection.send(JSON.stringify({ type: 'connected', visitorId }));
|
||||
});
|
||||
|
||||
try {
|
||||
await app.listen({ host: config.app.host, port: config.app.port });
|
||||
} catch (error) {
|
||||
app.log.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
Ссылка в новой задаче
Block a user