diff --git a/source/calendarwebapp/authenticator.d b/source/calendarwebapp/authenticator.d index 0322398..056aef9 100644 --- a/source/calendarwebapp/authenticator.d +++ b/source/calendarwebapp/authenticator.d @@ -90,6 +90,7 @@ private: @Autowire MySQLPool pool; @Autowire PasswordHasher passwordHasher; + @Value("mysql.table.users") string usersTableName; public: Nullable!AuthInfo checkUser(string username, string password) @trusted @@ -98,7 +99,8 @@ public: scope (exit) cn.close(); auto prepared = cn.prepare( - "SELECT id, username, passwordHash, privilege FROM users WHERE username = ?"); + "SELECT id, username, passwordHash, privilege FROM " + ~ usersTableName ~ " WHERE username = ?"); prepared.setArg(0, username); auto result = prepared.query(); /* checkHash should be called using vibe.core.concurrency.async to @@ -121,7 +123,7 @@ public: scope (exit) cn.close; auto prepared = cn.prepare( - "INSERT INTO users (username, passwordHash, privilege) VALUES(?, ?, ?)"); + "INSERT INTO " ~ usersTableName ~ " (username, passwordHash, privilege) VALUES(?, ?, ?)"); prepared.setArgs(authInfo.username, authInfo.passwordHash, authInfo.privilege.to!uint); prepared.exec(); } @@ -134,7 +136,7 @@ public: auto cn = pool.lockConnection(); scope (exit) cn.close; - auto prepared = cn.prepare("SELECT id, username, passwordHash, privilege FROM users"); + auto prepared = cn.prepare("SELECT id, username, passwordHash, privilege FROM " ~ usersTableName ~ ""); return prepared.querySet.map!(r => toAuthInfo(r)).inputRangeObject; } @@ -143,7 +145,7 @@ public: auto cn = pool.lockConnection(); scope (exit) cn.close; - auto prepared = cn.prepare("DELETE FROM users WHERE id = ?"); + auto prepared = cn.prepare("DELETE FROM " ~ usersTableName ~ " WHERE id = ?"); prepared.setArg(0, id.to!uint); prepared.exec(); } diff --git a/source/calendarwebapp/configuration.d b/source/calendarwebapp/configuration.d index 1ef363d..aa3f530 100644 --- a/source/calendarwebapp/configuration.d +++ b/source/calendarwebapp/configuration.d @@ -1,34 +1,53 @@ module calendarwebapp.configuration; -import calendarwebapp.authenticator : Authenticator, MongoDBAuthenticator, - MySQLAuthenticator; +import calendarwebapp.authenticator : Authenticator; import calendarwebapp.calendarwebapp : CalendarWebapp; -import calendarwebapp.event : EventStore, MongoDBEventStore, MySQLEventStore; +import calendarwebapp.event : EventStore; import calendarwebapp.passhash : PasswordHasher, SHA256PasswordHasher; -import mysql : MySQLPool; - import poodinis; -import vibe.db.mongo.client : MongoClient; +import vibe.core.log : logInfo; import vibe.db.mongo.collection : MongoCollection; -import vibe.db.mongo.mongo : connectMongoDB; class Context : ApplicationContext { public: override void registerDependencies(shared(DependencyContainer) container) { - auto mongoClient = connectMongoDB("localhost"); - auto pool = new MySQLPool("localhost", "username", "password", "CalendarWebapp"); - container.register!MySQLPool.existingInstance(pool); - container.register!MongoClient.existingInstance(mongoClient); - container.register!(EventStore, MySQLEventStore); - container.register!(Authenticator, MySQLAuthenticator); + container.register!(ValueInjector!Arguments, AppArgumentsInjector); + auto arguments = container.resolve!(AppArgumentsInjector).get(""); + final switch (arguments.database) with (DatabaseArgument) + { + case mongodb: + import vibe.db.mongo.client : MongoClient; + import vibe.db.mongo.mongo : connectMongoDB; + import calendarwebapp.authenticator : MongoDBAuthenticator; + import calendarwebapp.event : MongoDBEventStore; + + auto mongoClient = connectMongoDB(arguments.mongodb.host); + container.register!MongoClient.existingInstance(mongoClient); + container.register!(EventStore, MongoDBEventStore!()); + container.register!(Authenticator, MongoDBAuthenticator!()); + container.register!(ValueInjector!MongoCollection, MongoCollectionInjector); + logInfo("Using MongoDB as database system"); + break; + case mysql: + import mysql : MySQLPool; + import calendarwebapp.authenticator : MySQLAuthenticator; + import calendarwebapp.event : MySQLEventStore; + + auto pool = new MySQLPool(arguments.mysql.host, arguments.mysql.username, + arguments.mysql.password, arguments.mysql.database); + container.register!MySQLPool.existingInstance(pool); + container.register!(EventStore, MySQLEventStore); + container.register!(Authenticator, MySQLAuthenticator); + logInfo("Using MySQL as database system"); + break; + } container.register!(PasswordHasher, SHA256PasswordHasher); container.register!CalendarWebapp; container.register!(ValueInjector!string, StringInjector); - container.register!(ValueInjector!MongoCollection, MongoCollectionInjector); } } @@ -36,19 +55,18 @@ class StringInjector : ValueInjector!string { private: string[string] config; + @Value() Arguments arguments; + bool initialized = false; public: - this() const @safe pure nothrow - { - // dfmt off - config = ["Database name" : "CalendarWebapp", - "Users collection name": "users", - "Events collection name" : "events"]; - // dfmt on - } - override string get(string key) const @safe pure nothrow + override string get(string key) @safe nothrow { + if (!initialized) + { + config = ["MongoDB database name" : arguments.mongodb.database, + "mysql.table.users" : "users", "mysql.table.events" : "events"]; + } return config[key]; } } @@ -56,8 +74,10 @@ public: class MongoCollectionInjector : ValueInjector!MongoCollection { private: + import vibe.db.mongo.client : MongoClient; + @Autowire MongoClient mongoClient; - @Value("Database name") + @Value("MongoDB database name") string databaseName; public: @@ -66,3 +86,63 @@ public: return mongoClient.getCollection(databaseName ~ "." ~ key); } } + +class AppArgumentsInjector : ValueInjector!Arguments +{ +private: + Arguments arguments; +public: + + this() + { + import vibe.core.args : readOption; + + readOption("database", &arguments.database, "The database system to use."); + readOption("mongodb.host", &arguments.mongodb.host, + "The host of the MongoDB instance to use."); + readOption("mongodb.database", &arguments.mongodb.database, + "The name of the MongoDB database to use."); + readOption("mysql.host", &arguments.mysql.host, "The host of the MySQL instance to use."); + readOption("mysql.username", &arguments.mysql.username, + "The username to use for logging into the MySQL instance."); + readOption("mysql.password", &arguments.mysql.password, + "The password to use for logging into the MySQL instance."); + readOption("mysql.database", &arguments.mysql.database, + "The name of the MySQL database to use."); + } + + override Arguments get(string key) @safe + { + import std.exception : enforce; + + enforce(key == "", "There is only one instance of Arguments, to inject it use @Value()."); + return arguments; + } +} + +enum DatabaseArgument +{ + mongodb, + mysql +} + +struct MySQLArguments +{ + string host = "localhost"; + string username = "username"; + string password = "password"; + string database = "CalendarWebapp"; +} + +struct MongoDBArguments +{ + string host = "localhost"; + string database = "CalendarWebapp"; +} + +struct Arguments +{ + DatabaseArgument database = DatabaseArgument.mongodb; + MySQLArguments mysql; + MongoDBArguments mongodb; +} diff --git a/source/calendarwebapp/event.d b/source/calendarwebapp/event.d index bde695d..587c89a 100644 --- a/source/calendarwebapp/event.d +++ b/source/calendarwebapp/event.d @@ -73,6 +73,8 @@ class MySQLEventStore : EventStore private: import mysql; + @Value("mysql.table.events") string eventsTableName; + public: Event getEvent(string id) { @@ -80,7 +82,7 @@ public: scope (exit) cn.close; auto prepared = cn.prepare( - "SELECT id begin end name description type shout FROM events WHERE id = ?"); + "SELECT id begin end name description type shout FROM " ~ eventsTableName ~ " WHERE id = ?"); prepared.setArg(0, id.to!uint); return toEvent(prepared.query.front); } @@ -91,7 +93,7 @@ public: scope (exit) cn.close; auto prepared = cn.prepare( - "SELECT id, begin, end, name, description, type, shout FROM events"); + "SELECT id, begin, end, name, description, type, shout FROM " ~ eventsTableName ~ ""); return prepared.querySet.map!(r => toEvent(r)).inputRangeObject; } @@ -101,7 +103,7 @@ public: scope (exit) cn.close; auto prepared = cn.prepare( - "INSERT INTO events (begin, end, name, description, type, shout) VALUES(?, ?, ?, ?, ?, ?)"); + "INSERT INTO " ~ eventsTableName ~ " (begin, end, name, description, type, shout) VALUES(?, ?, ?, ?, ?, ?)"); prepared.setArgs(event.begin, event.end, event.name, event.description, event.type.to!uint, event.shout); prepared.exec(); @@ -119,7 +121,7 @@ public: auto cn = pool.lockConnection(); scope (exit) cn.close; - auto prepared = cn.prepare("DELETE FROM events WHERE id = ?"); + auto prepared = cn.prepare("DELETE FROM " ~ eventsTableName ~ " WHERE id = ?"); prepared.setArg(0, id.to!uint); prepared.exec(); }