Changeset 463 for TFP-WebServer/WebServer
- Timestamp:
- Aug 17, 2023, 4:57:23 PM (15 months ago)
- Location:
- TFP-WebServer/WebServer
- Files:
-
- 10 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
TFP-WebServer/WebServer/ModInfo.xml
r457 r463 5 5 <Description value="Integrated Webserver for the Web Dashboard and server APIs" /> 6 6 <Author value="The Fun Pimps LLC" /> 7 <Version value="21.1.1 5.0" />7 <Version value="21.1.16.0" /> 8 8 <Website value="" /> 9 9 </xml> -
TFP-WebServer/WebServer/WebServer.csproj
r462 r463 198 198 </ItemGroup> 199 199 <ItemGroup> 200 <EmbeddedResource Include="src\UrlHandlers\ItemIconHandler.openapi.yaml" /> 201 <EmbeddedResource Include="src\UrlHandlers\SessionHandler.openapi.yaml" /> 202 <EmbeddedResource Include="src\UrlHandlers\UserStatusHandler.openapi.yaml" /> 200 203 <EmbeddedResource Include="src\WebAPI\APIs\GameData\Item.openapi.yaml" /> 201 204 <EmbeddedResource Include="src\WebAPI\APIs\GameData\Mods.openapi.yaml" /> … … 210 213 <EmbeddedResource Include="src\WebAPI\APIs\Permissions\WebUsers.openapi.yaml" /> 211 214 <EmbeddedResource Include="src\WebAPI\APIs\Permissions\Whitelist.openapi.yaml" /> 215 <EmbeddedResource Include="src\WebAPI\APIs\ServerState\GamePrefs.openapi.yaml" /> 216 <EmbeddedResource Include="src\WebAPI\APIs\ServerState\GameStats.openapi.yaml" /> 217 <EmbeddedResource Include="src\WebAPI\APIs\ServerState\ServerInfo.openapi.yaml" /> 218 <EmbeddedResource Include="src\WebAPI\APIs\ServerState\ServerStats.openapi.yaml" /> 212 219 <EmbeddedResource Include="src\WebAPI\APIs\WorldState\Animal.openapi.yaml" /> 220 <EmbeddedResource Include="src\WebAPI\APIs\WorldState\Bloodmoon.openapi.yaml" /> 221 <EmbeddedResource Include="src\WebAPI\APIs\WorldState\Hostile.openapi.yaml" /> 222 <EmbeddedResource Include="src\WebAPI\APIs\WorldState\Player.openapi.yaml" /> 213 223 <EmbeddedResource Include="src\WebAPI\OpenAPI.master.yaml" /> 214 224 </ItemGroup> -
TFP-WebServer/WebServer/src/UrlHandlers/AbsHandler.cs
r440 r463 26 26 parent = _parent; 27 27 urlBasePath = _relativePath; 28 parent.OpenApiHelpers.LoadOpenApiSpec (this); 28 29 } 29 30 } -
TFP-WebServer/WebServer/src/UrlHandlers/ItemIconHandler.cs
r402 r463 31 31 } 32 32 33 if (!_context.RequestPath.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) { 34 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest; 35 return; 36 } 37 33 38 string requestFileName = _context.RequestPath.Remove (0, urlBasePath.Length); 34 requestFileName = requestFileName.Remove (requestFileName.LastIndexOf ('.')); 35 36 if (icons.ContainsKey (requestFileName) && _context.RequestPath.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) { 37 _context.Response.ContentType = MimeType.GetMimeType (".png"); 38 39 byte[] itemIconData = icons [requestFileName]; 40 41 _context.Response.ContentLength64 = itemIconData.Length; 42 _context.Response.OutputStream.Write (itemIconData, 0, itemIconData.Length); 43 } else { 44 _context.Response.StatusCode = (int) HttpStatusCode.NotFound; 39 int indexOfExtSep = requestFileName.LastIndexOf ('.'); 40 if (indexOfExtSep < 0) { 41 _context.Response.StatusCode = (int) HttpStatusCode.BadRequest; 42 return; 43 } 44 45 requestFileName = requestFileName.Remove (indexOfExtSep); 46 47 if (!icons.TryGetValue (requestFileName, out byte[] icon)) { 48 _context.Response.StatusCode = (int)HttpStatusCode.NotFound; 45 49 if (logMissingFiles) { 46 50 Log.Out ($"[Web] IconHandler: FileNotFound: \"{_context.RequestPath}\" "); 47 51 } 48 } 52 return; 53 } 54 55 _context.Response.ContentType = MimeType.GetMimeType (".png"); 56 57 _context.Response.ContentLength64 = icon.Length; 58 _context.Response.OutputStream.Write (icon, 0, icon.Length); 49 59 } 50 60 -
TFP-WebServer/WebServer/src/WebAPI/APIs/Command.openapi.yaml
r462 r463 195 195 $ref: './openapi.yaml#/components/responses/Unauthorized' 196 196 security: 197 - {} 197 198 - apiTokenName: [] 198 199 apiTokenSecret: [] … … 215 216 description: Command not found 216 217 $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse' 218 security: 219 - {} 220 - apiTokenName: [ ] 221 apiTokenSecret: [ ] 222 - sessionCookie: [ ] -
TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Animal.openapi.yaml
r460 r463 34 34 - WorldState 35 35 summary: Animals list 36 description: Fetch a list of the currently spawned animals in the world36 description: Fetch a list of the currently spawned (non-hostile) animals in the world 37 37 operationId: animal.get 38 38 responses: -
TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Player.cs
r459 r463 1 1 using System; 2 using System.Collections.Generic;3 2 using System.Net; 4 3 using JetBrains.Annotations; … … 215 214 } 216 215 217 protected override void HandleRestPost (RequestContext _context, IDictionary<string, object> _jsonInput, byte[] _jsonInputData) {218 if (!JsonCommons.TryGetJsonField (_jsonInput, "command", out string commandString)) {219 SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "NO_COMMAND");220 return;221 }222 223 WebCommandResult.ResultType responseType = WebCommandResult.ResultType.Full;224 225 if (JsonCommons.TryGetJsonField (_jsonInput, "format", out string formatString)) {226 if (formatString.EqualsCaseInsensitive ("raw")) {227 responseType = WebCommandResult.ResultType.Raw;228 } else if (formatString.EqualsCaseInsensitive ("simple")) {229 responseType = WebCommandResult.ResultType.ResultOnly;230 }231 }232 233 int commandSepIndex = commandString.IndexOf (' ');234 string commandPart = commandSepIndex > 0 ? commandString.Substring (0, commandSepIndex) : commandString;235 string argumentsPart = commandSepIndex > 0236 ? commandString.Substring (commandPart.Length + 1)237 : "";238 239 IConsoleCommand command = SdtdConsole.Instance.GetCommand (commandPart, true);240 241 if (command == null) {242 SendEmptyResponse (_context, HttpStatusCode.NotFound, _jsonInputData, "UNKNOWN_COMMAND");243 return;244 }245 246 int commandPermissionLevel = GameManager.Instance.adminTools.Commands.GetCommandPermissionLevel (command.GetCommands ());247 248 if (_context.PermissionLevel > commandPermissionLevel) {249 SendEmptyResponse (_context, HttpStatusCode.Forbidden, _jsonInputData, "NO_PERMISSION");250 return;251 }252 253 _context.Response.SendChunked = true;254 WebCommandResult wcr = new WebCommandResult (commandPart, argumentsPart, responseType, _context);255 SdtdConsole.Instance.ExecuteAsync (commandString, wcr);256 }257 258 216 public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest; 259 217 } -
TFP-WebServer/WebServer/src/WebAPI/OpenAPI.master.yaml
r462 r463 111 111 description: User ID 112 112 - type: 'null' 113 113 114 TypeGameTimeObject: 115 type: object 116 properties: 117 days: 118 type: integer 119 minimum: 1 120 hours: 121 type: integer 122 minimum: 0 123 maximum: 23 124 minutes: 125 type: integer 126 minimum: 0 127 maximum: 59 128 required: 129 - days 130 - hours 131 - minutes 132 114 133 115 134 parameters: -
TFP-WebServer/WebServer/src/WebAPI/OpenApiHelpers.cs
r460 r463 4 4 using System.Text; 5 5 using System.Text.RegularExpressions; 6 using Webserver.UrlHandlers; 6 7 7 8 namespace Webserver.WebAPI { … … 10 11 private const string masterDocName = "openapi.yaml"; 11 12 12 private const string masterDocRefLineBegin = " - $ref: ";13 private const string masterDocRefLineEnd = "#/paths";14 15 13 private struct OpenApiSpec { 16 public readonly List<string> ExportedPaths;14 public readonly Dictionary<string, string> ExportedPaths; 17 15 public readonly string Spec; 18 16 19 public OpenApiSpec (string _spec, List<string> _exportedPaths = null) {17 public OpenApiSpec (string _spec, Dictionary<string, string> _exportedPaths = null) { 20 18 ExportedPaths = _exportedPaths; 21 19 Spec = _spec; … … 57 55 } 58 56 59 if ( (spec.ExportedPaths?.Count ?? 0)< 1) {57 if (spec.ExportedPaths == null || spec.ExportedPaths.Count < 1) { 60 58 continue; 61 59 } 62 60 63 foreach ( string exportedPathin spec.ExportedPaths) {64 writePath (sb, apiSpecName, exportedPath );61 foreach ((string exportedPath, string rebasedPath) in spec.ExportedPaths) { 62 writePath (sb, apiSpecName, exportedPath, rebasedPath); 65 63 } 66 64 } … … 71 69 } 72 70 73 private void writePath (StringBuilder _sb, string _apiSpecName, string _exportedPath ) {74 _sb.AppendLine ($" {_ exportedPath}:");71 private void writePath (StringBuilder _sb, string _apiSpecName, string _exportedPath, string _rebasedPath) { 72 _sb.AppendLine ($" {_rebasedPath ?? _exportedPath}:"); 75 73 _sb.Append ($" $ref: './{_apiSpecName}#/paths/"); 76 74 77 for (int i = 0; i < _exportedPath.Length; i++) { 78 char c = _exportedPath[i]; 79 80 switch (c) { 81 // JSON string escaped characters 82 case '"': 83 _sb.Append ("\\\""); 84 break; 85 case '\\': 86 _sb.Append ("\\\\"); 87 break; 88 case '\b': 89 _sb.Append ("\\b"); 90 break; 91 case '\f': 92 _sb.Append ("\\f"); 93 break; 94 case '\n': 95 _sb.Append ("\\n"); 96 break; 97 case '\r': 98 _sb.Append ("\\r"); 99 break; 100 case '\t': 101 _sb.Append ("\\t"); 102 break; 103 case (char)0x00: 104 _sb.Append ("\\u0000"); 105 break; 106 case (char)0x01: 107 _sb.Append ("\\u0001"); 108 break; 109 case (char)0x02: 110 _sb.Append ("\\u0002"); 111 break; 112 case (char)0x03: 113 _sb.Append ("\\u0003"); 114 break; 115 case (char)0x04: 116 _sb.Append ("\\u0004"); 117 break; 118 case (char)0x05: 119 _sb.Append ("\\u0005"); 120 break; 121 case (char)0x06: 122 _sb.Append ("\\u0006"); 123 break; 124 case (char)0x07: 125 _sb.Append ("\\u0007"); 126 break; 127 case (char)0x0b: 128 _sb.Append ("\\u000b"); 129 break; 130 case (char)0x0e: 131 _sb.Append ("\\u000e"); 132 break; 133 case (char)0x0f: 134 _sb.Append ("\\u000f"); 135 break; 136 case (char)0x10: 137 _sb.Append ("\\u0010"); 138 break; 139 case (char)0x11: 140 _sb.Append ("\\u0011"); 141 break; 142 case (char)0x12: 143 _sb.Append ("\\u0012"); 144 break; 145 case (char)0x13: 146 _sb.Append ("\\u0013"); 147 break; 148 case (char)0x14: 149 _sb.Append ("\\u0014"); 150 break; 151 case (char)0x15: 152 _sb.Append ("\\u0015"); 153 break; 154 case (char)0x16: 155 _sb.Append ("\\u0016"); 156 break; 157 case (char)0x17: 158 _sb.Append ("\\u0017"); 159 break; 160 case (char)0x18: 161 _sb.Append ("\\u0018"); 162 break; 163 case (char)0x19: 164 _sb.Append ("\\u0019"); 165 break; 166 case (char)0x1a: 167 _sb.Append ("\\u001a"); 168 break; 169 case (char)0x1b: 170 _sb.Append ("\\u001b"); 171 break; 172 case (char)0x1c: 173 _sb.Append ("\\u001c"); 174 break; 175 case (char)0x1d: 176 _sb.Append ("\\u001d"); 177 break; 178 case (char)0x1e: 179 _sb.Append ("\\u001e"); 180 break; 181 case (char)0x1f: 182 _sb.Append ("\\u001f"); 183 break; 184 // JSON Pointer specific 185 case '/': 186 _sb.Append ("~1"); 187 break; 188 case '~': 189 _sb.Append ("~0"); 190 break; 191 // Non escaped characters 192 default: 193 _sb.Append (c); 194 break; 195 } 196 } 75 writeJsonPointerEncodedPath (_sb, _exportedPath); 197 76 198 77 _sb.AppendLine ("'"); … … 214 93 } 215 94 95 public void LoadOpenApiSpec (AbsHandler _pathHandler) { 96 Assembly apiAssembly = _pathHandler.GetType ().Assembly; 97 string apiName = _pathHandler.GetType ().Name; 98 string apiSpecName = $"{apiName}.openapi.yaml"; 99 100 string specText = ResourceHelpers.GetManifestResourceText (apiAssembly, apiSpecName, true); 101 if (specText == null) { 102 return; 103 } 104 105 Log.Out ($"[Web] Loaded OpenAPI spec for '{apiName}'"); 106 OpenApiSpec spec = new OpenApiSpec (specText, findExportedPaths (specText, _pathHandler.UrlBasePath)); 107 specs.Add (apiSpecName, spec); 108 } 109 110 public void RegisterCustomSpec (Assembly _assembly, string _apiSpecName, string _replaceBasePath = null) { 111 string apiSpecName = $"{_apiSpecName}.openapi.yaml"; 112 113 string specText = ResourceHelpers.GetManifestResourceText (_assembly, apiSpecName, true); 114 if (specText == null) { 115 return; 116 } 117 118 Log.Out ($"[Web] Loaded OpenAPI spec for '{_apiSpecName}'"); 119 OpenApiSpec spec = new OpenApiSpec (specText, findExportedPaths (specText, _replaceBasePath)); 120 specs.Add (apiSpecName, spec); 121 } 122 216 123 private static readonly Regex pathMatcher = new Regex ("^\\s{1,2}(/\\S+):.*$", RegexOptions.Compiled | RegexOptions.CultureInvariant); 217 private List<string> findExportedPaths (string _spec) {218 List<string> result = new List<string> ();124 private Dictionary<string, string> findExportedPaths (string _spec, string _replaceBasePath = null) { 125 Dictionary<string, string> result = new Dictionary<string, string> (); 219 126 220 127 using TextReader tr = new StringReader (_spec); … … 234 141 235 142 string path = match.Groups [1].Value; 143 string rebasedPath = null; 236 144 Log.Out ($"[Web] Exports: {path}"); 237 result.Add (path); 145 if (_replaceBasePath != null) { 146 rebasedPath = path.Replace ("/BASEPATH/", _replaceBasePath); 147 } 148 result [path] = rebasedPath; 238 149 } 239 150 } … … 255 166 return true; 256 167 } 168 169 private void writeJsonPointerEncodedPath (StringBuilder _targetSb, string _path) { 170 for (int i = 0; i < _path.Length; i++) { 171 char c = _path[i]; 172 173 switch (c) { 174 // JSON string escaped characters 175 case '"': 176 _targetSb.Append ("\\\""); 177 break; 178 case '\\': 179 _targetSb.Append ("\\\\"); 180 break; 181 case '\b': 182 _targetSb.Append ("\\b"); 183 break; 184 case '\f': 185 _targetSb.Append ("\\f"); 186 break; 187 case '\n': 188 _targetSb.Append ("\\n"); 189 break; 190 case '\r': 191 _targetSb.Append ("\\r"); 192 break; 193 case '\t': 194 _targetSb.Append ("\\t"); 195 break; 196 case (char)0x00: 197 _targetSb.Append ("\\u0000"); 198 break; 199 case (char)0x01: 200 _targetSb.Append ("\\u0001"); 201 break; 202 case (char)0x02: 203 _targetSb.Append ("\\u0002"); 204 break; 205 case (char)0x03: 206 _targetSb.Append ("\\u0003"); 207 break; 208 case (char)0x04: 209 _targetSb.Append ("\\u0004"); 210 break; 211 case (char)0x05: 212 _targetSb.Append ("\\u0005"); 213 break; 214 case (char)0x06: 215 _targetSb.Append ("\\u0006"); 216 break; 217 case (char)0x07: 218 _targetSb.Append ("\\u0007"); 219 break; 220 case (char)0x0b: 221 _targetSb.Append ("\\u000b"); 222 break; 223 case (char)0x0e: 224 _targetSb.Append ("\\u000e"); 225 break; 226 case (char)0x0f: 227 _targetSb.Append ("\\u000f"); 228 break; 229 case (char)0x10: 230 _targetSb.Append ("\\u0010"); 231 break; 232 case (char)0x11: 233 _targetSb.Append ("\\u0011"); 234 break; 235 case (char)0x12: 236 _targetSb.Append ("\\u0012"); 237 break; 238 case (char)0x13: 239 _targetSb.Append ("\\u0013"); 240 break; 241 case (char)0x14: 242 _targetSb.Append ("\\u0014"); 243 break; 244 case (char)0x15: 245 _targetSb.Append ("\\u0015"); 246 break; 247 case (char)0x16: 248 _targetSb.Append ("\\u0016"); 249 break; 250 case (char)0x17: 251 _targetSb.Append ("\\u0017"); 252 break; 253 case (char)0x18: 254 _targetSb.Append ("\\u0018"); 255 break; 256 case (char)0x19: 257 _targetSb.Append ("\\u0019"); 258 break; 259 case (char)0x1a: 260 _targetSb.Append ("\\u001a"); 261 break; 262 case (char)0x1b: 263 _targetSb.Append ("\\u001b"); 264 break; 265 case (char)0x1c: 266 _targetSb.Append ("\\u001c"); 267 break; 268 case (char)0x1d: 269 _targetSb.Append ("\\u001d"); 270 break; 271 case (char)0x1e: 272 _targetSb.Append ("\\u001e"); 273 break; 274 case (char)0x1f: 275 _targetSb.Append ("\\u001f"); 276 break; 277 // JSON Pointer specific 278 case '/': 279 _targetSb.Append ("~1"); 280 break; 281 case '~': 282 _targetSb.Append ("~0"); 283 break; 284 // Non escaped characters 285 default: 286 _targetSb.Append (c); 287 break; 288 } 289 } 290 } 291 257 292 } 258 293 } -
TFP-WebServer/WebServer/src/WebMod.cs
r434 r463 10 10 11 11 public readonly Mod ParentMod; 12 public readonly string ReactBundle; // Absolute web path to the React bundle if the mod has one, e.g. "/webmods/myMod/bundle.js" 13 public readonly string CssPath; // Absolute web path to a CSS if the mod has one, e.g. "/webmods/myMod/styling.css"; 12 /// <summary> 13 /// Absolute web path to the mod web root, e.g. "/webmods/myMod/" 14 /// </summary> 15 public readonly string ModUrl; 16 /// <summary> 17 /// Absolute web path to the React bundle if the mod has one, e.g. "/webmods/myMod/bundle.js" 18 /// </summary> 19 public readonly string ReactBundle; 20 /// <summary> 21 /// Absolute web path to a CSS if the mod has one, e.g. "/webmods/myMod/styling.css" 22 /// </summary> 23 public readonly string CssPath; 14 24 public readonly bool IsWebMod; 15 25 … … 24 34 } 25 35 26 string urlWebModBase = $"{modsBaseUrl}{_parentMod.FolderName}/";36 ModUrl = $"{modsBaseUrl}{_parentMod.Name}/"; 27 37 28 38 ReactBundle = $"{folder}/{reactBundleName}"; 29 ReactBundle = File.Exists (ReactBundle) ? $"{ urlWebModBase}{reactBundleName}" : null;39 ReactBundle = File.Exists (ReactBundle) ? $"{ModUrl}{reactBundleName}" : null; 30 40 31 41 CssPath = $"{folder}/{stylingFileName}"; 32 CssPath = File.Exists (CssPath) ? $"{ urlWebModBase}{stylingFileName}" : null;42 CssPath = File.Exists (CssPath) ? $"{ModUrl}{stylingFileName}" : null; 33 43 34 _parentWeb.RegisterPathHandler ( urlWebModBase, new StaticHandler (44 _parentWeb.RegisterPathHandler (ModUrl, new StaticHandler ( 35 45 folder, 36 46 _useStaticCache ? new SimpleCache () : new DirectAccess (),
Note:
See TracChangeset
for help on using the changeset viewer.