From 843e30e2ab7a36c2acfed2f803e23d07f66972ad Mon Sep 17 00:00:00 2001 From: Szymon Uglis Date: Mon, 18 Nov 2024 20:08:55 +0100 Subject: [PATCH] Implement session support --- lib/running_on_dart.dart | 2 +- lib/src/api/api_server.dart | 68 ------------------- lib/src/web_app/api_server.dart | 100 ++++++++++++++++++++++++++++ lib/src/{api => web_app}/utils.dart | 9 +++ pubspec.lock | 8 +++ pubspec.yaml | 1 + templates/index.html | 26 +++++++- 7 files changed, 144 insertions(+), 70 deletions(-) delete mode 100644 lib/src/api/api_server.dart create mode 100644 lib/src/web_app/api_server.dart rename lib/src/{api => web_app}/utils.dart (59%) diff --git a/lib/running_on_dart.dart b/lib/running_on_dart.dart index 080f796..25699cc 100644 --- a/lib/running_on_dart.dart +++ b/lib/running_on_dart.dart @@ -16,4 +16,4 @@ export 'src/converter.dart'; export 'src/error_handler.dart'; export 'src/init.dart'; -export 'src/api/api_server.dart' show WebServer; +export 'src/web_app/api_server.dart' show WebServer; diff --git a/lib/src/api/api_server.dart b/lib/src/api/api_server.dart deleted file mode 100644 index 007fdc9..0000000 --- a/lib/src/api/api_server.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:io'; - -import 'package:injector/injector.dart'; -import 'package:mustachex/mustachex.dart'; -import 'package:running_on_dart/running_on_dart.dart'; -import 'package:running_on_dart/src/api/utils.dart'; -import 'package:running_on_dart/src/services/bot_info.dart'; -import 'package:shelf_cors_headers/shelf_cors_headers.dart'; - -import 'package:shelf_router/shelf_router.dart' as shelf_router; -import 'package:shelf/shelf.dart' as shelf; -import 'package:shelf/shelf_io.dart' as shelf_io; - -final clientId = getEnv('DISCORD_CLIENT_ID'); -final clientSecret = getEnv('DISCORD_CLIENT_SECRET'); -final clientRedirectUri = getEnv('DISCORD_REDIRECT_URI'); - -class WebServer { - Future _handleBotInfo(shelf.Request request) async { - final botInfo = await Injector.appInstance.get().getCurrentBotInfo(); - - return createOkResponse(botInfo.toJson()); - } - - // Future _handleGuildInfo(shelf.Request request) async { - // final outputJson = Injector.appInstance.get().guilds.cache.values.map((guild) { - // return { - // "id": guild.id.toString(), - // "icon_hash": guild.iconHash, - // "banner_hash": guild.bannerHash, - // "name": guild.name, - // "cached_members": guild.members.cache.length, - // "cached_roles": guild.roles.cache.length, - // }; - // }); - // - // return createOkResponse(outputJson.toList()); - // } - - Future _setupRouter() async { - return shelf_router.Router() - ..get("/", (shelf.Request request) async { - final data = await Injector.appInstance.get().getCurrentBotInfo(); - - var processor = MustachexProcessor(initialVariables: data.toJson()); - - final templateData = await File("templates/index.html").readAsString(); - final rendered = await processor.process(templateData); - - return shelf.Response.ok(rendered, headers: {"Content-Type": 'text/html'}); - }); - // ..get("/api/info", _handleBotInfo); - // ..post("/api/login", _handleLogin) - // ..get("/api/guilds", _authorized(_handleGuildInfo, [WebApiPermission.guilds])); - } - - shelf.Handler _authorized(shelf.Handler inner) => - shelf.Pipeline()/*.addMiddleware()*/.addHandler(inner); - - Future startServer() async { - final router = await _setupRouter(); - - final app = - const shelf.Pipeline().addMiddleware(shelf.logRequests()).addMiddleware(corsHeaders()).addHandler(router.call); - - await shelf_io.serve(app, "0.0.0.0", 8088); - } -} diff --git a/lib/src/web_app/api_server.dart b/lib/src/web_app/api_server.dart new file mode 100644 index 0000000..41674a0 --- /dev/null +++ b/lib/src/web_app/api_server.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; + +import 'package:injector/injector.dart'; + +import 'package:running_on_dart/running_on_dart.dart'; +import 'package:running_on_dart/src/web_app/utils.dart'; +import 'package:running_on_dart/src/services/bot_info.dart'; +import 'package:shelf_cors_headers/shelf_cors_headers.dart'; + +import 'package:shelf_router/shelf_router.dart' as shelf_router; +import 'package:shelf/shelf.dart' as shelf; +import 'package:shelf/shelf_io.dart' as shelf_io; + +import 'package:http/http.dart' as http; +import 'package:shelf_session/cookies_middleware.dart'; +import 'package:shelf_session/session_middleware.dart'; + +final clientId = getEnv('DISCORD_CLIENT_ID'); +final clientSecret = getEnv('DISCORD_CLIENT_SECRET'); +final clientRedirectUri = getEnv('DISCORD_REDIRECT_URI'); + +class WebServer { + Future _handleIndex(shelf.Request request) async { + final data = await Injector.appInstance.get().getCurrentBotInfo(); + + return createTwigResponse("index.html", parameters: { + ...data.toJson(), + 'clientId': clientId, + 'redirectUri': clientRedirectUri, + 'user_data': Session.getSession(request)?.data['user_data'] ?? false, + }); + } + + Future _handleRedirect(shelf.Request request) async { + final authCode = request.url.queryParameters['code']; + + final tokenResponse = await http.post(Uri.https('discord.com', '/api/oauth2/token'), body: { + 'client_id': clientId, + 'client_secret': clientSecret, + 'redirect_uri': clientRedirectUri, + 'grant_type': 'authorization_code', + 'code': authCode, + }, headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }); + + final tokenBodyJson = jsonDecode(tokenResponse.body); + final token = tokenBodyJson['access_token']; + + print(tokenResponse.body); + print(tokenResponse.statusCode); + + final userData = await http.get(Uri.https('discord.com', '/api/oauth2/@me'), headers: { + "Accept": "application/json", + "Authorization": "Bearer $token", + }); + + final userDataJson = jsonDecode(userData.body); + print(userData.body); + print(userData.statusCode); + + var session = Session.getSession(request); + session ??= Session.createSession(request); + session.data['user_data'] = { + 'id': userDataJson['user']['id'], + 'name': userDataJson['user']['global_name'] ?? userDataJson['user']['username'], + 'avatar': userDataJson['user']['avatar'], + 'expires_at': userDataJson['expires'], + 'token': token, + }; + session.expires = DateTime.now().add(Duration(seconds: tokenBodyJson['expires_in'])); + + return shelf.Response.seeOther("/"); + } + + Future _handleLogOut(shelf.Request request) async { + Session.deleteSession(request); + + return shelf.Response.seeOther("/"); + } + + Future _setupRouter() async { + return shelf_router.Router() + ..get("/", _sessionAware(_handleIndex)) + ..get("/redirect", _sessionAware(_handleRedirect)) + ..get("/logout", _sessionAware(_handleLogOut)); + } + + shelf.Handler _sessionAware(shelf.Handler inner) => + shelf.Pipeline().addMiddleware(cookiesMiddleware()).addMiddleware(sessionMiddleware()).addHandler(inner); + + Future startServer() async { + final router = await _setupRouter(); + + final app = + const shelf.Pipeline().addMiddleware(shelf.logRequests()).addMiddleware(corsHeaders()).addHandler(router.call); + + await shelf_io.serve(app, "0.0.0.0", 8088); + } +} diff --git a/lib/src/api/utils.dart b/lib/src/web_app/utils.dart similarity index 59% rename from lib/src/api/utils.dart rename to lib/src/web_app/utils.dart index 9ca35e8..bccb208 100644 --- a/lib/src/api/utils.dart +++ b/lib/src/web_app/utils.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'dart:io'; +import 'package:mustachex/mustachex.dart'; import 'package:shelf/shelf.dart' as shelf; shelf.Response createJsonErrorResponse(int errorCode, String errorMessage) { @@ -11,6 +13,13 @@ shelf.Response createOkResponse(Object? body) { return shelf.Response.ok(jsonEncode(body), headers: {"Content-Type": 'application/json'}); } +Future createTwigResponse(String name, {Map? parameters}) async { + final processor = MustachexProcessor(initialVariables: parameters); + final templateData = await File("templates/$name").readAsString(); + + return shelf.Response.ok(await processor.process(templateData), headers: {"Content-Type": 'text/html'}); +} + shelf.Response createUnauthorizedResponse(String errorMessage) => createJsonErrorResponse(400, errorMessage); shelf.Response createForbiddenResponse() => shelf.Response.forbidden(null); diff --git a/pubspec.lock b/pubspec.lock index 4fa7999..7ee5071 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -514,6 +514,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.4" + shelf_session: + dependency: "direct main" + description: + name: shelf_session + sha256: "5c658d16ddac1ad364d047e9a22e1023fcb383f2f32d5b621feb57c6c3ca8748" + url: "https://pub.dev" + source: hosted + version: "0.1.2" shelf_static: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 747a1f8..40e857d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: shelf: ^1.4.2 shelf_router: ^1.1.4 shelf_cors_headers: ^0.1.0 + shelf_session: ^0.1.2 mustachex: ^1.0.0 dev_dependencies: diff --git a/templates/index.html b/templates/index.html index 8fc8d63..b5f57df 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3,8 +3,32 @@ Test + -

Memory usage: {{memory_usage_string}}

+ + +

Is logged in: {{user_data}}