import 'dart:convert'; import 'dart:io'; import 'package:sqlite3/sqlite3.dart'; const seedLinks = [ 'vless://adbb9513-991a-4d64-9b30-1bf2283e7ed8@93.77.185.114:444?security=reality&encryption=none&pbk=M_VX89rLtCxGh45cRzXITGBgV3HTxW5c2zOEvqHFbSs&headerType=none&fp=random&spx=%2F&type=tcp&sni=www.apple.com&sid=f33f#6uuknvqv', 'vless://833a8f71-2b99-4e69-b39a-6d242c82fabb@147.45.145.102:444?security=reality&encryption=none&pbk=jQ7nhZoFKsFAwl8lhR4g5rBl_PT_-BA_lQmt1kG3EAs&headerType=&fp=random&spx=%2F&type=tcp&sni=ya.ru&sid=5a#vless_1-zxdrfypi4', ]; Future main() async { final db = _initDatabase(); ProcessSignal.sigint.watch().listen((_) { db.dispose(); exit(0); }); final server = await HttpServer.bind(InternetAddress.anyIPv4, 8080); print('Server started on http://${server.address.address}:${server.port}'); await for (final request in server) { final isConnectionsRoute = request.uri.path == '/connections'; if (!isConnectionsRoute) { request.response ..statusCode = HttpStatus.notFound ..write('Not Found'); await request.response.close(); continue; } if (request.method == 'GET') { final result = db.select('SELECT url FROM connections ORDER BY id ASC'); final links = result.map((row) => row['url'] as String).toList(); request.response ..statusCode = HttpStatus.ok ..headers.contentType = ContentType.json ..write(jsonEncode({'links': links})); await request.response.close(); continue; } if (request.method == 'POST') { await _handleCreateConnection(request, db); continue; } if (request.method == 'DELETE') { await _handleDeleteConnection(request, db); continue; } request.response ..statusCode = HttpStatus.methodNotAllowed ..headers.set(HttpHeaders.allowHeader, 'GET, POST, DELETE') ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Method Not Allowed'})); await request.response.close(); } } Database _initDatabase() { final dataDir = Directory('data'); if (!dataDir.existsSync()) { dataDir.createSync(recursive: true); } final db = sqlite3.open('data/vpn_links.db'); db.execute(''' CREATE TABLE IF NOT EXISTS connections ( id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL ) '''); final insert = db.prepare( 'INSERT OR IGNORE INTO connections(url, created_at) VALUES (?, ?)', ); final createdAt = DateTime.now().toUtc().toIso8601String(); for (final link in seedLinks) { insert.execute([link, createdAt]); } insert.dispose(); return db; } Future _handleCreateConnection(HttpRequest request, Database db) async { final body = await utf8.decoder.bind(request).join(); final url = _extractUrl(body); if (url == null) { request.response ..statusCode = HttpStatus.badRequest ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Body must be JSON: {"url": "..."}'})); await request.response.close(); return; } try { db.execute( 'INSERT INTO connections(url, created_at) VALUES (?, ?)', [url, DateTime.now().toUtc().toIso8601String()], ); request.response ..statusCode = HttpStatus.created ..headers.contentType = ContentType.json ..write(jsonEncode({'message': 'Link added'})); await request.response.close(); } on SqliteException catch (e) { if (e.extendedResultCode == 2067) { request.response ..statusCode = HttpStatus.conflict ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Link already exists'})); await request.response.close(); return; } request.response ..statusCode = HttpStatus.internalServerError ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Database error'})); await request.response.close(); } } Future _handleDeleteConnection(HttpRequest request, Database db) async { final body = await utf8.decoder.bind(request).join(); final url = _extractUrl(body); if (url == null) { request.response ..statusCode = HttpStatus.badRequest ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Body must be JSON: {"url": "..."}'})); await request.response.close(); return; } final delete = db.prepare('DELETE FROM connections WHERE url = ?'); delete.execute([url]); final changes = db.updatedRows; delete.dispose(); if (changes == 0) { request.response ..statusCode = HttpStatus.notFound ..headers.contentType = ContentType.json ..write(jsonEncode({'error': 'Link not found'})); await request.response.close(); return; } request.response ..statusCode = HttpStatus.ok ..headers.contentType = ContentType.json ..write(jsonEncode({'message': 'Link deleted'})); await request.response.close(); } String? _extractUrl(String body) { try { final json = jsonDecode(body); if (json is! Map) { return null; } final url = json['url']; if (url is! String || url.trim().isEmpty) { return null; } return url.trim(); } catch (_) { return null; } }