Upgraded path-handler lib to 1.2.0, used @PathMapping annotation.

This commit is contained in:
andrewlalis 2026-01-13 15:44:12 -05:00
parent d9310d5979
commit 7ef9d63de7
5 changed files with 86 additions and 115 deletions

View File

@ -8,7 +8,7 @@
"d2sqlite3": "1.0.0", "d2sqlite3": "1.0.0",
"dxml": "0.4.5", "dxml": "0.4.5",
"handy-http-data": "1.3.0", "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-primitives": "1.8.1",
"handy-http-starter": "1.6.0", "handy-http-starter": "1.6.0",
"handy-http-transport": "1.10.0", "handy-http-transport": "1.10.0",

View File

@ -26,18 +26,12 @@ HttpRequestHandler mapApiHandlers(string webOrigin) {
// Auth endpoints: // Auth endpoints:
import auth.api; import auth.api;
h.map(HttpMethod.POST, "/login", &postLogin); import auth.api_public;
h.map(HttpMethod.POST, "/register", &postRegister); h.registerHandlers!(auth.api_public);
h.map(HttpMethod.GET, "/register/username-availability", &getUsernameAvailability);
// Authenticated endpoints: // Authenticated endpoints:
PathHandler a = new PathHandler(); PathHandler a = new PathHandler();
a.map(HttpMethod.GET, "/me", &getMyUser); a.registerHandlers!(auth.api);
a.map(HttpMethod.DELETE, "/me", &deleteMyUser);
a.map(HttpMethod.GET, "/me/token", &getNewToken);
a.map(HttpMethod.POST, "/me/password", &changeMyPassword);
import profile.api; import profile.api;
a.map(HttpMethod.GET, "/profiles", &handleGetProfiles); a.map(HttpMethod.GET, "/profiles", &handleGetProfiles);

View File

@ -10,8 +10,6 @@ import scheduled_jobs;
* Starts the Finnow API. * Starts the Finnow API.
*/ */
void main() { void main() {
// testUDA();
const config = readConfig(); const config = readConfig();
configureSlf4d(config); configureSlf4d(config);
startScheduledJobs(); startScheduledJobs();
@ -20,6 +18,7 @@ void main() {
void configureSlf4d(in AppConfig config) { void configureSlf4d(in AppConfig config) {
Level logLevel = getConfiguredLoggingLevel(config); Level logLevel = getConfiguredLoggingLevel(config);
logLevel = Levels.DEBUG;
auto provider = new DefaultProvider(logLevel); auto provider = new DefaultProvider(logLevel);
configureLoggingProvider(provider); configureLoggingProvider(provider);
} }
@ -30,44 +29,3 @@ void startWebServer(in AppConfig config) {
HttpTransport transport = new TaskPoolHttp1Transport(mapApiHandlers(config.webOrigin), transportConfig); HttpTransport transport = new TaskPoolHttp1Transport(mapApiHandlers(config.webOrigin), transportConfig);
transport.start(); 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;
// }

View File

@ -3,6 +3,7 @@ module auth.api;
import handy_http_primitives; import handy_http_primitives;
import handy_http_data.json; import handy_http_data.json;
import handy_http_handlers.path_handler;
import slf4d; import slf4d;
import auth.model; import auth.model;
@ -10,73 +11,13 @@ import auth.data;
import auth.service; import auth.service;
import auth.data_impl_fs; import auth.data_impl_fs;
void postLogin(ref ServerHttpRequest request, ref ServerHttpResponse response) { @PathMapping(HttpMethod.GET, "/me")
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);
}
void getMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) { void getMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) {
AuthContext auth = getAuthContext(request); AuthContext auth = getAuthContext(request);
response.writeBodyString(auth.user.username); response.writeBodyString(auth.user.username);
} }
@PathMapping(HttpMethod.DELETE, "/me")
void deleteMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) { void deleteMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response) {
AuthContext auth = getAuthContext(request); AuthContext auth = getAuthContext(request);
UserRepository userRepo = new FileSystemUserRepository(); UserRepository userRepo = new FileSystemUserRepository();
@ -84,6 +25,7 @@ void deleteMyUser(ref ServerHttpRequest request, ref ServerHttpResponse response
infoF!"Deleted user: %s"(auth.user.username); infoF!"Deleted user: %s"(auth.user.username);
} }
@PathMapping(HttpMethod.GET, "/me/token")
void getNewToken(ref ServerHttpRequest request, ref ServerHttpResponse response) { void getNewToken(ref ServerHttpRequest request, ref ServerHttpResponse response) {
AuthContext auth = getAuthContext(request); AuthContext auth = getAuthContext(request);
string token = generateTokenForUser(auth.user); string token = generateTokenForUser(auth.user);
@ -96,6 +38,7 @@ struct PasswordChangeRequest {
string newPassword; string newPassword;
} }
@PathMapping(HttpMethod.POST, "/me/password")
void changeMyPassword(ref ServerHttpRequest request, ref ServerHttpResponse response) { void changeMyPassword(ref ServerHttpRequest request, ref ServerHttpResponse response) {
AuthContext auth = getAuthContext(request); AuthContext auth = getAuthContext(request);
PasswordChangeRequest data = readJsonBodyAs!PasswordChangeRequest(request); PasswordChangeRequest data = readJsonBodyAs!PasswordChangeRequest(request);

View File

@ -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);
}