import 'dart:io'; import 'package:relic/relic.dart'; import 'config/config.dart'; import 'src/auth/auth_middleware.dart'; import 'src/gateway/gateway_handlers.dart'; import 'src/handlers/auth_handlers.dart'; import 'src/handlers/channel_handlers.dart'; import 'src/handlers/dev_db_handlers.dart'; import 'src/handlers/message_handlers.dart'; import 'src/handlers/public_handlers.dart'; import 'src/handlers/settings_handlers.dart'; import 'src/handlers/upload_handlers.dart'; import 'src/state/app_state.dart'; RelicApp? _currentApp; Future startServer() async { await _currentApp?.close(); _currentApp = RelicApp() // ============ PUBLIC ENDPOINTS (no auth) ============ // Auth ..post('/auth/register', registerHandler) ..post('/auth/login', loginHandler) ..post('/auth/join', joinServerHandler) // Public identity verification (for cross-server) ..get('/.well-known/users/:username', publicUserHandler) ..get('/.well-known/verify/:userId', publicVerifyHandler) ..get('/.well-known/server', publicServerInfoHandler) ..get('/uploads/:fileName', getUploadFileHandler) ..get('/attachments/:attachmentId', getAttachmentHandler) ..get('/avatars/:userId', getAvatarAliasHandler) ..get('/icons/:serverId', getServerIconAliasHandler) ..get('/banners/:serverId', getServerBannerAliasHandler) ..get('/gateway', gatewayHandler) // ============ PROTECTED ENDPOINTS (requires auth) ============ ..use('/api', jwtMiddleware) // Current user ..get('/api/me', getMeHandler) ..patch('/api/me', updateProfileHandler) ..put('/api/me/avatar', setAvatarHandler) // Server settings ..get('/api/server/settings', getServerSettingsHandler) ..patch('/api/server/settings', patchServerSettingsHandler) ..put('/api/server/icon', setServerIconHandler) ..put('/api/server/banner', setServerBannerHandler) // Dev DB tools (testing only, admin only, requires config flag) ..get('/api/dev/db/snapshot', getDevDbSnapshotHandler) ..post('/api/dev/db/reset', resetDevDbHandler) ..post('/api/dev/users/promote-admin', promoteUserToAdminHandler) ..post('/api/dev/users/demote-admin', demoteUserFromAdminHandler) // Channels ..get('/api/channels', getChannelsHandler) ..post('/api/channels', createChannelHandler) ..get('/api/channels/:channelId', getChannelHandler) // Messages ..get('/api/messages', getMessagesHandler) ..get('/api/channels/:channelId/media', getChannelMediaHandler) ..post('/api/uploads/init', initUploadHandler) ..put('/api/uploads/:uploadId', completeUploadHandler) ..post('/api/uploads', uploadFileHandler) ..post('/api/messages', sendMessageHandler) ..put('/api/messages/:messageId', editMessageHandler) ..delete('/api/messages/:messageId', deleteMessageHandler) // ============ LOGGING & FALLBACK ============ ..use('/', logRequests()) ..fallback = respondWith((_) => Response.notFound()); print(''); print('🚀 Server starting...'); print('Bind: ${'${Config.bindHost}:${Config.bindPort}'.padRight(34)}'); print('Public URL: ${Config.publicBaseUrl.padRight(26)}'); print(''); final bindAddress = InternetAddress.tryParse(Config.bindHost) ?? (await InternetAddress.lookup(Config.bindHost)).first; await _currentApp!.serve( address: bindAddress, port: Config.bindPort, ); } Future stopServer() async { await _currentApp?.close(); _currentApp = null; } void main() async { Config.initialize(); initializeStore(); await startServer(); }