diff --git a/finnow-api/dub.selections.json b/finnow-api/dub.selections.json index 8195e31..3a3863c 100644 --- a/finnow-api/dub.selections.json +++ b/finnow-api/dub.selections.json @@ -8,7 +8,7 @@ "d2sqlite3": "1.0.0", "dxml": "0.4.5", "handy-http-data": "1.3.0", - "handy-http-handlers": "1.1.0", + "handy-http-handlers": "1.2.0", "handy-http-primitives": "1.8.1", "handy-http-starter": "1.6.0", "handy-http-transport": "1.10.0", diff --git a/finnow-api/source/api_mapping.d b/finnow-api/source/api_mapping.d index a817e1f..d638462 100644 --- a/finnow-api/source/api_mapping.d +++ b/finnow-api/source/api_mapping.d @@ -26,18 +26,12 @@ HttpRequestHandler mapApiHandlers(string webOrigin) { // Auth endpoints: import auth.api; - h.map(HttpMethod.POST, "/login", &postLogin); - h.map(HttpMethod.POST, "/register", &postRegister); - h.map(HttpMethod.GET, "/register/username-availability", &getUsernameAvailability); - - + import auth.api_public; + h.registerHandlers!(auth.api_public); // Authenticated endpoints: PathHandler a = new PathHandler(); - a.map(HttpMethod.GET, "/me", &getMyUser); - a.map(HttpMethod.DELETE, "/me", &deleteMyUser); - a.map(HttpMethod.GET, "/me/token", &getNewToken); - a.map(HttpMethod.POST, "/me/password", &changeMyPassword); + a.registerHandlers!(auth.api); import profile.api; a.map(HttpMethod.GET, "/profiles", &handleGetProfiles); diff --git a/finnow-api/source/app.d b/finnow-api/source/app.d index 3fcc63f..9bab971 100644 --- a/finnow-api/source/app.d +++ b/finnow-api/source/app.d @@ -10,8 +10,6 @@ import scheduled_jobs; * Starts the Finnow API. */ void main() { - // testUDA(); - const config = readConfig(); configureSlf4d(config); startScheduledJobs(); @@ -20,6 +18,7 @@ void main() { void configureSlf4d(in AppConfig config) { Level logLevel = getConfiguredLoggingLevel(config); + logLevel = Levels.DEBUG; auto provider = new DefaultProvider(logLevel); configureLoggingProvider(provider); } @@ -30,44 +29,3 @@ void startWebServer(in AppConfig config) { HttpTransport transport = new TaskPoolHttp1Transport(mapApiHandlers(config.webOrigin), transportConfig); transport.start(); } - -// void testUDA() { -// import std.traits : getSymbolsByUDA; -// import std.stdio; -// static assert(getSymbolsByUDA!(app, Attr).length == 2); -// Attr a; -// static foreach(symbol; getSymbolsByUDA!(app, Attr)) { -// pragma(msg, symbol); -// pragma(msg, __traits(identifier, symbol)); -// pragma(msg, "------------------"); -// static foreach(attr; __traits(getAttributes, symbol)) { -// pragma(msg, "Attribute:"); -// pragma(msg, attr); -// pragma(msg, __traits(identifier, attr)); -// static if (is(typeof(attr) == Attr)) { -// pragma(msg, "Found target attribute!"); -// pragma(msg, attr.val); -// a = attr; -// writefln!"Function %s has attr val = %d"((__traits(identifier, symbol)), a.val); -// } else { -// pragma(msg, "Other attribute :("); -// } -// } -// } -// } - -// struct Attr { -// int val; -// } - -// enum OtherAttr; - -// @Attr(5) @OtherAttr -// void testMethod1() { -// int x = 5; -// } - -// @Attr(42) -// void testMethod2() { -// int y = 5; -// } diff --git a/finnow-api/source/auth/api.d b/finnow-api/source/auth/api.d index f3e60fd..ef2685d 100644 --- a/finnow-api/source/auth/api.d +++ b/finnow-api/source/auth/api.d @@ -3,6 +3,7 @@ module auth.api; import handy_http_primitives; import handy_http_data.json; +import handy_http_handlers.path_handler; import slf4d; import auth.model; @@ -10,73 +11,13 @@ import auth.data; import auth.service; import auth.data_impl_fs; -void postLogin(ref ServerHttpRequest request, ref ServerHttpResponse response) { - struct LoginData { - string username; - string password; - } - LoginData data = readJsonBodyAs!LoginData(request); - string token = generateTokenForLogin(data.username, data.password); - response.writeBodyString(token); - infoF!"Generated token for user: %s"(data.username); -} - -struct UsernameAvailabilityResponse { - const bool available; -} - -void getUsernameAvailability(ref ServerHttpRequest request, ref ServerHttpResponse response) { - string username = null; - foreach (param; request.queryParams) { - if (param.key == "username" && param.values.length > 0) { - username = param.values[0]; - break; - } - } - if (username is null || username.length == 0) { - response.status = HttpStatus.BAD_REQUEST; - response.writeBodyString("Missing username parameter."); - return; - } - UserRepository userRepo = new FileSystemUserRepository(); - bool available = userRepo.findByUsername(username).isNull; - writeJsonBody(response, UsernameAvailabilityResponse(available)); -} - -struct RegistrationData { - string username; - string password; -} - -void postRegister(ref ServerHttpRequest request, ref ServerHttpResponse response) { - RegistrationData registrationData = readJsonBodyAs!RegistrationData(request); - if (!validateUsername(registrationData.username)) { - response.status = HttpStatus.BAD_REQUEST; - response.writeBodyString("Invalid username."); - return; - } - if (!validatePassword(registrationData.password)) { - response.status = HttpStatus.BAD_REQUEST; - response.writeBodyString("Invalid password."); - return; - } - UserRepository userRepo = new FileSystemUserRepository(); - if (!userRepo.findByUsername(registrationData.username).isNull) { - response.status = HttpStatus.BAD_REQUEST; - response.writeBodyString("Username is taken."); - return; - } - - User user = createNewUser(userRepo, registrationData.username, registrationData.password); - infoF!"Created user: %s"(registrationData.username); - response.writeBodyString(user.username); -} - +@PathMapping(HttpMethod.GET, "/me") void getMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) { AuthContext auth = getAuthContext(request); response.writeBodyString(auth.user.username); } +@PathMapping(HttpMethod.DELETE, "/me") void deleteMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) { AuthContext auth = getAuthContext(request); UserRepository userRepo = new FileSystemUserRepository(); @@ -84,6 +25,7 @@ void deleteMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response infoF!"Deleted user: %s"(auth.user.username); } +@PathMapping(HttpMethod.GET, "/me/token") void getNewToken(ref ServerHttpRequest request, ref ServerHttpResponse response) { AuthContext auth = getAuthContext(request); string token = generateTokenForUser(auth.user); @@ -96,6 +38,7 @@ struct PasswordChangeRequest { string newPassword; } +@PathMapping(HttpMethod.POST, "/me/password") void changeMyPassword(ref ServerHttpRequest request, ref ServerHttpResponse response) { AuthContext auth = getAuthContext(request); PasswordChangeRequest data = readJsonBodyAs!PasswordChangeRequest(request); diff --git a/finnow-api/source/auth/api_public.d b/finnow-api/source/auth/api_public.d new file mode 100644 index 0000000..88991e3 --- /dev/null +++ b/finnow-api/source/auth/api_public.d @@ -0,0 +1,76 @@ +module auth.api_public; + +import handy_http_primitives; +import handy_http_handlers.path_handler; +import handy_http_data.json; +import slf4d; + +import auth.model; +import auth.data; +import auth.service; +import auth.data_impl_fs; + +@PathMapping(HttpMethod.POST, "/login") +void postLogin(ref ServerHttpRequest request, ref ServerHttpResponse response) { + struct LoginData { + string username; + string password; + } + LoginData data = readJsonBodyAs!LoginData(request); + string token = generateTokenForLogin(data.username, data.password); + response.writeBodyString(token); + infoF!"Generated token for user: %s"(data.username); +} + +struct UsernameAvailabilityResponse { + const bool available; +} + +@PathMapping(HttpMethod.GET, "/register/username-availability") +void getUsernameAvailability(ref ServerHttpRequest request, ref ServerHttpResponse response) { + string username = null; + foreach (param; request.queryParams) { + if (param.key == "username" && param.values.length > 0) { + username = param.values[0]; + break; + } + } + if (username is null || username.length == 0) { + response.status = HttpStatus.BAD_REQUEST; + response.writeBodyString("Missing username parameter."); + return; + } + UserRepository userRepo = new FileSystemUserRepository(); + bool available = userRepo.findByUsername(username).isNull; + writeJsonBody(response, UsernameAvailabilityResponse(available)); +} + +struct RegistrationData { + string username; + string password; +} + +@PathMapping(HttpMethod.POST, "/register") +void postRegister(ref ServerHttpRequest request, ref ServerHttpResponse response) { + RegistrationData registrationData = readJsonBodyAs!RegistrationData(request); + if (!validateUsername(registrationData.username)) { + response.status = HttpStatus.BAD_REQUEST; + response.writeBodyString("Invalid username."); + return; + } + if (!validatePassword(registrationData.password)) { + response.status = HttpStatus.BAD_REQUEST; + response.writeBodyString("Invalid password."); + return; + } + UserRepository userRepo = new FileSystemUserRepository(); + if (!userRepo.findByUsername(registrationData.username).isNull) { + response.status = HttpStatus.BAD_REQUEST; + response.writeBodyString("Username is taken."); + return; + } + + User user = createNewUser(userRepo, registrationData.username, registrationData.password); + infoF!"Created user: %s"(registrationData.username); + response.writeBodyString(user.username); +}