Commit 97545def by Mac Stephens

Initial template created with common.ini, using templateXDataServer.json for jwt…

Initial template created with common.ini, using templateXDataServer.json for jwt secret, logging is consistent, used kgorders as base and updated pieces based on webpolicereport, emimobile, and treestar to ensure best practice. auth.serviceimpl is not implemented yet
parent 6e769003
templateAppServer/__history/
templateAppServer/source/__history/
templateAppServer/Win32/Debug/
*.res
*.identcache
*.local
[2026-01-08 16:55:35.249][1] --LoadServerConfig - start
[2026-01-08 16:55:35.251][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 16:55:35.257][1] -- Config file found.
[2026-01-08 16:55:35.265][1] --TServerConfig.Create - start
[2026-01-08 16:55:35.273][1] --TServerConfig.Create - end
[2026-01-08 16:55:35.285][1] -- localConfig loaded from config file
[2026-01-08 16:55:35.293][1] -- serverConfig.Free - called
[2026-01-08 16:55:35.305][1] -- serverConfig := localConfig - called
[2026-01-08 16:55:35.350][1] --- Server Config Values ---
[2026-01-08 16:55:35.352][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 16:55:35.363][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 16:55:35.370][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 16:55:35.382][1] -- webAppFolder: static [default]
[2026-01-08 16:55:35.396][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:02:00.850][1] --TServerConfig.Create - start
[2026-01-08 17:02:00.852][1] --TServerConfig.Create - end
[2026-01-08 17:02:00.860][1] --LoadServerConfig - start
[2026-01-08 17:02:00.867][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:02:00.875][1] -- Config file found.
[2026-01-08 17:02:00.885][1] --TServerConfig.Create - start
[2026-01-08 17:02:00.893][1] --TServerConfig.Create - end
[2026-01-08 17:02:00.902][1] -- localConfig loaded from config file
[2026-01-08 17:02:00.908][1] -- serverConfig.Free - called
[2026-01-08 17:02:00.914][1] -- serverConfig := localConfig - called
[2026-01-08 17:02:00.923][1] --- Server Config Values ---
[2026-01-08 17:02:00.931][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:02:00.939][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:02:00.949][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:02:00.958][1] -- webAppFolder: static [default]
[2026-01-08 17:02:00.971][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:04:57.895][1] --TServerConfig.Create - start
[2026-01-08 17:04:57.895][1] --TServerConfig.Create - end
[2026-01-08 17:04:57.904][1] --LoadServerConfig - start
[2026-01-08 17:04:57.908][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:04:57.908][1] -- Config file found.
[2026-01-08 17:04:57.930][1] --TServerConfig.Create - start
[2026-01-08 17:04:57.937][1] --TServerConfig.Create - end
[2026-01-08 17:04:57.944][1] -- localConfig loaded from config file
[2026-01-08 17:04:57.951][1] -- serverConfig.Free - called
[2026-01-08 17:04:57.956][1] -- serverConfig := localConfig - called
[2026-01-08 17:04:57.963][1] --- Server Config Values ---
[2026-01-08 17:04:57.971][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:04:57.979][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:04:57.987][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:04:57.995][1] -- webAppFolder: static [default]
[2026-01-08 17:04:58.004][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:04:58.014][1] --LoadServerConfig - end
[2026-01-08 17:04:58.031][1] ******************************************************
[2026-01-08 17:04:58.040][1] template Server
[2026-01-08 17:04:58.048][1] Version: 1.0.0.0
[2026-01-08 17:04:58.057][1]
[2026-01-08 17:04:58.068][1] by EM Systems, Inc.
[2026-01-08 17:04:58.076][1] ******************************************************
[2026-01-08 17:04:58.097][1] --- Database ---
[2026-01-08 17:04:58.105][1] --Database->Server: 10.208.1.13
[2026-01-08 17:04:58.116][1] --Database->Port: 0
[2026-01-08 17:04:58.123][1] --Database->Database: sleepdb
[2026-01-08 17:04:58.135][1] --Database->Username: hstuser
[2026-01-08 17:04:58.143][1] --Database->Password: emsys!01
[2026-01-08 17:04:58.159][1] --- Settings ---
[2026-01-08 17:04:58.170][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:04:58.172][1] --Settings->FileLogLevel: 3
[2026-01-08 17:04:58.182][1] --Settings->LogFileNum: 10
[2026-01-08 17:04:58.193][1] --Settings->JWTSecret:
[2026-01-08 17:04:58.252][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:06:43.424][1] --TServerConfig.Create - start
[2026-01-08 17:06:43.426][1] --TServerConfig.Create - end
[2026-01-08 17:06:43.435][1] --LoadServerConfig - start
[2026-01-08 17:06:43.442][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:06:43.449][1] -- Config file found.
[2026-01-08 17:06:43.456][1] --TServerConfig.Create - start
[2026-01-08 17:06:43.465][1] --TServerConfig.Create - end
[2026-01-08 17:06:43.470][1] -- localConfig loaded from config file
[2026-01-08 17:06:43.478][1] -- serverConfig.Free - called
[2026-01-08 17:06:43.486][1] -- serverConfig := localConfig - called
[2026-01-08 17:06:43.492][1] --- Server Config Values ---
[2026-01-08 17:06:43.501][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:06:43.509][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:06:43.517][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:06:43.524][1] -- webAppFolder: static [default]
[2026-01-08 17:06:43.535][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:06:43.545][1] --LoadServerConfig - end
[2026-01-08 17:06:43.563][1] ******************************************************
[2026-01-08 17:06:43.570][1] template Server
[2026-01-08 17:06:43.576][1] Version: 1.0.0.0
[2026-01-08 17:06:43.583][1]
[2026-01-08 17:06:43.591][1] by EM Systems, Inc.
[2026-01-08 17:06:43.598][1] ******************************************************
[2026-01-08 17:06:43.617][1] --- Database ---
[2026-01-08 17:06:43.625][1] --Database->Server: 10.208.1.13
[2026-01-08 17:06:43.633][1] --Database->Port: 0
[2026-01-08 17:06:43.638][1] --Database->Database: sleepdb
[2026-01-08 17:06:43.649][1] --Database->Username: hstuser
[2026-01-08 17:06:43.659][1] --Database->Password: emsys!01
[2026-01-08 17:06:43.679][1] --- Settings ---
[2026-01-08 17:06:43.687][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:06:43.697][1] --Settings->FileLogLevel: 3
[2026-01-08 17:06:43.706][1] --Settings->LogFileNum: 11
[2026-01-08 17:06:43.713][1] --Settings->JWTSecret:
[2026-01-08 17:06:43.755][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:07:51.485][1] --TServerConfig.Create - start
[2026-01-08 17:07:51.485][1] --TServerConfig.Create - end
[2026-01-08 17:07:51.495][1] --LoadServerConfig - start
[2026-01-08 17:07:51.517][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:07:51.527][1] -- Config file found.
[2026-01-08 17:07:51.543][1] --TServerConfig.Create - start
[2026-01-08 17:07:51.550][1] --TServerConfig.Create - end
[2026-01-08 17:07:51.558][1] -- localConfig loaded from config file
[2026-01-08 17:07:51.564][1] -- serverConfig.Free - called
[2026-01-08 17:07:51.571][1] -- serverConfig := localConfig - called
[2026-01-08 17:07:51.580][1] --- Server Config Values ---
[2026-01-08 17:07:51.586][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:07:51.594][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:07:51.611][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:07:51.623][1] -- webAppFolder: static [default]
[2026-01-08 17:07:51.630][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:07:51.640][1] --LoadServerConfig - end
[2026-01-08 17:07:51.659][1] ******************************************************
[2026-01-08 17:07:51.667][1] template Server
[2026-01-08 17:07:51.676][1] Version: 1.0.0.0
[2026-01-08 17:07:51.682][1]
[2026-01-08 17:07:51.694][1] by EM Systems, Inc.
[2026-01-08 17:07:51.700][1] ******************************************************
[2026-01-08 17:07:51.716][1] --- Database ---
[2026-01-08 17:07:51.727][1] --Database->Server: 10.208.1.13
[2026-01-08 17:07:51.738][1] --Database->Port: 0
[2026-01-08 17:07:51.745][1] --Database->Database: sleepdb
[2026-01-08 17:07:51.758][1] --Database->Username: hstuser
[2026-01-08 17:07:51.769][1] --Database->Password: emsys!01
[2026-01-08 17:07:51.786][1] --- Settings ---
[2026-01-08 17:07:51.794][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:07:51.803][1] --Settings->FileLogLevel: 3
[2026-01-08 17:07:51.809][1] --Settings->LogFileNum: 12
[2026-01-08 17:07:51.816][1] --Settings->JWTSecret:
[2026-01-08 17:07:51.904][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:09:19.580][1] --TServerConfig.Create - start
[2026-01-08 17:09:19.582][1] --TServerConfig.Create - end
[2026-01-08 17:09:19.594][1] --LoadServerConfig - start
[2026-01-08 17:09:19.603][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:09:19.614][1] -- Config file found.
[2026-01-08 17:09:19.623][1] --TServerConfig.Create - start
[2026-01-08 17:09:19.631][1] --TServerConfig.Create - end
[2026-01-08 17:09:19.636][1] -- localConfig loaded from config file
[2026-01-08 17:09:19.650][1] -- serverConfig.Free - called
[2026-01-08 17:09:19.654][1] -- serverConfig := localConfig - called
[2026-01-08 17:09:19.656][1] --- Server Config Values ---
[2026-01-08 17:09:19.659][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:09:19.663][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:09:19.665][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:09:19.669][1] -- webAppFolder: static [default]
[2026-01-08 17:09:19.673][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:09:19.677][1] --LoadServerConfig - end
[2026-01-08 17:09:19.683][1] ******************************************************
[2026-01-08 17:09:19.689][1] template Server
[2026-01-08 17:09:19.691][1] Version: 1.0.0.0
[2026-01-08 17:09:19.693][1]
[2026-01-08 17:09:19.697][1] by EM Systems, Inc.
[2026-01-08 17:09:19.701][1] ******************************************************
[2026-01-08 17:09:19.709][1] --- Database ---
[2026-01-08 17:09:19.711][1] --Database->Server: 10.208.1.13
[2026-01-08 17:09:19.715][1] --Database->Port: 0
[2026-01-08 17:09:19.728][1] --Database->Database: sleepdb
[2026-01-08 17:09:19.740][1] --Database->Username: hstuser
[2026-01-08 17:09:19.748][1] --Database->Password: emsys!01
[2026-01-08 17:09:19.776][1] --- Settings ---
[2026-01-08 17:09:19.794][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:09:19.806][1] --Settings->FileLogLevel: 3
[2026-01-08 17:09:19.815][1] --Settings->LogFileNum: 13
[2026-01-08 17:09:19.824][1] --Settings->JWTSecret:
[2026-01-08 17:09:19.886][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:09:58.392][1] --TServerConfig.Create - start
[2026-01-08 17:09:58.392][1] --TServerConfig.Create - end
[2026-01-08 17:09:58.402][1] --LoadServerConfig - start
[2026-01-08 17:09:58.412][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:09:58.412][1] -- Config file found.
[2026-01-08 17:09:58.424][1] --TServerConfig.Create - start
[2026-01-08 17:09:58.432][1] --TServerConfig.Create - end
[2026-01-08 17:09:58.445][1] -- localConfig loaded from config file
[2026-01-08 17:09:58.452][1] -- serverConfig.Free - called
[2026-01-08 17:09:58.463][1] -- serverConfig := localConfig - called
[2026-01-08 17:09:58.470][1] --- Server Config Values ---
[2026-01-08 17:09:58.478][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:09:58.490][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:09:58.499][1] -- jwtTokenSecret: [from ini]
[2026-01-08 17:09:58.509][1] -- webAppFolder: static [default]
[2026-01-08 17:09:58.518][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:09:58.527][1] --LoadServerConfig - end
[2026-01-08 17:09:58.545][1] ******************************************************
[2026-01-08 17:09:58.555][1] template Server
[2026-01-08 17:09:58.564][1] Version: 1.0.0.0
[2026-01-08 17:09:58.573][1]
[2026-01-08 17:09:58.582][1] by EM Systems, Inc.
[2026-01-08 17:09:58.591][1] ******************************************************
[2026-01-08 17:09:58.606][1] --- Database ---
[2026-01-08 17:09:58.614][1] --Database->Server: 10.208.1.13
[2026-01-08 17:09:58.623][1] --Database->Port: 0
[2026-01-08 17:09:58.633][1] --Database->Database: sleepdb
[2026-01-08 17:09:58.641][1] --Database->Username: hstuser
[2026-01-08 17:09:58.647][1] --Database->Password: emsys!01
[2026-01-08 17:09:58.664][1] --- Settings ---
[2026-01-08 17:09:58.670][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:09:58.679][1] --Settings->FileLogLevel: 3
[2026-01-08 17:09:58.684][1] --Settings->LogFileNum: 14
[2026-01-08 17:09:58.692][1] --Settings->JWTSecret: super_secret0123super_secret4567
[2026-01-08 17:09:58.755][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:09:58.782][1] Api server module listening at "http://localhost:2004/emsys/template/api"
[2026-01-08 17:09:58.805][1] App server module listening at "http://localhost:2004/emsys/template/app", rootDir: static
[2026-01-08 17:25:33.595][1] --TServerConfig.Create - start
[2026-01-08 17:25:33.597][1] --TServerConfig.Create - end
[2026-01-08 17:25:33.605][1] --LoadServerConfig - start
[2026-01-08 17:25:33.622][1] -- Config file: C:\Projects\emWebAppTemplate\templateAppServer\bin\templateXDataServer.json
[2026-01-08 17:25:33.630][1] -- Config file found.
[2026-01-08 17:25:33.650][1] --TServerConfig.Create - start
[2026-01-08 17:25:33.656][1] --TServerConfig.Create - end
[2026-01-08 17:25:33.665][1] -- localConfig loaded from config file
[2026-01-08 17:25:33.672][1] -- serverConfig.Free - called
[2026-01-08 17:25:33.680][1] -- serverConfig := localConfig - called
[2026-01-08 17:25:33.687][1] --- Server Config Values ---
[2026-01-08 17:25:33.695][1] -- url: http://localhost:2004/emsys/template/ [default]
[2026-01-08 17:25:33.701][1] -- adminPassword: whatisthisusedfor [default]
[2026-01-08 17:25:33.710][1] -- jwtTokenSecret: [from config/default]
[2026-01-08 17:25:33.717][1] -- webAppFolder: static [default]
[2026-01-08 17:25:33.725][1] -- reportsFolder: static\reports\ [default]
[2026-01-08 17:25:33.734][1] --LoadServerConfig - end
[2026-01-08 17:25:33.746][1] ******************************************************
[2026-01-08 17:25:33.748][1] template Server
[2026-01-08 17:25:33.751][1] Version: 1.0.0.0
[2026-01-08 17:25:33.753][1]
[2026-01-08 17:25:33.755][1] by EM Systems, Inc.
[2026-01-08 17:25:33.755][1] ******************************************************
[2026-01-08 17:25:33.759][1] --- Database ---
[2026-01-08 17:25:33.762][1] --Database->Server: 10.208.1.13
[2026-01-08 17:25:33.770][1] --Database->Port: 0
[2026-01-08 17:25:33.781][1] --Database->Database: sleepdb
[2026-01-08 17:25:33.790][1] --Database->Username: hstuser
[2026-01-08 17:25:33.800][1] --Database->Password: emsys!01
[2026-01-08 17:25:33.813][1] --- Settings ---
[2026-01-08 17:25:33.815][1] --Settings->ConsoleLogLevel: 3
[2026-01-08 17:25:33.824][1] --Settings->FileLogLevel: 3
[2026-01-08 17:25:33.833][1] --Settings->LogFileNum: 15
[2026-01-08 17:25:33.894][1] Auth server module listening at "http://localhost:2004/emsys/template/auth"
[2026-01-08 17:25:33.925][1] Api server module listening at "http://localhost:2004/emsys/template/api"
[2026-01-08 17:25:33.949][1] App server module listening at "http://localhost:2004/emsys/template/app", rootDir: static
{"url":"http://localhost:2004/emsys/template/","jwtTokenSecret":"super_secret0123super_secret4567","adminPassword":"whatisthisusedfor","webAppFolder":"static","reportsFolder":"static\\reports\\"}
\ No newline at end of file
[Settings]
MemoLogLevel=5
FileLogLevel=5
LogFileNum=1
DevMode=0
webClientVersion=0.0.1
[DB]
Server=192.168.102.129
--Server=
--Port=
--Database=
--Username=
--Password=
[SMTP]
--Type=useExplicitTLS
--Host=mail.em-sys.net
Port=587
--Username=em-sys\emsys
--Password=Ridge!4043
--FromEmail=emsys@em-sys.net
object ApiDatabase: TApiDatabase
OnCreate = DataModuleCreate
Height = 358
Width = 519
object ucHST: TUniConnection
ProviderName = 'MySQL'
Database = 'kg_order_entry'
Username = 'root'
LoginPrompt = False
Left = 75
Top = 65
EncryptedPassword = '9AFF92FF8CFF86FF8CFFCFFFCEFF'
end
object UniQuery1: TUniQuery
Connection = ucHST
SQL.Strings = (
'')
Left = 73
Top = 148
end
object MySQLUniProvider1: TMySQLUniProvider
Left = 202
Top = 60
end
object UniQuery2: TUniQuery
Connection = ucHST
SQL.Strings = (
'')
Left = 179
Top = 148
end
end
// Where the database is kept. Only used by Lookup.ServiceImpl to retrieve info
// from the data base and send it to the client.
// Author: ???
unit Api.Database;
interface
uses
System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni, UniProvider,
PostgreSQLUniProvider, System.Variants, System.Generics.Collections, System.IniFiles,
Common.Logging, Vcl.Forms, MySQLUniProvider;
type
TApiDatabase = class(TDataModule)
ucHST: TUniConnection;
UniQuery1: TUniQuery;
MySQLUniProvider1: TMySQLUniProvider;
UniQuery2: TUniQuery;
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
class procedure ExecSQL(const SQL: string);
end;
var
ApiDatabase: TApiDatabase;
implementation
uses
uLibrary;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
procedure TApiDatabase.DataModuleCreate(Sender: TObject);
begin
Logger.Log( 1, 'TApiDatabase.DataModuleCreate' );
try
ucHST.Connect;
except
on E: Exception do
begin
Logger.Log(2, '--TApiDatabase.DataModuleCreate -Error connecting to database: ' + E.Message);
end;
end;
end;
class procedure TApiDatabase.ExecSQL(const SQL: string);
var
DB: TApiDatabase;
begin
DB := TApiDatabase.Create(nil);
try
DB.UniQuery1.SQL.Text := SQL;
DB.UniQuery1.ExecSQL;
finally
DB.Free;
end;
end;
end.
object ApiServerModule: TApiServerModule
Height = 273
Width = 230
object SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher
Left = 86
Top = 30
end
object XDataServer: TXDataServer
Dispatcher = SparkleHttpSysDispatcher
ModelName = 'Api'
EntitySetPermissions = <>
SwaggerOptions.Enabled = True
SwaggerOptions.AuthMode = Jwt
SwaggerUIOptions.Enabled = True
SwaggerUIOptions.ShowFilter = True
SwaggerUIOptions.TryItOutEnabled = True
Left = 89
Top = 112
object XDataServerLogging: TSparkleGenericMiddleware
OnMiddlewareCreate = XDataServerLoggingMiddlewareCreate
end
object XDataServerCORS: TSparkleCorsMiddleware
end
object XDataServerCompress: TSparkleCompressMiddleware
end
object XDataServerJWT: TSparkleJwtMiddleware
ForbidAnonymousAccess = True
OnGetSecret = XDataServerJWTGetSecret
OnForbidRequest = XDataServerJWTForbidRequest
end
end
end
// 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;
type
TApiServerModule = class(TDataModule)
SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
XDataServer: TXDataServer;
XDataServerLogging: TSparkleGenericMiddleware;
XDataServerCORS: TSparkleCorsMiddleware;
XDataServerCompress: TSparkleCompressMiddleware;
XDataServerJWT: TSparkleJwtMiddleware;
procedure XDataServerLoggingMiddlewareCreate(Sender: TObject;
var Middleware: IHttpServerMiddleware);
procedure XDataServerJWTForbidRequest(Sender: TObject;
Context: THttpServerContext; var Forbid: Boolean);
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;
{%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;
SparkleHttpSysDispatcher.Start;
Logger.Log(1, Format('Api server module listening at "%s"', [XDataServer.BaseUrl]));
end;
procedure TApiServerModule.XDataServerLoggingMiddlewareCreate(Sender: TObject;
var Middleware: IHttpServerMiddleware);
begin
Middleware := TLoggingMiddleware.Create(Logger);
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.XDataServerJWTGetSecret(Sender: TObject; var Secret:
string);
begin
Secret := serverConfig.jwtTokenSecret;
end;
end.
unit Api.Service;
interface
uses
XData.Service.Common,
Aurelius.Mapping.Attributes,
System.Generics.Collections,
System.JSON;
const
API_MODEL = 'Api';
type
[ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable)
['{793A44B7-2B00-4791-B249-97A681922D76}']
end;
implementation
initialization
RegisterServiceType(TypeInfo(IApiService));
end.
unit Api.ServiceImpl;
interface
uses
XData.Server.Module, XData.Service.Common, Api.Service, Api.Database,
System.SysUtils, System.Generics.Collections, Uni, System.JSON,
Common.Logging, System.IniFiles, Winapi.Windows, Winapi.Messages, System.Variants,
Winapi.ShellApi, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, Data.DB, uLibrary;
type
[ServiceImplementation]
TApiService = class(TInterfacedObject, IApiService)
private
FAppVersion: string;
public
FApiDatabase: TApiDatabase;
constructor Create;
destructor Destroy; override;
end;
implementation
uses
XData.Sys.Exceptions;
{ TApiService }
constructor TApiService.Create;
begin
Logger.Log(1, 'TApiService.Create');
inherited Create;
try
FApiDatabase := TApiDatabase.Create(nil);
except
on E: Exception do
begin
Logger.Log(2, '--Error creating TApiDatabase: ' + E.Message);
raise;
end;
end;
end;
destructor TApiService.Destroy;
begin
Logger.Log(1, 'TApiService.Destroy');
FApiDatabase.Free;
inherited Destroy;
end;
initialization
RegisterServiceType(TApiService);
end.
object AppServerModule: TAppServerModule
Height = 173
Width = 218
object SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher
Left = 88
Top = 16
end
object SparkleStaticServer: TSparkleStaticServer
BaseUrl = 'http://localhost:2006/webPoliceReports/app/'
Dispatcher = SparkleHttpSysDispatcher
Left = 88
Top = 88
object SparkleStaticServerCompress: TSparkleCompressMiddleware
end
object SparkleStaticServerLogging: TSparkleLoggingMiddleware
FormatString = ':method :url :statuscode - :responsetime ms'
ExceptionFormatString = '(%1:s: %4:s) %0:s - %2:s'
ErrorResponseOptions.ErrorCode = 'ServerError'
ErrorResponseOptions.ErrorMessageFormat = 'Internal server error: %4:s'
end
end
end
unit App.Server.Module;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections,
Sparkle.Comp.Server, Sparkle.Comp.StaticServer, Sparkle.Comp.HttpSysDispatcher,
Sparkle.Module.Static, Sparkle.Comp.CompressMiddleware,
Sparkle.HttpServer.Module, Sparkle.HttpServer.Context,
Sparkle.Comp.LoggingMiddleware;
type
TAppServerModule = class(TDataModule)
SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
SparkleStaticServer: TSparkleStaticServer;
SparkleStaticServerCompress: TSparkleCompressMiddleware;
SparkleStaticServerLogging: TSparkleLoggingMiddleware;
private
{ Private declarations }
public
{ Public declarations }
procedure StartAppServer(ABaseUrl: string);
end;
const
SERVER_PATH_SEGMENT = 'app';
var
AppServerModule: TAppServerModule;
implementation
uses
Sparkle.Middleware.Compress,
Common.Logging,
Common.Config;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
{ TAppServerModule }
procedure TAppServerModule.StartAppServer(ABaseUrl: string);
var
url: string;
begin
url := ABaseUrl;
if not url.EndsWith('/') then
url := url + '/';
url := url + SERVER_PATH_SEGMENT;
SparkleStaticServer.BaseUrl := url;
SparkleStaticServer.RootDir := serverConfig.webAppFolder;
SparkleHttpSysDispatcher.Start;
Logger.Log(1, Format('App server module listening at "%s", rootDir: %s', [url, serverConfig.webAppFolder]));
end;
end.
object AuthDatabase: TAuthDatabase
OnCreate = DataModuleCreate
OnDestroy = DataModuleDestroy
Height = 249
Width = 433
object uq: TUniQuery
Connection = ucHST
SQL.Strings = (
'select * from users')
FetchRows = 100
Left = 162
Top = 45
end
object ucHST: TUniConnection
ProviderName = 'MySQL'
Database = 'sleepdb'
Username = 'hstuser'
Server = '192.168.116.135'
LoginPrompt = False
Left = 65
Top = 41
EncryptedPassword = '9AFF92FF8CFF86FF8CFFDEFFCFFFCEFF'
end
object MySQLUniProvider1: TMySQLUniProvider
Left = 94
Top = 124
end
object uqSystem: TUniQuery
Connection = ucHST
SQL.Strings = (
'select * from system')
FetchRows = 100
Left = 276
Top = 125
end
object uqUser: TUniQuery
Connection = ucHST
SQL.Strings = (
'select * from users')
FetchRows = 100
Left = 332
Top = 127
object uqUseruser_id: TIntegerField
FieldName = 'user_id'
end
object uqUseruser_name: TStringField
FieldName = 'user_name'
Required = True
end
object uqUseruser_password: TStringField
FieldName = 'user_password'
Required = True
Size = 64
end
object uqUseruser_fullname: TStringField
FieldName = 'user_fullname'
Required = True
Size = 50
end
object uqUsergroup_id: TIntegerField
FieldName = 'group_id'
Required = True
end
object uqUseragency_id: TIntegerField
FieldName = 'agency_id'
Required = True
end
object uqUseractive: TIntegerField
FieldName = 'active'
Required = True
end
object uqUseremail: TStringField
FieldName = 'email'
Size = 100
end
end
end
// Auth Database to verify logins
unit Auth.Database;
interface
uses
System.SysUtils, System.Classes, IniFiles, Vcl.Forms, MemDS,
Data.DB, DBAccess, Uni, UniProvider, PostgreSQLUniProvider, MySQLUniProvider;
type
TAuthDatabase = class(TDataModule)
uq: TUniQuery;
ucHST: TUniConnection;
MySQLUniProvider1: TMySQLUniProvider;
uqSystem: TUniQuery;
uqUser: TUniQuery;
uqUseruser_id: TIntegerField;
uqUseruser_name: TStringField;
uqUseruser_password: TStringField;
uqUseruser_fullname: TStringField;
uqUsergroup_id: TIntegerField;
uqUseragency_id: TIntegerField;
uqUseractive: TIntegerField;
uqUseremail: TStringField;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
AuthDatabase: TAuthDatabase;
implementation
uses
System.JSON,
Common.Ini,
Common.Config,
Common.Logging,
uLibrary;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
procedure TAuthDatabase.DataModuleCreate(Sender: TObject);
begin
Logger.Log(5, 'TAuthDatabase.DataModuleCreate');
try
ucHST.Database := IniEntries.dbDatabase;
ucHST.Server := IniEntries.dbServer;
ucHST.Port := IniEntries.dbPort;
ucHST.Username := IniEntries.dbUsername;
ucHST.Password := IniEntries.dbPassword;
ucHST.Connect;
except
on E: Exception do
begin
Logger.Log(2, '--TAuthDatabase.DataModuleCreate -Error connecting to database: ' + E.Message);
end;
end;
end;
procedure TAuthDatabase.DataModuleDestroy(Sender: TObject);
begin
ucHST.Connected := false;
end;
end.
object AuthServerModule: TAuthServerModule
Height = 273
Width = 230
object SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher
Left = 88
Top = 16
end
object XDataServer: TXDataServer
Dispatcher = SparkleHttpSysDispatcher
ModelName = 'Auth'
EntitySetPermissions = <>
SwaggerOptions.Enabled = True
SwaggerUIOptions.Enabled = True
SwaggerUIOptions.ShowFilter = True
SwaggerUIOptions.TryItOutEnabled = True
Left = 91
Top = 92
object XDataServerLogging: TSparkleGenericMiddleware
OnMiddlewareCreate = XDataServerLoggingMiddlewareCreate
end
object XDataServerCORS: TSparkleCorsMiddleware
end
object XDataServerCompress: TSparkleCompressMiddleware
end
end
end
// Auth Server Module for the project
unit Auth.Server.Module;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections,
Aurelius.Comp.Connection,
Aurelius.Drivers.Interfaces,
XData.Aurelius.ConnectionPool, XData.Server.Module, XData.Comp.ConnectionPool,
Sparkle.Comp.Server, Sparkle.Comp.JwtMiddleware, XData.Comp.Server,
Sparkle.Comp.HttpSysDispatcher, Sparkle.Comp.CompressMiddleware,
Sparkle.Comp.CorsMiddleware, Sparkle.HttpServer.Module,
Sparkle.HttpServer.Context, Sparkle.Comp.GenericMiddleware;
type
TAuthServerModule = class(TDataModule)
SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
XDataServer: TXDataServer;
XDataServerLogging: TSparkleGenericMiddleware;
XDataServerCORS: TSparkleCorsMiddleware;
XDataServerCompress: TSparkleCompressMiddleware;
procedure XDataServerLoggingMiddlewareCreate(Sender: TObject;
var Middleware: IHttpServerMiddleware);
private
{ Private declarations }
public
{ Public declarations }
procedure StartAuthServer(ABaseUrl: string; AModelName: string);
end;
const
SERVER_PATH_SEGMENT = 'auth';
var
AuthServerModule: TAuthServerModule;
implementation
uses
Sparkle.Middleware.Cors,
Sparkle.Middleware.Compress,
XData.OpenApi.Service,
Common.Logging,
Common.Middleware.Logging;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
{ TAuthServerModule }
procedure TAuthServerModule.StartAuthServer(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;
SparkleHttpSysDispatcher.Start;
Logger.Log(1, Format('Auth server module listening at "%s"', [XDataServer.BaseUrl]));
end;
procedure TAuthServerModule.XDataServerLoggingMiddlewareCreate(Sender: TObject;
var Middleware: IHttpServerMiddleware);
begin
Middleware := TLoggingMiddleware.Create(Logger);
end;
end.
// Auth Interface service declaration
unit Auth.Service;
interface
uses
XData.Service.Common,
Aurelius.Mapping.Attributes,
System.Generics.Collections,
System.JSON;
const
AUTH_MODEL = 'Auth';
type
[ServiceContract, Model(AUTH_MODEL)]
IAuthService = interface(IInvokable)
['{AA668BA0-3129-4B02-88BF-4089D45D9249}']
[HttpPost]function Login(const computer_uid, username, password_hash: string): string;
function VerifyVersion(clientVersion: string): TJSONObject;
end;
implementation
end.
unit Auth.ServiceImpl;
interface
uses
XData.Service.Common,
XData.Server.Module,
Auth.Service,
Auth.Database,
Uni, Data.DB, System.Hash, System.IniFiles, System.JSON;
type
[ServiceImplementation]
TAuthService = class(TInterfacedObject, IAuthService)
strict private
authDB: TAuthDatabase;
private
public
[HttpPost]function Login(const computer_uid, username, password_hash: string): string;
function VerifyVersion(ClientVersion: string): TJSONObject;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;
implementation
uses
System.SysUtils,
System.DateUtils,
System.Generics.Collections,
Bcl.JOSE.Core.Builder,
Bcl.JOSE.Core.JWT,
Aurelius.Global.Utils,
XData.Sys.Exceptions,
Common.Logging,
Common.Config,
uLibrary;
{ TLoginService }
procedure TAuthService.AfterConstruction;
begin
inherited;
try
authDB := TAuthDatabase.Create(nil);
Logger.Log(5, 'TAuthService.AfterConstruction - authDB = TAuthDatabase.Create(nil)');
except
on E: Exception do
begin
Logger.Log(1, 'Error connecting to HSTManager database: ' + E.Message);
raise EXDataHttpException.Create(500, 'Error connecting to HSTManager database!');
end;
end;
end;
procedure TAuthService.BeforeDestruction;
begin
authDB.Free;
inherited;
Logger.Log(5, 'TAuthService.BeforeDestruction - authDB.Free');
end;
function TAuthService.VerifyVersion(clientVersion: string): TJSONObject;
var
iniFile: TIniFile;
reqClientVersion: string;
begin
Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
iniFile := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
try
reqClientVersion := iniFile.ReadString('Settings', 'reqClientVersion', '');
Result.AddPair('reqClientVersion', reqClientVersion);
if reqClientVersion = '' then
begin
Result.AddPair('error', 'reqClientVersion is not configured.');
Exit;
end;
if clientVersion <> reqClientVersion then
begin
Result.AddPair('error',
'Version mismatch' + sLineBreak + ' Client version: ' + clientVersion +
sLineBreak + ' Required version: ' + reqClientVersion +
sLineBreak + 'Please contact system administrator to get the correct version.');
end;
finally
iniFile.Free;
end;
end;
function TAuthService.Login(const computer_uid, username, password_hash: string): string;
var
sql, resultStr: string;
JWT: TJWT;
begin
Logger.Log(3, Format('AuthService.Login - User: "%s"', [username]));
sql := 'select * from phone_home where computer_uid = ' + QuotedStr(computer_uid);
Logger.Log(5, 'TAuthService.Login - sql: ' + sql);
doQuery( authDB.uq, sql );
if not authDB.uq.IsEmpty then
begin
sql := 'select * from users where user_name = ' + QuotedStr(username) + ' and user_password = ' + QuotedStr(password_hash);
Logger.Log(5, 'TAuthService.Login - sql: ' + sql);
doQuery(authDB.uqUser, sql);
if not authDB.uqUser.IsEmpty then
begin
if authDB.uqUseractive.AsInteger = 1 then
begin
logger.Log(3, '--login successful');
resultStr := 'success';
end
else
begin
logger.Log(3, '--login failed: inactive user');
resultStr := 'inactive user';
end;
end
else
begin
logger.Log(3, '--login failed: invalid user / password');
resultStr := 'invalid user / password';
end;
end
else
begin
logger.Log(3, '--login failed: invalid computer');
resultStr := 'invalid computer';
end;
if resultStr <> 'success' then
begin
Logger.Log( 3, 'raise Exception.Create(resultStr)' );
raise Exception.Create(resultStr);
end;
JWT := TJWT.Create;
try
JWT.Claims.JWTId := LowerCase(Copy(TUtils.GuidToVariant(TUtils.NewGuid), 2, 36));
JWT.Claims.SetClaimOfType<string>('USER_ID', authDB.uqUseruser_id.AsString);
JWT.Claims.IssuedAt := Now;
JWT.Claims.Expiration := IncHour(Now, 24);
JWT.Claims.SetClaimOfType<string>('USER_NAME', username);
Result := TJOSE.SHA256CompactToken( ServerConfig.jwtTokenSecret, JWT );
Logger.Log(5, 'Returning JWT Token:' + Result)
finally
JWT.Free;
end;
Logger.Log(3, 'AuthService.Login - Finished');
Logger.Log(3, '');
end;
initialization
RegisterServiceType(TAuthService);
end.
// The configuartion file for the program. Contains important info like the admin
// password and the secret token. Should likely move this to the ini file..
unit Common.Config;
interface
const
defaultServerUrl = 'http://localhost:2004/emsys/template/';
type
TServerConfig = class
private
Furl: string;
FjwtTokenSecret: string;
FadminPassword: string;
FwebAppFolder: string;
FreportsFolder: string;
public
property url: string read Furl write Furl;
property jwtTokenSecret: string read FjwtTokenSecret write FjwtTokenSecret;
property adminPassword: string read FadminPassword write FadminPassword;
property webAppFolder: string read FwebAppFolder write FwebAppFolder;
property reportsFolder: string read FreportsFolder write FreportsFolder;
constructor Create;
end;
procedure LoadServerConfig;
procedure SaveServerConfig(const AConfigFile: string = '');
var
serverConfig: TServerConfig;
implementation
uses
Bcl.Json, System.SysUtils, System.IOUtils, System.StrUtils,
Common.Logging, Common.Ini;
function GetConfigFileName: string;
begin
if TFile.Exists('serverconfig.json') then
Result := 'serverconfig.json'
else
Result := TPath.ChangeExtension(ParamStr(0), '.json');
end;
procedure SaveServerConfig(const AConfigFile: string = '');
var
configFile: string;
serverConfigStr: string;
begin
configFile := AConfigFile;
if configFile = '' then
configFile := GetConfigFileName;
serverConfigStr := Bcl.Json.TJson.Serialize(serverConfig);
TFile.WriteAllText(configFile, serverConfigStr);
Logger.Log(1, '-- ServerConfig saved to file: ' + configFile);
end;
procedure LoadServerConfig;
var
configFile: string;
localConfig: TServerConfig;
jwtFromIni: Boolean;
begin
Logger.Log(1, '--LoadServerConfig - start');
configFile := GetConfigFileName;
Logger.Log(1, '-- Config file: ' + configFile);
if TFile.Exists(configFile) then
begin
Logger.Log(1, '-- Config file found.');
localConfig := TJson.Deserialize<TServerConfig>(TFile.ReadAllText(configFile));
Logger.Log(1, '-- localConfig loaded from config file');
serverConfig.Free;
Logger.Log(1, '-- serverConfig.Free - called');
serverConfig := localConfig;
Logger.Log(1, '-- serverConfig := localConfig - called');
end
else
begin
Logger.Log(1, '-- Config file not found.');
SaveServerConfig(configFile);
end;
Logger.Log(1, '--- Server Config Values ---');
Logger.Log(1, '-- url: ' + serverConfig.url + IfThen(serverConfig.url = defaultServerUrl, ' [default]', ' [from config]'));
Logger.Log(1, '-- adminPassword: ' + serverConfig.adminPassword + IfThen(serverConfig.adminPassword = 'whatisthisusedfor', ' [default]', ' [from config]'));
Logger.Log(1, '-- jwtTokenSecret: [from config/default]');
Logger.Log(1, '-- webAppFolder: ' + serverConfig.webAppFolder + IfThen(serverConfig.webAppFolder = 'static', ' [default]', ' [from config]'));
Logger.Log(1, '-- reportsFolder: ' + serverConfig.reportsFolder + IfThen(serverConfig.reportsFolder = 'static\reports\', ' [default]', ' [from config]'));
Logger.Log(1, '--LoadServerConfig - end');
end;
{ TServerConfig }
constructor TServerConfig.Create;
begin
Logger.Log(1, '--TServerConfig.Create - start');
url := defaultServerUrl;
adminPassword := 'whatisthisusedfor';
jwtTokenSecret := '';
webAppFolder := 'static';
// reportsFolder := 'static\reports\';
Logger.Log(1, '--TServerConfig.Create - end');
end;
initialization
serverConfig := TServerConfig.Create;
finalization
serverConfig.Free;
end.
unit Common.Ini;
interface
uses
System.SysUtils, System.IniFiles, Vcl.Forms;
type
TIniEntries = class
private
// [Settings]
FConsoleLogLevel: Integer;
FFileLogLevel: Integer;
FLogFileNum: Integer;
FJWTSecret: string;
// [Database]
FDBServer: string;
FDBPort: Integer;
FDBDatabase: string;
FDBUsername: string;
FDBPassword: string;
public
constructor Create;
// Properties
property consoleLogLevel: Integer read FConsoleLogLevel;
property fileLogLevel: Integer read FFileLogLevel;
property logFileNum: Integer read FLogFileNum;
property dbServer: string read FDBServer;
property dbPort: Integer read FDBPort;
property dbDatabase: string read FDBDatabase;
property dbUsername: string read FDBUsername;
property dbPassword: string read FDBPassword;
end;
procedure LoadIniEntries;
var
IniEntries: TIniEntries;
implementation
procedure LoadIniEntries;
begin
if Assigned(IniEntries) then
IniEntries.Free;
IniEntries := TIniEntries.Create;
end;
{ TIniEntries }
constructor TIniEntries.Create;
var
iniFile: TIniFile;
begin
iniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
try
// [Settings]
FConsoleLogLevel := iniFile.ReadInteger('Settings', 'ConsoleLogLevel', 3);
FFileLogLevel := iniFile.ReadInteger('Settings', 'FileLogLevel', 3);
FLogFileNum := iniFile.ReadInteger('Settings', 'LogFileNum', 0);
// [Database]
FDBServer := iniFile.ReadString('Database', 'Server', '10.208.1.13');
FDBPort := iniFile.ReadInteger('Database', 'Port', 0);
FDBDatabase := iniFile.ReadString('Database', 'Database', 'sleepdb');
FDBUsername := iniFile.ReadString('Database', 'Username', 'hstuser');
FDBPassword := iniFile.ReadString('Database', 'Password', 'emsys!01');
finally
iniFile.Free;
end;
end;
end.
//////// Log Levels /////////
// 1 - Startup Information
// 2 - Application/Database Connections
// 3 - Endpoints
// 4 - CHARMS Interactions
// 5 - Email Poller
unit Common.Logging;
interface
uses
Generics.Collections;
type
ILog = interface;
ILogAppender = interface;
ILogger = interface
['{4D667DD2-BE11-496B-B92A-C47E03520BD6}']
procedure Log(logLevel: integer; Msg: string); overload;
procedure Log(logLevel: integer; Log: ILog); overload;
procedure AddAppender(ALogAppender: ILogAppender);
function Appenders: TArray<ILogAppender>;
end;
ILogAppender = interface
['{A3B7D6FB-C75F-4BEF-8797-907B6FDAD5D2}']
procedure Send(logLevel: integer; Log: ILog);
end;
ILog = interface
['{8E9C6580-C099-47C0-8B1B-6D7A28EC4FA3}']
function GetMessage: string;
end;
TLogger = class( TInterfacedObject, ILogger )
strict private
FAppenders: TList<ILogAppender>;
public
constructor Create; overload;
constructor Create(ALogger: ILogger); overload;
destructor Destroy; override;
procedure Log(logLevel: integer; Msg: string); overload;
procedure Log(logLevel: integer; Log: ILog); overload;
procedure AddAppender(ALogAppender: ILogAppender);
function Appenders: TArray<ILogAppender>;
end;
TLogMessage = class( TInterfacedObject, ILog )
private
FMsg: string;
public
constructor Create(AMsg: string);
function GetMessage: string;
end;
function Logger: ILogger;
implementation
var
_Logger: ILogger;
function Logger: ILogger;
begin
Result := _Logger;
end;
{ TLogMessage }
constructor TLogMessage.Create(AMsg: string);
begin
FMsg := AMsg;
end;
function TLogMessage.GetMessage: string;
begin
Result := FMsg;
end;
{ TLogger }
procedure TLogger.AddAppender(ALogAppender: ILogAppender);
begin
FAppenders.Add(ALogAppender);
end;
function TLogger.Appenders: TArray<ILogAppender>;
var
I: integer;
begin
SetLength(Result, FAppenders.Count);
for I := 0 to FAppenders.Count - 1 do
Result[I] := FAppenders[I];
end;
constructor TLogger.Create(ALogger: ILogger);
var
Appender: ILogAppender;
begin
FAppenders := TList<ILogAppender>.Create;
if ALogger <> nil then
for Appender in ALogger.Appenders do
AddAppender(Appender);
end;
constructor TLogger.Create;
begin
Create(nil);
end;
destructor TLogger.Destroy;
begin
FAppenders.Free;
inherited;
end;
procedure TLogger.Log(logLevel: integer; Log: ILog);
var
Appender: ILogAppender;
begin
for Appender in FAppenders do
Appender.Send(logLevel, Log);
end;
procedure TLogger.Log(logLevel: integer; Msg: string);
begin
Log(logLevel, TLogMessage.Create(Msg));
end;
initialization
_Logger := TLogger.Create;
end.
unit Common.Middleware.Logging;
interface
uses
System.Classes, System.SysUtils,
Sparkle.HttpServer.Module,
Sparkle.HttpServer.Context,
Sparkle.Http.Headers,
Common.Logging;
type
TLoggingMiddleware = class(THttpServerMiddleware, IHttpServerMiddleware)
private
FLogger: ILogger;
function GetNewHttpRequestLog(Request: THttpServerRequest): ILog;
protected
procedure ProcessRequest(Context: THttpServerContext; Next: THttpServerProc); override;
public
constructor Create(ALogger: ILogger);
end;
THttpRequestLog = class( TInterfacedObject, ILog )
strict private
FMethod: string;
FUriPath: string;
FUriQuery: string;
FProtocol: string;
FRemoteIp: string;
FHeaders: string;
FContent: string;
FContentLength: Int64;
public
constructor Create(AMethod: string; AUriPath: string; AUriQuery: string;
AProtocol: string; ARemoteIp: string; AHeaders: string; AContent: string;
AContentLength: Int64);
function GetMessage: string;
end;
// THttpResponseLog = class( TInterfacedObject, ILog )
// strict private
// FMethod: string;
// FUriPath: string;
// FUriQuery: string;
// FProtocol: string;
// FRemoteIp: string;
// FHeaders: string;
// FContent: string;
// FContentLength: Int64;
// public
// constructor Create(AMethod: string; AUriPath: string; AUriQuery: string;
// AProtocol: string; ARemoteIp: string; AHeaders: string; AContent: string;
// AContentLength: Int64);
// function GetMessage: string;
// end;
implementation
{ TLoggingMiddleware }
constructor TLoggingMiddleware.Create(ALogger: ILogger);
begin
FLogger := TLogger.Create(ALogger);
end;
function TLoggingMiddleware.GetNewHttpRequestLog(
Request: THttpServerRequest): ILog;
var
Msg: TStrings;
Header: THttpHeaderInfo;
StringStream: TStringStream;
Headers, Content: string;
begin
Result := nil;
Msg := TStringList.Create;
try
if Length(Request.Headers.AllHeaders.ToArray) = 0 then
Headers := ''
else
begin
for Header in Request.Headers.AllHeaders do
Msg.Add(Header.Name + ': ' + Header.Value);
Headers := Msg.Text;
end;
finally
Msg.Free;
end;
StringStream := TStringStream.Create(Request.Content);
try
Content := StringStream.DataString
finally
StringStream.Free;
end;
Result := THttpRequestLog.Create(
Request.Method,
Request.Uri.Path,
Request.Uri.Query,
Request.Protocol,
Request.RemoteIp,
Headers,
Content,
Request.ContentLength
);
end;
procedure TLoggingMiddleware.ProcessRequest(Context: THttpServerContext;
Next: THttpServerProc);
var
RequestLogMessage: string;
begin
FLogger.Log(1, 'TLoggingMiddleware.ProcessRequest:');
RequestLogMessage := GetNewHttpRequestLog(Context.Request).GetMessage;
Context.Response.OnHeaders(
procedure(Resp: THttpServerResponse)
begin
FLogger.Log(1, Format('--Resp.StatusCode: %d Resp.StatusReason: %s on %s',
[Resp.StatusCode, Resp.StatusReason, RequestLogMessage]));
end
);
FLogger.Log(1, Format('--Request.RemoteIP: %s RequestLogMessage: %s',
[Context.Request.RemoteIp, RequestLogMessage]));
Next(Context);
end;
{ THttpRequestLog }
constructor THttpRequestLog.Create(AMethod, AUriPath, AUriQuery,
AProtocol, ARemoteIp, AHeaders, AContent: string; AContentLength: Int64);
begin
FMethod := AMethod;
FUriPath := AUriPath;
FUriQuery := AUriQuery;
FProtocol := AProtocol;
FRemoteIp := ARemoteIp;
FHeaders := AHeaders;
FContent := AContent;
FContentLength := AContentLength;
end;
function THttpRequestLog.GetMessage: string;
var
Msg: TStrings;
begin
Result := '';
Msg := TStringList.Create;
try
Msg.Add(Format('%s %s %s',
[
FMethod,
FUriPath + FUriQuery,
FProtocol,
FRemoteIp
]));
// if Not FHeaders.IsEmpty then
// Msg.Add(FHeaders);
// if (Not FContent.IsEmpty) then
// Msg.Add(FContent);
Result := Trim(Msg.Text);
finally
Msg.Free;
end;
end;
end.
object FMain: TFMain
Left = 0
Top = 0
Caption = 'templateXDataServer'
ClientHeight = 580
ClientWidth = 620
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OnClose = FormClose
DesignSize = (
620
580)
TextHeight = 13
object memoLogs: TMemo
Left = 8
Top = 40
Width = 604
Height = 532
Anchors = [akLeft, akTop, akRight, akBottom]
ReadOnly = True
TabOrder = 0
ExplicitWidth = 456
ExplicitHeight = 194
end
object btnSwaggerAuth: TButton
Left = 36
Top = 8
Width = 75
Height = 25
Caption = 'Auth Swagger'
TabOrder = 1
OnClick = btnSwaggerAuthClick
end
object btnSwaggerApi: TButton
Left = 140
Top = 8
Width = 75
Height = 25
Caption = 'Api Swagger'
TabOrder = 2
OnClick = btnSwaggerApiClick
end
object btnExit: TButton
Left = 537
Top = 9
Width = 75
Height = 25
Caption = 'Exit'
TabOrder = 3
OnClick = btnExitClick
end
object exeInfoVersion: TExeInfo
Version = '1.6.1.1'
Left = 188
Top = 414
end
object timerStartServer: TTimer
OnTimer = timerStartServerTimer
Left = 84
Top = 412
end
end
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, Winapi.ShellApi,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, ExeInfo, System.StrUtils;
type
TFMain = class(TForm)
memoLogs: TMemo;
btnSwaggerApi: TButton;
btnSwaggerAuth: TButton;
btnExit: TButton;
timerStartServer: TTimer;
exeInfoVersion: TExeInfo;
procedure btnSwaggerApiClick(Sender: TObject);
procedure btnSwaggerAuthClick(Sender: TObject);
procedure btnExitClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure timerStartServerTimer(Sender: TObject);
strict private
procedure StartServers;
function LogValue(const labelName: string; const value: string; fromIni: Boolean): string;
end;
var
FMain: TFMain;
implementation
uses
Common.Logging,
Common.Config,
Common.Ini,
Auth.Server.Module,
Api.Server.Module,
App.Server.Module,
Api.Service,
Auth.Service,
Sparkle.Utils;
{$R *.dfm}
procedure TFMain.btnSwaggerApiClick(Sender: TObject);
begin
ShellExecute(Handle, 'open',
PChar(TSparkleUtils.CombineUrlFast(ApiServerModule.XDataServer.BaseUrl, 'swaggerui')),
nil, nil, SW_SHOWNORMAL);
end;
procedure TFMain.btnSwaggerAuthClick(Sender: TObject);
begin
ShellExecute(Handle, 'open',
PChar(TSparkleUtils.CombineUrlFast(AuthServerModule.XDataServer.BaseUrl, 'swaggerui')),
nil, nil, SW_SHOWNORMAL);
end;
procedure TFMain.btnExitClick(Sender: TObject);
begin
Close;
end;
procedure TFMain.timerStartServerTimer(Sender: TObject);
begin
timerStartServer.Enabled := False;
Caption := Caption + ' ver ' + exeInfoVersion.FileVersion;
ServerConfig := TServerConfig.Create;
LoadIniEntries;
LoadServerConfig;
StartServers;
end;
function TFMain.LogValue(const labelName: string; const value: string; fromIni: Boolean): string;
begin
Result := labelName + ': ' + value + IfThen(fromIni, ' [from ini]', ' [default]');
end;
procedure TFMain.StartServers;
begin
Logger.Log(1, '');
Logger.Log(1, '******************************************************');
Logger.Log(1, ' template Server ');
Logger.Log(1, Format(' Version: %s ', [exeInfoVersion.FileVersion]));
Logger.Log(1, ' ');
Logger.Log(1, ' by EM Systems, Inc. ');
Logger.Log(1, '******************************************************');
Logger.Log(1, '');
Logger.Log(1, '--- Database ---');
Logger.Log(1, '--Database->Server: ' + IniEntries.dbServer);
Logger.Log(1, '--Database->Port: ' + IniEntries.dbPort.ToString);
Logger.Log(1, '--Database->Database: ' + IniEntries.dbDatabase);
Logger.Log(1, '--Database->Username: ' + IniEntries.dbUsername);
Logger.Log(1, '--Database->Password: ' + IniEntries.dbPassword);
Logger.Log(1, '');
Logger.Log(1, '--- Settings ---');
Logger.Log(1, '--Settings->ConsoleLogLevel: ' + IniEntries.consoleLogLevel.ToString);
Logger.Log(1, '--Settings->FileLogLevel: ' + IniEntries.fileLogLevel.ToString);
Logger.Log(1, '--Settings->LogFileNum: ' + IniEntries.logFileNum.ToString);
Logger.Log(1, '');
try
AuthServerModule := TAuthServerModule.Create(Self);
AuthServerModule.StartAuthServer(ServerConfig.url, AUTH_MODEL);
ApiServerModule := TApiServerModule.Create(Self);
ApiServerModule.StartApiServer(ServerConfig.url, API_MODEL);
AppServerModule := TAppServerModule.Create(Self);
AppServerModule.StartAppServer(ServerConfig.url);
Logger.Log(1, '');
except
on E: Exception do
Logger.Log(2, 'Failed to start server modules: ' + E.Message);
end;
end;
procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ServerConfig.Free;
AuthServerModule.Free;
ApiServerModule.Free;
AppServerModule.Free;
IniEntries.Free;
end;
end.
unit uLibrary;
interface
uses
System.Classes, Uni;
procedure DoQuery( uq: TUniQuery; sql: string );
implementation
uses
System.SysUtils,
Data.DB;
procedure DoQuery(uq: TUniQuery; sql: string);
begin
uq.Close;
uq.SQL.Text := sql;
uq.Open;
end;
end.
program templateXDataServer;
uses
Vcl.Forms,
FastMM4,
System.SyncObjs,
System.SysUtils,
Vcl.StdCtrls,
IniFiles,
Main in 'source\Main.pas' {FMain},
Common.Config in 'source\Common.Config.pas',
Common.Ini in 'source\Common.Ini.pas',
Common.Logging in 'source\Common.Logging.pas',
Common.Middleware.Logging in 'source\Common.Middleware.Logging.pas',
Api.Server.Module in 'source\Api.Server.Module.pas' {ApiServerModule: TDataModule},
Api.Database in 'source\Api.Database.pas' {ApiDatabase: TDataModule},
Api.Service in 'source\Api.Service.pas',
Api.ServiceImpl in 'source\Api.ServiceImpl.pas',
Auth.Database in 'source\Auth.Database.pas' {AuthDatabase: TDataModule},
Auth.Service in 'source\Auth.Service.pas',
Auth.ServiceImpl in 'source\Auth.ServiceImpl.pas',
uLibrary in 'source\uLibrary.pas',
App.Server.Module in 'source\App.Server.Module.pas' {AppServerModule: TDataModule},
Auth.Server.Module in 'source\Auth.Server.Module.pas' {AuthServerModule: TDataModule};
{$R *.res}
type
TMemoLogAppender = class(TInterfacedObject, ILogAppender)
private
FLogLevel: Integer;
FLogMemo: TMemo;
FCriticalSection: TCriticalSection;
public
constructor Create(ALogLevel: Integer; ALogMemo: TMemo);
destructor Destroy; override;
procedure Send(logLevel: Integer; Log: ILog);
end;
TFileLogAppender = class(TInterfacedObject, ILogAppender)
private
FLogLevel: Integer;
FFilename: string;
FLogDirectory: string;
FCriticalSection: TCriticalSection;
function GetLogFilePath: string;
public
constructor Create(ALogLevel: Integer; AFilename: string);
destructor Destroy; override;
procedure Send(logLevel: Integer; Log: ILog);
end;
{ TMemoLogAppender }
constructor TMemoLogAppender.Create(ALogLevel: Integer; ALogMemo: TMemo);
begin
FLogLevel := ALogLevel;
FLogMemo := ALogMemo;
FCriticalSection := TCriticalSection.Create;
end;
destructor TMemoLogAppender.Destroy;
begin
FCriticalSection.Free;
inherited;
end;
procedure TMemoLogAppender.Send(logLevel: Integer; Log: ILog);
var
logMsg: string;
logTime: TDateTime;
formattedMessage: string;
begin
FCriticalSection.Acquire;
try
logTime := Now;
formattedMessage := FormatDateTime('[yyyy-mm-dd HH:nn:ss.zzz]', logTime);
logMsg := Log.GetMessage;
if logMsg.IsEmpty then
formattedMessage := ''
else
formattedMessage := formattedMessage + '[' + IntToStr(logLevel) + '] ' + logMsg;
if logLevel <= FLogLevel then
FLogMemo.Lines.Add(formattedMessage);
finally
FCriticalSection.Release;
end;
end;
{ TFileLogAppender }
constructor TFileLogAppender.Create(ALogLevel: integer; AFilename: string);
var
iniFile: TIniFile;
fileNum: integer;
logsDir: string;
begin
FLogLevel := ALogLevel;
FCriticalSection := TCriticalSection.Create;
logsDir := ExtractFilePath(Application.ExeName) + 'logs\';
if logsDir = '' then
raise Exception.Create('logsDir is blank. ExeName="' + Application.ExeName + '"');
if not DirectoryExists(logsDir) then
CreateDir(logsDir);
iniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
try
fileNum := iniFile.ReadInteger('Settings', 'LogFileNum', 0);
FFilename := AFilename + Format('%.4d', [fileNum]);
iniFile.WriteInteger('Settings', 'LogFileNum', fileNum + 1);
finally
iniFile.Free;
end;
FLogDirectory := logsDir;
end;
destructor TFileLogAppender.Destroy;
begin
FCriticalSection.Free;
inherited;
end;
function TFileLogAppender.GetLogFilePath: string;
begin
Result := FLogDirectory + FFilename + '.log';
end;
procedure TFileLogAppender.Send(logLevel: Integer; Log: ILog);
var
logFile: TextFile;
logMsg: string;
logTime: TDateTime;
formattedMessage: string;
begin
if logLevel > FLogLevel then
Exit;
FCriticalSection.Acquire;
try
logTime := Now;
formattedMessage := FormatDateTime('[yyyy-mm-dd HH:nn:ss.zzz]', logTime);
logMsg := Log.GetMessage;
if logMsg.IsEmpty then
formattedMessage := ''
else
formattedMessage := formattedMessage + '[' + IntToStr(logLevel) + '] ' + logMsg;
AssignFile(logFile, GetLogFilePath);
if FileExists(GetLogFilePath) then
Append(logFile)
else
Rewrite(logFile);
try
Writeln(logFile, formattedMessage);
finally
CloseFile(logFile);
end;
finally
FCriticalSection.Release;
end;
end;
{$R *.res}
begin
ReportMemoryLeaksOnShutdown := True;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TFMain, FMain);
LoadIniEntries;
Logger.AddAppender(TMemoLogAppender.Create(IniEntries.consoleLogLevel, FMain.memoLogs));
Logger.AddAppender(TFileLogAppender.Create(IniEntries.fileLogLevel, 'templateServer'));
Application.Run;
end.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment