diff --git a/src/scorpion/bootstrap.d b/src/scorpion/bootstrap.d new file mode 100644 index 0000000..573265f --- /dev/null +++ b/src/scorpion/bootstrap.d @@ -0,0 +1,394 @@ +module scorpion.bootstrap; + +import std.conv : to; +import std.exception : enforce; +import std.experimental.logger : sharedLog, LogLevel, info; +import std.regex : Regex; +import std.string : split, join; +import std.traits : hasUDA, getUDAs, isFunction, Parameters, ParameterIdentifierTuple; + +import lighttp.resource : Resource; +import lighttp.router : Router, routeInfo; +import lighttp.server : ServerOptions, Server; +import lighttp.util : StatusCodes, ServerRequest, ServerResponse; + +import scorpion.component : Component, Init, Value; +import scorpion.config : Config, Configuration, LanguageConfiguration, ProfilesConfiguration; +import scorpion.context : Context; +import scorpion.controller : Controller, Route, Path, Param, Body; +import scorpion.entity : Entity, ExtendEntity; +import scorpion.lang : LanguageManager; +import scorpion.profile : Profile; +import scorpion.repository : Repository, DatabaseRepository; +import scorpion.session : Session; +import scorpion.validation : Validation, validateParam, validateBody; +import scorpion.view : View; + +import shark : Database, MysqlDatabase, PostgresqlDatabase; + +/** + * Stores instructions on how to build controllers, components + * and entities. + * This object is only used internally by scorpion for + * initialization and bootstrapping of the server. + */ +final class ScorpionServer { + + /** + * Instance of the language manager. During the bootstrapping + * of the server language files are given to the language manager + * which converts them in key-value pairs. + * The language manager is the only object in the register class + * that is used after the server initialization. + */ + private LanguageManager languageManager; + + /** + * Contains instances of the `ProfilesConfiguration` configuration. + */ + private ProfilesConfiguration[] profilesConfigurations; + + /** + * Contains instructions on how to build entities. + */ + private EntityInfo[] entities; + + /** + * Contains informations about components and instructions on how + * to build a new one. + */ + private ComponentInfo[] components; + + /** + * Contains informations about services and instructions on how + * to build a new one. + */ + private ServiceInfo[] services; + + /** + * Contains informations about controllers and a function to initialize + * routes. + */ + private ControllerInfo[] controllers; + + /** + * Scans a module for controllers, components and entities and + * adds the instructions for initialization in the register. + */ + public void registerModule(alias module_)() { + foreach(immutable member ; __traits(allMembers, module_)) { + static if(__traits(getProtection, __traits(getMember, module_, member)) == "public") { + alias T = __traits(getMember, module_, member); + static if(hasUDA!(T, Configuration)) { + T configuration = new T(); + static if(is(T : LanguageConfiguration)) { + foreach(lang, data; configuration.loadLanguages()) { + languageManager.add(lang, data); + } + } + static if(is(T : ProfilesConfiguration)) { + profilesConfigurations ~= configuration; + } + } + static if(hasUDA!(T, Entity)) { + entities ~= new EntityInfoImpl!(ExtendEntity!(T, getUDAs!(T, Entity)[0].name))(Profile.get(getUDAs!(T, Profile))); + } + static if(hasUDA!(T, Component)) { + //static if(is(T : Repository!R, R)) services ~= new ServiceInfoImpl!(DatabaseRepository!T)(); + static if(is(T : Repository!R, R)) components ~= new ComponentInfoImpl!(DatabaseRepository!T, true)(Profile.get(getUDAs!(T, Profile))); + else components ~= new ComponentInfoImpl!(T, false)(Profile.get(getUDAs!(T, Profile))); + } + static if(hasUDA!(T, Controller)) { + controllers ~= new ControllerInfoImpl!(T)(Profile.get(getUDAs!(T, Profile))); + } + } + } + } + + /** + * Starts the server by reading the configuration files and + * runs it. This function starts the event loop snd rever returns. + */ + public void run(string[] args) { + + Config config = Config.load(); + + //TODO override configurations using args + + // sets the log level reading it from `scorpion.log` in the configuration files + sharedLog.logLevel = { + switch(config.get("scorpion.log", "info")) { + case "all": return LogLevel.all; + case "trace": return LogLevel.trace; + case "info": return LogLevel.info; + case "warning": return LogLevel.warning; + case "error": return LogLevel.error; + case "critical": return LogLevel.critical; + case "fatal": return LogLevel.fatal; + case "off": return LogLevel.off; + default: throw new Exception("Invalid value for scorpion.log"); + } + }(); + + immutable ip = config.get!string("scorpion.ip", "0.0.0.0"); + immutable port = config.get!ushort("scorpion.port", 80); + + info("Starting server on ", ip, ":", port); + + // initialize the database using values from `scorpion.database.*` + Database database; + immutable type = config.get("scorpion.database.driver", string.init); + if(type !is null) { + Database getDatabase() { + switch(type) { + case "mysql": return new MysqlDatabase(config.get("scorpion.database.host", "localhost"), config.get("scorpion.database.port", ushort(3306))); + case "postgresql": return new PostgresqlDatabase(config.get("scorpion.database.host", "localhost"), config.get("scorpion.database.port", ushort(5432))); + default: throw new Exception("Cannot create a database of type '" ~ type ~ "'"); + } + } + database = getDatabase(); + database.connect(config.get("scorpion.database.name", string.init), config.get("scorpion.database.user", "root"), config.get("scorpion.database.password", "")); + } + + // creates the default options for the server + ServerOptions options; + options.name = "Scorpion/0.1"; + options.max = config.get("scorpion.upload.max", 2 ^^ 24); // 16 MB + + // creates the server, initializes it and starts the event loop + Server server = new Server(options); + init(server.router, config, database); + server.host(ip, port); + server.run(); + + } + + /** + * Initializes entities and controllers. + */ + private void init(Router router, Config config, Database database) { + Context context = new Context(config); + foreach(profilesConfiguration ; profilesConfigurations) { + config.addProfiles(profilesConfiguration.defaultProfiles()); + } + info("Active profiles: ", config.profiles.join(", ")); + void filter(T)(ref T[] array) { + T[] ret; + foreach(element ; array) { + auto info = cast(Info)element; + if(info.profiles.length == 0 || config.hasProfile(info.profiles)) ret ~= element; + } + array = ret; + } + filter(entities); + filter(components); + filter(controllers); + foreach(entityInfo ; entities) { + entityInfo.init(context, database); + } + foreach(controllerInfo ; controllers) { + controllerInfo.init(router, context, database); + } + } + + /** + * Tries to initialize a component and throws and exception + * on failure. + */ + private void initComponent(T)(ref T value, Database database) { + foreach(component ; components) { + T instance = cast(T)component.instance(database); + if(instance !is null) { + value = component.useCached ? instance : cast(T)component.newInstance(database); + return; + } + } + throw new Exception("Failed to initialize component of type " ~ T.stringof); + } + + private class Info { + + string[] profiles; + + this(string[] profiles) { + this.profiles = profiles; + } + + } + + private interface EntityInfo { + + void init(Context context, Database database); + + } + + private class EntityInfoImpl(T) : Info, EntityInfo { + + this(string[] profiles) { + super(profiles); + } + + override void init(Context context, Database database) { + enforce!Exception(database !is null, "A database connection is required"); + database.init!T(); + } + + } + + private interface ComponentInfo { + + @property bool useCached(); + + Object instance(Database); + + Object newInstance(Database); + + } + + private class ComponentInfoImpl(T, bool repository) : Info, ComponentInfo { + + private T cached; + + this(string[] profiles) { + super(profiles); + static if(!repository) cached = new T(); + } + + override bool useCached() { + return repository; + } + + override Object instance(Database database) { + static if(repository) if(cached is null) cached = new T(database); + return cached; + } + + override Object newInstance(Database database) { + static if(repository) T ret = new T(database); + else T ret = new T(); + initComponent(ret, database); + return ret; + } + + } + + private interface ControllerInfo { + + void init(Router router, Context context, Database); + + } + + private class ControllerInfoImpl(T) : Info, ControllerInfo { + + this(string[] profiles) { + super(profiles); + } + + override void init(Router router, Context context, Database database) { + T controller = new T(); + static if(!__traits(compiles, getUDAs!(T, Controller)[0]())) enum controllerPath = getUDAs!(T, Controller)[0].path; + foreach(immutable member ; __traits(allMembers, T)) { + static if(__traits(getProtection, __traits(getMember, T, member)) == "public") { + immutable full = "controller." ~ member; + alias F = __traits(getMember, T, member); + enum tests = { + string[] ret; + foreach(i, immutable uda; __traits(getAttributes, F)) { + static if(is(typeof(__traits(getMember, uda, "test")) == function)) { + static if(__traits(compiles, uda())) ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "].init"; + else ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "]"; + } + } + return ret; + }(); + // weird bug on DMD 2.084: without the static foreach the compiler + // says that variable `uda` cannot be read at compile time. + static foreach(immutable uda ; __traits(getAttributes, F)) { + static if(is(typeof(uda) == Route) || is(typeof(uda()) == Route)) { + static if(is(typeof(controllerPath))) enum path = controllerPath ~ uda.path; + else enum path = uda.path; + enum regexPath = path.join(`\/`); + static if(isFunction!F) { + auto fun = mixin(generateFunction!F(T.stringof, member, regexPath, tests)); + } else { + static assert(is(typeof(F) : Resource), "Member annotated with @Route must be callable or an instance of Resource"); + auto fun = delegate(ServerRequest request, ServerResponse response){ + context.refresh(request, response); + static foreach(test ; tests) { + if(!mixin(test).test(context)) return; + } + response.headers["X-Scorpion-Controller"] = T.stringof ~ "." ~ member; + response.headers["X-Scorpion-Path"] = regexPath; + mixin(full).apply(request, response); + }; + } + router.add(routeInfo(uda.method, uda.hasBody, regexPath), fun); + info("Routing ", uda.method, " /", path.join("/"), " to ", T.stringof, ".", member, (isFunction!F ? "()" : "")); + } + } + static if(hasUDA!(F, Init)) { + initComponent(mixin(full), database); + } + static if(hasUDA!(F, Value)) { + mixin(full) = context.config.get(getUDAs!(F, Value)[0].key, mixin(full)); + } + } + } + } + + } + +} + +private string generateFunction(alias M)(string controller, string member, string path, string[] tests) { + string[] ret = ["ServerRequest request", "ServerResponse response"]; + string body1 = "context.refresh(request,response);response.headers[`X-Scorpion-Controller`]=`" ~ controller ~ "." ~ member ~ "`;response.headers[`X-Scorpion-Path`]=`" ~ path ~ "`;"; + string body2, body3; + string[Parameters!M.length] call; + bool validation = false; + foreach(test ; tests) { + body1 ~= "if(!" ~ test ~ ".test(context)){return;}"; + } + body1 ~= "response.status=StatusCodes.ok;Validation validation=new Validation();"; + foreach(i, param; Parameters!M) { + static if(is(param == ServerRequest)) call[i] = "request"; + else static if(is(param == ServerResponse)) call[i] = "response"; + else static if(is(param == View)) { + body2 ~= "View view=View(request,response,languageManager);"; + call[i] = "view"; + } else static if(is(param == Session)) { + body2 ~= "Session session=Session.get(request);"; + call[i] = "session"; + } else static if(is(param == Validation)) { + call[i] = "validation"; + validation = true; + } else static if(is(typeof(M) Params == __parameters)) { + immutable p = "Parameters!F[" ~ i.to!string ~ "] " ~ member ~ i.to!string; + call[i] = member ~ i.to!string; + foreach(attr ; __traits(getAttributes, Params[i..i+1])) { + static if(is(attr == Path)) { + ret ~= p; + } else static if(is(attr == Param) || is(typeof(attr) == Param)) { + static if(is(attr == Param)) enum name = ParameterIdentifierTuple!M[i]; + else enum name = attr.param; + body1 ~= p ~ "=validateParam!(Parameters!F[" ~ i.to!string ~ "])(\"" ~ name ~ "\",request,response);"; + body1 ~= "if(response.status.code==400){return;}"; + } else static if(is(attr == Body)) { + body1 ~= p ~ "=validateBody!(Parameters!F[" ~ i.to!string ~ "])(request,response,validation);"; + } + } + } + } + if(validation) body2 ~= "validation.apply(response);if(response.status.code==400){return;}"; + else body3 = "validation.apply(response);"; + return "delegate(" ~ ret.join(",") ~ "){" ~ body1 ~ body2 ~ "controller." ~ member ~ "(" ~ join(cast(string[])call, ",") ~ ");" ~ body3 ~ "}"; +} + +unittest { + + static import scorpion.welcome; + + ScorpionServer server = new ScorpionServer(); + server.registerModule!(scorpion.welcome); + +} diff --git a/src/scorpion/context.d b/src/scorpion/context.d index 5472a9d..937cc01 100644 --- a/src/scorpion/context.d +++ b/src/scorpion/context.d @@ -5,7 +5,10 @@ import lighttp : ServerRequest, ServerResponse; import scorpion.config : Config; import scorpion.session : SessionManager, Session; -class Context { +/** + * Represents the context of the application in the current thread. + */ +final class Context { private Config _config; @@ -17,26 +20,42 @@ class Context { this(Config config) { _config = config; + _sessionManager = new SessionManager(config.get("scorpion.session.cookie", "DSESSIONID")); } + /** + * Updates the context using the data from the next request. + */ void refresh(ServerRequest request, ServerResponse response) { _session = null; _request = request; _response = response; } + /** + * Gets the server's configuration. + */ @property Config config() { return _config; } + /** + * Gets the current request. + */ @property ServerRequest request() { return _request; } + /** + * Gets the current response. + */ @property ServerResponse response() { return _response; } + /** + * Gets the current session, lazily initialized. + */ @property Session session() { if(_session is null) _session = _sessionManager.get(request); return _session; diff --git a/src/scorpion/register.d b/src/scorpion/register.d deleted file mode 100644 index fe1adaa..0000000 --- a/src/scorpion/register.d +++ /dev/null @@ -1,284 +0,0 @@ -module scorpion.register; - -import std.conv : to; -import std.exception : enforce; -import std.experimental.logger : info; -import std.regex : Regex; -import std.string : split, join; -import std.traits : hasUDA, getUDAs, isFunction, Parameters, ParameterIdentifierTuple; - -import lighttp.resource : Resource; -import lighttp.router : Router, routeInfo; -import lighttp.util : StatusCodes, ServerRequest, ServerResponse; - -import scorpion.component : Component, Init, Value; -import scorpion.config : Config, Configuration, LanguageConfiguration, ProfilesConfiguration; -import scorpion.context : Context; -import scorpion.controller : Controller, Route, Path, Param, Body; -import scorpion.entity : Entity, ExtendEntity; -import scorpion.lang : LanguageManager; -import scorpion.profile : Profile; -import scorpion.repository : Repository, DatabaseRepository; -import scorpion.session : Session; -import scorpion.validation : Validation, validateParam, validateBody; -import scorpion.view : View; - -import shark : Database; - -private LanguageManager languageManager; - -private ProfilesConfiguration[] profilesConfigurations; - -private EntityInfo[] entities; - -private ComponentInfo[] components; - -private ServiceInfo[] services; - -private ControllerInfo[] controllers; - -private class Info { - - string[] profiles; - - this(string[] profiles) { - this.profiles = profiles; - } - -} - -private interface EntityInfo { - - void init(Context context, Database database); - -} - -private class EntityInfoImpl(T) : Info, EntityInfo { - - this(string[] profiles) { - super(profiles); - } - - override void init(Context context, Database database) { - if(profiles.length == 0 || context.config.hasProfile(profiles)) { - enforce!Exception(database !is null, "A database connection is required"); - database.init!T(); - } - } - -} - -private interface ComponentInfo { - - Object instance(); - - Object newInstance(Database); - -} - -private class ComponentInfoImpl(T) : ComponentInfo { - - private T cached; - - static this() { - cached = new T(); - } - - override Object instance() { - return cached; - } - - override Object newInstance(Database database) { - T ret = new T(); - initComponent(ret, database); - return ret; - } - -} - -private interface ServiceInfo { - - Object instance(Database); - -} - -private class ServiceInfoImpl(T) : ServiceInfo { - - private T cached; - - override Object instance(Database database) { - if(cached is null) cached = new T(database); - return cached; - } - -} - -private interface ControllerInfo { - - void init(Router router, Context context, Database); - -} - -private class ControllerInfoImpl(T) : Info, ControllerInfo { - - this(string[] profiles) { - super(profiles); - } - - override void init(Router router, Context context, Database database) { - if(profiles.length == 0 || context.config.hasProfile(profiles)) { - T controller = new T(); - static if(!__traits(compiles, getUDAs!(T, Controller)[0]())) enum controllerPath = getUDAs!(T, Controller)[0].path; - foreach(immutable member ; __traits(allMembers, T)) { - static if(__traits(getProtection, __traits(getMember, T, member)) == "public") { - immutable full = "controller." ~ member; - alias F = __traits(getMember, T, member); - enum tests = { - string[] ret; - foreach(i, immutable uda; __traits(getAttributes, F)) { - static if(is(typeof(__traits(getMember, uda, "test")) == function)) { - static if(__traits(compiles, uda())) ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "].init"; - else ret ~= "__traits(getAttributes, F)[" ~ i.to!string ~ "]"; - } - } - return ret; - }(); - // weird bug on DMD 2.084: without the static foreach the compiler - // says that variable `uda` cannot be read at compile time. - static foreach(immutable uda ; __traits(getAttributes, F)) { - static if(is(typeof(uda) == Route) || is(typeof(uda()) == Route)) { - static if(is(typeof(controllerPath))) enum path = controllerPath ~ uda.path; - else enum path = uda.path; - enum regexPath = path.join(`\/`); - static if(isFunction!F) { - auto fun = mixin(generateFunction!F(T.stringof, member, regexPath, tests)); - } else { - static assert(is(typeof(F) : Resource), "Member annotated with @Route must be an instance of Resource"); - auto fun = delegate(ServerRequest request, ServerResponse response){ - context.refresh(request, response); - static foreach(test ; tests) { - if(!mixin(test).test(context)) return; - } - response.headers["X-Scorpion-Controller"] = T.stringof ~ "." ~ member; - response.headers["X-Scorpion-Path"] = regexPath; - mixin(full).apply(request, response); - }; - } - router.add(routeInfo(uda.method, uda.hasBody, regexPath), fun); - info("Routing ", uda.method, " /", path.join("/"), " to ", T.stringof, ".", member, (isFunction!F ? "()" : "")); - } - } - static if(hasUDA!(F, Init)) { - initComponent(mixin(full), database); - } - static if(hasUDA!(F, Value)) { - mixin(full) = context.config.get(getUDAs!(F, Value)[0].key, mixin(full)); - } - } - } - } - } - -} - -private string generateFunction(alias M)(string controller, string member, string path, string[] tests) { - string[] ret = ["ServerRequest request", "ServerResponse response"]; - string body1 = "context.refresh(request,response);response.headers[`X-Scorpion-Controller`]=`" ~ controller ~ "." ~ member ~ "`;response.headers[`X-Scorpion-Path`]=`" ~ path ~ "`;"; - string body2, body3; - string[Parameters!M.length] call; - bool validation = false; - foreach(test ; tests) { - body1 ~= "if(!" ~ test ~ ".test(context)){return;}"; - } - body1 ~= "response.status=StatusCodes.ok;Validation validation=new Validation();"; - foreach(i, param; Parameters!M) { - static if(is(param == ServerRequest)) call[i] = "request"; - else static if(is(param == ServerResponse)) call[i] = "response"; - else static if(is(param == View)) { - body2 ~= "View view=View(request,response,languageManager);"; - call[i] = "view"; - } else static if(is(param == Session)) { - body2 ~= "Session session=Session.get(request);"; - call[i] = "session"; - } else static if(is(param == Validation)) { - call[i] = "validation"; - validation = true; - } else static if(is(typeof(M) Params == __parameters)) { - immutable p = "Parameters!F[" ~ i.to!string ~ "] " ~ member ~ i.to!string; - call[i] = member ~ i.to!string; - foreach(attr ; __traits(getAttributes, Params[i..i+1])) { - static if(is(attr == Path)) { - ret ~= p; - } else static if(is(attr == Param) || is(typeof(attr) == Param)) { - static if(is(attr == Param)) enum name = ParameterIdentifierTuple!M[i]; - else enum name = attr.param; - body1 ~= p ~ "=validateParam!(Parameters!F[" ~ i.to!string ~ "])(\"" ~ name ~ "\",request,response);"; - body1 ~= "if(response.status.code==400){return;}"; - } else static if(is(attr == Body)) { - body1 ~= p ~ "=validateBody!(Parameters!F[" ~ i.to!string ~ "])(request,response,validation);"; - } - } - } - } - if(validation) body2 ~= "validation.apply(response);if(response.status.code==400){return;}"; - else body3 = "validation.apply(response);"; - return "delegate(" ~ ret.join(",") ~ "){" ~ body1 ~ body2 ~ "controller." ~ member ~ "(" ~ join(cast(string[])call, ",") ~ ");" ~ body3 ~ "}"; -} - -private void initComponent(T)(ref T value, Database database) { - foreach(component ; components) { - if(cast(T)component.instance) { - value = cast(T)component.newInstance(database); - return; - } - } - foreach(service ; services) { - if(cast(T)service.instance(database)) { - value = cast(T)service.instance(database); - return; - } - } -} - -void init(Router router, Config config, Database database) { - Context context = new Context(config); - foreach(profilesConfiguration ; profilesConfigurations) { - config.addProfiles(profilesConfiguration.defaultProfiles()); - } - info("Active profiles: ", config.profiles.join(", ")); - foreach(entityInfo ; entities) { - entityInfo.init(context, database); - } - foreach(controllerInfo ; controllers) { - controllerInfo.init(router, context, database); - } -} - -void registerModule(alias module_)() { - foreach(immutable member ; __traits(allMembers, module_)) { - static if(__traits(getProtection, __traits(getMember, module_, member)) == "public") { - alias T = __traits(getMember, module_, member); - static if(hasUDA!(T, Configuration)) { - T configuration = new T(); - static if(is(T : LanguageConfiguration)) { - foreach(lang, data; configuration.loadLanguages()) { - languageManager.add(lang, data); - } - } - static if(is(T : ProfilesConfiguration)) { - profilesConfigurations ~= configuration; - } - } - static if(hasUDA!(T, Entity)) { - entities ~= new EntityInfoImpl!(ExtendEntity!(T, getUDAs!(T, Entity)[0].name))(Profile.get(getUDAs!(T, Profile))); - } - static if(hasUDA!(T, Component)) { - static if(is(T : Repository!R, R)) services ~= new ServiceInfoImpl!(DatabaseRepository!T)(); - else components ~= new ComponentInfoImpl!(T)(); - } - static if(hasUDA!(T, Controller)) { - controllers ~= new ControllerInfoImpl!(T)(Profile.get(getUDAs!(T, Profile))); - } - } - } -} diff --git a/src/scorpion/session.d b/src/scorpion/session.d index 8829c4a..2129db0 100644 --- a/src/scorpion/session.d +++ b/src/scorpion/session.d @@ -8,38 +8,41 @@ import lighttp : ServerRequest, ServerResponse, StatusCodes, Cookie; import scorpion.context : Context; -private enum cookieName = "__scorpion_session_id"; +final class SessionManager { -private Session[UUID] _sessions; - -class SessionManager { + public immutable string cookieName; private Session[UUID] _sessions; + public this(string cookieName) { + this.cookieName = cookieName; + } + + public void add(Session session, UUID uuid) { + _sessions[uuid] = session; + } + public Session get(ServerRequest request) { if(auto cookie = cookieName in request.cookies) { try { if(auto ret = parseUUID(idup(*cookie)) in _sessions) return *ret; } catch(UUIDParsingException) {} } - return new Session(); + return new Session(this); } } -class Session { +final class Session { - public static Session get(ServerRequest request) { - if(auto cookie = cookieName in request.cookies) { - try { - if(auto ret = parseUUID(idup(*cookie)) in _sessions) return *ret; - } catch(UUIDParsingException) {} - } - return new Session(); - } + private SessionManager _sessionManager; private Authentication _authentication; + public this(SessionManager sessionManager) { + _sessionManager = sessionManager; + } + public @property bool loggedIn() { return _authentication !is null; } @@ -50,12 +53,12 @@ class Session { public void login(ServerResponse response, Authentication authentication) { UUID uuid = randomUUID(); - Cookie cookie = Cookie(cookieName, uuid.toString()); + Cookie cookie = Cookie(_sessionManager.cookieName, uuid.toString()); cookie.maxAge = 3600; // 1 hour cookie.path = "/"; cookie.httpOnly = true; response.add(cookie); - _sessions[uuid] = this; + _sessionManager.add(this, uuid); _authentication = authentication; } diff --git a/src/scorpion/starter.d b/src/scorpion/starter.d deleted file mode 100644 index 724dea8..0000000 --- a/src/scorpion/starter.d +++ /dev/null @@ -1,64 +0,0 @@ -module scorpion.starter; - -import std.ascii : newline; -import std.experimental.logger : sharedLog, LogLevel, info; -import std.file : exists, write; -import std.string : join; - -import lighttp; - -import scorpion.config : Config; -import scorpion.register : init; - -import shark : Database, MysqlDatabase, PostgresqlDatabase; - -void start(string[] args) { - - if(!exists(".gitignore")) write(".gitignore", join([".dub", ".scorpion", "*.selections.json", "*.dll", "*.exe", "*.lib", "*.a"], newline)); - - Config config = Config.load(); - - sharedLog.logLevel = { - switch(config.get("scorpion.log", "info")) { - case "all": return LogLevel.all; - case "trace": return LogLevel.trace; - case "info": return LogLevel.info; - case "warning": return LogLevel.warning; - case "error": return LogLevel.error; - case "critical": return LogLevel.critical; - case "fatal": return LogLevel.fatal; - case "off": return LogLevel.off; - default: throw new Exception("Invalid value for scorpion.log"); - } - }(); - - immutable ip = config.get!string("scorpion.ip", "0.0.0.0"); - immutable port = config.get!ushort("scorpion.port", 80); - - info("Starting server on ", ip, ":", port); - - Database database; - immutable type = config.get("scorpion.database.driver", string.init); - if(type !is null) { - database = getDatabase(config, type); - database.connect(config.get("scorpion.database.name", string.init), config.get("scorpion.database.user", "root"), config.get("scorpion.database.password", "")); - } - - ServerOptions options; - options.name = "Scorpion/0.1"; - options.max = config.get("scorpion.upload.max", 2 ^^ 24); // 16 MB - - Server server = new Server(options); - init(server.router, config, database); - server.host(ip, port); - server.run(); - -} - -private Database getDatabase(Config config, string type) { - switch(type) { - case "mysql": return new MysqlDatabase(config.get("scorpion.database.host", "localhost"), config.get("scorpion.database.port", ushort(3306))); - case "postgresql": return new PostgresqlDatabase(config.get("scorpion.database.host", "localhost"), config.get("scorpion.database.port", ushort(5432))); - default: throw new Exception("Cannot create a database of type '" ~ type ~ "'"); - } -} diff --git a/src/scorpion/view.d b/src/scorpion/view.d index 5b67830..e45c38a 100644 --- a/src/scorpion/view.d +++ b/src/scorpion/view.d @@ -44,8 +44,6 @@ struct View { } -deprecated("use render instead") alias compile = render; - void render(string file, E...)(View view) { view.response.body_ = renderImpl!(file, E)(view); }