xtool/contrib/mORMot/SQLite3/DDD/infra/dddInfraSettings.pas

1206 lines
51 KiB
ObjectPascal

/// shared DDD Infrastructure: Application/Daemon settings classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit dddInfraSettings;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
TODO:
- store settings in database, or in a centralized service?
}
{$I Synopse.inc} // define HASINLINE DDDNOSYNDB DDDNOMONGODB WITHLOG
{.$define DDDNOSYNDB}
// if defined, SynDB / external SQL DB won't be linked to the executable
{.$define DDDNOMONGODB}
// if defined, the Mongo DB client won't be linked to the executable
interface
uses
{$ifdef MSWINDOWS}
Windows, // for DeleteFile() inlining under Delphi 2006/2007
{$endif}
SysUtils,
Classes,
SynCommons,
SynTable,
SynLog,
SynCrypto,
mORMot,
mORMotDDD,
SynCrtSock,
SynSQLite3,
mORMotSQLite3, // for internal SQlite3 database
{$ifndef DDDNOSYNDB}
SynDB,
mORMotDB, // for TDDDRestSettings on external SQL database
{$endif}
{$ifndef DDDNOMONGODB}
SynMongoDB,
mORMotMongoDB, // for TDDDRestSettings on external NoSQL database
{$endif}
mORMotWrappers; // for TDDDRestSettings to publish wrapper methods
{ ----- Manage Service/Daemon settings }
type
/// settings used to define how logging take place
// - will map the most used TSynLogFamily parameters
TDDDLogSettings = class(TSynPersistent)
protected
fLevels: TSynLogInfos;
fConsoleLevels: TSynLogInfos;
fAutoFlush: integer;
fStackTraceViaAPI: boolean;
fLowLevelWebSocketsFrames: boolean;
fDestinationPath: TFileName;
fCustomFileName: TFileName;
fRotateFileCount: cardinal;
fRotateFileSize: cardinal;
fRotateFileAtHour: integer;
fSyslogLevels: TSynLogInfos;
fSyslogFacility: TSyslogFacility;
fSyslogServer: RawUTF8;
public
/// initialize the settings to their (TSynLogFamily) default values
constructor Create; override;
published
/// the log levels to be used for the log file
// - i.e. a combination of none or several logging event
// - if "*" is serialized, unneeded sllNone won't be part of the set
property Levels: TSynLogInfos read fLevels write fLevels;
/// the optional log levels to be used for the console
// - by default, only errors would be logged to the console
// - you can specify here another set of levels, e.g. '*' for a verbose
// console output - note that console is very slow to write, so usually
// you should better not set a verbose definition here, unless you are
// in debugging mode
property ConsoleLevels: TSynLogInfos read fConsoleLevels write fConsoleLevels;
/// if low-level WebSockets frames should be logged
// - disabled by default, to minimize logged content
// - may be enabled to monitor most (asynchronous) activity, especially
// in background threads
property LowLevelWebSocketsFrames: boolean read fLowLevelWebSocketsFrames write fLowLevelWebSocketsFrames;
/// the time (in seconds) after which the log content must be written on
// disk, whatever the current content size is
// - by default, the log file will be written for every 4 KB of log (see
// TSynLogFamily.BufferSize property) - this will ensure that the main
// application won't be slow down by logging
// - in order not to loose any log, a background thread can be created
// and will be responsible of flushing all pending log content every
// period of time (e.g. every 10 seconds)
property AutoFlushTimeOut: integer read fAutoFlush write fAutoFlush;
/// by default (false), logging will use manual stack trace browsing
// - if you experiment unexpected EAccessViolation, try to set this setting
// to TRUE so that the RtlCaptureStackBackTrace() API would be used instead
property StackTraceViaAPI: boolean read FStackTraceViaAPI write FStackTraceViaAPI;
/// allows to customize where the log files will be stored
property DestinationPath: TFileName read FDestinationPath write FDestinationPath;
/// allows to customize the log file name
property CustomFileName: TFileName read fCustomFileName write fCustomFileName;
/// auto-rotation of logging files
// - set to 0 by default, meaning no rotation
property RotateFileCount: cardinal read fRotateFileCount write fRotateFileCount;
/// maximum size of auto-rotated logging files, in kilo-bytes (per 1024 bytes)
property RotateFileSizeKB: cardinal read fRotateFileSize write fRotateFileSize;
/// fixed hour of the day where logging files rotation should be performed
property RotateFileDailyAtHour: integer read fRotateFileAtHour write fRotateFileAtHour;
/// the optional log levels to be used for remote UDP syslog server sending
// - works in conjunction with SyslogServer property
// - default will transmit all warnings, errors and exceptions
property SyslogLevels: TSynLogInfos read fSyslogLevels write fSyslogLevels;
/// the optional log levels to be used for remote UDP syslog server sending
// - works in conjunction with SyslogServer/SyslogLevels properties
// - default is sfLocal0
property SyslogFacility: TSyslogFacility read fSyslogFacility write fSyslogFacility;
/// the optional remote UDP syslog server
// - expecting https://tools.ietf.org/html/rfc5424 messages over UDP
// - e.g. '1.2.3.4' to connect to UDP server 1.2.3.4 using default port 514 -
// but you can specify an alternative port as '1.2.3.4:2514'
// - works in conjunction with SyslogLevels/SyslogFacility properties
// - default is '' to disable syslog remote logging
property SyslogServer: RawUTF8 read fSyslogServer write fSyslogServer;
end;
TDDDAppSettingsAbstract = class;
/// abstract parent class for storing application settings
TDDDAppSettingsStorageAbstract = class(TSynAutoCreateFields)
protected
fInitialJsonContent: RawUTF8;
fOwner: TDDDAppSettingsAbstract;
/// called by TDDDAppSettingsAbstract.Create
function SetOwner(aOwner: TDDDAppSettingsAbstract): boolean;
/// inherited classes would override this to persist fInitialJsonContent
procedure InternalStore; virtual; abstract;
public
/// initialize the storage instance
constructor Create(const aInitialJSON: RawUTF8); reintroduce; virtual;
/// TDDDAppSettingsAbstract would use this to actually persist the data
procedure Store(const aJSON: RawUTF8); virtual;
/// the JSON content, as specified when creating the instance
// - will allow SettingsDidChange to check if has changed
// - here the JSON content is stored with default ObjectToJSON() options,
// so will be the normalized representation of the content, which may not
// match the JSON supplied to SetInitialJsonContent() protected method
property InitialJsonContent: RawUTF8 read fInitialJsonContent;
/// the associated settings values
property Owner: TDDDAppSettingsAbstract read fOwner;
end;
/// abstract class for storing application settings
// - this class implements IAutoCreateFieldsResolve so is able to inject
// its own values to any TInjectableAutoCreateFields instance
// - you have to manage instance lifetime of these inherited classes with a
// local IAutoCreateFieldsResolve variable, just like any TInterfaceObject
TDDDAppSettingsAbstract = class(TInterfacedObjectAutoCreateFields,
IAutoCreateFieldsResolve, IDDDSettingsStorable)
protected
fAllProps: PPropInfoDynArray;
fDescription: string;
fLog: TDDDLogSettings;
fSyslog: TCrtSocket;
fSyslogProcID: RawUTF8;
fStorage: TDDDAppSettingsStorageAbstract;
procedure SetProperties(Instance: TObject); virtual;
function SyslogEvent(Sender: TTextWriter; Level: TSynLogInfo;
const Text: RawUTF8): boolean;
public
/// initialize the settings, with a corresponding storage process
constructor Create(aStorage: TDDDAppSettingsStorageAbstract); reintroduce;
/// persist if needed, and finalize the settings
destructor Destroy; override;
/// to be called when the application starts, to initialize settings
// - you can specify a default Description value
// - it will set the global SQLite3Log.Family according to Log values
procedure Initialize(const aDescription: string); virtual;
/// persist the settings if needed
// - just a wrapper around Storage.Store(AsJson)
// - implements IDDDSettingsStorable for "#settings save" admin command
procedure StoreIfUpdated; virtual;
/// serialize the settings as JSON
// - any enumerated or set published property will be commented with their
// textual values, and 'stored false' properties would be included
// - returns the new JSON content corresponding to the updated settings
function AsJson: RawUTF8; virtual;
/// low-level method returning all TSynPersistentPassword full paths
// of all previously created TDDDAppSettingsStorageFile .settings
// - as settingsfile=class1@full.path.to.pass1,class2@full.path.to.pass2,...
// - you may use this method to create a 'passwords' resource for
// /HardenPasswords command line switch as implemented in dddInfraSettings.pas:
// ! passwords := SynLZCompress(TDDDAppSettingsAbstract.PasswordFields);
// ! FileFromString(passwords, 'passwords.data');
// then create e.g. a passwords.rc file as such:
// $ passwords 10 "passwords.data"
// compile this resource:
// $ brcc32 passwords.rc
// and link the resulting .res file to your daemon executable:
// ! {$R passwords.res}
// then /HardenPasswords and /PlainPasswords command line switchs will
// cypher/uncypher all TSynPersistentPassword protected fields using safe
// per-user CryptDataForCurrentUser() encryption
class function PasswordFields: RawUTF8;
/// access to the associated settings storage
property Storage: TDDDAppSettingsStorageAbstract read fStorage;
/// transmitted as PROCID as part of any Log.SyslogServer message
property SyslogProcID: RawUTF8 read fSyslogProcID write fSyslogProcID;
published
/// some text which will be used to describe this application
property Description: string read FDescription write FDescription;
/// defines how logging will be done for this application
property Log: TDDDLogSettings read fLog;
end;
/// class type used for storing application settings
TDDDAppSettingsAbstractClass = class of TDDDAppSettingsAbstract;
/// class used for storing application settings as a JSON file
TDDDAppSettingsStorageFile = class(TDDDAppSettingsStorageAbstract)
protected
fSettingsJsonFileName: TFileName;
procedure InternalStore; override;
public
/// initialize and read the settings from the supplied JSON file name
// - if no file name is specified, will use the executable name with
// '.settings' as extension
constructor Create(const aSettingsJsonFileName: TFileName=''); reintroduce; virtual;
/// compute a file name relative to the .settings file path
function FileNameRelativeToSettingsFile(const aFileName: TFileName): TFileName;
/// the .settings file name, including full path
property SettingsJsonFileName: TFileName
read fSettingsJsonFileName write fSettingsJsonFileName;
end;
/// some options to be used for TDDDRestSettings
// - as part of the .settings, they may be tuned for specific installations,
// whereas TDDDNewRestInstanceOptions are defined in code
TDDDRestSettingsOption =
(optEraseDBFileAtStartup,
optSQlite3FileSafeSlowMode,
optSQlite3FileSafeNonExclusive,
optNoSystemUse,
optSQlite3File4MBCacheSize,
optForceAjaxJson,
optSQLite3LogQueryPlan);
/// define options to be used for TDDDRestSettings
TDDDRestSettingsOptions = set of TDDDRestSettingsOption;
/// how TDDDRestSettings.NewRestInstance would create its instances
// - riOwnModel will set ModelInstance.Owner := RestInstance
// - riHandleAuthentication will set the corresponding parameter to true
// - riDefaultLocalSQlite3IfNone/riDefaultInMemorySQLite3IfNone will create
// a SQLite3 engine with a local file/memory storage, if
// TDDDRestSettings.ORM.Kind is not set
// - riDefaultFullMemoryIfNone will create a TSQLRestServerFullMemory non
// persistent storage, or riDefaultLocalBinaryFullMemoryIfNone with a
// binary local file, if TDDDRestSettings.ORM.Kind is not set
// - riCreateMissingTables will call RestInstance.CreateMissingTables
// - riRaiseExceptionIfNoRest will raise an EDDDInfraException if
// TDDDRestSettings.NewRestInstance would return nil
// - riWithInternalState will enable 'Server-InternalState:' header transmission
// i.e. disable rsoNoInternalState for TSQLRestServer.Options
TDDDNewRestInstanceOptions = set of (
riOwnModel, riCreateVoidModelIfNone,
riHandleAuthentication,
riDefaultLocalSQlite3IfNone, riDefaultInMemorySQLite3IfNone,
riDefaultFullMemoryIfNone, riDefaultLocalBinaryFullMemoryIfNone,
riCreateMissingTables,
riRaiseExceptionIfNoRest,
riWithInternalState);
/// storage class for initializing an ORM REST class
// - this class will contain some generic properties to initialize a TSQLRest
// pointing to a local or remote SQL/NoSQL database, with optional wrappers
TDDDRestSettings = class(TSynAutoCreateFields)
protected
fORM: TSynConnectionDefinition;
fDefaultDataFolder: TFileName;
fDefaultDataFileName: RawUTF8;
fRoot: RawUTF8;
fWrapperTemplateFolder: TFileName;
fWrapperSourceFolders: TFileName;
fOptions: TDDDRestSettingsOptions;
fWrapperTemplateFolderFixed: TFileName;
fWrapperSourceFolderFixed: TFileName;
public
/// is able to instantiate a REST instance according to the stored definition
// - Definition.Kind will identify the TSQLRestServer or TSQLRestClient class
// to be instantiated, or if equals 'MongoDB'/'MongoDBS' use a full MongoDB
// engine, or an external SQL database if it matches a TSQLDBConnectionProperties
// classname
// - if aDefaultLocalSQlite3 is TRUE, then if Definition.Kind is '',
// a local SQlite3 file database will be initiated
// - if aMongoDBIdentifier is not 0, then it will be supplied to every
// TSQLRestStorageMongoDB.SetEngineAddComputeIdentifier() created
// - will return nil if the supplied Definition is not correct
// - note that the supplied Model.Root is expected to be the default root
// URI, which will be overriden with this TDDDRestSettings.Root property
// - will also publish /wrapper HTML page if WrapperTemplateFolder is set
function NewRestInstance(aRootSettings: TDDDAppSettingsAbstract;
aModel: TSQLModel; aOptions: TDDDNewRestInstanceOptions
{$ifndef DDDNOSYNDB}; aExternalDBOptions:
TVirtualTableExternalRegisterOptions=[regDoNotRegisterUserGroupTables]{$endif}
{$ifndef DDDNOMONGODB}; aMongoDBIdentifier: word=0; aMongoDBOptions:
TStaticMongoDBRegisterOptions=[mrDoNotRegisterUserGroupTables]{$endif}): TSQLRest; overload; virtual;
/// is able to instantiate a REST instance according to the stored definition
// - just an overloaded version which will create an owned TSQLModel with
// the supplied TSQLRecord classes
function NewRestInstance(aRootSettings: TDDDAppSettingsAbstract;
const aTables: array of TSQLRecordClass; aOptions: TDDDNewRestInstanceOptions
{$ifndef DDDNOSYNDB}; aExternalDBOptions:
TVirtualTableExternalRegisterOptions=[regDoNotRegisterUserGroupTables]{$endif}
{$ifndef DDDNOMONGODB}; aMongoDBIdentifier: word=0; aMongoDBOptions:
TStaticMongoDBRegisterOptions=[mrDoNotRegisterUserGroupTables]{$endif}): TSQLRest; overload; virtual;
/// initialize a stand-alone TSQLRestServerDB instance
// - with its own database file located in DefaultDataFileName + aDBFileName
// - will own its own TSQLModel with aModelRoot/aModelTables
// - you can tune aCacheSize if the default 40MB value is not right
// - will eventually call CreateMissingTables
// - define custom TDDDRestSettingsOptions if needed
function NewRestServerDB(const aDBFileName: TFileName; const aModelRoot: RawUTF8;
const aModelTables: array of TSQLRecordClass; aOptions: TDDDRestSettingsOptions=[];
aCacheSize: cardinal=10000): TSQLRestServerDB;
/// if DB is a TSQLRestServerDB, will define the expection options
// - DB.FileName will be erased from disk if optEraseDBFileAtStartup is defined
// - force LockingMode=exclusive and Synchrounous=off unless
// optSQlite3FileSafeNonExclusive/optSQlite3FileSafeSlowMode options are set
class procedure RestServerDBSetOptions(DB: TSQLRestServer; Options: TDDDRestSettingsOptions);
/// returns the WrapperTemplateFolder property, all / chars replaced by \
// - so that you would be able to store the paths with /, avoiding JSON escape
function WrapperTemplateFolderFixed(ReturnLocalIfNoneSet: boolean=false): TFileName;
/// returns the WrapperSourceFolder property, all / chars replaced by \
// - so that you would be able to store the paths with /, avoiding JSON escape
function WrapperSourceFolderFixed: TFileName;
/// generate API documentation corresponding to REST SOA interfaces
procedure WrapperGenerate(Rest: TSQLRestServer; Port: integer;
const DestFile: TFileName; const Template: TFileName = 'API.adoc.mustache');
/// the default folder where database files are to be stored
// - will be used by NewRestInstance instead of the .exe folder, if set
property DefaultDataFolder: TFileName read fDefaultDataFolder write fDefaultDataFolder;
/// the default database file name
property DefaultDataFileName: RawUTF8 read fDefaultDataFileName write fDefaultDataFileName;
published
/// the URI Root to be used for the REST Model
property Root: RawUTF8 read fRoot write fRoot;
/// defines a mean of access to a TSQLRest instance
// - using Kind/ServerName/DatabaseName/User properties: Kind would define
// the TSQLRest class to be instantiated by function NewRestInstance()
property ORM: TSynConnectionDefinition read fORM;
/// if set to a valid folder, the generated TSQLRest will publish a
// '/Root/wrapper' HTML page so that client code could be generated
property WrapperTemplateFolder: TFileName
read fWrapperTemplateFolder write fWrapperTemplateFolder;
/// where the source code may be searched, for comment extraction of types
// - several folders may be defined, separated by ; (just like in Delphi IDE)
// - only used if WrapperTemplateFolder is defined
property WrapperSourceFolders: TFileName
read fWrapperSourceFolders write fWrapperSourceFolders;
/// how the REST instance is to be initialized
property Options: TDDDRestSettingsOptions read fOptions write fOptions;
end;
/// parent class for storing REST-based application settings
// - this class could be used for an application with a single REST server
// running on a given HTTP port
TDDDAppSettingsRest = class(TDDDAppSettingsAbstract)
protected
fRest: TDDDRestSettings;
fServerPort: RawUTF8;
public
/// to be called when the application starts, to initialize settings
// - will call inherited TDDDAppSettingsFile.Initialize, and
// set ServerPort to a default 888/8888 value under Windows/Linux
procedure Initialize(const aDescription: string); override;
published
/// allow to instantiate a REST instance from its JSON definition
property Rest: TDDDRestSettings read fRest;
/// the IP port to be used for the HTTP server associated with the application
property ServerPort: RawUTF8 read fServerPort write fServerPort;
end;
/// define how an administrated service/daemon is remotely accessed via REST
// - the IAdministratedDaemon service will be published to administrate
// this service/daemon instance
// - those values should match the ones used on administrative tool side
TDDDAdministratedDaemonRemoteAdminSettings = class(TSynAutoCreateFields)
protected
FAuthRootURI: RawUTF8;
FAuthHashedPassword: RawUTF8;
FAuthUserName: RawUTF8;
FAuthNamedPipeName: TFileName;
FAuthHttp: TSQLHttpServerDefinition;
public
published
/// the root URI used for the REST data model
// - default URI is 'admin'
property AuthRootURI: RawUTF8 read FAuthRootURI write FAuthRootURI;
/// if set, expect authentication with this single user name
// - that is, the TSQLRestServer will register a single TSQLAuthUser
// instance with the supplied AuthUserName/AuthHashedPassword credentials
property AuthUserName: RawUTF8 read FAuthUserName write FAuthUserName;
/// the SHA-256 hashed password to authenticate AuthUserName
// - follows the TSQLAuthUser.ComputeHashedPassword() encryption
// - marked as 'stored false' so that it won't appear e.g. in the logs
property AuthHashedPassword: RawUTF8 read FAuthHashedPassword write FAuthHashedPassword
stored false;
/// if defined, the following pipe name would be used for REST publishing
// - by definition, will work only on Windows
property AuthNamedPipeName: TFileName read FAuthNamedPipeName write FAuthNamedPipeName;
/// if defined, these parameters would be used for REST publishing over HTTP
property AuthHttp: TSQLHttpServerDefinition read FAuthHttp;
end;
/// parent class for storing a service/daemon settings
// - under Windows, some Service* properties will handle installation as a
// regular Windows Service, thanks to TDDDDaemon
TDDDAdministratedDaemonSettings = class(TDDDAppSettingsAbstract)
protected
FRemoteAdmin: TDDDAdministratedDaemonRemoteAdminSettings;
FServiceDisplayName: string;
FServiceName: string;
FServiceDependencies: TStringDynArray;
FServiceAutoStart: boolean;
FAppUserModelID: string;
public
/// to be called when the application starts, to initialize settings
// - you can specify default Description and Service identifiers
// - the service-related parameters are Windows specific, and will be
// ignored on other platforms
procedure Initialize(const aDescription,
aServiceName,aServiceDisplayName,aAppUserModelID: string;
const aServiceDependencies: TStringDynArray = nil); reintroduce; virtual;
/// returns the folder containing .settings files - .exe folder by default
function SettingsFolder: TFileName; virtual;
/// under Windows, will define optional Service internal Dependencies
// - not published by default: could be defined if needed, or e.g. set in
// overriden constructor
property ServiceDependencies: TStringDynArray read FServiceDependencies write FServiceDependencies;
published
/// define how this administrated service/daemon is accessed via REST
property RemoteAdmin: TDDDAdministratedDaemonRemoteAdminSettings read FRemoteAdmin;
/// under Windows, will define the Service internal name
property ServiceName: string read FServiceName write FServiceName;
/// under Windows, will define the Service displayed name
property ServiceDisplayName: string read FServiceDisplayName write FServiceDisplayName;
/// under Windows, will define if the Service should auto-start at boot
// - FALSE means that it should be started on demand
property ServiceAutoStart: boolean read FServiceAutoStart write FServiceAutoStart;
/// under Windows 7 and later, will set an unique application-defined
// Application User Model ID (AppUserModelID) that identifies the current
// process to the taskbar
// - this identifier allows an application to group its associated processes
// and windows under a single taskbar button
// - should follow SetAppUserModelID() expectations, i.e. 'Company.Product'
property AppUserModelID: string read FAppUserModelID write FAppUserModelID;
end;
/// a Factory event allowing to customize/mock a socket connection
// - the supplied aOwner should be a TDDDSocketThread instance
// - returns a IDDDSocket interface instance (e.g. a TDDDSynCrtSocket)
TOnIDDDSocketThreadCreate = procedure(aOwner: TObject; out Obj) of object;
/// the settings of a TDDDThreadSocketProcess thread
// - defines how to connect (and reconnect) to the associated TCP server
TDDDSocketThreadSettings = class(TSynAutoCreateFields)
protected
fHost: RawUTF8;
fPort: integer;
fSocketLoopPeriod: integer;
fSocketTimeout: integer;
fSocketBufferBytes: integer;
fSocketMaxBufferBytes: integer;
fConnectionAttemptsInterval: Integer;
fAutoReconnectAfterSocketError: boolean;
fMonitoringInterval: integer;
fOnIDDDSocketThreadCreate: TOnIDDDSocketThreadCreate;
public
/// used to set the default values
constructor Create; override;
/// set Host and Port values from a 'ip:port' or 'ip' text
function SetHostPort(const IpPort: RawByteString; defaultPort: integer): boolean;
/// retrieve Host and Port values as a single 'ip:port' text
function GetHostPort: RawUTF8;
/// you could set here a factory method to mock the socket connection
// - this property is public, but not published, since it should not be
// serialized on the settings file, but overloaded at runtime
property OnIDDDSocketThreadCreate: TOnIDDDSocketThreadCreate
read fOnIDDDSocketThreadCreate write fOnIDDDSocketThreadCreate;
published
/// the associated TCP server host
property Host: RawUTF8 read FHost write FHost;
/// the associated TCP server port
property Port: integer read FPort write FPort;
/// how many millisecond the main socket reading loop should wait
// for pending data, before calling TDDDSocketThread.InternalExecuteIdle
// - default is 100 ms
property SocketLoopPeriod: integer read fSocketLoopPeriod write fSocketLoopPeriod;
/// the time out period, in milliseconds, for socket access
// - default is 2000 ms, i.e. 2 seconds
property SocketTimeout: integer read FSocketTimeout write FSocketTimeout;
/// the internal size of the input socket buffer
// - default is 32768, i.e. 32 KB
property SocketBufferBytes: integer read FSocketBufferBytes write FSocketBufferBytes;
/// the maximum size of the thread input buffer
// - i.e. how many bytes are stored in fSocketInputBuffer memory, before
// nothing is retrieved from the socket buffer
// - set to avoid any "out of memory" of the currrent process, if the
// incoming data is not processed fast enough
// - default is 16777216, i.e. 16 MB
property SocketMaxBufferBytes: integer read FSocketMaxBufferBytes write FSocketMaxBufferBytes;
/// the time, in seconds, between any reconnection attempt
// - default value is 5 - i.e. five seconds
// - if you set -1 as value, thread would end without any retrial
property ConnectionAttemptsInterval: Integer
read fConnectionAttemptsInterval write fConnectionAttemptsInterval;
/// if TRUE, any communication error would try to reconnect the socket
property AutoReconnectAfterSocketError: boolean
read FAutoReconnectAfterSocketError write FAutoReconnectAfterSocketError;
/// the period, in milliseconds, on which Monitoring information is logged
// - default value is 120000, i.e. 2 minutes
property MonitoringLogInterval: integer read FMonitoringInterval write FMonitoringInterval;
end;
/// storage class for a ServicesLog settings
TDDDServicesLogRestSettings = class(TDDDRestSettings)
protected
fShardDBCount: Integer;
public
/// compute a stand-alone REST instance for interface-based services logging
// - all services of aMainRestWithServices would log their calling information
// into a dedicated table, but the methods defined in aExcludedMethodNamesCSV
// (which should be specified, even as '', to avoid FPC compilation error)
// - by default, will create a local SQLite3 file for storage, optionally
// via TSQLRestStorageShardDB if ShardDBCount is set
// - the first supplied item of aLogClass array would be used for the
// service logging; any additional item would be part of the model of the
// returned REST instance, but may be used later on (e.g. to handle
// DB-based asynchronous remote notifications as processed by
// TServiceFactoryClient.SendNotificationsVia method)
// - if aLogClass=[], plain TSQLRecordServiceLog would be used as default
// - aShardRange is used for TSQLRestStorageShardDB if ShardDBCount>0
function NewRestInstance(aRootSettings: TDDDAppSettingsAbstract;
aMainRestWithServices: TSQLRestServer; const aLogClass: array of TSQLRecordServiceLogClass;
const aExcludedMethodNamesCSV: RawUTF8; aShardRange: TID=50000): TSQLRest; reintroduce;
published
/// if set, will define MaxShardCount for TSQLRestStorageShardDB persistence
property ShardDBCount: Integer read fShardDBCount write fShardDBCount;
end;
/// parent class for storing a HTTP published service/daemon settings
TDDDAdministratedDaemonHttpSettings = class(TDDDAdministratedDaemonSettings)
protected
fRest: TDDDRestSettings;
fHttp: TSQLHttpServerDefinition;
fServicesLog: TDDDServicesLogRestSettings;
published
/// how the main REST server is implemented
// - most probably using a TSQLRestServerDB, i.e. local SQLite3 storage
property Rest: TDDDRestSettings read fRest;
/// how the HTTP server should be defined
property Http: TSQLHttpServerDefinition read fHttp;
/// how the SOA calls would be logged into their own SQlite3 database
property ServicesLog: TDDDServicesLogRestSettings read fServicesLog;
end;
/// stand-alone property to publish a secondary TSQLRestServer over HTTP
TDDDRestHttpSettings = class(TSynAutoCreateFields)
protected
fRest: TDDDRestSettings;
fHttp: TSQLHttpServerDefinition;
published
/// how the REST server is implemented
// - most probably using a TSQLRestServerDB, i.e. local SQLite3 storage
property Rest: TDDDRestSettings read fRest;
/// how the HTTP server should be defined
property Http: TSQLHttpServerDefinition read fHttp;
end;
/// stand-alone property to publish a secondary logged service over HTTP
TDDDRestHttpLogSettings = class(TDDDRestHttpSettings)
protected
fServicesLog: TDDDServicesLogRestSettings;
published
/// how the SOA calls would be logged into their own SQlite3 database
property ServicesLog: TDDDServicesLogRestSettings read fServicesLog;
end;
/// storage class for a remote MongoDB server direct access settings
TDDDMongoDBRestSettings = class(TDDDRestSettings)
public
/// set the default values for direct MongoDB server connection
// - if MongoServerAddress is e.g. '?:27017', entry with default value would
// be saved in the settings, but NewRestInstance() would ignore it: once the
// remote MongoDB server IP is known, you may just replace '?' to use it
// - if MongoUser and MongoPassword are set, would call TMongoClient.OpenAuth()
procedure SetDefaults(const Root, MongoServerAddress, MongoDatabase,
MongoUser, MongoPassword: RawUTF8; TLS: boolean=false);
end;
TDDDEmailerSettings = class(TSynPersistent)
protected
fSMTP: RawUTF8;
fRecipients: RawUTF8;
public
constructor Create; override;
published
property SMTP: RawUTF8 read fSMTP write fSMTP;
property Recipients: RawUTF8 read fRecipients write fRecipients;
end;
implementation
{ TDDDAppSettingsAbstract }
procedure TDDDAppSettingsAbstract.Initialize(const aDescription: string);
var
uri: TURI;
begin
{$ifdef WITHLOG}
with SQLite3Log.Family do begin
Level := Log.Levels-[sllNone]; // '*' would include sllNone
if Log.ConsoleLevels<>[] then
EchoToConsole := Log.ConsoleLevels-[sllNone];
PerThreadLog := ptIdentifiedInOnFile;
if Log.DestinationPath<>'' then
DestinationPath := Log.DestinationPath;
if Log.CustomFileName<>'' then
CustomFileName := Log.CustomFileName;
RotateFileCount := Log.RotateFileCount;
RotateFileSizeKB := Log.RotateFileSizeKB;
RotateFileDailyAtHour := Log.RotateFileDailyAtHour;
if Log.RotateFileCount<=0 then
HighResolutionTimestamp := true;
FileExistsAction := acAppend; // default rotation mode
if Log.StackTraceViaAPI then
StackTraceUse := stOnlyAPI;
// AutoFlushTimeOut not set now, since won't work with /form
if (Log.SyslogServer<>'') and (Log.SyslogServer[1]<>'?') and
not Assigned(EchoCustom) and (fSyslog=nil) and (Log.SyslogLevels<>[]) and
uri.From(Log.SyslogServer,'514') then
try
fSyslog := TCrtSocket.Open(uri.Server,uri.Port,cslUDP,2000);
EchoCustom := SyslogEvent;
except
fSyslog := nil;
end;
end;
{$endif}
if fDescription='' then
fDescription := aDescription;
end;
function TDDDAppSettingsAbstract.SyslogEvent(Sender: TTextWriter; Level: TSynLogInfo;
const Text: RawUTF8): boolean;
var
buf: array[0..511] of AnsiChar; // 512 bytes for fast unfragmented UDP packet
len: PtrInt;
begin
result := false;
if (fSyslog=nil) or not (Level in Log.SyslogLevels) then
exit;
len := SyslogMessage(Log.SyslogFacility,LOG_TO_SYSLOG[Level],Text,
fSyslogProcID,ToText(Level),@buf,sizeof(buf),true);
if len<>0 then
if fSyslog.TrySndLow(@buf,len) then // works even if no server is available
result := true else
raise ESynException.CreateUTF8('%.SyslogEvent failed for %:% as error %',
[self,fSyslog.Server,fSyslog.Port,fSyslog.LastLowSocketError]);
end;
procedure TDDDAppSettingsAbstract.SetProperties(Instance: TObject);
begin
CopyObject(self,Instance);
end;
destructor TDDDAppSettingsAbstract.Destroy;
begin
StoreIfUpdated;
inherited Destroy;
fStorage.Free;
if fSyslog<>nil then begin
{$ifdef WITHLOG}
SQLite3Log.Family.EchoCustom := nil;
{$endif}
FreeAndNil(fSyslog);
end;
end;
function TDDDAppSettingsAbstract.AsJson: RawUTF8;
begin
result := ObjectToJSON(Self,[woHumanReadable,woStoreStoredFalse,
woHumanReadableFullSetsAsStar,woHumanReadableEnumSetAsComment]);
end;
procedure TDDDAppSettingsAbstract.StoreIfUpdated;
begin
if fStorage<>nil then
fStorage.Store(AsJson);
end;
var
TDDDAppSettingsAbstractFiles: array of record
FileName: RawUTF8;
SettingClass: TDDDAppSettingsAbstractClass;
end;
constructor TDDDAppSettingsAbstract.Create(aStorage: TDDDAppSettingsStorageAbstract);
begin
inherited Create;
if aStorage=nil then
aStorage := TDDDAppSettingsStorageFile.Create;
fStorage := aStorage;
fStorage.SetOwner(self);
if aStorage.InheritsFrom(TDDDAppSettingsStorageFile) then begin
SetLength(TDDDAppSettingsAbstractFiles,length(TDDDAppSettingsAbstractFiles)+1);
with TDDDAppSettingsAbstractFiles[high(TDDDAppSettingsAbstractFiles)] do begin
FileName := Split(StringToUTF8(ExtractFileName(TDDDAppSettingsStorageFile(aStorage).
fSettingsJsonFileName)),'.');
SettingClass := pointer(ClassType);
end;
end;
end;
class function TDDDAppSettingsAbstract.PasswordFields: RawUTF8;
procedure InternalAdd(const path: RawUTF8; C: TClass; var res: TRawUTF8DynArray);
var PI,PP: PPropInfo;
CT: TClass;
p: RawUTF8;
i: integer;
offset: pointer;
begin
offset := TSynPersistentWithPassword(nil).GetPasswordFieldAddress;
while C<>nil do begin
for i := 1 to InternalClassPropInfo(C,PI) do begin
if PI^.PropType^.Kind=tkClass then begin
FormatUTF8('%%.',[path,PI^.Name],p);
CT := PI^.PropType^.ClassType^.ClassType;
if CT.InheritsFrom(TSynPersistentWithPassword) then begin
PP := ClassFieldPropWithParentsFromClassOffset(CT,offset);
if PP<>nil then
AddRawUTF8(res,FormatUTF8('%@%%', [ClassNameShort(CT)^, p, PP^.Name]));
end;
InternalAdd(p,CT,res); // recursive search of all password fields
end;
PI := PI^.Next;
end;
C := GetClassParent(C);
end;
end;
var i: integer;
res: TRawUTF8DynArray;
begin
result := '';
for i := 0 to high(TDDDAppSettingsAbstractFiles) do
with TDDDAppSettingsAbstractFiles[i] do begin
res := nil;
InternalAdd('',SettingClass,res);
if res<>nil then
result := FormatUTF8('%%=%'#13#10,[result,FileName,RawUTF8ArrayToCSV(res)]);
end;
end;
{ TDDDLogSettings }
constructor TDDDLogSettings.Create;
begin
inherited Create;
fLevels := [low(TSynLogInfo)..high(TSynLogInfo)]; // "Levels":"*" by default
fRotateFileAtHour := -1;
fRotateFileCount := 20;
fRotateFileSize := 128*1024; // 128 MB per rotation log by default
fAutoFlush := 5;
fSyslogLevels := [sllWarning,sllLastError,sllError,
sllException,sllExceptionOS,sllNewRun,sllDDDError];
fSyslogFacility := sfLocal0;
end;
{ TDDDRestSettings }
function TDDDRestSettings.NewRestInstance(aRootSettings: TDDDAppSettingsAbstract;
const aTables: array of TSQLRecordClass; aOptions: TDDDNewRestInstanceOptions
{$ifndef DDDNOSYNDB}; aExternalDBOptions: TVirtualTableExternalRegisterOptions {$endif}
{$ifndef DDDNOMONGODB}; aMongoDBIdentifier: word;
aMongoDBOptions: TStaticMongoDBRegisterOptions{$endif}): TSQLRest;
begin
include(aOptions,riOwnModel);
result := NewRestInstance(aRootSettings,TSQLModel.Create(aTables,fRoot),aOptions
{$ifndef DDDNOSYNDB},aExternalDBOptions{$endif}
{$ifndef DDDNOMONGODB},aMongoDBIdentifier,aMongoDBOptions{$endif});
end;
function TDDDRestSettings.NewRestInstance(aRootSettings: TDDDAppSettingsAbstract;
aModel: TSQLModel; aOptions: TDDDNewRestInstanceOptions
{$ifndef DDDNOSYNDB}; aExternalDBOptions: TVirtualTableExternalRegisterOptions{$endif}
{$ifndef DDDNOMONGODB}; aMongoDBIdentifier: word;
aMongoDBOptions: TStaticMongoDBRegisterOptions{$endif}): TSQLRest;
procedure ComputeDefaultORMServerName(const Ext: RawUTF8);
var FN: RawUTF8;
begin
if fORM.ServerName='' then begin
if fDefaultDataFolder='' then
fDefaultDataFolder := ExeVersion.ProgramFilePath;
if fDefaultDataFileName='' then
FN := ExeVersion.ProgramName else
FN := fDefaultDataFileName;
fORM.ServerName := StringToUTF8(IncludeTrailingPathDelimiter(
fDefaultDataFolder))+FN+Ext;
end;
end;
begin
if aModel=nil then
if riCreateVoidModelIfNone in aOptions then begin
aModel := TSQLModel.Create([],fRoot);
include(aOptions,riOwnModel);
end else
raise EDDDInfraException.CreateUTF8('%.NewRestInstance(aModel=nil)',[self]);
if fRoot='' then // supplied TSQLModel.Root is the default root URI
fRoot := aModel.Root else
aModel.Root := fRoot;
if fORM.Kind='' then
if riDefaultLocalSQlite3IfNone in aOptions then begin
fORM.Kind := 'TSQLRestServerDB'; // SQlite3 engine by default
ComputeDefaultORMServerName('.db');
end else
if riDefaultInMemorySQLite3IfNone in aOptions then begin
fORM.Kind := 'TSQLRestServerDB';
fORM.ServerName := SQLITE_MEMORY_DATABASE_NAME;
end else
if riDefaultFullMemoryIfNone in aOptions then
fORM.Kind := 'TSQLRestServerFullMemory' else
if riDefaultLocalBinaryFullMemoryIfNone in aOptions then begin
fORM.Kind := 'TSQLRestServerFullMemory';
fORM.DatabaseName := 'binary'; // as TSQLRestServerFullMemory.DefinitionTo
ComputeDefaultORMServerName('.data');
end;
result := nil;
try
if fORM.Kind='' then
exit;
if (optEraseDBFileAtStartup in Options) and (fORM.ServerName<>'') then
if (fORM.Kind='TSQLRestServerDB') or
(fORM.Kind='TSQLRestServerFullMemory') then
DeleteFile(UTF8ToString(fORM.ServerName));
{$ifndef DDDNOMONGODB}
result := TSQLRestMongoDBCreate(aModel,ORM,
riHandleAuthentication in aOptions,aMongoDBOptions,aMongoDBIdentifier);
{$endif}
{$ifdef DDDNOSYNDB}
result := TSQLRest.CreateTryFrom(aModel,ORM,riHandleAuthentication in aOptions);
{$else}
if result=nil then // failed to use MongoDB -> try external or internal DB
result := TSQLRestExternalDBCreate(aModel,ORM,
riHandleAuthentication in aOptions,aExternalDBOptions);
{$endif}
if result=nil then
exit; // no match or wrong parameters
if result.InheritsFrom(TSQLRestServer) then
try // initialize server features
if (WrapperTemplateFolder<>'') and DirectoryExists(WrapperTemplateFolderFixed) then
AddToServerWrapperMethod(TSQLRestServer(result),[WrapperTemplateFolderFixed],
WrapperSourceFolderFixed);
RestServerDBSetOptions(TSQLRestServer(result), Options);
if not (riWithInternalState in aOptions) then
TSQLRestServer(result).Options := TSQLRestServer(result).Options+[rsoNoInternalState];
if riCreateMissingTables in aOptions then
TSQLRestServer(result).CreateMissingTables;
except
FreeAndNil(result);
end; // note: TSQLRestClient.SetUser() has been called in TSQLRest*DBCreate()
if not(optNoSystemUse in Options) then
// if not already set, update cpu/ram info every 10 sec + 10 min history
result.SystemUseTrack(10);
finally
if riOwnModel in aOptions then
if result=nil then // avoid memory leak
aModel.Free else
aModel.Owner := result;
if (result=nil) and (riRaiseExceptionIfNoRest in aOptions) then
raise EDDDInfraException.CreateUTF8('Impossible to initialize % on %/%',
[fORM.Kind,fORM.ServerName,fRoot]);
end;
end;
class procedure TDDDRestSettings.RestServerDBSetOptions(DB: TSQLRestServer;
Options: TDDDRestSettingsOptions);
begin
if DB = nil then
exit;
if DB.InheritsFrom(TSQLRestServerDB) then
with TSQLRestServerDB(DB).DB do begin // tune internal SQlite3 engine
if optEraseDBFileAtStartup in Options then
DeleteFile(FileName);
if optSQlite3FileSafeNonExclusive in Options then
LockingMode := lmNormal else
LockingMode := lmExclusive;
if optSQlite3FileSafeSlowMode in Options then
Synchronous := smNormal else
Synchronous := smOff;
if optSQlite3File4MBCacheSize in Options then
CacheSize := (4 shl 20) div PageSize;
if optSQLite3LogQueryPlan in Options then
TSQLRestServerDB(DB).StatementPreparedSelectQueryPlan := true;
end;
DB.NoAJAXJSON := not (optForceAjaxJson in Options);
end;
function TDDDRestSettings.NewRestServerDB(const aDBFileName: TFileName;
const aModelRoot: RawUTF8; const aModelTables: array of TSQLRecordClass;
aOptions: TDDDRestSettingsOptions; aCacheSize: cardinal): TSQLRestServerDB;
begin
result := TSQLRestServerDB.CreateWithOwnModel(aModelTables, DefaultDataFolder +
UTF8ToString(DefaultDataFileName) + aDBFileName, false, aModelRoot, '', aCacheSize);
RestServerDBSetOptions(result, Options); // tune internal SQlite3 engine
result.Options := result.Options+[rsoNoInternalState];
result.CreateMissingTables;
end;
procedure TDDDRestSettings.WrapperGenerate(Rest: TSQLRestServer; Port: integer;
const DestFile, Template: TFileName);
var dest: TFileName;
mus: RawUTF8;
begin
if (self = nil) or (Rest = nil) then
exit;
if DestFile = '' then
dest := ExeVersion.ProgramFilePath+'mORMotClient.asc' else
dest := DestFile;
mus := StringFromFile(WrapperTemplateFolderFixed(true)+Template);
FileFromString(WrapperFromModel(Rest,mus,'',Port),dest);
end;
function TDDDRestSettings.WrapperSourceFolderFixed: TFileName;
begin
if fWrapperSourceFolders='' then
result := '' else begin
if fWrapperSourceFolderFixed='' then
fWrapperSourceFolderFixed := IncludeTrailingPathDelimiter(StringReplace(
fWrapperSourceFolders,'/',PathDelim,[rfReplaceAll]));
result := fWrapperSourceFolders;
end;
end;
function TDDDRestSettings.WrapperTemplateFolderFixed(ReturnLocalIfNoneSet: boolean): TFileName;
begin
if fWrapperTemplateFolder='' then
if ReturnLocalIfNoneSet then
result := ExeVersion.ProgramFilePath
else
result := '' else begin
if fWrapperTemplateFolderFixed='' then
fWrapperTemplateFolderFixed := StringReplace(
fWrapperTemplateFolder,'/',PathDelim,[rfReplaceAll]);
result := fWrapperTemplateFolder;
end;
end;
{ TDDDAppSettingsRest }
procedure TDDDAppSettingsRest.Initialize(const aDescription: string);
begin
inherited Initialize(aDescription);
if ServerPort='' then
ServerPort := {$ifdef LINUX}'8888'{$else}'888'{$endif};
end;
{ TDDDAdministratedDaemonSettings }
procedure TDDDAdministratedDaemonSettings.Initialize(
const aDescription, aServiceName, aServiceDisplayName, aAppUserModelID: string;
const aServiceDependencies: TStringDynArray);
begin
inherited Initialize(aDescription);
if FServiceName='' then
FServiceName := aServiceName;
if FServiceDisplayName='' then
FServiceDisplayName := aServiceDisplayName;
FServiceDependencies := aServiceDependencies;
if FAppUserModelID='' then
FAppUserModelID := aAppUserModelID;
end;
function TDDDAdministratedDaemonSettings.SettingsFolder: TFileName;
begin
if fStorage.InheritsFrom(TDDDAppSettingsStorageFile) then
result := ExtractFilePath(TDDDAppSettingsStorageFile(fStorage).fSettingsJsonFileName)
else
result := ExeVersion.ProgramFilePath;
end;
{ TDDDSocketThreadSettings }
constructor TDDDSocketThreadSettings.Create;
begin
inherited Create;
fSocketLoopPeriod := 100;
fConnectionAttemptsInterval := 5;
fMonitoringInterval := 120*1000; // log monitoring information every 2 minutes
fSocketBufferBytes := 32768; // 32KB
fSocketMaxBufferBytes := 16777216; // 16MB
end;
function TDDDSocketThreadSettings.GetHostPort: RawUTF8;
begin
FormatUTF8('%:%', [fHost, fPort], result);
end;
function TDDDSocketThreadSettings.SetHostPort(
const IpPort: RawByteString; defaultPort: integer): boolean;
var p: RawUTF8;
begin
Split(IpPort,':',fHost,p);
result := false;
if trim(fHost) = '' then
exit;
fPort := GetIntegerDef(pointer(p), defaultPort);
result := fPort > 0;
end;
{ TDDDServicesLogRestSettings }
function TDDDServicesLogRestSettings.NewRestInstance(
aRootSettings: TDDDAppSettingsAbstract; aMainRestWithServices: TSQLRestServer;
const aLogClass: array of TSQLRecordServiceLogClass;
const aExcludedMethodNamesCSV: RawUTF8; aShardRange: TID): TSQLRest;
var classes: TSQLRecordClassDynArray;
server: TSQLRestServer;
fn: TFileName;
i: integer;
begin
if length(aLogClass)=0 then begin
SetLength(classes,1);
classes[0] := TSQLRecordServiceLog;
end else begin
SetLength(classes,length(aLogClass));
for i := 0 to high(aLogClass) do
classes[i] := aLogClass[i];
end;
if (fShardDBCount > 0) and (aShardRange > 100) and (length(classes)=1) then
{$WARNINGS OFF} // methods are pure abstract, but fine with a single class
result := TSQLRestServer.CreateWithOwnModel(classes,false,fRoot) else
{$WARNINGS ON}
result := inherited NewRestInstance(aRootSettings,TSQLModel.Create(classes),
[riOwnModel,riDefaultLocalSQlite3IfNone,riCreateMissingTables]);
if result=nil then
exit;
if (fShardDBCount > 0) and (aShardRange > 100) then begin
server := result as TSQLRestServer;
fn := IncludeTrailingPathDelimiter(fDefaultDataFolder)+TFileName(fDefaultDataFileName);
if not server.StaticDataAdd(TSQLRestStorageShardDB.Create(
classes[0], server, aShardRange, [], fn, fShardDBCount)) then
raise EDDDInfraException.CreateUTF8('%.NewRestInstance(%) StaticDataAdd(%)=false',
[self,fRoot,classes[0]]);
end else
if result.InheritsFrom(TSQLRestServerDB) then
TSQLRestServerDB(result).DB.UseCache := false;
// set the first supplied class type to log services
if (aMainRestWithServices <> nil) and classes[0].InheritsFrom(TSQLRecordServiceLog) then
(aMainRestWithServices.ServiceContainer as TServiceContainerServer).
SetServiceLog(result,TSQLRecordServiceLogClass(classes[0]),aExcludedMethodNamesCSV);
end;
{ TDDDAppSettingsStorageAbstract }
constructor TDDDAppSettingsStorageAbstract.Create(
const aInitialJSON: RawUTF8);
begin
inherited Create;
if aInitialJSON='' then
exit;
fInitialJsonContent := aInitialJSON;
end;
function TDDDAppSettingsStorageAbstract.SetOwner(
aOwner: TDDDAppSettingsAbstract): boolean;
begin
if self=nil then
result := false
else begin
fOwner := aOwner;
result := JSONSettingsToObject(fInitialJsonContent, fOwner);
end;
end;
procedure TDDDAppSettingsStorageAbstract.Store(const aJSON: RawUTF8);
begin
if aJSON=fInitialJsonContent then
exit;
fInitialJsonContent := aJSON;
InternalStore;
end;
{ TDDDAppSettingsStorageFile }
constructor TDDDAppSettingsStorageFile.Create(const aSettingsJsonFileName: TFileName);
var content: RawUTF8;
begin
if aSettingsJsonFileName<>'' then
fSettingsJsonFileName := aSettingsJsonFileName else
fSettingsJsonFileName := ChangeFileExt(ExeVersion.ProgramFileName,'.settings');
fSettingsJsonFileName := ExpandFileName(fSettingsJsonFileName);
content := AnyTextFileToRawUTF8(fSettingsJsonFileName,true);
inherited Create(content);
end;
function TDDDAppSettingsStorageFile.FileNameRelativeToSettingsFile(
const aFileName: TFileName): TFileName;
var path,settings: TFileName;
begin
path := ExtractFilePath(ExpandFileName(aFileName));
settings := ExtractFilePath(ExpandFileName(SettingsJsonFileName));
result := ExtractRelativePath(settings,path)+ExtractFileName(aFileName);
end;
procedure TDDDAppSettingsStorageFile.InternalStore;
begin
FileFromString(fInitialJsonContent,fSettingsJsonFileName);
end;
{ TDDDMongoDBRestSettings }
procedure TDDDMongoDBRestSettings.SetDefaults(const Root, MongoServerAddress,
MongoDatabase, MongoUser, MongoPassword: RawUTF8; TLS: boolean);
begin
if fORM.Kind<>'' then
exit;
fRoot := Root;
if TLS then
fORM.Kind := 'MongoDBS' else
fORM.Kind := 'MongoDB';
fORM.ServerName := MongoServerAddress;
fORM.DatabaseName := MongoDatabase;
fORM.User := MongoUser;
fORM.PasswordPlain := MongoPassword;
end;
{ TDDDEmailerSettings }
constructor TDDDEmailerSettings.Create;
begin
inherited Create;
fSMTP := SMTP_DEFAULT;
end;
initialization
TSynPersistentWithPasswordUserCrypt := CryptDataForCurrentUser;
end.