// Server Module for the API part of the project.

unit Api.Server.Module;

interface

uses
  System.SysUtils, System.Classes, System.Generics.Collections,
  Aurelius.Drivers.SQLite,
  Aurelius.Comp.Connection,
  Aurelius.Drivers.Interfaces,
  XData.Aurelius.ConnectionPool, XData.Server.Module, Sparkle.Comp.Server,
  XData.Comp.Server, XData.Comp.ConnectionPool, Sparkle.Comp.HttpSysDispatcher,
  Sparkle.Comp.JwtMiddleware, Sparkle.Middleware.Jwt, Aurelius.Criteria.Linq,
  Sparkle.HttpServer.Module, Sparkle.HttpServer.Context,
  Sparkle.Comp.CompressMiddleware, Sparkle.Comp.CorsMiddleware,
  Sparkle.Comp.GenericMiddleware, Aurelius.Drivers.UniDac, UniProvider,
  Data.DB, DBAccess, Uni, Sparkle.Comp.LoggingMiddleware;

type
  TApiServerModule = class(TDataModule)
    SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
    XDataServer: TXDataServer;
    XDataServerJWT: TSparkleJwtMiddleware;
    XDataServerCompress: TSparkleCompressMiddleware;
    XDataServerCORS: TSparkleCorsMiddleware;
    XDataServerLogging: TSparkleLoggingMiddleware;
    procedure XDataServerJWTForbidRequest(Sender: TObject; Context:
        THttpServerContext; var Forbid: Boolean);
    procedure XDataServerLoggingMiddlewareCreate(Sender: TObject;
      var Middleware: IHttpServerMiddleware);
    procedure XDataServerJWTGetSecret(Sender: TObject; var Secret: string);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure StartApiServer(ABaseUrl: string; AModelName: string);
  end;

const
  SERVER_PATH_SEGMENT = 'api';

var
  ApiServerModule: TApiServerModule;

implementation

uses
  Sparkle.HttpServer.Request,
  Sparkle.Middleware.Cors,
  Sparkle.Middleware.Compress,
  XData.OpenApi.Service,
  XData.Sys.Exceptions,
  Common.Logging,
  Common.Middleware.Logging,
  Common.Config, Vcl.Forms, IniFiles, TaskItem.Service, TimeEntry.Service;

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

{ TApiServerModule }

function IsAdmin(Request: THttpServerRequest): Boolean;
var
  User: IUserIdentity;
begin
  User := Request.User;
  Result := (User <> nil) and User.Claims.Exists('admin') and User.Claims['admin'].AsBoolean;
end;

procedure TApiServerModule.StartApiServer(ABaseUrl: string; AModelName: string);
var
  Url: string;
begin
  RegisterOpenApiService;

  Url := ABaseUrl;
  if not Url.EndsWith('/') then
    Url := Url + '/';
  Url := Url + SERVER_PATH_SEGMENT;

  XDataServer.BaseUrl := Url;
  XDataServer.ModelName := AModelName;
  Logger.Log(1, 'API XDataServer.ModelName=' + XDataServer.ModelName);

  SparkleHttpSysDispatcher.Start;
  Logger.Log(1, Format('Api server module listening at "%s"', [XDataServer.BaseUrl]));
end;

procedure TApiServerModule.XDataServerJWTForbidRequest(Sender: TObject;
    Context: THttpServerContext; var Forbid: Boolean);
var
  Path: string;
begin
  Path := Context.Request.Uri.Path;

  if SameText(Context.Request.Method, 'OPTIONS') then
    Forbid := False;

  if Path.Contains('/swaggerui') then
    Forbid := False;

  if Path.Contains('/openapi/swagger.json') then
    Forbid := False;

  if Forbid then
    Logger.Log(1, '[JWT] ForbidRequest fired (token missing/invalid/expired?)');

end;

procedure TApiServerModule.XDataServerLoggingMiddlewareCreate(Sender: TObject;
  var Middleware: IHttpServerMiddleware);
begin
  Middleware := TLoggingMiddleware.Create(Logger);
end;

procedure TApiServerModule.XDataServerJWTGetSecret(Sender: TObject;
  var Secret: string);
begin
  Secret := serverConfig.jwtTokenSecret;
end;

end.
