mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-15 08:24:52 +01:00
asp.net migration, fix resourcehosts2, remove admin panel
This commit is contained in:
104
EpinelPS/Controllers/AccountController.cs
Normal file
104
EpinelPS/Controllers/AccountController.cs
Normal file
@@ -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 + "\"}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
EpinelPS/Controllers/LauncherController.cs
Normal file
97
EpinelPS/Controllers/LauncherController.cs
Normal file
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
EpinelPS/Controllers/LevelInfiniteControlller.cs
Normal file
116
EpinelPS/Controllers/LevelInfiniteControlller.cs
Normal file
@@ -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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
EpinelPS/Controllers/LobbyApiController.cs
Normal file
18
EpinelPS/Controllers/LobbyApiController.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
@@ -30,6 +30,16 @@
|
|||||||
<Protobuf Include="Protos\*.*" GrpcServices="Server" />
|
<Protobuf Include="Protos\*.*" GrpcServices="Server" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="wwwroot\admin\assets\login.css" />
|
||||||
|
<None Include="wwwroot\admin\assets\login.jpg" />
|
||||||
|
<None Include="wwwroot\admin\assets\style.css" />
|
||||||
|
<None Include="wwwroot\admin\dashbrd.html" />
|
||||||
|
<None Include="wwwroot\admin\index.html" />
|
||||||
|
<None Include="wwwroot\admin\nav.html" />
|
||||||
|
<None Include="wwwroot\nikke_launcher\index.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="gameconfig.json">
|
<None Update="gameconfig.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
@@ -40,7 +50,7 @@
|
|||||||
<None Update="site.pfx">
|
<None Update="site.pfx">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="www\**\*">
|
<None Update="wwwroot\**\*">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -121,9 +121,9 @@ namespace EpinelPS.IntlServer
|
|||||||
}
|
}
|
||||||
public class AuthPkt2
|
public class AuthPkt2
|
||||||
{
|
{
|
||||||
public string token = "";
|
public string token { get; set; } = "";
|
||||||
public string openid = "";
|
public string openid { get; set; } = "";
|
||||||
public string account_token = "";
|
public string account_token { get; set; } = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ namespace EpinelPS.LobbyServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static async Task DispatchSingle(IHttpContext ctx)
|
public static async Task DispatchSingle(HttpContext ctx)
|
||||||
{
|
{
|
||||||
LobbyMsgHandler? handler = null;
|
LobbyMsgHandler? handler = null;
|
||||||
|
string path = ctx.Request.Path.Value.Replace("/v1", "");
|
||||||
foreach (var item in Handlers)
|
foreach (var item in Handlers)
|
||||||
{
|
{
|
||||||
if (ctx.RequestedPath == item.Key)
|
if (path == item.Key)
|
||||||
{
|
{
|
||||||
handler = item.Value;
|
handler = item.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using EmbedIO;
|
using Google.Protobuf;
|
||||||
using Google.Protobuf;
|
|
||||||
using EpinelPS.Database;
|
using EpinelPS.Database;
|
||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
@@ -7,7 +6,7 @@ namespace EpinelPS.LobbyServer
|
|||||||
{
|
{
|
||||||
public abstract class LobbyMsgHandler
|
public abstract class LobbyMsgHandler
|
||||||
{
|
{
|
||||||
protected IHttpContext? ctx;
|
protected HttpContext? ctx;
|
||||||
protected ulong UserId;
|
protected ulong UserId;
|
||||||
protected string UsedAuthToken = "";
|
protected string UsedAuthToken = "";
|
||||||
public byte[] ReturnBytes = [];
|
public byte[] ReturnBytes = [];
|
||||||
@@ -25,12 +24,12 @@ namespace EpinelPS.LobbyServer
|
|||||||
ctx = null;
|
ctx = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleAsync(IHttpContext ctx)
|
public async Task HandleAsync(HttpContext ctx)
|
||||||
{
|
{
|
||||||
this.ctx = 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)
|
if (token != null)
|
||||||
{
|
{
|
||||||
UsedAuthToken = token;
|
UsedAuthToken = token;
|
||||||
@@ -49,7 +48,7 @@ namespace EpinelPS.LobbyServer
|
|||||||
UserId = item.Value.UserId;
|
UserId = item.Value.UserId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (UserId == 0) throw new HttpException(403);
|
if (UserId == 0) throw new Exception("403");
|
||||||
await HandleAsync();
|
await HandleAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,20 +67,20 @@ namespace EpinelPS.LobbyServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.Response.ContentEncoding = null;
|
|
||||||
ctx.Response.ContentType = "application/octet-stream+protobuf";
|
ctx.Response.ContentType = "application/octet-stream+protobuf";
|
||||||
ctx.Response.ContentLength64 = data.CalculateSize();
|
ctx.Response.ContentLength = data.CalculateSize();
|
||||||
bool encrypted = false;
|
bool encrypted = false;
|
||||||
var responseBytes = encrypted ? new MemoryStream() : ctx.Response.OutputStream;
|
var responseBytes = encrypted ? new MemoryStream() : ctx.Response.Body;
|
||||||
var x = new CodedOutputStream(responseBytes);
|
var x = new CodedOutputStream(responseBytes);
|
||||||
data.WriteTo(x);
|
data.WriteTo(x);
|
||||||
|
|
||||||
x.Flush();
|
x.Flush();
|
||||||
|
|
||||||
if (encrypted)
|
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);
|
var enc = PacketDecryption.EncryptData(((MemoryStream)responseBytes).ToArray(), UsedAuthToken);
|
||||||
await ctx.Response.OutputStream.WriteAsync(enc, ctx.CancellationToken);
|
await ctx.Response.Body.WriteAsync(enc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace EpinelPS.LobbyServer.Msgs.Misc
|
|||||||
|
|
||||||
var r = new ResGetResourceHosts2();
|
var r = new ResGetResourceHosts2();
|
||||||
r.BaseUrl = GameConfig.Root.ResourceBaseURL;
|
r.BaseUrl = GameConfig.Root.ResourceBaseURL;
|
||||||
|
r.Version = req.Version;
|
||||||
|
|
||||||
await WriteDataAsync(r);
|
await WriteDataAsync(r);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,21 @@
|
|||||||
using EmbedIO;
|
using EpinelPS.Database;
|
||||||
using EmbedIO.Actions;
|
|
||||||
using EmbedIO.WebApi;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using EpinelPS.Database;
|
|
||||||
using EpinelPS.IntlServer;
|
|
||||||
using EpinelPS.LobbyServer;
|
using EpinelPS.LobbyServer;
|
||||||
using EpinelPS.LobbyServer.Msgs.Stage;
|
using EpinelPS.LobbyServer.Msgs.Stage;
|
||||||
using EpinelPS.StaticInfo;
|
using EpinelPS.StaticInfo;
|
||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Swan.Logging;
|
using Swan.Logging;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using EmbedIO.Files;
|
|
||||||
using Paseto;
|
|
||||||
using Paseto.Builder;
|
|
||||||
|
|
||||||
namespace EpinelPS
|
namespace EpinelPS
|
||||||
{
|
{
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
static async Task Main()
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -37,8 +32,128 @@ namespace EpinelPS
|
|||||||
Logger.Info("Starting server");
|
Logger.Info("Starting server");
|
||||||
new Thread(() =>
|
new Thread(() =>
|
||||||
{
|
{
|
||||||
var server = CreateWebServer();
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
server.RunAsync();
|
|
||||||
|
// 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();
|
}).Start();
|
||||||
|
|
||||||
CliLoop();
|
CliLoop();
|
||||||
@@ -238,90 +353,8 @@ namespace EpinelPS
|
|||||||
}
|
}
|
||||||
private static string LauncherEndpoint = Encoding.UTF8.GetString(Convert.FromBase64String("L25pa2tlX2xhdW5jaGVy"));
|
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
|
private static async Task HandleBatchRequests(HttpContext ctx)
|
||||||
.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)
|
|
||||||
{
|
{
|
||||||
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
|
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
|
||||||
|
|
||||||
@@ -330,7 +363,7 @@ namespace EpinelPS
|
|||||||
using MemoryStream streamforparser = new(theBytes.Contents);
|
using MemoryStream streamforparser = new(theBytes.Contents);
|
||||||
StreamContent content = new(streamforparser);
|
StreamContent content = new(streamforparser);
|
||||||
content.Headers.Remove("Content-Type");
|
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,
|
// we have the form contents,
|
||||||
var multipart = await content.ReadAsMultipartAsync();
|
var multipart = await content.ReadAsMultipartAsync();
|
||||||
@@ -356,21 +389,21 @@ namespace EpinelPS
|
|||||||
List<byte> ResponseWithBytes =
|
List<byte> ResponseWithBytes =
|
||||||
[
|
[
|
||||||
.. Encoding.UTF8.GetBytes("HTTP/1.1 200 OK\r\n"),
|
.. 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-Type: application/octet-stream+protobuf\r\n"),
|
||||||
.. Encoding.UTF8.GetBytes($"Content-Length: {res.Length}\r\n"),
|
.. Encoding.UTF8.GetBytes($"Content-Length: {res.Length}\r\n"),
|
||||||
.. Encoding.UTF8.GetBytes($"\r\n"),
|
.. Encoding.UTF8.GetBytes($"\r\n"),
|
||||||
.. res,
|
.. res,
|
||||||
];
|
];
|
||||||
response.AddRange([.. ResponseWithBytes]);
|
response.AddRange([.. ResponseWithBytes]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<byte> ResponseWithBytes =
|
List<byte> ResponseWithBytes =
|
||||||
[ .. Encoding.UTF8.GetBytes("HTTP/1.1 404 Not Found\r\n"),
|
[ .. 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-Type: application/octet-stream+protobuf\r\n"),
|
||||||
.. Encoding.UTF8.GetBytes($"Content-Length: 0\r\n"),
|
.. Encoding.UTF8.GetBytes($"Content-Length: 0\r\n"),
|
||||||
.. Encoding.UTF8.GetBytes($"\r\n"),
|
.. Encoding.UTF8.GetBytes($"\r\n"),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// add boundary, also include http newline if there is binary content
|
// add boundary, also include http newline if there is binary content
|
||||||
@@ -384,136 +417,24 @@ namespace EpinelPS
|
|||||||
|
|
||||||
var responseBytes = response.ToArray();
|
var responseBytes = response.ToArray();
|
||||||
ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\"";
|
ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\"";
|
||||||
ctx.Response.OutputStream.Write(responseBytes);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//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)
|
public static string GetCachePathForPath(string path)
|
||||||
{
|
{
|
||||||
return AppDomain.CurrentDomain.BaseDirectory + "cache/" + 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)
|
private static (string key, string value) GetHeader(string line)
|
||||||
{
|
{
|
||||||
var pieces = line.Split([':'], 2);
|
var pieces = line.Split([':'], 2);
|
||||||
|
|||||||
12
EpinelPS/Properties/launchSettings.json
Normal file
12
EpinelPS/Properties/launchSettings.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"EpinelPS": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:64668;http://localhost:64669"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,22 @@ namespace EpinelPS.Utils
|
|||||||
return targetFile;
|
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<string> GetCloudIpAsync()
|
private static async Task<string> GetCloudIpAsync()
|
||||||
{
|
{
|
||||||
var lookup = new LookupClient();
|
var lookup = new LookupClient();
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace EpinelPS.Utils
|
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();
|
static readonly object lockObject = new();
|
||||||
public void Log(LogMessageReceivedEventArgs logEvent)
|
public void Log(LogMessageReceivedEventArgs logEvent)
|
||||||
{
|
{
|
||||||
@@ -44,13 +44,13 @@ namespace EpinelPS.Utils
|
|||||||
return ConsoleColor.DarkGreen;
|
return ConsoleColor.DarkGreen;
|
||||||
return logEvent.MessageType switch
|
return logEvent.MessageType switch
|
||||||
{
|
{
|
||||||
LogLevel.None => ConsoleColor.White,
|
Swan.Logging.LogLevel.None => ConsoleColor.White,
|
||||||
LogLevel.Trace => ConsoleColor.Gray,
|
Swan.Logging.LogLevel.Trace => ConsoleColor.Gray,
|
||||||
LogLevel.Debug => ConsoleColor.Gray,
|
Swan.Logging.LogLevel.Debug => ConsoleColor.Gray,
|
||||||
LogLevel.Info => ConsoleColor.Gray,
|
Swan.Logging.LogLevel.Info => ConsoleColor.Gray,
|
||||||
LogLevel.Warning => ConsoleColor.Yellow,
|
Swan.Logging.LogLevel.Warning => ConsoleColor.Yellow,
|
||||||
LogLevel.Error => ConsoleColor.Red,
|
Swan.Logging.LogLevel.Error => ConsoleColor.Red,
|
||||||
LogLevel.Fatal => ConsoleColor.Red,
|
Swan.Logging.LogLevel.Fatal => ConsoleColor.Red,
|
||||||
_ => ConsoleColor.White,
|
_ => ConsoleColor.White,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,26 @@ namespace EpinelPS.Utils
|
|||||||
{
|
{
|
||||||
public class NetUtils
|
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)
|
public static NetUserItemData ToNet(ItemData item)
|
||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ namespace EpinelPS.Utils
|
|||||||
{
|
{
|
||||||
public class PacketDecryption
|
public class PacketDecryption
|
||||||
{
|
{
|
||||||
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(IHttpContext ctx)
|
public static async Task<PacketDecryptResponse> DecryptOrReturnContentAsync(HttpContext ctx)
|
||||||
{
|
{
|
||||||
byte[] bin = [];
|
byte[] bin = [];
|
||||||
|
|
||||||
using MemoryStream buffer = new();
|
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;
|
Stream decryptedStream;
|
||||||
switch (encoding)
|
switch (encoding)
|
||||||
@@ -30,6 +30,7 @@ namespace EpinelPS.Utils
|
|||||||
break;
|
break;
|
||||||
case CompressionMethodNames.None:
|
case CompressionMethodNames.None:
|
||||||
case null:
|
case null:
|
||||||
|
case "":
|
||||||
decryptedStream = stream;
|
decryptedStream = stream;
|
||||||
break;
|
break;
|
||||||
case "gzip,enc":
|
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() };
|
return new PacketDecryptResponse() { Contents = buffer.ToArray() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
Reference in New Issue
Block a user