Ignore:
Timestamp:
Aug 17, 2023, 4:57:23 PM (15 months ago)
Author:
alloc
Message:

21.1.16.0 release
Completed OpenAPI specs
Add support to path handlers to register OpenAPI specs
Fixed ItemIconHandler throwing error when requested path contains no dot

Location:
TFP-WebServer/WebServer
Files:
10 added
10 edited

Legend:

Unmodified
Added
Removed
  • TFP-WebServer/WebServer/ModInfo.xml

    r457 r463  
    55        <Description value="Integrated Webserver for the Web Dashboard and server APIs" />
    66        <Author value="The Fun Pimps LLC" />
    7         <Version value="21.1.15.0" />
     7        <Version value="21.1.16.0" />
    88        <Website value="" />
    99</xml>
  • TFP-WebServer/WebServer/WebServer.csproj

    r462 r463  
    198198  </ItemGroup>
    199199  <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" />
    200203    <EmbeddedResource Include="src\WebAPI\APIs\GameData\Item.openapi.yaml" />
    201204    <EmbeddedResource Include="src\WebAPI\APIs\GameData\Mods.openapi.yaml" />
     
    210213    <EmbeddedResource Include="src\WebAPI\APIs\Permissions\WebUsers.openapi.yaml" />
    211214    <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" />
    212219    <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" />
    213223    <EmbeddedResource Include="src\WebAPI\OpenAPI.master.yaml" />
    214224  </ItemGroup>
  • TFP-WebServer/WebServer/src/UrlHandlers/AbsHandler.cs

    r440 r463  
    2626                        parent = _parent;
    2727                        urlBasePath = _relativePath;
     28                        parent.OpenApiHelpers.LoadOpenApiSpec (this);
    2829                }
    2930        }
  • TFP-WebServer/WebServer/src/UrlHandlers/ItemIconHandler.cs

    r402 r463  
    3131                        }
    3232
     33                        if (!_context.RequestPath.EndsWith (".png", StringComparison.OrdinalIgnoreCase)) {
     34                                _context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
     35                                return;
     36                        }
     37
    3338                        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;
    4549                                if (logMissingFiles) {
    4650                                        Log.Out ($"[Web] IconHandler: FileNotFound: \"{_context.RequestPath}\" ");
    4751                                }
    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);
    4959                }
    5060
  • TFP-WebServer/WebServer/src/WebAPI/APIs/Command.openapi.yaml

    r462 r463  
    195195          $ref: './openapi.yaml#/components/responses/Unauthorized'
    196196      security:
     197        - {}
    197198        - apiTokenName: []
    198199          apiTokenSecret: []
     
    215216          description: Command not found
    216217          $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  
    3434        - WorldState
    3535      summary: Animals list
    36       description: Fetch a list of the currently spawned animals in the world
     36      description: Fetch a list of the currently spawned (non-hostile) animals in the world
    3737      operationId: animal.get
    3838      responses:
  • TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Player.cs

    r459 r463  
    11using System;
    2 using System.Collections.Generic;
    32using System.Net;
    43using JetBrains.Annotations;
     
    215214                }
    216215
    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 > 0
    236                                 ? 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 
    258216                public override int DefaultPermissionLevel () => AdminWebModules.PermissionLevelGuest;
    259217        }
  • TFP-WebServer/WebServer/src/WebAPI/OpenAPI.master.yaml

    r462 r463  
    111111          description: User ID
    112112        - 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       
    114133
    115134  parameters:
  • TFP-WebServer/WebServer/src/WebAPI/OpenApiHelpers.cs

    r460 r463  
    44using System.Text;
    55using System.Text.RegularExpressions;
     6using Webserver.UrlHandlers;
    67
    78namespace Webserver.WebAPI {
     
    1011                private const string masterDocName = "openapi.yaml";
    1112
    12                 private const string masterDocRefLineBegin = "    - $ref: ";
    13                 private const string masterDocRefLineEnd = "#/paths";
    14 
    1513                private struct OpenApiSpec {
    16                         public readonly List<string> ExportedPaths;
     14                        public readonly Dictionary<string, string> ExportedPaths;
    1715                        public readonly string Spec;
    1816
    19                         public OpenApiSpec (string _spec, List<string> _exportedPaths = null) {
     17                        public OpenApiSpec (string _spec, Dictionary<string, string> _exportedPaths = null) {
    2018                                ExportedPaths = _exportedPaths;
    2119                                Spec = _spec;
     
    5755                                }
    5856
    59                                 if ((spec.ExportedPaths?.Count ?? 0) < 1) {
     57                                if (spec.ExportedPaths == null || spec.ExportedPaths.Count < 1) {
    6058                                        continue;
    6159                                }
    6260
    63                                 foreach (string exportedPath in spec.ExportedPaths) {
    64                                         writePath (sb, apiSpecName, exportedPath);
     61                                foreach ((string exportedPath, string rebasedPath) in spec.ExportedPaths) {
     62                                        writePath (sb, apiSpecName, exportedPath, rebasedPath);
    6563                                }
    6664                        }
     
    7169                }
    7270
    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}:");
    7573                        _sb.Append ($"    $ref: './{_apiSpecName}#/paths/");
    7674
    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);
    19776                       
    19877                        _sb.AppendLine ("'");
     
    21493                }
    21594
     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
    216123                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> ();
    219126
    220127                        using TextReader tr = new StringReader (_spec);
     
    234141
    235142                                        string path = match.Groups [1].Value;
     143                                        string rebasedPath = null;
    236144                                        Log.Out ($"[Web]   Exports: {path}");
    237                                         result.Add (path);
     145                                        if (_replaceBasePath != null) {
     146                                                rebasedPath = path.Replace ("/BASEPATH/", _replaceBasePath);
     147                                        }
     148                                        result [path] = rebasedPath;
    238149                                }
    239150                        }
     
    255166                        return true;
    256167                }
     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
    257292        }
    258293}
  • TFP-WebServer/WebServer/src/WebMod.cs

    r434 r463  
    1010               
    1111                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;
    1424                public readonly bool IsWebMod;
    1525
     
    2434                        }
    2535
    26                         string urlWebModBase = $"{modsBaseUrl}{_parentMod.FolderName}/";
     36                        ModUrl = $"{modsBaseUrl}{_parentMod.Name}/";
    2737
    2838                        ReactBundle = $"{folder}/{reactBundleName}";
    29                         ReactBundle = File.Exists (ReactBundle) ? $"{urlWebModBase}{reactBundleName}" : null;
     39                        ReactBundle = File.Exists (ReactBundle) ? $"{ModUrl}{reactBundleName}" : null;
    3040
    3141                        CssPath = $"{folder}/{stylingFileName}";
    32                         CssPath = File.Exists (CssPath) ? $"{urlWebModBase}{stylingFileName}" : null;
     42                        CssPath = File.Exists (CssPath) ? $"{ModUrl}{stylingFileName}" : null;
    3343
    34                         _parentWeb.RegisterPathHandler (urlWebModBase, new StaticHandler (
     44                        _parentWeb.RegisterPathHandler (ModUrl, new StaticHandler (
    3545                                folder,
    3646                                _useStaticCache ? new SimpleCache () : new DirectAccess (),
Note: See TracChangeset for help on using the changeset viewer.