admin panel work

This commit is contained in:
Mikhail
2025-05-04 17:35:11 -04:00
parent e4969c723d
commit ca787a384c
23 changed files with 496 additions and 269 deletions

View File

@@ -1,8 +1,10 @@
using EpinelPS.Database;
using EpinelPS.LobbyServer;
using EpinelPS.Controllers.AdminPanel;
using EpinelPS.Data;
using EpinelPS.Database;
using EpinelPS.LobbyServer.Stage;
using EpinelPS.Models.Admin;
using EpinelPS.Utils;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Asn1.X509;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;
using System.Text;
@@ -12,8 +14,7 @@ namespace EpinelPS.Controllers
[ApiController]
public class AdminApiController : ControllerBase
{
public static Dictionary<string, User> AdminAuthTokens = new();
private static MD5 md5 = MD5.Create();
private static readonly MD5 md5 = MD5.Create();
[HttpPost]
[Route("login")]
@@ -23,8 +24,8 @@ namespace EpinelPS.Controllers
bool nullusernames = false;
if (b.Username != null && b.Password != null)
{
var passwordHash = Convert.ToHexString(md5.ComputeHash(Encoding.ASCII.GetBytes(b.Password))).ToLower();
foreach (var item in JsonDb.Instance.Users)
string passwordHash = Convert.ToHexString(md5.ComputeHash(Encoding.ASCII.GetBytes(b.Password))).ToLower();
foreach (User item in JsonDb.Instance.Users)
{
if (item.Username == b.Username)
{
@@ -37,25 +38,20 @@ namespace EpinelPS.Controllers
}
else
{
nullusernames = true;
nullusernames = true;
}
if (user == null)
{
if (nullusernames)
{
return new LoginApiResponse() { Message = "Please enter a username and password" };
}
else
{
return new LoginApiResponse() { Message = "Username or password is incorrect" };
}
return nullusernames
? new LoginApiResponse() { Message = "Please enter a username and password" }
: new LoginApiResponse() { Message = "Username or password is incorrect" };
}
else
{
if (user.IsAdmin)
{
var tok = CreateAuthToken(user);
string tok = CreateAuthToken(user);
HttpContext.Response.Cookies.Append("token", tok);
return new LoginApiResponse() { OK = true, Token = tok };
}
@@ -64,35 +60,48 @@ namespace EpinelPS.Controllers
return new LoginApiResponse() { Message = "User is not an administrator." };
}
}
}
[HttpPost("RunCmd")]
public RunCmdResponse RunCmd([FromBody] RunCmdRequest req)
{
if (!AdminController.CheckAuth(HttpContext)) return new RunCmdResponse() { error = "bad token" };
switch (req.cmdName)
{
case "reloadDb":
JsonDb.Reload();
return RunCmdResponse.OK;
case "completestage":
return AdminCommands.CompleteStage(ulong.Parse(req.p1), req.p2);
case "addallcharacters":
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == ulong.Parse(req.p1));
if (user == null) return new RunCmdResponse() { error = "invalid user ID" };
return AdminCommands.AddAllCharacters(user);
}
case "addallmaterials":
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == ulong.Parse(req.p1));
if (user == null) return new RunCmdResponse() { error = "invalid user ID" };
return AdminCommands.AddAllMaterials(user, int.Parse(req.p2));
}
}
return new RunCmdResponse() { error = "Not implemented" };
}
private static string CreateAuthToken(User user)
{
var tok = RandomString(128);
AdminAuthTokens.Add(tok, user);
string tok = RandomString(128);
JsonDb.Instance.AdminAuthTokens.Add(tok, user);
JsonDb.Save();
return tok;
}
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[new Random().Next(s.Length)]).ToArray());
}
public class LoginApiBody
{
[Required]
public string Username { get; set; } = "";
[Required]
public string Password { get; set; } = "";
}
public class LoginApiResponse
{
public string Message { get; set; } = "";
public bool OK { get; set; }
public string Token { get; set; } = "";
return new string([.. Enumerable.Repeat(chars, length).Select(static s => s[new Random().Next(s.Length)])]);
}
}
}

View File

@@ -1,6 +1,9 @@
using EpinelPS.Models;
using EpinelPS.Database;
using EpinelPS.Models;
using EpinelPS.Models.Admin;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Net.Security;
namespace EpinelPS.Controllers.AdminPanel
{
@@ -14,8 +17,8 @@ namespace EpinelPS.Controllers.AdminPanel
string? token = context.Request.Cookies["token"];
if (token == null) return false;
foreach (var item in AdminApiController.AdminAuthTokens)
// TODO better authentication
foreach (var item in JsonDb.Instance.AdminAuthTokens)
{
if (item.Key == token) return true;
}
@@ -36,12 +39,33 @@ namespace EpinelPS.Controllers.AdminPanel
return View();
}
[Route("Configuration")]
public IActionResult Configuration()
{
if (!CheckAuth(HttpContext)) return Redirect("/admin/");
return View();
ServerConfiguration model = new()
{
LogType = JsonDb.Instance.LogLevel
};
return View(model);
}
[Route("Configuration"), ActionName("Configuration")]
[HttpPost]
public IActionResult ConfigurationSave([FromForm] ServerConfiguration cfg)
{
if (!CheckAuth(HttpContext)) return Redirect("/admin/");
if (!ModelState.IsValid)
return View();
JsonDb.Instance.LogLevel = cfg.LogType;
JsonDb.Save();
return View(new ServerConfiguration() { LogType = cfg.LogType });
}
[Route("Mail")]

View File

@@ -1,7 +1,6 @@
using EpinelPS.Database;
using EpinelPS.Models;
using EpinelPS.Models.Admin;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
@@ -31,7 +30,53 @@ namespace EpinelPS.Controllers.AdminPanel
return NotFound();
}
return View(user);
return View(
new ModUserModel()
{
IsAdmin = user.IsAdmin,
IsBanned = user.IsBanned,
Nickname = user.Nickname,
sickpulls = user.sickpulls,
Username = user.Username,
ID = user.ID
}
);
}
[Route("Modify/{id}"), ActionName("Modify")]
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DoModifyUser(ulong id, [FromForm] ModUserModel toSet)
{
if (!AdminController.CheckAuth(HttpContext)) return Redirect("/admin/");
if (!ModelState.IsValid) throw new Exception("model state invalid");
var user = JsonDb.Instance.Users.Where(x => x.ID == id).FirstOrDefault();
if (user == null)
{
return NotFound();
}
if (string.IsNullOrEmpty(toSet.Username))
throw new Exception("username cannot be empty");
user.Username = toSet.Username;
user.IsAdmin = toSet.IsAdmin;
user.sickpulls = toSet.sickpulls;
user.IsBanned = toSet.IsBanned;
user.Nickname = toSet.Nickname;
JsonDb.Save();
return View(new ModUserModel()
{
IsAdmin = user.IsAdmin,
IsBanned = user.IsBanned,
Nickname = user.Nickname,
sickpulls = user.sickpulls,
Username = user.Username,
ID = user.ID
});
}
[Route("SetPassword/{id}")]

View File

@@ -1,5 +1,4 @@
using EpinelPS.LobbyServer;
using EpinelPS.Data;
using EpinelPS.Data;
using EpinelPS.Utils;
using Newtonsoft.Json;
using Paseto.Builder;
@@ -578,6 +577,7 @@ namespace EpinelPS.Database
public List<User> Users = [];
public List<AccessToken> LauncherAccessTokens = [];
public Dictionary<string, User> AdminAuthTokens = [];
public string ServerName = "<color=\"green\">Private Server</color>";
public byte[] LauncherTokenKey = [];

View File

@@ -43,9 +43,7 @@
<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>

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace EpinelPS.Models.Admin;
public class LoginApiBody
{
[Required]
public string Username { get; set; } = "";
[Required]
public string Password { get; set; } = "";
}

View File

@@ -0,0 +1,12 @@
using EpinelPS.Utils;
using Microsoft.AspNetCore.Mvc;
namespace EpinelPS.Models.Admin;
public class LoginApiResponse
{
public string Message { get; set; } = "";
public bool OK { get; set; }
public string Token { get; set; } = "";
}

View File

@@ -0,0 +1,15 @@
using EpinelPS.Utils;
using Microsoft.AspNetCore.Mvc;
namespace EpinelPS.Models.Admin;
public class ModUserModel
{
public string Username { get; set; } = "";
public string Nickname { get; set; } = "";
public bool IsAdmin { get; set; } = false;
public bool sickpulls { get; set; } = false;
public bool IsBanned { get; set; } = false;
public ulong ID { get; set; }
}

View File

@@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
using EpinelPS.Utils;
using Microsoft.AspNetCore.Mvc;
namespace EpinelPS.Models.Admin;
public class RunCmdRequest
{
[Required]
public string cmdName { get; set; } = "";
public string p1 { get; set; } = "";
public string p2 { get; set; } = "";
}
public class RunCmdResponse
{
public bool ok { get; set; }
public string error { get; set; } = "";
public static readonly RunCmdResponse OK = new() { ok = true };
}

View File

@@ -0,0 +1,10 @@
using EpinelPS.Utils;
using Microsoft.AspNetCore.Mvc;
namespace EpinelPS.Models.Admin;
public class ServerConfiguration
{
[BindProperty]
public LogType LogType { get; set; }
}

View File

@@ -168,7 +168,8 @@ namespace EpinelPS
]
}".Replace("{GameMinVer}", GameConfig.Root.GameMinVer).Replace("{GameMaxVer}", GameConfig.Root.GameMaxVer));
app.MapGet("/", () => {
app.MapGet("/", () =>
{
return $"EpinelPS v{Assembly.GetExecutingAssembly().GetName().Version} - https://github.com/EpinelPS/EpinelPS/";
});
@@ -204,15 +205,14 @@ namespace EpinelPS
else if (input == "?" || input == "help")
{
Console.WriteLine("EpinelPS CLI");
Console.WriteLine("NOTICE: Admin panel is available at https://localhost/admin/");
Console.WriteLine();
Console.WriteLine("Commands:");
Console.WriteLine(" help - show this help");
Console.WriteLine(" ls /users - show all users");
Console.WriteLine(" cd (user id) - select user by id");
Console.WriteLine(" show users - show all users");
Console.WriteLine(" user (user id) - select user by id");
Console.WriteLine(" rmuser - delete selected user");
Console.WriteLine(" r - load changes to database from disk. Discards data in memory.");
Console.WriteLine(" ban - ban selected user from game");
Console.WriteLine(" unban - unban selected user from game");
Console.WriteLine(" exit - exit server application");
Console.WriteLine(" completestage (chapter num)-(stage number) - complete selected stage and get rewards (and all previous ones). Example completestage 15-1. Note that the exact stage number cleared may not be exact.");
Console.WriteLine(" sickpulls (requires selecting user first) allows for all characters to have equal chances of getting pulled");
@@ -225,7 +225,7 @@ namespace EpinelPS
Console.WriteLine(" AddItem (id) (amount) - Adds an item to the selected user (takes effect on game and server restart)");
Console.WriteLine(" AddCharacter (id) - Adds a character to the selected user (takes effect on game and server restart)");
}
else if (input == "ls /users")
else if (input == "show users")
{
Console.WriteLine("Id,Username,Nickname");
foreach (var item in JsonDb.Instance.Users)
@@ -233,7 +233,7 @@ namespace EpinelPS
Console.WriteLine($"{item.ID},{item.Username},{item.Nickname}");
}
}
else if (input.StartsWith("cd"))
else if (input.StartsWith("user"))
{
if (args.Length == 2)
{
@@ -281,89 +281,39 @@ namespace EpinelPS
}
else
{
// Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201
var allCharacters = GameData.Instance.CharacterTable.Values
.GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian
.SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103
.ToList();
foreach (var character in allCharacters)
{
if (!user.HasCharacter(character.id))
{
user.Characters.Add(new Database.Character()
{
CostumeId = 0,
Csn = user.GenerateUniqueCharacterId(),
Grade = 0,
Level = 1,
Skill1Lvl = 1,
Skill2Lvl = 1,
Tid = character.id, // Tid is the character ID
UltimateLevel = 1
});
user.BondInfo.Add(new() { NameCode = character.name_code, Level = 1 });
user.AddTrigger(TriggerType.ObtainCharacter, 1, character.name_code);
user.AddTrigger(TriggerType.ObtainCharacterNew, 1);
}
}
Console.WriteLine("Added all missing characters to user " + user.Username);
JsonDb.Save();
var rsp = AdminCommands.AddAllCharacters(user);
if (!rsp.ok) Console.WriteLine(rsp.error);
}
}
}
else if (input.StartsWith("addallmaterials"))
{
if (selectedUser == 0)
{
Console.WriteLine("No user selected");
}
else
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser);
if (user == null)
{
Console.WriteLine("Selected user does not exist");
selectedUser = 0;
prompt = "# ";
}
else
{
int amount = 1; // Default amount if not provided
if (args.Length >= 2 && int.TryParse(args[1], out int parsedAmount))
{
amount = parsedAmount;
}
foreach (var tableItem in GameData.Instance.itemMaterialTable.Values)
{
ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == tableItem.id);
if (item == null)
{
user.Items.Add(new ItemData
{
Isn = user.GenerateUniqueItemId(),
ItemType = tableItem.id,
Level = 1,
Exp = 1,
Count = amount
});
}
else
{
item.Count += amount;
}
}
Console.WriteLine($"Added {amount} of all materials to user " + user.Username);
JsonDb.Save();
}
}
}
else if (input.StartsWith("addallmaterials"))
{
if (selectedUser == 0)
{
Console.WriteLine("No user selected");
}
else
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser);
if (user == null)
{
Console.WriteLine("Selected user does not exist");
selectedUser = 0;
prompt = "# ";
}
else
{
int amount = 1; // Default amount if not provided
if (args.Length >= 2 && int.TryParse(args[1], out int parsedAmount))
{
amount = parsedAmount;
}
var rsp = AdminCommands.AddAllMaterials(user, amount);
if (!rsp.ok) Console.WriteLine(rsp.error);
}
}
}
else if (input == "finishalltutorials")
{
if (selectedUser == 0)
@@ -515,9 +465,6 @@ namespace EpinelPS
JsonDb.Save();
}
else if (input == "sickpulls")
{
if (selectedUser == 0)
@@ -684,93 +631,15 @@ namespace EpinelPS
}
else
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser);
if (user == null)
if (args.Length == 2)
{
Console.WriteLine("Selected user does not exist");
selectedUser = 0;
prompt = "# ";
var input2 = args[1];
var rsp = AdminCommands.CompleteStage(selectedUser, input2);
if (!rsp.ok) Console.WriteLine(rsp.error);
}
else
{
if (args.Length == 2)
{
var input2 = args[1];
try
{
var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber);
var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber);
if (chapterParsed && stageParsed)
{
Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}");
// Complete main stages
for (int i = 0; i <= chapterNumber; i++)
{
var stages = GameData.Instance.GetStageIdsForChapter(i, true);
int target = 1;
foreach (var item in stages)
{
if (!user.IsStageCompleted(item, true))
{
Console.WriteLine("Completing stage " + item);
ClearStage.CompleteStage(user, item, true);
}
if (i == chapterNumber && target == stageNumber)
{
break;
}
target++;
}
}
// Process scenario and regular stages
Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}");
for (int chapter = 0; chapter <= chapterNumber; chapter++)
{
Console.WriteLine($"Processing chapter: {chapter}");
var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter)
.Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber))
.ToList();
Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}");
foreach (var stage in stages)
{
if (!user.CompletedScenarios.Contains(stage))
{
user.CompletedScenarios.Add(stage);
Console.WriteLine($"Added stage {stage} to CompletedScenarios");
}
else
{
Console.WriteLine($"Stage {stage} is already completed");
}
}
}
// Save changes to user data
JsonDb.Save();
}
else
{
Console.WriteLine("Chapter and stage number must be valid integers");
}
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.ToString());
}
}
else
{
Console.WriteLine("Invalid argument length, must be 1");
}
Console.WriteLine("Invalid argument length, must be 1");
}
}
}
@@ -892,14 +761,6 @@ namespace EpinelPS
{
Environment.Exit(0);
}
else if (input == "ban")
{
Console.WriteLine("Not implemented");
}
else if (input == "unban")
{
Console.WriteLine("Not implemented");
}
else if (input == "r")
{
JsonDb.Reload();
@@ -968,7 +829,7 @@ namespace EpinelPS
response.AddRange([.. ResponseWithBytes]);
}
}
catch(Exception ex)
catch (Exception ex)
{
List<byte> ResponseWithBytes =
[ .. Encoding.UTF8.GetBytes("HTTP/1.1 500 Internal Server Error\r\n"),

View File

@@ -0,0 +1,155 @@
using DnsClient;
using EpinelPS.Data;
using EpinelPS.Database;
using EpinelPS.LobbyServer.Stage;
using EpinelPS.Models.Admin;
using System.Net;
namespace EpinelPS.Utils
{
public class AdminCommands
{
public static RunCmdResponse CompleteStage(ulong userId, string input2)
{
var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == userId);
if (user == null) return new RunCmdResponse() { error = "invalid user ID" };
try
{
var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber);
var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber);
if (chapterParsed && stageParsed)
{
Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}");
// Complete main stages
for (int i = 0; i <= chapterNumber; i++)
{
var stages = GameData.Instance.GetStageIdsForChapter(i, true);
int target = 1;
foreach (var item in stages)
{
if (!user.IsStageCompleted(item, true))
{
Console.WriteLine("Completing stage " + item);
ClearStage.CompleteStage(user, item, true);
}
if (i == chapterNumber && target == stageNumber)
{
break;
}
target++;
}
}
// Process scenario and regular stages
Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}");
for (int chapter = 0; chapter <= chapterNumber; chapter++)
{
Console.WriteLine($"Processing chapter: {chapter}");
var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter)
.Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber))
.ToList();
Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}");
foreach (var stage in stages)
{
if (!user.CompletedScenarios.Contains(stage))
{
user.CompletedScenarios.Add(stage);
Console.WriteLine($"Added stage {stage} to CompletedScenarios");
}
else
{
Console.WriteLine($"Stage {stage} is already completed");
}
}
}
// Save changes to user data
JsonDb.Save();
}
else
{
return new RunCmdResponse() { error = "Chapter and stage number must be valid integers" };
}
}
catch (Exception ex)
{
return new RunCmdResponse() { error = "Exception: " + ex.ToString() };
}
return RunCmdResponse.OK;
}
public static RunCmdResponse AddAllCharacters(User user)
{
// Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201
var allCharacters = GameData.Instance.CharacterTable.Values
.GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian
.SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103
.ToList();
foreach (var character in allCharacters)
{
if (!user.HasCharacter(character.id))
{
user.Characters.Add(new Database.Character()
{
CostumeId = 0,
Csn = user.GenerateUniqueCharacterId(),
Grade = 0,
Level = 1,
Skill1Lvl = 1,
Skill2Lvl = 1,
Tid = character.id, // Tid is the character ID
UltimateLevel = 1
});
user.BondInfo.Add(new() { NameCode = character.name_code, Level = 1 });
user.AddTrigger(TriggerType.ObtainCharacter, 1, character.name_code);
user.AddTrigger(TriggerType.ObtainCharacterNew, 1);
}
}
JsonDb.Save();
return RunCmdResponse.OK;
}
public static RunCmdResponse AddAllMaterials(User user, int amount)
{
foreach (var tableItem in GameData.Instance.itemMaterialTable.Values)
{
ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == tableItem.id);
if (item == null)
{
user.Items.Add(new ItemData
{
Isn = user.GenerateUniqueItemId(),
ItemType = tableItem.id,
Level = 1,
Exp = 1,
Count = amount
});
}
else
{
item.Count += amount;
}
}
Console.WriteLine($"Added {amount} of all materials to user " + user.Username);
JsonDb.Save();
return RunCmdResponse.OK;
}
}
}

View File

@@ -1,3 +1,4 @@
using System.ComponentModel.DataAnnotations;
using EpinelPS.Database;
namespace EpinelPS.Utils
@@ -25,7 +26,6 @@ namespace EpinelPS.Utils
{
case LogType.Debug: return ConsoleColor.DarkGray;
case LogType.Info: return ConsoleColor.Gray;
case LogType.InfoSuccess: return ConsoleColor.Green;
case LogType.Warning: return ConsoleColor.Yellow;
case LogType.WarningAntiCheat: return ConsoleColor.DarkMagenta;
case LogType.Error: return ConsoleColor.Red;
@@ -36,11 +36,15 @@ namespace EpinelPS.Utils
public enum LogType
{
[Display(Name = "Debug")]
Debug,
InfoSuccess,
[Display(Name = "Info")]
Info,
[Display(Name = "Warning")]
Warning,
[Display(Name = "Anticheat warnings")]
WarningAntiCheat,
[Display(Name = "Errors")]
Error
}
}

View File

@@ -13,8 +13,10 @@ namespace EpinelPS.Utils
}
public static NetRewardData RegisterRewardsForUser(User user, RewardTableRecord rewardData)
{
NetRewardData ret = new();
ret.PassPoint = new();
NetRewardData ret = new()
{
PassPoint = new()
};
if (rewardData.rewards == null) return ret;
if (rewardData.user_exp != 0)

View File

@@ -1,8 +1,22 @@
@model EpinelPS.Models.Admin.ServerConfiguration
@{
ViewData["Title"] = "Configuration";
}
<div class="text-center">
<h1 class="display-4">Configuration</h1>
<p>Coming soon!</p>
<h1 class="display-4">Server configuration</h1>
<form asp-action="Configuration">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<dl class="row">
<dt class = "col-sm-2">Log Level:</dt>
<dd class = "col-sm-10">
@Html.DropDownListFor(model => model.LogType, Html.GetEnumSelectList<LogType>(), "", new { @class = "form-control" })
</dd>
</dl>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>

View File

@@ -4,5 +4,5 @@
<div class="text-center">
<h1 class="display-4">Database configuration</h1>
<p>Coming soon!</p>
<button id="reloadDB" type="button" class="btn btn-danger" title="Loads changes from db.json into memory. Discards unsaved changes." onclick="runSimpleCmd('reloadDb')">Reload database</button>
</div>

View File

@@ -15,12 +15,13 @@
@Html.DisplayNameFor(model => model.Username)
</th>
<th>
@Html.DisplayNameFor(model => model.IsAdmin)
@Html.DisplayNameFor(model => model.Nickname)
</th>
<th>
@Html.DisplayNameFor(model => model.PlayerName)
@Html.DisplayNameFor(model => model.IsAdmin)
</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
@@ -30,10 +31,10 @@
@Html.DisplayFor(modelItem => item.Username)
</td>
<td>
@Html.DisplayFor(modelItem => item.IsAdmin)
@Html.DisplayFor(modelItem => item.Nickname)
</td>
<td>
@Html.DisplayFor(modelItem => item.PlayerName)
@Html.DisplayFor(modelItem => item.IsAdmin)
</td>
<td>
<a asp-action="SetPassword" asp-route-id="@item.ID">Change Password</a> |

View File

@@ -1,4 +1,4 @@
@model EpinelPS.Database.User
@model EpinelPS.Models.Admin.ModUserModel
@{
ViewData["Title"] = "Modify user";
@@ -12,19 +12,23 @@
<div class="col-md-4">
<form asp-action="Modify">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Username" class="control-label col-sm-2"></label>
<div class="col-sm-10"><input asp-for="Username" class="form-control" /></div>
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsAdmin" class="control-label"></label>
<label for="IsAdmin" class="control-label">Is Admin: </label>
<input asp-for="IsAdmin" class="form-check-input" />
<span asp-validation-for="IsAdmin" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IsBanned" class="control-label"></label>
<label class="control-label" title="allows for all characters to have equal chances of getting pulled">Disable Gacha System: </label>
<input asp-for="sickpulls" class="form-check-input" title="allows for all characters to have equal chances of getting pulled"/>
<span asp-validation-for="sickpulls" class="text-danger"></span>
</div>
<div class="form-group">
<label for="IsBanned" class="control-label">Banned:</label>
<input asp-for="IsBanned" class="form-check-input" />
<span asp-validation-for="IsBanned" class="text-danger"></span>
</div>
@@ -37,6 +41,21 @@
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
<p>Campaign:</p>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('completestage', '@Model.ID', 'Enter chapter number and stage number seperated by -')">Skip stages</button>
<p>Characters:</p>
<button class="btn btn-secondary" onclick="runSimpleCmd('addallcharacters', '@Model.ID')">Add all characters</button>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('AddCharacter', '@Model.ID', 'Enter character ID. Wrong ID may cause game not to boot.')">Add character</button>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('SetLevel', '@Model.ID', 'Enter level (1-999) to apply to all characters')">Set character levels</button>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('SetLevel', '@Model.ID', 'Enter skill level (1-10) to apply to all characters')">Set character skill levels</button>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('finishalltutorials', '@Model.ID', 'core level / 0-3 sets stars')">Set core level</button>
<p>Inventory:</p>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('addallmaterials', '@Model.ID', 'Enter material amount:')">Add all equipment</button>
<button class="btn btn-secondary" onclick="runSimpleCmdWithPr('AddItem', '@Model.ID', 'Enter item ID and amount seperated by -')">Add item</button>
<p>Misc:</p>
<button class="btn btn-secondary" onclick="runSimpleCmd('finishalltutorials', '@Model.ID')">Finish all tutorials</button>
</div>
</div>

View File

@@ -1,4 +1,6 @@
@using EpinelPS
@using EpinelPS.Models
@using EpinelPS.Database
@using EpinelPS.Data
@using EpinelPS.Utils
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@@ -19,4 +19,12 @@ html {
body {
margin-bottom: 60px;
}
.btn {
margin-bottom: 3px !important;
}
p{
margin-bottom: 0px !important;
}

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Security System Controller</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
<link rel="stylesheet" href="/admin/assets/style.css">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
</head>
<body>
{{navbar}}
<div class="containter">
<h1>Welcome to Nikke Private Server Admin Panel</h1>
<p>There are no settings to display.</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
</body>
</html>

View File

@@ -1,4 +1,43 @@
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.
function runCmd(cmdName, cb, p1, p2)
{
fetch("/adminapi/RunCmd", {
method: "POST",
body: JSON.stringify({
cmdName: cmdName,
p1: p1,
p2: p2
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => response.json())
.then((json) => cb(json)).catch((error) => {
alert(error)
});
}
function runSimpleCmd(cmdName, p1, p2)
{
runCmd(cmdName, function(json){
if (json.ok)
alert("Operation completed.")
else
alert("Error: " + json.error);
}, p1, p2);
}
function runSimpleCmdWithPr(cmdName, p1, p2Title)
{
let p2 = prompt(p2Title);
if (p2 === undefined || p2 == null || p2 == "") return;
runCmd(cmdName, function(json){
if (json.ok)
alert("Operation completed.")
else
alert("Error: " + json.error);
}, p1, p2);
}

View File

@@ -1,3 +0,0 @@
<div class="navbar2">
<a href="/admin/dashboard" class="navbar-item">Overview</a>
</div>