From 7a09c5960e8f001f0ef307d37f2e676d98059399 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 25 Aug 2024 13:02:42 -0400 Subject: [PATCH] asp.net migration, fix resourcehosts2, remove admin panel --- EpinelPS/Controllers/AccountController.cs | 104 +++++ EpinelPS/Controllers/LauncherController.cs | 97 +++++ .../Controllers/LevelInfiniteControlller.cs | 116 ++++++ EpinelPS/Controllers/LobbyApiController.cs | 18 + EpinelPS/EpinelPS.csproj | 14 +- EpinelPS/IntlServer/IntlMsgHandler.cs | 6 +- EpinelPS/LobbyServer/LobbyHandler.cs | 5 +- EpinelPS/LobbyServer/LobbyMsgHandler.cs | 23 +- .../Msgs/Misc/GetResourceHosts2.cs | 1 + EpinelPS/Program.cs | 381 +++++++----------- EpinelPS/Properties/launchSettings.json | 12 + EpinelPS/Utils/AssetDownloadUtil.cs | 16 + EpinelPS/Utils/GreatLogger.cs | 18 +- EpinelPS/Utils/NetUtils.cs | 20 + EpinelPS/Utils/PacketDecryption.cs | 9 +- .../{www => wwwroot}/admin/assets/login.css | 0 .../{www => wwwroot}/admin/assets/login.jpg | Bin .../{www => wwwroot}/admin/assets/style.css | 0 EpinelPS/{www => wwwroot}/admin/dashbrd.html | 0 EpinelPS/{www => wwwroot}/admin/index.html | 0 EpinelPS/{www => wwwroot}/admin/nav.html | 0 .../nikke_launcher}/index.html | 0 22 files changed, 578 insertions(+), 262 deletions(-) create mode 100644 EpinelPS/Controllers/AccountController.cs create mode 100644 EpinelPS/Controllers/LauncherController.cs create mode 100644 EpinelPS/Controllers/LevelInfiniteControlller.cs create mode 100644 EpinelPS/Controllers/LobbyApiController.cs create mode 100644 EpinelPS/Properties/launchSettings.json rename EpinelPS/{www => wwwroot}/admin/assets/login.css (100%) rename EpinelPS/{www => wwwroot}/admin/assets/login.jpg (100%) rename EpinelPS/{www => wwwroot}/admin/assets/style.css (100%) rename EpinelPS/{www => wwwroot}/admin/dashbrd.html (100%) rename EpinelPS/{www => wwwroot}/admin/index.html (100%) rename EpinelPS/{www => wwwroot}/admin/nav.html (100%) rename EpinelPS/{www/launcher => wwwroot/nikke_launcher}/index.html (100%) diff --git a/EpinelPS/Controllers/AccountController.cs b/EpinelPS/Controllers/AccountController.cs new file mode 100644 index 0000000..0653d71 --- /dev/null +++ b/EpinelPS/Controllers/AccountController.cs @@ -0,0 +1,104 @@ +using EpinelPS.Database; +using EpinelPS.IntlServer; +using EpinelPS.Utils; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Org.BouncyCastle.Ocsp; +using Swan.Logging; +using static EpinelPS.IntlServer.IntlAccountRegisterEndpoint; +using static EpinelPS.IntlServer.IntlLogin1Endpoint; +using static EpinelPS.IntlServer.IntlLogin2Endpoint; +using static EpinelPS.IntlServer.IntlMsgHandler; +using static EpinelPS.IntlServer.SendCodeEndpoint; + +namespace EpinelPS.Controllers +{ + [Route("account")] + [ApiController] + public class AccountController : ControllerBase + { + private const string BadAuthToken = "{\"msg\":\"the account does not exists!\",\"ret\":2001,\"seq\":\"123" + "\"}"; + + [HttpPost] + [Route("login")] + public string Login(string seq, [FromBody] LoginEndpoint2Req req) + { + foreach (var item in JsonDb.Instance.Users) + { + if (item.Username == req.account && item.Password == req.password) + { + var tok = IntlHandler.CreateLauncherTokenForUser(item); + item.LastLogin = DateTime.UtcNow; + JsonDb.Save(); + + return "{\"expire\":" + tok.ExpirationTime + ",\"is_login\":true,\"msg\":\"Success\",\"register_time\":" + item.RegisterTime + ",\"ret\":0,\"seq\":\"" + seq + "\",\"token\":\"" + tok.Token + "\",\"uid\":\"" + item.ID + "\"}"; + } + } + + return "{\"msg\":\"the account does not exists!\",\"ret\":2001,\"seq\":\"" + seq + "\"}"; + } + + + [HttpPost] + [Route("sendcode")] + public string SendCode(string seq, [FromBody] SendCodeRequest req) + { + // Pretend that we send a code. + return "{\"expire_time\":898,\"msg\":\"Success\",\"ret\":0,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("codestatus")] + public string CodeStatus(string seq, [FromBody] SendCodeRequest req) + { + // Pretend that code is valid + return "{\"expire_time\":759,\"msg\":\"Success\",\"ret\":0,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("getuserinfo")] + public string GetUserInfo(string seq, [FromBody] AuthPkt2 req) + { + (User?, AccessToken?) res; + if ((res = NetUtils.GetUser(req.token)).Item1 == null) return BadAuthToken; + User user = res.Item1; + AccessToken? tok = res.Item2; + + // Pretend that code is valid + return "{\"account_type\":1,\"birthday\":\"1970-01\",\"email\":\"" + user.Username + "\",\"expire\":" + tok.ExpirationTime + ",\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"is_receive_video\":-1,\"lang_type\":\"en\",\"msg\":\"Success\",\"nick_name\":\"\",\"phone\":\"\",\"phone_area_code\":\"\",\"privacy_policy\":\"1\",\"privacy_update_time\":1717783097,\"region\":\"724\",\"ret\":0,\"seq\":\"" + seq + "\",\"terms_of_service\":\"\",\"terms_update_time\":0,\"uid\":\"" + user.ID + "\",\"user_agreed_dt\":\"\",\"user_agreed_pp\":\"1\",\"user_agreed_tos\":\"\",\"user_name\":\"" + user.PlayerName + "\",\"username_pass_verify\":0}"; + } + + [HttpPost] + [Route("register")] + public string RegisterAccount(string seq, [FromBody] RegisterEPReq req) + { + // check if the account already exists + foreach (var item in JsonDb.Instance.Users) + { + if (item.Username == req.account) + { + return "{\"msg\":\"send code failed; invalid account\",\"ret\":2112,\"seq\":\"" + seq + "\"}"; + } + } + + var uid = (ulong)new Random().Next(1, int.MaxValue); + + // Check if we havent generated a UID that exists + foreach (var item in JsonDb.Instance.Users) + { + if (item.ID == uid) + { + uid -= (ulong)new Random().Next(1, 1221); + } + } + + var user = new User() { ID = uid, Password = req.password, RegisterTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Username = req.account, PlayerName = "Player_" + Rng.RandomString(8) }; + + JsonDb.Instance.Users.Add(user); + + var tok = IntlHandler.CreateLauncherTokenForUser(user); + + return "{\"expire\":" + tok.ExpirationTime + ",\"is_login\":false,\"msg\":\"Success\",\"register_time\":" + user.RegisterTime + ",\"ret\":0,\"seq\":\"" + seq + "\",\"token\":\"" + tok.Token + "\",\"uid\":\"" + user.ID + "\"}"; + } + } +} diff --git a/EpinelPS/Controllers/LauncherController.cs b/EpinelPS/Controllers/LauncherController.cs new file mode 100644 index 0000000..068eeae --- /dev/null +++ b/EpinelPS/Controllers/LauncherController.cs @@ -0,0 +1,97 @@ +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Controllers +{ + [Route("/api/v1")] + [ApiController] + public class LauncherController : Controller + { + [HttpPost] + [Route("fleet.auth.game.AuthSvr/Login")] + public string LauncherLogin() + { + return @"{ + ""result"": { + ""error_code"": 0, + ""error_message"": ""COMM_SUCC"" + }, + ""channel"": 0, + ""game_id"": ""0"", + ""openid"": """", + ""uid"": """", + ""biz_ticket"": """", + ""expire_interval"": 0, + ""refresh_interval"": 0, + ""login_key"": """", + ""login_ticket"": """", + ""third_uid"": """" +}"; + } + + [HttpPost] + [Route("fleet.repo.game.RepoSVC/GetRegion")] + public string LauncherGetRegion() + { + return @"{ + ""result"": { + ""error_code"": 0, + ""error_message"": ""success"" + }, + ""region_info"": [ + { + ""game_id"": ""16601"", + ""region_id"": ""10001"", + ""region_name_en_us"": ""Global"", + ""region_name_i18n"": """", + ""region_description_en_us"": ""Nikke Global Version"", + ""region_description_i18n"": """", + ""bind_branches"": """", + ""meta_data"": """", + ""sequence"": 0, + ""status"": 2, + ""branch_info"": [ + { + ""game_id"": ""16601"", + ""branch_id"": ""1"", + ""branch_name"": ""Official_release"", + ""branch_type"": 0, + ""description"": ""正式发布环境 release包"" + } + ] + } + ] +}"; + } + + [HttpPost] + [Route("fleet.repo.game.RepoSVC/GetGameLauncher")] + public string LauncherGetLauncher() + { + return @"{ + ""result"": { + ""error_code"": 0, + ""error_message"": ""COMM_SUCC"" + }, + ""game_launcher_info"": [ + { + ""id"": 27, + ""execute_file"": ""NIKKE\\Game\\NIKKE.exe"", + ""param"": """", + ""description"": ""Nikke main process"", + ""os"": ""any"", + ""branch_id"": 0, + ""status"": 1, + ""param_type"": 1 + } + ] +}"; + } + + [HttpPost] + [Route("fleet.repo.game.RepoSVC/GetVersion")] + public string LauncherGetVersion() + { + return System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "gameversion.json")); + } + } +} diff --git a/EpinelPS/Controllers/LevelInfiniteControlller.cs b/EpinelPS/Controllers/LevelInfiniteControlller.cs new file mode 100644 index 0000000..df9986e --- /dev/null +++ b/EpinelPS/Controllers/LevelInfiniteControlller.cs @@ -0,0 +1,116 @@ +using EpinelPS.Database; +using EpinelPS.Utils; +using Microsoft.AspNetCore.Mvc; +using Org.BouncyCastle.Ocsp; +using static EpinelPS.IntlServer.IntlLogin1Endpoint; +using static EpinelPS.IntlServer.IntlLogin2Endpoint; +using static EpinelPS.IntlServer.IntlMsgHandler; + +namespace EpinelPS.Controllers +{ + [Route("/v2")] + [ApiController] + public class LevelInfiniteControlller : Controller + { + private const string BadAuthToken = "{\"msg\":\"the account does not exists!\",\"ret\":2001,\"seq\":\"123" + "\"}"; + + [HttpPost] + [Route("conf/get_conf")] + public string GetConfig(string sig) + { + return "{\"conf_version\":\"102\",\"msg\":\"\",\"ret\":1,\"seq\":\"" + sig + "\"}"; + } + + + [HttpPost] + [Route("auth/login")] + public string AuthLogin(string seq, [FromBody] LoginEndpoint1Req req) + { + foreach (var tok in JsonDb.Instance.LauncherAccessTokens) + { + if (tok.Token == req.channel_info.account_token) + { + var user = JsonDb.Instance.Users.Find(x => x.ID == tok.UserID); + if (user != null) + { + // todo: they use another token here, but we will reuse the same one. + // todo: use a class for this, this is a mess + return "{\"birthday\":\"1970-01\",\"channel_info\":{\"account\":\"" + user.Username + "\",\"account_plat_type\":131,\"account_token\":\"" + req.channel_info.account_token + "\",\"account_type\":1,\"account_uid\":\"" + user.ID + "\",\"expire_ts\":1721667004,\"is_login\":true,\"lang_type\":\"en\",\"phone_area_code\":\"\",\"token\":\"" + req.channel_info.account_token + "\"},\"del_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"1719075066-0339089836-025921-1161847390\\\"}\",\"del_account_status\":0,\"del_li_account_status\":0,\"email\":\"" + user.Username + "\",\"extra_json\":{\"del_li_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"" + seq + "\\\"}\",\"get_status_rsp\":{\"adult_age\":14,\"adult_age_map\":{},\"adult_check_status\":1,\"adult_check_status_expiration\":\"0\",\"adult_status_map\":{},\"certificate_type\":3,\"email\":\"\",\"eu_user_agree_status\":0,\"game_grade\":0,\"game_grade_map\":{},\"is_dma\":true,\"is_eea\":false,\"is_need_li_cert\":false,\"msg\":\"success\",\"need_parent_control\":0,\"need_realname_auth\":0,\"parent_certificate_status\":0,\"parent_certificate_status_expiration\":\"0\",\"parent_control_map\":{},\"qr_code_ret\":0,\"realname_auth_status\":0,\"region\":\"724\",\"ret\":0,\"ts\":\"1719075065\"},\"need_notify_rsp\":{\"game_sacc_openid\":\"\",\"game_sacc_uid\":\"\",\"has_game_sacc_openid\":false,\"has_game_sacc_uid\":false,\"has_li_openid\":true,\"has_li_uid\":true,\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"li_openid\":\"" + user.ID + "\",\"li_uid\":\"2752409592679849\",\"need_notify\":false,\"user_agreed_game_dma\":\"2\",\"user_agreed_game_pp\":\"1\",\"user_agreed_game_tos\":\"1\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"1\",\"user_agreed_li_tos\":\"\"}},\"first_login\":0,\"gender\":0,\"msg\":\"success\",\"need_name_auth\":false,\"openid\":\"" + user.ID + "\",\"pf\":\"LevelInfinite_LevelInfinite-Windows-windows-Windows-LevelInfinite-09af79d65d6e4fdf2d2569f0d365739d-" + user.ID + "\",\"pf_key\":\"abc\",\"picture_url\":\"\",\"reg_channel_dis\":\"Windows\",\"ret\":0,\"seq\":\"29080-2d28ea26-d71f-4822-9118-0156f1e2dba4-1719075060-99\",\"token\":\"" + tok.Token + "\",\"token_expire_time\":" + tok.ExpirationTime + ",\"uid\":\"" + user.ID + "\",\"user_name\":\"" + user.PlayerName + "\"}"; + } + + break; + } + } + + // TODO: proper token expired message + return "{\"msg\":\"the account does not exists!\",\"ret\":2001,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("auth/auto_login")] + public string AutoLogin(string seq) + { + return "{\"del_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"" + seq + "\\\"}\",\"del_account_status\":0,\"del_li_account_status\":0,\"extra_json\":{\"del_li_account_info\":\"{\\\"ret\\\":0,\\\"msg\\\":\\\"\\\",\\\"status\\\":0,\\\"created_at\\\":\\\"0\\\",\\\"target_destroy_at\\\":\\\"0\\\",\\\"destroyed_at\\\":\\\"0\\\",\\\"err_code\\\":0,\\\"seq\\\":\\\"" + seq + "\\\"}\",\"get_status_msg\":\"success\",\"get_status_ret\":0,\"get_status_rsp\":{\"adult_age\":14,\"adult_age_map\":{},\"adult_check_status\":1,\"adult_check_status_expiration\":\"0\",\"adult_status_map\":{},\"certificate_type\":3,\"email\":\"\",\"eu_user_agree_status\":0,\"game_grade\":0,\"game_grade_map\":{},\"is_dma\":true,\"is_eea\":false,\"is_need_li_cert\":false,\"msg\":\"success\",\"need_parent_control\":0,\"need_realname_auth\":0,\"parent_certificate_status\":0,\"parent_certificate_status_expiration\":\"0\",\"parent_control_map\":{},\"qr_code_ret\":0,\"realname_auth_status\":0,\"region\":\"724\",\"ret\":0,\"ts\":\"" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() ++ "\"},\"need_notify_msg\":\"success\",\"need_notify_ret\":0,\"need_notify_rsp\":{\"has_bind_li\":true,\"is_receive_email\":1,\"is_receive_email_in_night\":0,\"user_agreed_game_dma\":\"2\",\"user_agreed_game_pp\":\"1\",\"user_agreed_game_tos\":\"1\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"1\",\"user_agreed_li_tos\":\"\"}},\"msg\":\"success\",\"ret\":0,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("minorcer/get_status")] + public string MinorcerStatus(string seq) + { + return "{\"adult_age\":15,\"adult_age_map\":{},\"adult_check_status\":1,\"adult_check_status_expiration\":\"0\",\"adult_status_map\":{},\"certificate_type\":3,\"email\":\"\",\"eu_user_agree_status\":0,\"game_grade\":0,\"game_grade_map\":{},\"is_dma\":true,\"is_eea\":false,\"is_need_li_cert\":false,\"msg\":\"success\",\"need_parent_control\":0,\"need_realname_auth\":0,\"parent_certificate_status\":0,\"parent_certificate_status_expiration\":\"0\",\"parent_control_map\":{},\"qr_code_ret\":0,\"realname_auth_status\":0,\"region\":\"300\",\"ret\":0,\"seq\":\"" + seq + "\",\"ts\":\"1719156511\"}"; + } + + [HttpPost] + [Route("profile/userinfo")] + public string QueryUserInfo(string seq, [FromBody] AuthPkt2 req) + { + User? user; + if ((user = NetUtils.GetUser(req.token).Item1) == null) return BadAuthToken; + + return "{\"bind_list\":[{\"channel_info\":{\"birthday\":\"1970-01\",\"email\":\"" + user.Username + "\",\"is_receive_email\":1,\"lang_type\":\"en\",\"last_login_time\":1719075003,\"nick_name\":\"\",\"phone\":\"\",\"phone_area_code\":\"\",\"region\":\"724\",\"register_account\":\"" + user.Username + "\",\"register_account_type\":1,\"register_time\":" + user.RegisterTime + ",\"seq\":\"abc\",\"uid\":\"" + user.ID + "\",\"user_name\":\"" + user.PlayerName + "\",\"username_pass_verify\":0},\"channelid\":131,\"email\":\"" + user.Username + "\",\"picture_url\":\"\",\"user_name\":\"" + user.PlayerName + "\"}],\"birthday\":\"1970-01\",\"email\":\"" + user.Username + "\",\"gender\":0,\"msg\":\"success\",\"picture_url\":\"\",\"ret\":0,\"seq\":\"" + seq + "\",\"user_name\":\"" + user.PlayerName + "\"}"; + } + + [HttpPost] + [Route("profile/query_account_info")] + public string QueryAccountInfo(string seq, [FromBody] AuthPkt req) + { + User? user; + if ((user = NetUtils.GetUser(req.channel_info.token).Item1) == null) return BadAuthToken; + + // Pretend that code is valid + return "{\"game_sacc_openid\":\"\",\"game_sacc_uid\":\"\",\"has_game_sacc_openid\":false,\"has_game_sacc_uid\":false,\"has_li_openid\":false,\"has_li_uid\":true,\"is_receive_email\":-1,\"is_receive_email_in_night\":-1,\"li_openid\":\"\",\"li_uid\":\"" + user.ID + "\",\"msg\":\"success\",\"need_notify\":false,\"ret\":0,\"seq\":\"" + seq + "\",\"user_agreed_game_dma\":\"\",\"user_agreed_game_pp\":\"\",\"user_agreed_game_tos\":\"\",\"user_agreed_li_dt\":\"\",\"user_agreed_li_pp\":\"\",\"user_agreed_li_tos\":\"\"}"; + } + + + [HttpPost] + [Route("reward/send")] + public string SendDailyReward(string seq) + { + // Level infinite pass daily reward coints, not implemented as they are inaccessible currently + return "{\"msg\":\"success\",\"ret\":0,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("profile/set_protocol")] + public string SetProtocol(string seq) + { + // Enable encryption, not used in this server. + return "{\"msg\":\"success\",\"ret\":0,\"seq\":\"" + seq + "\"}"; + } + + [HttpPost] + [Route("notice/get_notice_content")] + public string GetNotices(string seq) + { + return "{\r\n \"msg\": \"success\",\r\n \"notice_list\": [\r\n {\r\n \"app_id\": \"3001001\",\r\n \"app_notice_id\": \"post-6rpvwgrdx1b\",\r\n \"area_list\": \"[\\\"81\\\",\\\"82\\\",\\\"83\\\",\\\"84\\\",\\\"85\\\"]\",\r\n \"content_list\": [\r\n {\r\n \"app_content_id\": \"post-9ilpu79xxzp\",\r\n \"content\": \"This isn't working\",\r\n \"extra_data\": \"{}\",\r\n \"id\": 48706,\r\n \"lang_type\": \"en\",\r\n \"picture_list\": [\r\n {\r\n \"extra_data\": \"{\\\"id\\\":\\\"TitleImage\\\"}\",\r\n \"hash\": \"44a99a61152b5b80a0466ff9f0cee2bc\",\r\n \"redirect_url\": \"\",\r\n \"url\": \"pnt-console-cdn.playernetwork.intlgame.com/prod/29080/notice/022681b1121a40259a575fbe587651b4.jpg\"\r\n }\r\n ],\r\n \"title\": \"New Character\",\r\n \"update_time\": 1717637493\r\n }\r\n ],\r\n \"end_time\": 1819431999,\r\n \"extra_data\": \"{\\\"NoticeType\\\":\\\"Event\\\",\\\"Order\\\":\\\"11\\\",\\\"extra_reserved\\\":\\\"{\\\\\\\"Author\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"Category\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"CreateType\\\\\\\":\\\\\\\"4\\\\\\\",\\\\\\\"IsOpenService\\\\\\\":\\\\\\\"0\\\\\\\",\\\\\\\"IsToping\\\\\\\":true,\\\\\\\"Keyword\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"Sort\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"TopEnd\\\\\\\":\\\\\\\"2030-01-01 00:00:01\\\\\\\",\\\\\\\"TopStart\\\\\\\":\\\\\\\"2000-01-01 00:00:01\\\\\\\"}\\\"}\",\r\n \"id\": 7560,\r\n \"picture_list\": [],\r\n \"start_time\": 1717617599,\r\n \"status\": 1,\r\n \"update_time\": 1717637494\r\n }\r\n ],\r\n \"ret\": 0,\r\n \"seq\": \"" + seq + "\"\r\n}"; + } + + [HttpPost] + [Route("lbs/ipregion")] + public string GetIpRegion(string seq) + { + return "{\"alpha2\":\"GR\",\"extra_json\":{\"certificate_type_map\":{}},\"msg\":\"success\",\"region\":\"300\",\"ret\":0,\"seq\":\"" + seq + "\",\"timestamp\":324234322}"; + } + } +} diff --git a/EpinelPS/Controllers/LobbyApiController.cs b/EpinelPS/Controllers/LobbyApiController.cs new file mode 100644 index 0000000..55f5cf1 --- /dev/null +++ b/EpinelPS/Controllers/LobbyApiController.cs @@ -0,0 +1,18 @@ +using EpinelPS.LobbyServer; +using Microsoft.AspNetCore.Mvc; + +namespace EpinelPS.Controllers +{ + [Route("v1")] + [ApiController] + public class LobbyApiController : ControllerBase + { + [HttpPost] + [Route("{**all}", Order = int.MaxValue)] + [Consumes("application/octet-stream+protobuf")] + public async Task CatchAll(string all) + { + await LobbyHandler.DispatchSingle(HttpContext); + } + } +} diff --git a/EpinelPS/EpinelPS.csproj b/EpinelPS/EpinelPS.csproj index c344cfe..be35cb5 100644 --- a/EpinelPS/EpinelPS.csproj +++ b/EpinelPS/EpinelPS.csproj @@ -1,4 +1,4 @@ - + Exe @@ -30,6 +30,16 @@ + + + + + + + + + + Always @@ -40,7 +50,7 @@ Always - + Always diff --git a/EpinelPS/IntlServer/IntlMsgHandler.cs b/EpinelPS/IntlServer/IntlMsgHandler.cs index 1a2909f..fee371c 100644 --- a/EpinelPS/IntlServer/IntlMsgHandler.cs +++ b/EpinelPS/IntlServer/IntlMsgHandler.cs @@ -121,9 +121,9 @@ namespace EpinelPS.IntlServer } public class AuthPkt2 { - public string token = ""; - public string openid = ""; - public string account_token = ""; + public string token { get; set; } = ""; + public string openid { get; set; } = ""; + public string account_token { get; set; } = ""; } } } diff --git a/EpinelPS/LobbyServer/LobbyHandler.cs b/EpinelPS/LobbyServer/LobbyHandler.cs index 9c994e7..519af5f 100644 --- a/EpinelPS/LobbyServer/LobbyHandler.cs +++ b/EpinelPS/LobbyServer/LobbyHandler.cs @@ -36,12 +36,13 @@ namespace EpinelPS.LobbyServer } } } - public static async Task DispatchSingle(IHttpContext ctx) + public static async Task DispatchSingle(HttpContext ctx) { LobbyMsgHandler? handler = null; + string path = ctx.Request.Path.Value.Replace("/v1", ""); foreach (var item in Handlers) { - if (ctx.RequestedPath == item.Key) + if (path == item.Key) { handler = item.Value; } diff --git a/EpinelPS/LobbyServer/LobbyMsgHandler.cs b/EpinelPS/LobbyServer/LobbyMsgHandler.cs index b26d0c7..74d1c9d 100644 --- a/EpinelPS/LobbyServer/LobbyMsgHandler.cs +++ b/EpinelPS/LobbyServer/LobbyMsgHandler.cs @@ -1,5 +1,4 @@ -using EmbedIO; -using Google.Protobuf; +using Google.Protobuf; using EpinelPS.Database; using EpinelPS.Utils; @@ -7,7 +6,7 @@ namespace EpinelPS.LobbyServer { public abstract class LobbyMsgHandler { - protected IHttpContext? ctx; + protected HttpContext? ctx; protected ulong UserId; protected string UsedAuthToken = ""; public byte[] ReturnBytes = []; @@ -25,12 +24,12 @@ namespace EpinelPS.LobbyServer ctx = null; } - public async Task HandleAsync(IHttpContext ctx) + public async Task HandleAsync(HttpContext ctx) { this.ctx = ctx; - if (ctx.Request.Headers.AllKeys.Contains("Authorization")) + if (ctx.Request.Headers.Keys.Contains("Authorization")) { - var token = ctx.Request.Headers["Authorization"]; + var token = ctx.Request.Headers.Authorization.FirstOrDefault(); if (token != null) { UsedAuthToken = token; @@ -49,7 +48,7 @@ namespace EpinelPS.LobbyServer UserId = item.Value.UserId; } } - if (UserId == 0) throw new HttpException(403); + if (UserId == 0) throw new Exception("403"); await HandleAsync(); } @@ -68,20 +67,20 @@ namespace EpinelPS.LobbyServer } else { - ctx.Response.ContentEncoding = null; ctx.Response.ContentType = "application/octet-stream+protobuf"; - ctx.Response.ContentLength64 = data.CalculateSize(); + ctx.Response.ContentLength = data.CalculateSize(); bool encrypted = false; - var responseBytes = encrypted ? new MemoryStream() : ctx.Response.OutputStream; + var responseBytes = encrypted ? new MemoryStream() : ctx.Response.Body; var x = new CodedOutputStream(responseBytes); data.WriteTo(x); + x.Flush(); if (encrypted) { - ctx.Response.Headers.Set(System.Net.HttpRequestHeader.ContentEncoding, "gzip,enc"); + ctx.Response.Headers.ContentEncoding = new Microsoft.Extensions.Primitives.StringValues("gzip,enc"); var enc = PacketDecryption.EncryptData(((MemoryStream)responseBytes).ToArray(), UsedAuthToken); - await ctx.Response.OutputStream.WriteAsync(enc, ctx.CancellationToken); + await ctx.Response.Body.WriteAsync(enc); } } } diff --git a/EpinelPS/LobbyServer/Msgs/Misc/GetResourceHosts2.cs b/EpinelPS/LobbyServer/Msgs/Misc/GetResourceHosts2.cs index 881b639..710d5f6 100644 --- a/EpinelPS/LobbyServer/Msgs/Misc/GetResourceHosts2.cs +++ b/EpinelPS/LobbyServer/Msgs/Misc/GetResourceHosts2.cs @@ -11,6 +11,7 @@ namespace EpinelPS.LobbyServer.Msgs.Misc var r = new ResGetResourceHosts2(); r.BaseUrl = GameConfig.Root.ResourceBaseURL; + r.Version = req.Version; await WriteDataAsync(r); } diff --git a/EpinelPS/Program.cs b/EpinelPS/Program.cs index 47794f5..5c6d9ad 100644 --- a/EpinelPS/Program.cs +++ b/EpinelPS/Program.cs @@ -1,26 +1,21 @@ -using EmbedIO; -using EmbedIO.Actions; -using EmbedIO.WebApi; -using Newtonsoft.Json; -using EpinelPS.Database; -using EpinelPS.IntlServer; +using EpinelPS.Database; using EpinelPS.LobbyServer; using EpinelPS.LobbyServer.Msgs.Stage; using EpinelPS.StaticInfo; using EpinelPS.Utils; +using Microsoft.AspNetCore.Server.Kestrel.Https; +using Microsoft.Extensions.Options; using Swan.Logging; +using System.Net; using System.Net.Http.Headers; using System.Security.Cryptography.X509Certificates; using System.Text; -using EmbedIO.Files; -using Paseto; -using Paseto.Builder; namespace EpinelPS { internal class Program { - static async Task Main() + static async Task Main(string[] args) { try { @@ -37,8 +32,128 @@ namespace EpinelPS Logger.Info("Starting server"); new Thread(() => { - var server = CreateWebServer(); - server.RunAsync(); + var builder = WebApplication.CreateBuilder(args); + + // Configure HTTPS + var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions + { + SslProtocols = System.Security.Authentication.SslProtocols.Tls12, + ClientCertificateMode = ClientCertificateMode.AllowCertificate, + ServerCertificate = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + @"site.pfx") + }; + + builder.WebHost.ConfigureKestrel(serverOptions => + { + serverOptions.Listen(IPAddress.Any, 443, + listenOptions => + { + listenOptions.UseHttps(AppDomain.CurrentDomain.BaseDirectory + @"site.pfx", ""); + }); + + // TODO + serverOptions.AllowSynchronousIO = true; + }); + + // Add services to the container. + + builder.Services.AddControllers(); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddRouting(); + + var app = builder.Build(); + + app.UseDefaultFiles(); + app.UseStaticFiles(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + app.UseHttpsRedirection(); + app.UseRouting(); + + app.MapControllers(); + + app.MapPost("/$batch", HandleBatchRequests); + app.MapGet("/prdenv/{**all}", AssetDownloadUtil.HandleReq); + app.MapGet("/PC/{**all}", AssetDownloadUtil.HandleReq); + app.MapGet("/media/{**all}", AssetDownloadUtil.HandleReq); + + // NOTE: pub prefixes shows public (production server), local is local server (does not have any effect), dev is development server, etc. + // It does not have any effect, except for the publisher server, which adds a watermark? + + app.MapGet("/route/*/route_config.json", () => @"{ + ""Config"": [ + { + ""VersionRange"": { + ""From"": ""124.6.10"", + ""To"": ""124.6.11"", + ""PackageName"": ""com.proximabeta.nikke"" + }, + ""Route"": [ + { + ""WorldId"": 81, + ""Name"": ""pub:live-jp"", + ""Url"": ""https://jp-lobby.nikke-kr.com/"", + ""Description"": ""JAPAN"", + ""Tags"": [] + }, + { + ""WorldId"": 82, + ""Name"": ""pub:live-na"", + ""Url"": ""https://us-lobby.nikke-kr.com/"", + ""Description"": ""NA"", + ""Tags"": [] + }, + { + ""WorldId"": 83, + ""Name"": ""pub:live-kr"", + ""Url"": ""https://kr-lobby.nikke-kr.com/"", + ""Description"": ""KOREA"", + ""Tags"": [] + }, + { + ""WorldId"": 84, + ""Name"": ""pub:live-global"", + ""Url"": ""https://global-lobby.nikke-kr.com/"", + ""Description"": ""GLOBAL"", + ""Tags"": [] + }, + { + ""WorldId"": 85, + ""Name"": ""pub:live-sea"", + ""Url"": ""https://sea-lobby.nikke-kr.com/"", + ""Description"": ""SEA"", + ""Tags"": [] + } + ] + }, + { + ""VersionRange"": { + ""From"": ""124.6.10"", + ""To"": ""124.6.11"", + ""PackageName"": ""com.gamamobi.nikke"" + }, + ""Route"": [ + { + ""WorldId"": 91, + ""Name"": ""pub:live-hmt"", + ""Url"": ""https://hmt-lobby.nikke-kr.com/"", + ""Description"": ""HMT"", + ""Tags"": [] + } + ] + } + ] + }"); + + app.Run(); }).Start(); CliLoop(); @@ -238,90 +353,8 @@ namespace EpinelPS } private static string LauncherEndpoint = Encoding.UTF8.GetString(Convert.FromBase64String("L25pa2tlX2xhdW5jaGVy")); - private static FileModule AssetModule; - private static WebServer CreateWebServer() - { - var cert = new X509Certificate2(new X509Certificate(AppDomain.CurrentDomain.BaseDirectory + @"site.pfx")); - var server = new WebServer(o => o - .WithUrlPrefixes("https://*:443") - .WithMode(HttpListenerMode.EmbedIO).WithAutoLoadCertificate().WithCertificate(cert)) - // First, we will configure our web server by adding Modules. - .WithLocalSessionManager() - .WithModule(new ActionModule("/route/", HttpVerbs.Any, HandleRouteData)) - .WithModule(new ActionModule("/v1/", HttpVerbs.Any, LobbyHandler.DispatchSingle)) - .WithModule(new ActionModule("/v2/", HttpVerbs.Any, IntlHandler.Handle)) - .WithModule(new ActionModule("/account/", HttpVerbs.Any, IntlHandler.Handle)) - .WithModule(new ActionModule("/data/", HttpVerbs.Any, HandleDataEndpoint)) - .WithModule(new ActionModule("/$batch", HttpVerbs.Any, HandleBatchRequests)) - .WithModule(new ActionModule("/api/v1/", HttpVerbs.Any, IntlHandler.Handle)) - .WithStaticFolder(LauncherEndpoint, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "www", "launcher"), true) - .WithStaticFolder("/admin/assets/", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "www", "admin", "assets"), true) - .WithModule(new ActionModule("/admin", HttpVerbs.Any, HandleAdminRequest)) - .WithWebApi("/adminapi", m => m.WithController(typeof(AdminApiController))) - .WithModule(new ActionModule("/", HttpVerbs.Any, HandleAsset)); - - - FileSystemProvider fileSystemProvider = new FileSystemProvider(AppDomain.CurrentDomain.BaseDirectory + "cache/", false); - AssetModule = new FileModule("/", fileSystemProvider); - AssetModule.Start(CancellationToken.None); - return server; - } - - private static async Task HandleAdminRequest(IHttpContext context) - { - //check if user is logged in - if (context.Request.Cookies["token"] == null && context.Request.Url.PathAndQuery != "/api/login") - { - context.Redirect("/adminapi/login"); - return; - } - - //Check if authenticated correctly - User? currentUser = null; - if (context.Request.Url.PathAndQuery != "/api/login") - { - //verify token - foreach (var item in AdminApiController.AdminAuthTokens) - { - if (item.Key == context.Request.Cookies["token"].Value) - { - currentUser = item.Value; - } - } - } - if (currentUser == null) - { - context.Redirect("/adminapi/login"); - return; - } - - if (context.Request.Url.PathAndQuery == "/admin/") - { - context.Redirect("/admin/dashboard"); - } - else if (context.Request.Url.PathAndQuery == "/admin/dashboard") - { - await context.SendStringAsync(ProcessAdminPage("dashbrd.html", currentUser), "text/html", Encoding.Unicode); - } - else - { - context.Response.StatusCode = 404; - await context.SendStringAsync("404 not found", "text/html", Encoding.Unicode); - } - } - - private static string ProcessAdminPage(string pg, User? currentUser) - { - var pgContent = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "www", "admin", pg)); - var nav = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "www", "admin", "nav.html")); - - //navbar - pgContent = pgContent.Replace("{{navbar}}", nav); - - return pgContent; - } - private static async Task HandleBatchRequests(IHttpContext ctx) + private static async Task HandleBatchRequests(HttpContext ctx) { var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx); @@ -330,7 +363,7 @@ namespace EpinelPS using MemoryStream streamforparser = new(theBytes.Contents); StreamContent content = new(streamforparser); content.Headers.Remove("Content-Type"); - content.Headers.TryAddWithoutValidation("Content-Type", ctx.Request.Headers["Content-Type"]); + content.Headers.TryAddWithoutValidation("Content-Type", (string?)ctx.Request.Headers["Content-Type"]); // we have the form contents, var multipart = await content.ReadAsMultipartAsync(); @@ -356,21 +389,21 @@ namespace EpinelPS List ResponseWithBytes = [ .. Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\n"), - .. Encoding.UTF8.GetBytes($"Content-Type: application/octet-stream+protobuf\r\n"), - .. Encoding.UTF8.GetBytes($"Content-Length: {res.Length}\r\n"), - .. Encoding.UTF8.GetBytes($"\r\n"), - .. res, - ]; + .. Encoding.UTF8.GetBytes($"Content-Type: application/octet-stream+protobuf\r\n"), + .. Encoding.UTF8.GetBytes($"Content-Length: {res.Length}\r\n"), + .. Encoding.UTF8.GetBytes($"\r\n"), + .. res, + ]; response.AddRange([.. ResponseWithBytes]); } else { List ResponseWithBytes = [ .. Encoding.UTF8.GetBytes("HTTP/1.1 404 Not Found\r\n"), - //.. Encoding.UTF8.GetBytes($"Content-Type: application/octet-stream+protobuf\r\n"), - .. Encoding.UTF8.GetBytes($"Content-Length: 0\r\n"), - .. Encoding.UTF8.GetBytes($"\r\n"), - ]; + //.. Encoding.UTF8.GetBytes($"Content-Type: application/octet-stream+protobuf\r\n"), + .. Encoding.UTF8.GetBytes($"Content-Length: 0\r\n"), + .. Encoding.UTF8.GetBytes($"\r\n"), + ]; } // add boundary, also include http newline if there is binary content @@ -384,136 +417,24 @@ namespace EpinelPS var responseBytes = response.ToArray(); ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\""; - ctx.Response.OutputStream.Write(responseBytes); - } - private static async Task HandleDataEndpoint(IHttpContext ctx) - { - // this endpoint does not appear to be needed, it is used for telemetry - if (ctx.RequestedPath == "/v1/dsr/query") - { - await WriteJsonStringAsync(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}"); - } - else - { - ctx.Response.StatusCode = 404; - } + ctx.Response.Body.Write(responseBytes); } + //private static async Task HandleDataEndpoint(IHttpContext ctx) + //{ + // //this endpoint does not appear to be needed, it is used for telemetry + // if (ctx.RequestedPath == "/v1/dsr/query") + // { + // await WriteJsonStringAsync(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}"); + // } + // else + // { + // ctx.Response.StatusCode = 404; + // } + //} public static string GetCachePathForPath(string path) { return AppDomain.CurrentDomain.BaseDirectory + "cache/" + path; } - private static async Task HandleAsset(IHttpContext ctx) - { - if (!ctx.Request.RawUrl.StartsWith("/PC") && !ctx.Request.RawUrl.StartsWith("/media") && !ctx.Request.RawUrl.StartsWith("/prdenv")) - { - ctx.Response.StatusCode = 404; - return; - } - string? targetFile = await AssetDownloadUtil.DownloadOrGetFileAsync(ctx.Request.RawUrl, ctx.CancellationToken); - - if (targetFile == null) - { - Logger.Error("Download failed: " + ctx.RequestedPath); - ctx.Response.StatusCode = 404; - return; - } - - // without this, content-type will be video/mp4; charset=utf-8 which is wrong - ctx.Response.ContentEncoding = null; - - - await AssetModule.HandleRequestAsync(ctx); - } - private static async Task HandleRouteData(IHttpContext ctx) - { - if (ctx.RequestedPath.Contains("/route_config.json")) - { - // NOTE: pub prefixes shows public (production server), local is local server (does not have any effect), dev is development server, etc. - // It does not have any effect, except for the publisher server, which adds a watermark? - var response = @"{ - ""Config"": [ - { - ""VersionRange"": { - ""From"": ""124.6.10"", - ""To"": ""124.6.11"", - ""PackageName"": ""com.proximabeta.nikke"" - }, - ""Route"": [ - { - ""WorldId"": 81, - ""Name"": ""pub:live-jp"", - ""Url"": ""https://jp-lobby.nikke-kr.com/"", - ""Description"": ""JAPAN"", - ""Tags"": [] - }, - { - ""WorldId"": 82, - ""Name"": ""pub:live-na"", - ""Url"": ""https://us-lobby.nikke-kr.com/"", - ""Description"": ""NA"", - ""Tags"": [] - }, - { - ""WorldId"": 83, - ""Name"": ""pub:live-kr"", - ""Url"": ""https://kr-lobby.nikke-kr.com/"", - ""Description"": ""KOREA"", - ""Tags"": [] - }, - { - ""WorldId"": 84, - ""Name"": ""pub:live-global"", - ""Url"": ""https://global-lobby.nikke-kr.com/"", - ""Description"": ""GLOBAL"", - ""Tags"": [] - }, - { - ""WorldId"": 85, - ""Name"": ""pub:live-sea"", - ""Url"": ""https://sea-lobby.nikke-kr.com/"", - ""Description"": ""SEA"", - ""Tags"": [] - } - ] - }, - { - ""VersionRange"": { - ""From"": ""124.6.10"", - ""To"": ""124.6.11"", - ""PackageName"": ""com.gamamobi.nikke"" - }, - ""Route"": [ - { - ""WorldId"": 91, - ""Name"": ""pub:live-hmt"", - ""Url"": ""https://hmt-lobby.nikke-kr.com/"", - ""Description"": ""HMT"", - ""Tags"": [] - } - ] - } - ] -}"; - response = response.Replace("{GameMinVer}", GameConfig.Root.GameMinVer); - response = response.Replace("{GameMaxVer}", GameConfig.Root.GameMaxVer); - response = response.Replace("{ServerName}", JsonConvert.ToString(JsonDb.Instance.ServerName)); - await ctx.SendStringAsync(response, "application/json", Encoding.Default); - } - else - { - Console.WriteLine("ROUTE - Unknown: " + ctx.RequestedPath); - ctx.Response.StatusCode = 404; - } - } - private static async Task WriteJsonStringAsync(IHttpContext ctx, string data) - { - var bt = Encoding.UTF8.GetBytes(data); - ctx.Response.ContentEncoding = null; - ctx.Response.ContentType = "application/json"; - ctx.Response.ContentLength64 = bt.Length; - await ctx.Response.OutputStream.WriteAsync(bt, ctx.CancellationToken); - await ctx.Response.OutputStream.FlushAsync(); - } private static (string key, string value) GetHeader(string line) { var pieces = line.Split([':'], 2); diff --git a/EpinelPS/Properties/launchSettings.json b/EpinelPS/Properties/launchSettings.json new file mode 100644 index 0000000..1096c3b --- /dev/null +++ b/EpinelPS/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "EpinelPS": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64668;http://localhost:64669" + } + } +} \ No newline at end of file diff --git a/EpinelPS/Utils/AssetDownloadUtil.cs b/EpinelPS/Utils/AssetDownloadUtil.cs index 9260325..b205930 100644 --- a/EpinelPS/Utils/AssetDownloadUtil.cs +++ b/EpinelPS/Utils/AssetDownloadUtil.cs @@ -54,6 +54,22 @@ namespace EpinelPS.Utils return targetFile; } + public static async Task HandleReq(HttpContext context) + { + string? targetFile = await DownloadOrGetFileAsync(context.Request.Path.Value ?? "", CancellationToken.None); + + if (targetFile != null) + { + string? contentType = null; + if (targetFile.EndsWith("mp4")) + contentType = "video/mp4"; + + await Results.Stream(new FileStream(targetFile, FileMode.Open, FileAccess.Read, FileShare.Read), contentType: contentType, enableRangeProcessing: true).ExecuteAsync(context); + } + else + context.Response.StatusCode = 404; + } + private static async Task GetCloudIpAsync() { var lookup = new LookupClient(); diff --git a/EpinelPS/Utils/GreatLogger.cs b/EpinelPS/Utils/GreatLogger.cs index 6736a97..4fd1281 100644 --- a/EpinelPS/Utils/GreatLogger.cs +++ b/EpinelPS/Utils/GreatLogger.cs @@ -2,9 +2,9 @@ namespace EpinelPS.Utils { - public class GreatLogger : ILogger + public class GreatLogger : Swan.Logging.ILogger { - public LogLevel LogLevel => LogLevel.Info; + public Swan.Logging.LogLevel LogLevel => Swan.Logging.LogLevel.Info; static readonly object lockObject = new(); public void Log(LogMessageReceivedEventArgs logEvent) { @@ -44,13 +44,13 @@ namespace EpinelPS.Utils return ConsoleColor.DarkGreen; return logEvent.MessageType switch { - LogLevel.None => ConsoleColor.White, - LogLevel.Trace => ConsoleColor.Gray, - LogLevel.Debug => ConsoleColor.Gray, - LogLevel.Info => ConsoleColor.Gray, - LogLevel.Warning => ConsoleColor.Yellow, - LogLevel.Error => ConsoleColor.Red, - LogLevel.Fatal => ConsoleColor.Red, + Swan.Logging.LogLevel.None => ConsoleColor.White, + Swan.Logging.LogLevel.Trace => ConsoleColor.Gray, + Swan.Logging.LogLevel.Debug => ConsoleColor.Gray, + Swan.Logging.LogLevel.Info => ConsoleColor.Gray, + Swan.Logging.LogLevel.Warning => ConsoleColor.Yellow, + Swan.Logging.LogLevel.Error => ConsoleColor.Red, + Swan.Logging.LogLevel.Fatal => ConsoleColor.Red, _ => ConsoleColor.White, }; } diff --git a/EpinelPS/Utils/NetUtils.cs b/EpinelPS/Utils/NetUtils.cs index 8f63b3a..83ab646 100644 --- a/EpinelPS/Utils/NetUtils.cs +++ b/EpinelPS/Utils/NetUtils.cs @@ -6,6 +6,26 @@ namespace EpinelPS.Utils { public class NetUtils { + public static (User?, AccessToken?) GetUser(string tokToCheck) + { + if (string.IsNullOrEmpty(tokToCheck)) + throw new Exception("missing auth token"); + + + foreach (var tok in JsonDb.Instance.LauncherAccessTokens) + { + if (tok.Token == tokToCheck) + { + var user = JsonDb.Instance.Users.Find(x => x.ID == tok.UserID); + if (user != null) + { + return (user, tok); + } + } + } + + return (null, null); + } public static NetUserItemData ToNet(ItemData item) { return new() diff --git a/EpinelPS/Utils/PacketDecryption.cs b/EpinelPS/Utils/PacketDecryption.cs index d797395..19208b1 100644 --- a/EpinelPS/Utils/PacketDecryption.cs +++ b/EpinelPS/Utils/PacketDecryption.cs @@ -9,15 +9,15 @@ namespace EpinelPS.Utils { public class PacketDecryption { - public static async Task DecryptOrReturnContentAsync(IHttpContext ctx) + public static async Task DecryptOrReturnContentAsync(HttpContext ctx) { byte[] bin = []; using MemoryStream buffer = new(); - var stream = ctx.Request.InputStream; + var stream = ctx.Request.Body; - var encoding = ctx.Request.Headers[HttpHeaderNames.ContentEncoding]?.Trim(); + var encoding = ctx.Request.Headers.ContentEncoding.FirstOrDefault(); Stream decryptedStream; switch (encoding) @@ -30,6 +30,7 @@ namespace EpinelPS.Utils break; case CompressionMethodNames.None: case null: + case "": decryptedStream = stream; break; case "gzip,enc": @@ -77,7 +78,7 @@ namespace EpinelPS.Utils } - await stream.CopyToAsync(buffer, 81920, ctx.CancellationToken).ConfigureAwait(continueOnCapturedContext: false); + await stream.CopyToAsync(buffer, 81920).ConfigureAwait(continueOnCapturedContext: false); return new PacketDecryptResponse() { Contents = buffer.ToArray() }; } diff --git a/EpinelPS/www/admin/assets/login.css b/EpinelPS/wwwroot/admin/assets/login.css similarity index 100% rename from EpinelPS/www/admin/assets/login.css rename to EpinelPS/wwwroot/admin/assets/login.css diff --git a/EpinelPS/www/admin/assets/login.jpg b/EpinelPS/wwwroot/admin/assets/login.jpg similarity index 100% rename from EpinelPS/www/admin/assets/login.jpg rename to EpinelPS/wwwroot/admin/assets/login.jpg diff --git a/EpinelPS/www/admin/assets/style.css b/EpinelPS/wwwroot/admin/assets/style.css similarity index 100% rename from EpinelPS/www/admin/assets/style.css rename to EpinelPS/wwwroot/admin/assets/style.css diff --git a/EpinelPS/www/admin/dashbrd.html b/EpinelPS/wwwroot/admin/dashbrd.html similarity index 100% rename from EpinelPS/www/admin/dashbrd.html rename to EpinelPS/wwwroot/admin/dashbrd.html diff --git a/EpinelPS/www/admin/index.html b/EpinelPS/wwwroot/admin/index.html similarity index 100% rename from EpinelPS/www/admin/index.html rename to EpinelPS/wwwroot/admin/index.html diff --git a/EpinelPS/www/admin/nav.html b/EpinelPS/wwwroot/admin/nav.html similarity index 100% rename from EpinelPS/www/admin/nav.html rename to EpinelPS/wwwroot/admin/nav.html diff --git a/EpinelPS/www/launcher/index.html b/EpinelPS/wwwroot/nikke_launcher/index.html similarity index 100% rename from EpinelPS/www/launcher/index.html rename to EpinelPS/wwwroot/nikke_launcher/index.html