Sqids support - base classes + converter mechanism. Added ":sqid" converter.

This commit is contained in:
Daniele Teti 2024-05-09 23:50:01 +02:00
parent c2646a2e28
commit 7420015c5c
29 changed files with 3846 additions and 745 deletions

View File

@ -177,6 +177,8 @@ type
end;
TUnitMainBeginEndCommand = class(TCustomCommand)
private
function GetScrambledAlphabet: String;
public
procedure ExecuteImplementation(
Section: TStringBuilder;
@ -212,7 +214,6 @@ type
implementation
{ TUnitUsesCommand }
procedure TUnitUsesCommand.ExecuteInterface(Section: TStringBuilder;
@ -982,6 +983,8 @@ begin
.AppendLine(' // When MVCSerializeNulls = False empty nullables and nil are not serialized at all.')
.AppendLine(' MVCSerializeNulls := True;')
.AppendLine(' UseConsoleLogger := True;')
.AppendLine(' TMVCSqids.SQIDS_ALPHABET := dotEnv.Env(''dmvc.sqids.alphabet'', ''' + GetScrambledAlphabet + ''');')
.AppendLine(' TMVCSqids.SQIDS_MIN_LENGTH := dotEnv.Env(''dmvc.sqids.min_length'', 6);')
.AppendLine
.AppendLine(' LogI(''** DMVCFramework Server ** build '' + DMVCFRAMEWORK_VERSION);')
.AppendLine(' try')
@ -1055,6 +1058,29 @@ begin
end;
function TUnitMainBeginEndCommand.GetScrambledAlphabet: String;
const
DEFAULT_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var
I: Integer;
lIdx1: Integer;
lSize: Integer;
lIdx2: Integer;
lTmp: Char;
begin
Randomize;
Result := DEFAULT_ALPHABET;
lSize := Length(Result);
for I := 1 to 100 do
begin
lIdx1 := Random(lSize) + 1;
lIdx2 := Random(lSize) + 1;
lTmp := Result[lIdx1];
Result[lIdx1] := Result[lIdx2];
Result[lIdx2] := lTmp;
end;
end;
{ TUnitRunServerProcBody }
procedure TUnitRunServerProcBody.ExecuteImplementation(Section: TStringBuilder;

View File

@ -117,7 +117,8 @@ contains
MVCFramework.Serializer.URLEncoded in '..\..\sources\MVCFramework.Serializer.URLEncoded.pas',
MVCFramework.Signal in '..\..\sources\MVCFramework.Signal.pas',
MVCFramework.Serializer.Text in '..\..\sources\MVCFramework.Serializer.Text.pas',
MVCFramework.Container in '..\..\sources\MVCFramework.Container.pas';
MVCFramework.Container in '..\..\sources\MVCFramework.Container.pas',
sqids in '..\..\sources\sqids.pas';
end.

View File

@ -221,6 +221,7 @@
<DCCReference Include="..\..\sources\MVCFramework.Signal.pas"/>
<DCCReference Include="..\..\sources\MVCFramework.Serializer.Text.pas"/>
<DCCReference Include="..\..\sources\MVCFramework.Container.pas"/>
<DCCReference Include="..\..\sources\sqids.pas"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>

View File

@ -26,8 +26,8 @@ procedure RunServer(APort: Integer);
var
LServer: TIdHTTPWebBrokerBridge;
begin
Writeln('** DMVCFramework Server ** build ' + DMVCFRAMEWORK_VERSION);
Writeln(Format('Starting HTTP Server on port %d', [APort]));
LogI('** DMVCFramework Server ** build ' + DMVCFRAMEWORK_VERSION);
LogI(Format('Starting HTTP Server on port %d', [APort]));
LServer := TIdHTTPWebBrokerBridge.Create(nil);
try
LServer.KeepAlive := True;
@ -43,7 +43,7 @@ begin
{$IFDEF MSWINDOWS}
ShellExecute(0, 'open', PChar('http://localhost:' + inttostr(APort) + '/static'), nil, nil, SW_SHOWMAXIMIZED);
{$ENDIF}
Write('CTRL+C to stop the server');
LogI('CTRL+C to stop the server');
WaitForTerminationSignal;
EnterInShutdownState;
finally
@ -54,6 +54,7 @@ end;
begin
ReportMemoryLeaksOnShutdown := True;
IsMultiThread := True;
UseConsoleLogger := True;
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
@ -61,7 +62,7 @@ begin
RunServer(8080);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
LogE(E.ClassName + ': ' + E.Message);
end;
end.

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{56928A09-5B7B-4920-ABAA-CB68F0AC2958}</ProjectGuid>
<ProjectVersion>19.5</ProjectVersion>
<ProjectVersion>20.1</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>SSESample.dpr</MainSource>
<Base>True</Base>
@ -9,6 +9,7 @@
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Console</AppType>
<ProjectName Condition="'$(ProjectName)'==''">SSESample</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -146,8 +147,8 @@
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="4">
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="SSESample.exe" Configuration="Debug" Class="ProjectOutput"/>
@ -237,6 +238,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDefV21">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -257,6 +268,66 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV31">
<Platform Name="Android">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconBackground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconForeground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconMonochrome">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconV33">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -267,6 +338,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_ColorsDark">
<Platform Name="Android">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -437,6 +518,56 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedNotificationIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplash">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashDark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31Dark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -542,6 +673,130 @@
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
@ -742,127 +997,6 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
@ -875,6 +1009,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Android64">False</Platform>

View File

@ -1,5 +1,4 @@
object MyWebModule: TMyWebModule
OldCreateOrder = False
OnCreate = WebModuleCreate
OnDestroy = WebModuleDestroy
Actions = <>

View File

@ -1,68 +1,69 @@
unit WebModuleU;
interface
uses System.SysUtils,
System.Classes,
Web.HTTPApp,
MVCFramework;
type
TMyWebModule = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
procedure WebModuleDestroy(Sender: TObject);
private
FMVC: TMVCEngine;
public
{ Public declarations }
end;
var
WebModuleClass: TComponentClass = TMyWebModule;
implementation
{$R *.dfm}
uses
MVCFramework.Commons,
MVCFramework.Middleware.StaticFiles,
StatusControllerU;
procedure TMyWebModule.WebModuleCreate(Sender: TObject);
begin
FMVC := TMVCEngine.Create(Self,
procedure(Config: TMVCConfig)
begin
// session timeout (0 means session cookie)
Config[TMVCConfigKey.SessionTimeout] := '0';
// default content-type
Config[TMVCConfigKey.DefaultContentType] :=
TMVCConstants.DEFAULT_CONTENT_TYPE;
// default content charset
Config[TMVCConfigKey.DefaultContentCharset] :=
TMVCConstants.DEFAULT_CONTENT_CHARSET;
// unhandled actions are permitted?
Config[TMVCConfigKey.AllowUnhandledAction] := 'false';
// default view file extension
Config[TMVCConfigKey.DefaultViewFileExtension] := 'html';
// view path
Config[TMVCConfigKey.ViewPath] := 'templates';
// Enable Server Signature in response
Config[TMVCConfigKey.ExposeServerSignature] := 'true';
end);
FMVC.AddController(TStatusController);
FMVC.AddMiddleware(TMVCStaticFilesMiddleware.Create(
'/static', { StaticFilesPath }
ExtractFilePath(GetModuleName(HInstance)) + '\www', { DocumentRoot }
'index.html' {IndexDocument - Before it was named fallbackresource}
));
end;
procedure TMyWebModule.WebModuleDestroy(Sender: TObject);
begin
FMVC.Free;
end;
interface
uses System.SysUtils,
System.Classes,
Web.HTTPApp,
MVCFramework;
type
TMyWebModule = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
procedure WebModuleDestroy(Sender: TObject);
private
FMVC: TMVCEngine;
public
{ Public declarations }
end;
var
WebModuleClass: TComponentClass = TMyWebModule;
implementation
{$R *.dfm}
uses
MVCFramework.Commons,
MVCFramework.Middleware.StaticFiles,
StatusControllerU;
procedure TMyWebModule.WebModuleCreate(Sender: TObject);
begin
FMVC := TMVCEngine.Create(Self,
procedure(Config: TMVCConfig)
begin
// session timeout (0 means session cookie)
Config[TMVCConfigKey.SessionTimeout] := '0';
// default content-type
Config[TMVCConfigKey.DefaultContentType] :=
TMVCConstants.DEFAULT_CONTENT_TYPE;
// default content charset
Config[TMVCConfigKey.DefaultContentCharset] :=
TMVCConstants.DEFAULT_CONTENT_CHARSET;
// unhandled actions are permitted?
Config[TMVCConfigKey.AllowUnhandledAction] := 'false';
// default view file extension
Config[TMVCConfigKey.DefaultViewFileExtension] := 'html';
// view path
Config[TMVCConfigKey.ViewPath] := 'templates';
// Enable Server Signature in response
Config[TMVCConfigKey.ExposeServerSignature] := 'true';
end);
FMVC.AddController(TStatusController);
FMVC.AddMiddleware(TMVCStaticFilesMiddleware.Create(
'/static', { StaticFilesPath }
ExtractFilePath(GetModuleName(HInstance)) + '\www', { DocumentRoot }
'index.html' {IndexDocument - Before it was named fallbackresource}
));
end;
procedure TMyWebModule.WebModuleDestroy(Sender: TObject);
begin
FMVC.Free;
end;
end.

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{FD2E10E8-0B57-424F-BB4B-827E8087AC33}</ProjectGuid>
<ProjectVersion>19.5</ProjectVersion>
<ProjectVersion>20.1</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>serversentevents2.dpr</MainSource>
<Base>True</Base>
@ -9,6 +9,7 @@
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Console</AppType>
<ProjectName Condition="'$(ProjectName)'==''">serversentevents2</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -143,8 +144,8 @@
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="4">
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="bin\serversentevents2.exe" Configuration="Debug" Class="ProjectOutput"/>
@ -234,6 +235,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDefV21">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -254,6 +265,66 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV31">
<Platform Name="Android">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconBackground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconForeground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconMonochrome">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconV33">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -264,6 +335,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_ColorsDark">
<Platform Name="Android">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -434,6 +515,56 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedNotificationIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplash">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashDark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31Dark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -539,6 +670,130 @@
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
@ -739,127 +994,6 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
@ -872,6 +1006,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Android64">False</Platform>

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{08D68152-4263-463F-A029-75E58C0AAD41}</ProjectGuid>
<ProjectVersion>19.5</ProjectVersion>
<ProjectVersion>20.1</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>serversentevents2sender.dpr</MainSource>
<Base>True</Base>
@ -9,6 +9,7 @@
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<ProjectName Condition="'$(ProjectName)'==''">serversentevents2sender</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -226,6 +227,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDefV21">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -246,6 +257,66 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV31">
<Platform Name="Android">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconBackground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconForeground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconMonochrome">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconV33">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -256,6 +327,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_ColorsDark">
<Platform Name="Android">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -426,6 +507,56 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedNotificationIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplash">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashDark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31Dark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -556,6 +687,200 @@
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
@ -756,197 +1081,6 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
@ -959,6 +1093,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{4185FCEE-B4FE-490C-8E3A-E2E8F2953663}</ProjectGuid>
<ProjectVersion>19.5</ProjectVersion>
<ProjectVersion>20.1</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>serversentevents2viewer.dpr</MainSource>
<Base>True</Base>
@ -9,6 +9,7 @@
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<ProjectName Condition="'$(ProjectName)'==''">serversentevents2viewer</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -226,6 +227,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDefV21">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -246,6 +257,66 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV31">
<Platform Name="Android">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconBackground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconForeground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconMonochrome">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconV33">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
@ -256,6 +327,16 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_ColorsDark">
<Platform Name="Android">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
@ -426,6 +507,56 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedNotificationIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplash">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashDark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31Dark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
@ -556,6 +687,200 @@
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
@ -756,197 +1081,6 @@
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
@ -959,6 +1093,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>

View File

@ -0,0 +1,90 @@
unit ControllerU;
interface
uses
MVCFramework, MVCFramework.Commons, MVCFramework.Nullables, MVCFramework.Serializer.Commons, System.Generics.Collections,
ServicesU, EntityU;
type
[MVCPath('/api')]
TMyController = class(TMVCController)
public
[MVCPath]
[MVCHTTPMethod([httpGET])]
function Index: String;
[MVCPath('/reversedstrings/($Value)')]
[MVCHTTPMethod([httpGET])]
[MVCProduces(TMVCMediaType.TEXT_PLAIN)]
function GetReversedString(const Value: String): String;
//Sample CRUD Actions for a "People" entity
[MVCPath('/people')]
[MVCHTTPMethod([httpGET])]
function GetPeople([MVCInject] PeopleService: IPeopleService): IMVCResponse;
[MVCPath('/people/($ID:sqid)')]
[MVCHTTPMethod([httpGET])]
function GetPerson(ID: Integer): TPerson;
[MVCPath('/people')]
[MVCHTTPMethod([httpPOST])]
function CreatePerson([MVCFromBody] Person: TPerson): IMVCResponse;
[MVCPath('/people/($ID)')]
[MVCHTTPMethod([httpPUT])]
function UpdatePerson(ID: Integer; [MVCFromBody] Person: TPerson): IMVCResponse;
[MVCPath('/people/($ID)')]
[MVCHTTPMethod([httpDELETE])]
function DeletePerson(ID: Integer): IMVCResponse;
end;
implementation
uses
System.StrUtils, System.SysUtils, MVCFramework.Logger;
function TMyController.Index: String;
begin
//use Context property to access to the HTTP request and response
Result := 'Hello DelphiMVCFramework World';
end;
function TMyController.GetReversedString(const Value: String): String;
begin
Result := System.StrUtils.ReverseString(Value.Trim);
end;
//Sample CRUD Actions for a "People" entity (with service injection)
function TMyController.GetPeople(PeopleService: IPeopleService): IMVCResponse;
begin
Result := OkResponse(PeopleService.GetAll);
end;
function TMyController.GetPerson(ID: Integer): TPerson;
begin
Result := TPerson.Create(ID, 'Daniele', 'Teti', EncodeDate(1979, 11, 4));
end;
function TMyController.CreatePerson([MVCFromBody] Person: TPerson): IMVCResponse;
begin
LogI('Created ' + Person.FirstName + ' ' + Person.LastName);
Result := CreatedResponse('', 'Person created');
end;
function TMyController.UpdatePerson(ID: Integer; [MVCFromBody] Person: TPerson): IMVCResponse;
begin
LogI('Updated ' + Person.FirstName + ' ' + Person.LastName);
Result := NoContentResponse();
end;
function TMyController.DeletePerson(ID: Integer): IMVCResponse;
begin
LogI('Deleted person with id ' + ID.ToString);
Result := NoContentResponse();
end;
end.

36
samples/sqids/EntityU.pas Normal file
View File

@ -0,0 +1,36 @@
unit EntityU;
interface
uses
MVCFramework.Nullables, MVCFramework.Serializer.Commons;
type
[MVCNameCase(ncCamelCase)]
TPerson = class
private
fID: NullableInt32;
fFirstName: String;
fLastName: String;
fDOB: TDate;
public
property ID: NullableInt32 read fID write fID;
property FirstName: String read fFirstName write fFirstName;
property LastName: String read fLastName write fLastName;
property DOB: TDate read fDOB write fDOB;
constructor Create(ID: Integer; FirstName, LastName: String; DOB: TDate);
end;
implementation
constructor TPerson.Create(ID: Integer; FirstName, LastName: String; DOB: TDate);
begin
inherited Create;
fID := ID;
fFirstName := FirstName;
fLastName := LastName;
fDOB := DOB;
end;
end.

View File

@ -0,0 +1,44 @@
unit ServicesU;
interface
uses
MVCFramework.Container, System.Generics.Collections, EntityU;
type
IPeopleService = interface
['{0198920C-4CD1-4E90-ABEA-B86B5C36FF36}']
function GetAll: TObjectList<TPerson>;
end;
TPeopleService = class(TInterfacedObject, IPeopleService)
protected
function GetAll: TObjectList<TPerson>;
end;
procedure RegisterServices(Container: IMVCServiceContainer);
implementation
uses
System.SysUtils;
procedure RegisterServices(Container: IMVCServiceContainer);
begin
Container.RegisterType(TPeopleService, IPeopleService, TRegistrationType.SingletonPerRequest);
// Register other services here
end;
function TPeopleService.GetAll: TObjectList<TPerson>;
begin
Result := TObjectList<TPerson>.Create;
Result.AddRange([
TPerson.Create(1, 'Henry', 'Ford', EncodeDate(1863, 7, 30)),
TPerson.Create(2, 'Guglielmo', 'Marconi', EncodeDate(1874, 4, 25)),
TPerson.Create(3, 'Antonio', 'Meucci', EncodeDate(1808, 4, 13)),
TPerson.Create(4, 'Michael', 'Faraday', EncodeDate(1867, 9, 22))
]);
end;
end.

View File

@ -0,0 +1,7 @@
object MyWebModule: TMyWebModule
OnCreate = WebModuleCreate
OnDestroy = WebModuleDestroy
Actions = <>
Height = 230
Width = 415
end

View File

@ -0,0 +1,82 @@
unit WebModuleU;
interface
uses
System.SysUtils,
System.Classes,
Web.HTTPApp,
MVCFramework;
type
TMyWebModule = class(TWebModule)
procedure WebModuleCreate(Sender: TObject);
procedure WebModuleDestroy(Sender: TObject);
private
fMVC: TMVCEngine;
end;
var
WebModuleClass: TComponentClass = TMyWebModule;
implementation
{$R *.dfm}
uses
System.IOUtils,
MVCFramework.Commons,
MVCFramework.Middleware.ActiveRecord,
MVCFramework.Middleware.StaticFiles,
MVCFramework.Middleware.Analytics,
MVCFramework.Middleware.Trace,
MVCFramework.Middleware.CORS,
MVCFramework.Middleware.ETag,
MVCFramework.Middleware.Compression, ControllerU;
procedure TMyWebModule.WebModuleCreate(Sender: TObject);
begin
FMVC := TMVCEngine.Create(Self,
procedure(Config: TMVCConfig)
begin
// session timeout (0 means session cookie)
Config[TMVCConfigKey.SessionTimeout] := dotEnv.Env('dmvc.session_timeout', '0');
//default content-type
Config[TMVCConfigKey.DefaultContentType] := dotEnv.Env('dmvc.default.content_type', TMVCConstants.DEFAULT_CONTENT_TYPE);
//default content charset
Config[TMVCConfigKey.DefaultContentCharset] := dotEnv.Env('dmvc.default.content_charset', TMVCConstants.DEFAULT_CONTENT_CHARSET);
//unhandled actions are permitted?
Config[TMVCConfigKey.AllowUnhandledAction] := dotEnv.Env('dmvc.allow_unhandled_actions', 'false');
//enables or not system controllers loading (available only from localhost requests)
Config[TMVCConfigKey.LoadSystemControllers] := dotEnv.Env('dmvc.load_system_controllers', 'true');
//default view file extension
Config[TMVCConfigKey.DefaultViewFileExtension] := dotEnv.Env('dmvc.default.view_file_extension', 'html');
//view path
Config[TMVCConfigKey.ViewPath] := dotEnv.Env('dmvc.view_path', 'templates');
//use cache for server side views (use "false" in debug and "true" in production for faster performances
Config[TMVCConfigKey.ViewCache] := dotEnv.Env('dmvc.view_cache', 'false');
//Max Record Count for automatic Entities CRUD
Config[TMVCConfigKey.MaxEntitiesRecordCount] := dotEnv.Env('dmvc.max_entities_record_count', IntToStr(TMVCConstants.MAX_RECORD_COUNT));
//Enable Server Signature in response
Config[TMVCConfigKey.ExposeServerSignature] := dotEnv.Env('dmvc.expose_server_signature', 'false');
//Enable X-Powered-By Header in response
Config[TMVCConfigKey.ExposeXPoweredBy] := dotEnv.Env('dmvc.expose_x_powered_by', 'true');
// Max request size in bytes
Config[TMVCConfigKey.MaxRequestSize] := dotEnv.Env('dmvc.max_request_size', IntToStr(TMVCConstants.DEFAULT_MAX_REQUEST_SIZE));
end);
// Controllers
FMVC.AddController(TMyController);
// Controllers - END
// Middleware
// Middleware - END
end;
procedure TMyWebModule.WebModuleDestroy(Sender: TObject);
begin
FMVC.Free;
end;
end.

View File

@ -0,0 +1,80 @@
program sqids_sample;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Web.ReqMulti,
Web.WebReq,
Web.WebBroker,
IdContext,
IdHTTPWebBrokerBridge,
MVCFramework,
MVCFramework.Logger,
MVCFramework.DotEnv,
MVCFramework.Commons,
MVCFramework.Container,
MVCFramework.Signal,
EntityU in 'EntityU.pas',
ServicesU in 'ServicesU.pas',
ControllerU in 'ControllerU.pas',
WebModuleU in 'WebModuleU.pas' {MyWebModule: TWebModule};
{$R *.res}
procedure RunServer(APort: Integer);
var
LServer: TIdHTTPWebBrokerBridge;
begin
LServer := TIdHTTPWebBrokerBridge.Create(nil);
try
LServer.OnParseAuthentication := TMVCParseAuthentication.OnParseAuthentication;
LServer.DefaultPort := APort;
LServer.KeepAlive := dotEnv.Env('dmvc.indy.keep_alive', True);
LServer.MaxConnections := dotEnv.Env('dmvc.webbroker.max_connections', 0);
LServer.ListenQueue := dotEnv.Env('dmvc.indy.listen_queue', 500);
LServer.Active := True;
LogI('Listening on port ' + APort.ToString);
LogI('Application started. Press Ctrl+C to shut down.');
WaitForTerminationSignal;
EnterInShutdownState;
LServer.Active := False;
finally
LServer.Free;
end;
end;
begin
{ Enable ReportMemoryLeaksOnShutdown during debug }
// ReportMemoryLeaksOnShutdown := True;
IsMultiThread := True;
// DMVCFramework Specific Configuration
// When MVCSerializeNulls = True empty nullables and nil are serialized as json null.
// When MVCSerializeNulls = False empty nullables and nil are not serialized at all.
MVCSerializeNulls := True;
UseConsoleLogger := True;
LogI('** DMVCFramework Server ** build ' + DMVCFRAMEWORK_VERSION);
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;
WebRequestHandlerProc.MaxConnections := dotEnv.Env('dmvc.handler.max_connections', 1024);
{$IF CompilerVersion >= 34} //SYDNEY+
if dotEnv.Env('dmvc.profiler.enabled', false) then
begin
Profiler.ProfileLogger := Log;
Profiler.WarningThreshold := dotEnv.Env('dmvc.profiler.warning_threshold', 2000);
end;
{$ENDIF}
RegisterServices(DefaultMVCServiceContainer);
DefaultMVCServiceContainer.Build;
RunServer(dotEnv.Env('dmvc.server.port', 8080));
except
on E: Exception do
LogF(E.ClassName + ': ' + E.Message);
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@ uses
System.Generics.Collections,
MVCFramework.DuckTyping,
JsonDataObjects,
MVCFramework.DotEnv, MVCFramework.Container;
MVCFramework.DotEnv, MVCFramework.Container, sqids;
{$I dmvcframeworkbuildconsts.inc}
@ -115,7 +115,7 @@ type
TMVCConstants = record
public const
SESSION_TOKEN_NAME = 'dtsessionid';
DEFAULT_CONTENT_CHARSET = 'UTF-8';
DEFAULT_CONTENT_CHARSET = TMVCCharSet.UTF_8;
DEFAULT_CONTENT_TYPE = TMVCMediaType.APPLICATION_JSON;
CURRENT_USER_SESSION_KEY = '__DMVC_CURRENT_USER__';
LAST_AUTHORIZATION_HEADER_VALUE = '__DMVC_LAST_AUTHORIZATION_HEADER_VALUE_';
@ -774,9 +774,23 @@ type
VPassword: string; var VHandled: Boolean);
end;
TMVCSqids = class sealed
private
class var fInstance: TSqids;
public
class var SQIDS_ALPHABET: String;
class var SQIDS_MIN_LENGTH: Integer;
class destructor Destroy;
class function GetInstance: TSqids;
{ sqids }
class function SqidToInt(const Sqid: String): UInt64;
class function IntToSqid(const Value: UInt64): String;
end;
function dotEnv: IMVCDotEnv; overload;
procedure dotEnvConfigure(const dotEnvDelegate: TFunc<IMVCDotEnv>);
implementation
uses
@ -786,7 +800,8 @@ uses
MVCFramework.Serializer.JsonDataObjects,
MVCFramework.Serializer.Commons,
MVCFramework.Utils,
System.RegularExpressions, MVCFramework.Logger;
System.RegularExpressions,
MVCFramework.Logger;
var
GlobalAppName, GlobalAppPath, GlobalAppExe: string;
@ -795,6 +810,38 @@ var
GdotEnv: IMVCDotEnv = nil;
GdotEnvDelegate: TFunc<IMVCDotEnv> = nil;
class destructor TMVCSqids.Destroy;
begin
FreeAndNil(fInstance);
end;
class function TMVCSqids.GetInstance: TSqids;
begin
if fInstance = nil then
begin
TMonitor.Enter(gLock);
try
if fInstance = nil then
begin
fInstance := TSqids.Create(SQIDS_ALPHABET, SQIDS_MIN_LENGTH);
end;
finally
TMonitor.Exit(gLock);
end;
end;
Result := fInstance;
end;
class function TMVCSqids.IntToSqid(const Value: UInt64): String;
begin
Result := GetInstance.EncodeSingle(Value);
end;
class function TMVCSqids.SqidToInt(const Sqid: String): UInt64;
begin
Result := GetInstance.DecodeSingle(Sqid);
end;
function URLEncode(const Value: string): string; overload;
begin
{$IF defined(BERLINORBETTER)}
@ -1839,6 +1886,6 @@ GlobalAppPath := IncludeTrailingPathDelimiter(ExtractFilePath(GetModuleName(HIns
finalization
FreeAndNil(gLock);
FreeAndNil(GLock);
end.

View File

@ -43,13 +43,13 @@ type
TMVCActionParamCacheItem = class
private
FValue: string;
FParams: TList<string>;
FParams: TList<TPair<String, String>>;
FRegEx: TRegEx;
public
constructor Create(aValue: string; aParams: TList<string>); virtual;
constructor Create(aValue: string; aParams: TList<TPair<String, String>>); virtual;
destructor Destroy; override;
function Value: string;
function Params: TList<string>; // this should be read-only...
function Params: TList<TPair<String, String>>; // this should be read-only...
function Match(const Value: String): TMatch; inline;
end;
@ -83,7 +83,7 @@ type
const AMVCPath: string;
const APath: string;
var aParams: TMVCRequestParamsTable): Boolean;
function GetParametersNames(const V: string): TList<string>;
function GetParametersNames(const V: string): TList<TPair<string, string>>;
protected
procedure FillControllerMappedPaths(
const aControllerName: string;
@ -347,23 +347,32 @@ function TMVCRouter.IsCompatiblePath(
const APath: string;
var aParams: TMVCRequestParamsTable): Boolean;
function ToPattern(const V: string; const Names: TList<string>): string;
function ToPattern(const V: string; const Names: TList<TPair<String, String>>): string;
var
S: string;
S: TPair<String, String>;
begin
Result := V;
for S in Names do
Result := StringReplace(Result, '($' + S + ')', '([' + TMVCConstants.URL_MAPPED_PARAMS_ALLOWED_CHARS + ']*)',
[rfReplaceAll]);
if Names.Count > 0 then
begin
for S in Names do
begin
Result := StringReplace(
Result,
'($' + S.Key + S.Value + ')', '([' + TMVCConstants.URL_MAPPED_PARAMS_ALLOWED_CHARS + ']*)',
[rfReplaceAll]);
end;
end;
end;
var
// lRegEx: TRegEx;
lMatch: TMatch;
lPattern: string;
I: Integer;
lNames: TList<string>;
lNames: TList<TPair<String, String>>;
lCacheItem: TMVCActionParamCacheItem;
P: TPair<string, string>;
lConv: string;
lParValue: String;
begin
if (APath = AMVCPath) or ((APath = '/') and (AMVCPath = '')) then
begin
@ -375,46 +384,85 @@ begin
lNames := GetParametersNames(AMVCPath);
lPattern := ToPattern(AMVCPath, lNames);
lCacheItem := TMVCActionParamCacheItem.Create('^' + lPattern + '$', lNames);
FActionParamsCache.Add(AMVCPath, lCacheItem);
FActionParamsCache.Add(AMVCPath, lCacheItem); {do not commit this!}
end;
// lRegEx := TRegEx.Create(lCacheItem.Value, [roIgnoreCase, roCompiled, roSingleLine]);
// lMatch := lRegEx.Match(APath);
lMatch := lCacheItem.Match(APath);
Result := lMatch.Success;
if Result then
begin
for I := 1 to Pred(lMatch.Groups.Count) do
begin
aParams.Add(lCacheItem.Params[I - 1], TIdURI.URLDecode(lMatch.Groups[I].Value));
P := lCacheItem.Params[I - 1];
{
P.Key = Parameter name
P.Value = Converter applied to the value before to be injected (eg. :sqid)
}
lParValue := TIdURI.URLDecode(lMatch.Groups[I].Value);
if P.Value.IsEmpty then
begin
{no converter}
aParams.Add(P.Key, lParValue);
end
else
begin
lConv := P.Value;
if SameText(lConv, ':sqid') then
begin
{sqids converter (so far the only one)}
aParams.Add(P.Key, TMVCSqids.SqidToInt(lParValue).ToString);
end
else
begin
raise EMVCException.CreateFmt('Unknown converter: %s', [lConv]);
end;
end;
end;
end;
end;
function TMVCRouter.GetParametersNames(const V: string): TList<string>;
function TMVCRouter.GetParametersNames(const V: string): TList<TPair<string, string>>;
var
S: string;
Matches: TMatchCollection;
M: TMatch;
I: Integer;
lList: TList<string>;
lList: TList<TPair<string, string>>;
lNameFound: Boolean;
lConverter: string;
lName: string;
begin
lList := TList<string>.Create;
lList := TList<TPair<string, string>>.Create;
try
S := '\(\$([A-Za-z0-9\_]+)\)';
S := '\(\$([A-Za-z0-9\_]+)(\:[a-z]+)?\)';
Matches := TRegEx.Matches(V, S, [roIgnoreCase, roCompiled, roSingleLine]);
for M in Matches do
begin
lNameFound := False;
lConverter := '';
for I := 0 to M.Groups.Count - 1 do
begin
S := M.Groups[I].Value;
if (Length(S) > 0) and (S.Chars[0] <> '(') then
if Length(S) > 0 then
begin
lList.Add(S);
Break;
if (not lNameFound) and (S.Chars[0] <> '(') and (S.Chars[0] <> ':') then
begin
lName := S;
lNameFound := True;
Continue;
end;
if lNameFound and (S.Chars[0] = ':') then
begin
lConverter := S;
end;
end;
end;
if lNameFound then
begin
lList.Add(TPair<string,string>.Create(lName,lConverter));
end;
end;
Result := lList;
except
@ -533,12 +581,12 @@ end;
{ TMVCActionParamCacheItem }
constructor TMVCActionParamCacheItem.Create(aValue: string;
aParams: TList<string>);
aParams: TList<TPair<String, String>>);
begin
inherited Create;
FValue := aValue;
FParams := aParams;
FRegEx := TRegEx.Create(FValue, [roIgnoreCase, roCompiled, roSingleLine]);
fValue := aValue;
fParams := aParams;
fRegEx := TRegEx.Create(FValue, [roIgnoreCase, roCompiled, roSingleLine]);
end;
destructor TMVCActionParamCacheItem.Destroy;
@ -552,7 +600,7 @@ begin
Result := fRegEx.Match(Value);
end;
function TMVCActionParamCacheItem.Params: TList<string>;
function TMVCActionParamCacheItem.Params: TList<TPair<String, String>>;
begin
Result := FParams;
end;

View File

@ -330,6 +330,10 @@ type
end;
MVCFromSqidAttribute = class(MVCInjectableParamAttribute)
end;
MVCFromCookieAttribute = class(MVCInjectableParamAttribute)
end;
@ -4070,7 +4074,7 @@ end;
function TMVCRenderer.GetContentType: string;
begin
Result := GetContext.Response.ContentType;
if Result.IsEmpty then
if Result.IsEmpty or FContentCharset.IsEmpty then
begin
Result := FContext.FConfig[MVCFramework.Commons.TMVCConfigKey.DefaultContentType];
GetContext.Response.ContentType := Result;
@ -4776,14 +4780,14 @@ procedure TMVCRenderer.RenderSSE(const EventID, EventData: string; EventName: st
const Retry: Integer);
begin
// setting up the correct SSE headers
SetContentType('text/event-stream');
SetContentType(BuildContentType(TMVCMediaType.TEXT_EVENTSTREAM, TMVCCharSet.UTF_8));
GetContext.Response.SetCustomHeader('Cache-Control', 'no-cache');
GetContext.Response.StatusCode := http_status.OK;
GetContext.Response.StatusCode := HTTP_STATUS.OK;
// render the response using SSE compliant data format
// current event id (the client will resend this number at the next request)
ResponseStream.Append(Format('id: %s'#13, [EventID]));
ResponseStream.Append(Format('id:%s'#13, [EventID]));
// The browser attempts to reconnect to the source roughly 3 seconds after
// each connection is closed. You can change that timeout by including a line
@ -4792,16 +4796,16 @@ begin
if Retry > -1 then
begin
ResponseStream.Append(Format('retry: %d'#13, [Retry]));
ResponseStream.Append(Format('retry:%d'#13, [Retry]));
end;
if not EventName.IsEmpty then
begin
ResponseStream.Append(Format('event: %s'#13, [EventName]));
ResponseStream.Append(Format('event:%s'#13, [EventName]));
end;
// actual message
ResponseStream.Append('data: ' + EventData.Replace(sLineBreak, '', [rfReplaceAll]) + #13#13);
ResponseStream.Append('data:' + EventData.Replace(sLineBreak, '', [rfReplaceAll]) + #13#13);
// render all the stuff
RenderResponseStream;

564
sources/blocklist.inc Normal file
View File

@ -0,0 +1,564 @@
const
DEFAULT_BLOCKLIST: TArray<string> = [
'0rgasm',
'1d10t',
'1d1ot',
'1di0t',
'1diot',
'1eccacu10',
'1eccacu1o',
'1eccacul0',
'1eccaculo',
'1mbec11e',
'1mbec1le',
'1mbeci1e',
'1mbecile',
'a11upat0',
'a11upato',
'a1lupat0',
'a1lupato',
'aand',
'ah01e',
'ah0le',
'aho1e',
'ahole',
'al1upat0',
'al1upato',
'allupat0',
'allupato',
'ana1',
'ana1e',
'anal',
'anale',
'anus',
'arrapat0',
'arrapato',
'arsch',
'arse',
'ass',
'b00b',
'b00be',
'b01ata',
'b0ceta',
'b0iata',
'b0ob',
'b0obe',
'b0sta',
'b1tch',
'b1te',
'b1tte',
'ba1atkar',
'balatkar',
'bastard0',
'bastardo',
'batt0na',
'battona',
'bitch',
'bite',
'bitte',
'bo0b',
'bo0be',
'bo1ata',
'boceta',
'boiata',
'boob',
'boobe',
'bosta',
'bran1age',
'bran1er',
'bran1ette',
'bran1eur',
'bran1euse',
'branlage',
'branler',
'branlette',
'branleur',
'branleuse',
'c0ck',
'c0g110ne',
'c0g11one',
'c0g1i0ne',
'c0g1ione',
'c0gl10ne',
'c0gl1one',
'c0gli0ne',
'c0glione',
'c0na',
'c0nnard',
'c0nnasse',
'c0nne',
'c0u111es',
'c0u11les',
'c0u1l1es',
'c0u1lles',
'c0ui11es',
'c0ui1les',
'c0uil1es',
'c0uilles',
'c11t',
'c11t0',
'c11to',
'c1it',
'c1it0',
'c1ito',
'cabr0n',
'cabra0',
'cabrao',
'cabron',
'caca',
'cacca',
'cacete',
'cagante',
'cagar',
'cagare',
'cagna',
'cara1h0',
'cara1ho',
'caracu10',
'caracu1o',
'caracul0',
'caraculo',
'caralh0',
'caralho',
'cazz0',
'cazz1mma',
'cazzata',
'cazzimma',
'cazzo',
'ch00t1a',
'ch00t1ya',
'ch00tia',
'ch00tiya',
'ch0d',
'ch0ot1a',
'ch0ot1ya',
'ch0otia',
'ch0otiya',
'ch1asse',
'ch1avata',
'ch1er',
'ch1ng0',
'ch1ngadaz0s',
'ch1ngadazos',
'ch1ngader1ta',
'ch1ngaderita',
'ch1ngar',
'ch1ngo',
'ch1ngues',
'ch1nk',
'chatte',
'chiasse',
'chiavata',
'chier',
'ching0',
'chingadaz0s',
'chingadazos',
'chingader1ta',
'chingaderita',
'chingar',
'chingo',
'chingues',
'chink',
'cho0t1a',
'cho0t1ya',
'cho0tia',
'cho0tiya',
'chod',
'choot1a',
'choot1ya',
'chootia',
'chootiya',
'cl1t',
'cl1t0',
'cl1to',
'clit',
'clit0',
'clito',
'cock',
'cog110ne',
'cog11one',
'cog1i0ne',
'cog1ione',
'cogl10ne',
'cogl1one',
'cogli0ne',
'coglione',
'cona',
'connard',
'connasse',
'conne',
'cou111es',
'cou11les',
'cou1l1es',
'cou1lles',
'coui11es',
'coui1les',
'couil1es',
'couilles',
'cracker',
'crap',
'cu10',
'cu1att0ne',
'cu1attone',
'cu1er0',
'cu1ero',
'cu1o',
'cul0',
'culatt0ne',
'culattone',
'culer0',
'culero',
'culo',
'cum',
'cunt',
'd11d0',
'd11do',
'd1ck',
'd1ld0',
'd1ldo',
'damn',
'de1ch',
'deich',
'depp',
'di1d0',
'di1do',
'dick',
'dild0',
'dildo',
'dyke',
'encu1e',
'encule',
'enema',
'enf01re',
'enf0ire',
'enfo1re',
'enfoire',
'estup1d0',
'estup1do',
'estupid0',
'estupido',
'etr0n',
'etron',
'f0da',
'f0der',
'f0ttere',
'f0tters1',
'f0ttersi',
'f0tze',
'f0utre',
'f1ca',
'f1cker',
'f1ga',
'fag',
'fica',
'ficker',
'figa',
'foda',
'foder',
'fottere',
'fotters1',
'fottersi',
'fotze',
'foutre',
'fr0c10',
'fr0c1o',
'fr0ci0',
'fr0cio',
'fr0sc10',
'fr0sc1o',
'fr0sci0',
'fr0scio',
'froc10',
'froc1o',
'froci0',
'frocio',
'frosc10',
'frosc1o',
'frosci0',
'froscio',
'fuck',
'g00',
'g0o',
'g0u1ne',
'g0uine',
'gandu',
'go0',
'goo',
'gou1ne',
'gouine',
'gr0gnasse',
'grognasse',
'haram1',
'harami',
'haramzade',
'hund1n',
'hundin',
'id10t',
'id1ot',
'idi0t',
'idiot',
'imbec11e',
'imbec1le',
'imbeci1e',
'imbecile',
'j1zz',
'jerk',
'jizz',
'k1ke',
'kam1ne',
'kamine',
'kike',
'leccacu10',
'leccacu1o',
'leccacul0',
'leccaculo',
'm1erda',
'm1gn0tta',
'm1gnotta',
'm1nch1a',
'm1nchia',
'm1st',
'mam0n',
'mamahuev0',
'mamahuevo',
'mamon',
'masturbat10n',
'masturbat1on',
'masturbate',
'masturbati0n',
'masturbation',
'merd0s0',
'merd0so',
'merda',
'merde',
'merdos0',
'merdoso',
'mierda',
'mign0tta',
'mignotta',
'minch1a',
'minchia',
'mist',
'musch1',
'muschi',
'n1gger',
'neger',
'negr0',
'negre',
'negro',
'nerch1a',
'nerchia',
'nigger',
'orgasm',
'p00p',
'p011a',
'p01la',
'p0l1a',
'p0lla',
'p0mp1n0',
'p0mp1no',
'p0mpin0',
'p0mpino',
'p0op',
'p0rca',
'p0rn',
'p0rra',
'p0uff1asse',
'p0uffiasse',
'p1p1',
'p1pi',
'p1r1a',
'p1rla',
'p1sc10',
'p1sc1o',
'p1sci0',
'p1scio',
'p1sser',
'pa11e',
'pa1le',
'pal1e',
'palle',
'pane1e1r0',
'pane1e1ro',
'pane1eir0',
'pane1eiro',
'panele1r0',
'panele1ro',
'paneleir0',
'paneleiro',
'patakha',
'pec0r1na',
'pec0rina',
'pecor1na',
'pecorina',
'pen1s',
'pendej0',
'pendejo',
'penis',
'pip1',
'pipi',
'pir1a',
'pirla',
'pisc10',
'pisc1o',
'pisci0',
'piscio',
'pisser',
'po0p',
'po11a',
'po1la',
'pol1a',
'polla',
'pomp1n0',
'pomp1no',
'pompin0',
'pompino',
'poop',
'porca',
'porn',
'porra',
'pouff1asse',
'pouffiasse',
'pr1ck',
'prick',
'pussy',
'put1za',
'puta',
'puta1n',
'putain',
'pute',
'putiza',
'puttana',
'queca',
'r0mp1ba11e',
'r0mp1ba1le',
'r0mp1bal1e',
'r0mp1balle',
'r0mpiba11e',
'r0mpiba1le',
'r0mpibal1e',
'r0mpiballe',
'rand1',
'randi',
'rape',
'recch10ne',
'recch1one',
'recchi0ne',
'recchione',
'retard',
'romp1ba11e',
'romp1ba1le',
'romp1bal1e',
'romp1balle',
'rompiba11e',
'rompiba1le',
'rompibal1e',
'rompiballe',
'ruff1an0',
'ruff1ano',
'ruffian0',
'ruffiano',
's1ut',
'sa10pe',
'sa1aud',
'sa1ope',
'sacanagem',
'sal0pe',
'salaud',
'salope',
'saugnapf',
'sb0rr0ne',
'sb0rra',
'sb0rrone',
'sbattere',
'sbatters1',
'sbattersi',
'sborr0ne',
'sborra',
'sborrone',
'sc0pare',
'sc0pata',
'sch1ampe',
'sche1se',
'sche1sse',
'scheise',
'scheisse',
'schlampe',
'schwachs1nn1g',
'schwachs1nnig',
'schwachsinn1g',
'schwachsinnig',
'schwanz',
'scopare',
'scopata',
'sexy',
'sh1t',
'shit',
'slut',
'sp0mp1nare',
'sp0mpinare',
'spomp1nare',
'spompinare',
'str0nz0',
'str0nza',
'str0nzo',
'stronz0',
'stronza',
'stronzo',
'stup1d',
'stupid',
'succh1am1',
'succh1ami',
'succhiam1',
'succhiami',
'sucker',
't0pa',
'tapette',
'test1c1e',
'test1cle',
'testic1e',
'testicle',
'tette',
'topa',
'tr01a',
'tr0ia',
'tr0mbare',
'tr1ng1er',
'tr1ngler',
'tring1er',
'tringler',
'tro1a',
'troia',
'trombare',
'turd',
'twat',
'vaffancu10',
'vaffancu1o',
'vaffancul0',
'vaffanculo',
'vag1na',
'vagina',
'verdammt',
'verga',
'w1chsen',
'wank',
'wichsen',
'x0ch0ta',
'x0chota',
'xana',
'xoch0ta',
'xochota',
'z0cc01a',
'z0cc0la',
'z0cco1a',
'z0ccola',
'z1z1',
'z1zi',
'ziz1',
'zizi',
'zocc01a',
'zocc0la',
'zocco1a',
'zoccola'
];

396
sources/sqids.pas Normal file
View File

@ -0,0 +1,396 @@
unit sqids;
{$IFDEF FPC}
{$mode delphi}
{$ENDIF}
interface
uses
{$IFDEF FPC}
SysUtils, StrUtils
{$ELSE}
System.SysUtils, System.StrUtils
{$ENDIF}
;
const
DEFAULT_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
DEFAULT_MIN_LENGTH = 0;
MIN_ALPHABET_LENGTH = 3;
MAX_ALPHABET_LENGTH = High(Byte);
{$I blocklist.inc}
type
TNumber = UInt64;
TNumbers = TArray<TNumber>;
{ TSqids }
TSqids = class
private
FAlphabet: string;
FMinLength: Byte;
FBlockList: TArray<string>;
function Shuffle(AAlphabet: string): string;
function ToId(ANum: TNumber; AAlphabet: string): string;
function ToNumber(AId: string; AAlphabet: string): TNumber;
function IsBlocked(AId: string): Boolean;
function EncodeNumbers(ANumbers: TNumbers; AIncrement: Word = 0): string;
public
constructor Create(AAlphabet: string; AMinLength: Byte;
ABlockList: TArray<string>); overload;
constructor Create(AAlphabet: string = DEFAULT_ALPHABET;
AMinLength: Byte = DEFAULT_MIN_LENGTH); overload;
constructor Create(AMinLength: Byte); overload;
constructor Create(ABlockList: TArray<string>); overload;
function Encode(ANumbers: TNumbers): string;
function EncodeSingle(ANumber: TNumber): string;
function Decode(AId: string): TNumbers;
function DecodeSingle(AId: string): TNumber;
end;
ESqidsException = class(Exception);
implementation
{ TSqids }
constructor TSqids.Create(AAlphabet: string; AMinLength: Byte; ABlockList: TArray<string>);
var
I: Integer;
LFilteredBlockList: TArray<string>;
LAlphabetLower: string;
C: Char;
LCharsInAlphabet: Boolean;
begin
inherited Create;
if AAlphabet = '' then
AAlphabet := DEFAULT_ALPHABET;
for C in AAlphabet do
if Ord(C) > 127 then
raise ESqidsException.Create('Alphabet cannot contain multibyte characters');
if Length(AAlphabet) < MIN_ALPHABET_LENGTH then
raise ESqidsException.CreateFmt('Alphabet length must be at least %d', [MIN_ALPHABET_LENGTH]);
if Length(AAlphabet) > MAX_ALPHABET_LENGTH then
raise ESqidsException.CreateFmt('Alphabet length must not be longer than %d', [MAX_ALPHABET_LENGTH]);
if Pos(' ', AAlphabet) > 0 then
raise ESqidsException.Create('Alphabet must not contain spaces');
for I := 1 to Length(AAlphabet) do
if Pos(AAlphabet[I], AAlphabet, I+1) > 0 then
raise ESqidsException.Create('Alphabet must contain unique characters');
// clean up blocklist:
// 1. all blocklist words should be lowercase
// 2. no words less than 3 chars
// 3. if some words contain chars that are not in the alphabet, remove those
LFilteredBlockList := Copy(ABlockList, 0);
LAlphabetLower := Lowercase(AAlphabet);
I := 0;
while I < Length(LFilteredBlockList) do
if Length(LFilteredBlockList[I]) < 3 then
Delete(LFilteredBlockList, I, 1)
else
begin
LFilteredBlockList[I] := Lowercase(LFilteredBlockList[I]);
LCharsInAlphabet := True;
for C in LFilteredBlockList[I] do
LCharsInAlphabet := LCharsInAlphabet and LAlphabetLower.Contains(C);
if not LCharsInAlphabet then
Delete(LFilteredBlockList, I, 1)
else
Inc(I);
end;
FAlphabet := Shuffle(AAlphabet);
FMinLength := AMinLength;
FBlockList := LFilteredBlockList;
end;
constructor TSqids.Create(AAlphabet: string; AMinLength: Byte);
begin
Create(AAlphabet, AMinLength, DEFAULT_BLOCKLIST);
end;
constructor TSqids.Create(AMinLength: Byte);
begin
Create(DEFAULT_ALPHABET, AMinLength, DEFAULT_BLOCKLIST);
end;
constructor TSqids.Create(ABlockList: TArray<string>);
begin
Create(DEFAULT_ALPHABET, DEFAULT_MIN_LENGTH, ABlockList);
end;
// consistent shuffle (always produces the same result given the input)
function TSqids.Shuffle(AAlphabet: string): string;
var
I: Integer;
J: Integer;
L: Integer;
R: Integer;
C: Char;
begin
Result := AAlphabet;
L := Length(Result);
I := 0;
J := L - 1;
while J > 0 do
begin
// In Pascal, string index is 1-based
// So when accessing string chars, it is needed to add 1, e.g. [I+1]
R := (I * J + Ord(Result[I+1]) + Ord(Result[J+1])) mod L;
// swap characters at position I+1 and R+1
C := Result[I+1];
Result[I+1] := Result[R+1];
Result[R+1] := C;
Inc(I);
Dec(J);
end;
end;
function TSqids.ToId(ANum: TNumber; AAlphabet: string): string;
var
L: Byte;
LNumResult: TNumber;
begin
Result := '';
L := Length(AAlphabet);
LNumResult := ANum;
repeat
Result := AAlphabet[(LNumResult mod L) + 1] + Result;
LNumResult := LNumResult div L;
until LNumResult = 0;
end;
function TSqids.ToNumber(AId: string; AAlphabet: string): TNumber;
var
C: Char;
L: Byte;
begin
Result := 0;
L := Length(AAlphabet);
for C in AId do
Result := Result * L + TNumber(Pos(C, AAlphabet)) - 1;
end;
function TSqids.IsBlocked(AId: string): Boolean;
function ContainsDigits(S: string): Boolean;
var
C: Char;
begin
for C in S do
if CharInSet(C, ['0'..'9']) then
Exit(True);
Result := False;
end;
var
LWord: string;
begin
AId := AId.ToLower;
for LWord in FBlockList do
// no point in checking words that are longer than the ID
if Length(LWord) <= Length(AId) then
if (Length(AId) <= 3) or (Length(LWord) <= 3) then
begin
// short words have to match completely; otherwise, too many matches
if AId = LWord then
Exit(True);
end
else if ContainsDigits(LWord) then
begin
// if blocklist words contain numbers (leetspeak),
// they will only trigger a match if they're at
// the beginning or the end of the ID.
if AId.StartsWith(LWord) or AId.EndsWith(LWord) then
Exit(True);
end
else if AId.Contains(LWord) then
Exit(True);
Result := False;
end;
function TSqids.Encode(ANumbers: TNumbers): string;
begin
if Length(ANumbers) = 0 then
Exit('');
// no range checking implemented; compiler enforces numbers are within
// range of TNumber type
Result := EncodeNumbers(ANumbers);
end;
function TSqids.EncodeSingle(ANumber: TNumber): string;
begin
Result := Encode([ANumber]);
end;
function TSqids.EncodeNumbers(ANumbers: TNumbers; AIncrement: Word = 0): string;
var
LOffset: TNumber;
I: TNumber;
L: Byte;
LAlphabet: string;
LAlphabetWithoutSeparator: string;
LPrefix: Char;
LNum: TNumber;
begin
// if increment is greater than alphabet length, we've reached max attempts
if AIncrement > Length(FAlphabet) then
raise ESqidsException.Create('Reached max attempts to re-generate the ID');
// get a semi-random LOffset from input numbers
LOffset := Length(ANumbers);
L := Length(FAlphabet);
for I := 0 to Length(ANumbers) - 1 do
LOffset := LOffset + Ord(FAlphabet[(ANumbers[I] mod L) + 1]) + I;
LOffset := LOffset mod L;
// if there is a non-zero `increment`, it's an internal attempt to re-generate the ID
LOffset := (LOffset + AIncrement) mod L;
// re-arrange alphabet so that second-half goes in front of the first-half
LAlphabet := Copy(FAlphabet, LOffset + 1) + Copy(FAlphabet, 1, LOffset);
// `prefix` is the first character in the generated ID, used for randomization
LPrefix := LAlphabet[1];
// reverse alphabet (otherwise for [0, x] `LOffset` and `separator` will be the same char)
LAlphabet := ReverseString(LAlphabet);
// final ID will always have the `prefix` character at the beginning
Result := LPrefix;
// encode input array
for I := 0 to Length(ANumbers) - 1 do
begin
LNum := ANumbers[I];
// the first character of the alphabet is going to be reserved for the `separator`
LAlphabetWithoutSeparator := Copy(LAlphabet, 2);
Result := Result + ToId(LNum, LAlphabetWithoutSeparator);
// if not the last number
if I < TNumber(Length(ANumbers)) - 1 then
begin
// `separator` character is used to isolate numbers within the ID
Result := Result + LAlphabet[1];
// shuffle on every iteration
LAlphabet := Shuffle(LAlphabet);
end;
end;
// handle `minLength` requirement, if the ID is too short
if FMinLength > Length(Result) then
begin
// append a separator
Result := Result + LAlphabet[1];
// keep appending `separator` + however much alphabet is needed
// for decoding: two separators next to each other is what tells us the rest are junk characters
while FMinLength - Length(Result) > 0 do
begin
LAlphabet := Shuffle(LAlphabet);
Result := Result + Copy(LAlphabet, 1, FMinLength - Length(Result));
end;
end;
// if ID has a blocked word anywhere, restart with a +1 increment
if IsBlocked(Result) then
Result := EncodeNumbers(ANumbers, AIncrement + 1);
end;
function TSqids.Decode(AId: string): TNumbers;
var
C: Char;
LPrefix: Char;
LSeparator: Char;
LOffset: TNumber;
LAlphabet: string;
LAlphabetWithoutSeparator: string;
LChunks: TArray<string>;
begin
Result := [];
if AId = '' then Exit;
// if a character is not in the alphabet, return an empty array
for C in AId do
if not FAlphabet.Contains(C) then
Exit;
// first character is always the `prefix`
LPrefix := AId[1];
// `offset` is the semi-random position that was generated during encoding
LOffset := Pos(LPrefix, FAlphabet) - 1;
// re-arrange alphabet back into its original form
LAlphabet := Copy(FAlphabet, LOffset + 1) + Copy(FAlphabet, 1, LOffset);
// reverse alphabet
LAlphabet := ReverseString(LAlphabet);
// now it's safe to remove the prefix character from ID, it's not needed anymore
AId := Copy(AId, 2);
// decode
while not AId.IsEmpty do
begin
LSeparator := LAlphabet[1];
// we need the first part to the left of the separator to decode the number
LChunks := AId.Split([LSeparator]);
if Length(LChunks) > 0 then
begin
// if chunk is empty, we are done (the rest are junk characters)
if LChunks[0] = '' then
Exit;
// decode the number without using the `separator` character
LAlphabetWithoutSeparator := Copy(LAlphabet, 2);
Result := Result + [ToNumber(LChunks[0], LAlphabetWithoutSeparator)];
// if this ID has multiple numbers, shuffle the alphabet because that's what encoding function did
if Length(LChunks) > 1 then
LAlphabet := Shuffle(LAlphabet);
end;
// `id` is now going to be everything to the right of the `separator`
Delete(LChunks, 0, 1);
AId := string.Join(LSeparator, LChunks);
end;
end;
function TSqids.DecodeSingle(AId: string): TNumber;
var
LNumbers: TNumbers;
begin
LNumbers := Decode(AId);
if Length(LNumbers) = 1 then
Result := LNumbers[0]
else
raise ESqidsException.CreateFmt('%d numbers in Id, expected: 1', [Length(LNumbers)]);
end;
end.

View File

@ -148,6 +148,9 @@ end;
begin
ReportMemoryLeaksOnShutdown := True;
UseConsoleLogger := False;
TMVCSqids.SQIDS_ALPHABET := 'axDlw8dRnsPCrbZIAEMFG4TQ6gc3iWtOy9v5NBz0LfSmuKV71JHkUhYpej2Xqo';
TMVCSqids.SQIDS_MIN_LENGTH := 6;
{$IF Defined(CONSOLE_TESTRUNNER)}
MainConsole();
{$ELSE}

View File

@ -9,6 +9,7 @@
<TargetedPlatforms>3</TargetedPlatforms>
<AppType>Console</AppType>
<MainSource>DMVCFrameworkTests.dpr</MainSource>
<ProjectName Condition="'$(ProjectName)'==''">DMVCFrameworkTests</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -991,6 +992,9 @@
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
@ -1253,6 +1257,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>

View File

@ -85,9 +85,6 @@ type
procedure TestPathPrefix;
[Test]
procedure TestReservedIPs;
// procedure TestRoutingSpeed;
// objects mappers
end;
[TestFixture]
@ -288,6 +285,14 @@ type
procedure TestInLineComments;
end;
[TestFixture]
TTestSqids = class(TObject)
public
[Test]
procedure TestSingle;
end;
implementation
{$WARN SYMBOL_DEPRECATED OFF}
@ -2397,6 +2402,14 @@ end;
{ TTestSqids }
procedure TTestSqids.TestSingle;
begin
Assert.AreEqual('Im1JUf',TMVCSqids.IntToSqid(1)); {https://sqids.org/playground}
Assert.AreEqual<Integer>(1, TMVCSqids.SqidToInt(TMVCSqids.IntToSqid(1)));
end;
initialization
TDUnitX.RegisterTestFixture(TTestRouting);
@ -2408,6 +2421,7 @@ TDUnitX.RegisterTestFixture(TTestCryptUtils);
TDUnitX.RegisterTestFixture(TTestLRUCache);
TDUnitX.RegisterTestFixture(TTestDotEnv);
TDUnitX.RegisterTestFixture(TTestDotEnvParser);
TDUnitX.RegisterTestFixture(TTestSqids);
finalization

View File

@ -266,6 +266,19 @@ type
[Test]
procedure TestSerializeAndDeserializeNullables_Passing_Integers_InsteadOf_Floats;
//test sqids
[Test]
[TestCase('1', '1,Im1JUf')]
[TestCase('2','1234567890,LhXiwKz')]
[TestCase('3','9007199254740991,PTP7uQmcmk')]
procedure TestSqidSingle(IntValue: UInt64; Sqid: String);
[Test]
procedure TestWrongSqid;
[Test]
procedure TestInvalidConverter;
// test responses objects
[Test]
procedure TestResponseCreated;
@ -1776,6 +1789,14 @@ begin
Assert.areEqual('', res.Content);
end;
procedure TServerTest.TestInvalidConverter;
var
lRes: IMVCRESTResponse;
begin
lRes := RESTClient.Get('/wrongconverter/1');
Assert.areEqual(500, lRes.StatusCode);
end;
procedure TServerTest.TestIssue406;
var
r: IMVCRESTResponse;
@ -2834,6 +2855,16 @@ begin
end;
end;
procedure TServerTest.TestSqidSingle(IntValue: UInt64; Sqid: String);
var
lRes: IMVCRESTResponse;
begin
lRes := RESTClient.Get('/sqids/itos/' + IntValue.ToString);
Assert.areEqual(200, lRes.StatusCode);
Assert.AreEqual(TMVCSqids.IntToSqid(IntValue), lRes.Content.Trim, '(local)');
Assert.AreEqual(Sqid, lRes.Content.Trim, '(remote)');
end;
procedure TServerTest.TestStringDictionary;
var
lRes: IMVCRESTResponse;
@ -3024,6 +3055,14 @@ begin
Assert.areEqual(HTTP_STATUS.BadRequest, lRes.StatusCode);
end;
procedure TServerTest.TestWrongSqid;
var
lRes: IMVCRESTResponse;
begin
lRes := RESTClient.Get('/sqids/itos/123456789123456789123456789123456789');
Assert.areEqual(400, lRes.StatusCode);
end;
procedure TServerTest.TestTypedDateTimeTypes;
var
res: IMVCRESTResponse;

View File

@ -86,6 +86,8 @@ begin
ReportMemoryLeaksOnShutdown := True;
gLocalTimeStampAsUTC := False;
UseConsoleLogger := False;
TMVCSqids.SQIDS_ALPHABET := dotEnv.Env('dmvc.sqids.alphabet', 'axDlw8dRnsPCrbZIAEMFG4TQ6gc3iWtOy9v5NBz0LfSmuKV71JHkUhYpej2Xqo');
TMVCSqids.SQIDS_MIN_LENGTH := dotEnv.Env('dmvc.sqids.min_length', 6);
try
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass := WebModuleClass;

View File

@ -9,6 +9,7 @@
<FrameworkType>None</FrameworkType>
<ProjectVersion>20.1</ProjectVersion>
<Platform Condition="'$(Platform)'==''">Win64</Platform>
<ProjectName Condition="'$(ProjectName)'==''">TestServer</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
@ -809,6 +810,9 @@
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
@ -1070,6 +1074,7 @@
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>

View File

@ -373,6 +373,21 @@ type
[MVCHTTPMethod([httpGET])]
[MVCPath('/issues/542')]
procedure TestIssue542;
{sqids}
[MVCHTTPMethod([httpGET])]
[MVCPath('/sqids/stoi/($id:sqid)')]
function TestReceiveSqidAsInteger(id: Int64): Int64;
[MVCHTTPMethod([httpGET])]
[MVCPath('/sqids/itos/($id)')]
function TestReceiveIntegerAndReturnSqid(id: Int64): String;
{invalid converter}
[MVCHTTPMethod([httpGET])]
[MVCPath('/wrongconverter/($id:blablabla)')]
function TestInvalidConverter(id: Int64): Int64;
end;
[MVCPath('/private')]
@ -1045,6 +1060,11 @@ begin
Render('hello world');
end;
function TTestServerController.TestInvalidConverter(id: Int64): Int64;
begin
Result := id; //never called
end;
procedure TTestServerController.TestIssue406;
begin
Render(HTTP_STATUS.UnprocessableEntity, TMVCErrorResponseItem.Create('The Message'));
@ -1129,6 +1149,18 @@ begin
Render(Person);
end;
function TTestServerController.TestReceiveIntegerAndReturnSqid(
id: Int64): String;
begin
Result := TMVCSqids.IntToSqid(id);
end;
function TTestServerController.TestReceiveSqidAsInteger(
id: Int64): Int64;
begin
Result := id;
end;
procedure TTestServerController.TestRenderStreamAndFreeWithOwnerFalse;
var
LStream: TMemoryStream;