module profile.api; import std.json; import asdf; import handy_http_primitives; import handy_http_data.json; import handy_http_handlers.path_handler : getPathParamAs; import slf4d; import profile.model; import profile.service; import profile.data; import profile.data_impl_sqlite; import auth.model; import auth.service; struct NewProfilePayload { string name; } void handleCreateNewProfile(ref ServerHttpRequest request, ref ServerHttpResponse response) { auto payload = readJsonBodyAs!NewProfilePayload(request); string name = payload.name; if (!validateProfileName(name)) { response.status = HttpStatus.BAD_REQUEST; response.writeBodyString("Invalid profile name."); return; } AuthContext auth = getAuthContext(request); ProfileRepository profileRepo = new FileSystemProfileRepository(auth.user.username); Profile p = profileRepo.createProfile(name); writeJsonBody(response, p); } void handleGetProfiles(ref ServerHttpRequest request, ref ServerHttpResponse response) { AuthContext auth = getAuthContext(request); ProfileRepository profileRepo = new FileSystemProfileRepository(auth.user.username); Profile[] profiles = profileRepo.findAll(); writeJsonBody(response, profiles); } void handleGetProfile(ref ServerHttpRequest request, ref ServerHttpResponse response) { ProfileContext profileCtx = getProfileContextOrThrow(request); writeJsonBody(response, profileCtx.profile); } void handleDeleteProfile(ref ServerHttpRequest request, ref ServerHttpResponse response) { string name = request.getPathParamAs!string("profile"); if (!validateProfileName(name)) { response.status = HttpStatus.BAD_REQUEST; response.writeBodyString("Invalid profile name."); return; } AuthContext auth = getAuthContext(request); ProfileRepository profileRepo = new FileSystemProfileRepository(auth.user.username); profileRepo.deleteByName(name); } void handleGetProperties(ref ServerHttpRequest request, ref ServerHttpResponse response) { ProfileContext profileCtx = getProfileContextOrThrow(request); ProfileRepository profileRepo = new FileSystemProfileRepository(profileCtx.user.username); ProfileDataSource ds = profileRepo.getDataSource(profileCtx.profile); auto propsRepo = ds.getPropertiesRepository(); ProfileProperty[] props = propsRepo.findAll(); writeJsonBody(response, props); } void handleDownloadProfile(ref ServerHttpRequest request, ref ServerHttpResponse response) { ProfileContext profileCtx = getProfileContextOrThrow(request); ProfileRepository profileRepo = new FileSystemProfileRepository(profileCtx.user.username); Optional!ProfileDownloadData data = profileRepo.getProfileData(profileCtx.profile.name); if (data.isNull) { response.status = HttpStatus.NOT_FOUND; response.writeBodyString("Profile data not found."); return; } import streams : StreamResult; import std.conv : to; response.headers.add("Content-Type", data.value.contentType); response.headers.add("Content-Disposition", "attachment; filename=" ~ data.value.filename); response.headers.add("Content-Length", data.value.size.to!string); // Transfer the file: StreamResult result; ubyte[4096] buffer; ulong bytesWritten = 0; while (bytesWritten < data.value.size) { result = data.value.inputStream.readFromStream(buffer); if (result.hasError) { errorF!"Failed to read from stream: %s"(result.error); return; } if (result.count == 0) { return; // Done! } result = response.outputStream.writeToStream(buffer[0 .. result.count]); if (result.hasError) { errorF!"Failed to write to stream: %s"(result.error); return; } bytesWritten += result.count; } }