feat: implement doctor schedule module, add detail fields to doctor profile, and integrate new appointment API client
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package vn.sis.appointment;
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
final class AppointmentWebServer {
|
||||
private final HttpServer server;
|
||||
private final SisApiClient apiClient = new SisApiClient();
|
||||
|
||||
AppointmentWebServer(int port) throws IOException {
|
||||
server = HttpServer.create(new InetSocketAddress("0.0.0.0", port), 0);
|
||||
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
|
||||
|
||||
server.createContext("/api/health", exchange -> {
|
||||
if (!requireMethod(exchange, "GET")) {
|
||||
return;
|
||||
}
|
||||
sendJson(exchange, 200, Json.stringify(Map.of(
|
||||
"ok", true,
|
||||
"application", "SisAppointmentJavaWeb",
|
||||
"javaVersion", System.getProperty("java.version"))));
|
||||
});
|
||||
|
||||
server.createContext("/api/token", postHandler((form, config) -> apiClient.checkToken(config)));
|
||||
server.createContext("/api/doctors", postHandler((form, config) -> apiClient.getDoctors(config)));
|
||||
server.createContext("/api/schedule", postHandler((form, config) ->
|
||||
apiClient.getSchedule(config, form.getOrDefault("doctorCode", ""))));
|
||||
server.createContext("/api/clear-token", postHandler((form, config) -> {
|
||||
apiClient.clearToken(config);
|
||||
return new ProxyResponse(true, 200, "OK", "-", "-", 0,
|
||||
"", "", "", null, "Da xoa access token trong bo nho may chu.");
|
||||
}));
|
||||
|
||||
server.createContext("/", new StaticHandler());
|
||||
}
|
||||
|
||||
void start() {
|
||||
server.start();
|
||||
}
|
||||
|
||||
private HttpHandler postHandler(ApiOperation operation) {
|
||||
return exchange -> {
|
||||
if (!requireMethod(exchange, "POST")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Map<String, String> form = FormData.read(exchange);
|
||||
ApiConfig config = ApiConfig.fromForm(form);
|
||||
ProxyResponse result = operation.execute(form, config);
|
||||
sendJson(exchange, 200, result.toJson());
|
||||
} catch (IllegalArgumentException ex) {
|
||||
ProxyResponse error = new ProxyResponse(false, 400, "Bad Request", "-", "-", 0,
|
||||
"", "", "", null, ex.getMessage());
|
||||
sendJson(exchange, 400, error.toJson());
|
||||
} catch (Exception ex) {
|
||||
ProxyResponse error = new ProxyResponse(false, 500, "Internal Server Error", "-", "-", 0,
|
||||
"", "", "", null,
|
||||
ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage());
|
||||
sendJson(exchange, 500, error.toJson());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean requireMethod(HttpExchange exchange, String expected) throws IOException {
|
||||
if (exchange.getRequestMethod().equalsIgnoreCase(expected)) {
|
||||
return true;
|
||||
}
|
||||
exchange.getResponseHeaders().set("Allow", expected);
|
||||
sendJson(exchange, 405, Json.stringify(Map.of(
|
||||
"ok", false,
|
||||
"message", "Phuong thuc khong duoc ho tro.")));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void sendJson(HttpExchange exchange, int status, String json) throws IOException {
|
||||
byte[] data = json.getBytes(StandardCharsets.UTF_8);
|
||||
applySecurityHeaders(exchange.getResponseHeaders());
|
||||
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||
exchange.getResponseHeaders().set("Cache-Control", "no-store");
|
||||
exchange.sendResponseHeaders(status, data.length);
|
||||
exchange.getResponseBody().write(data);
|
||||
exchange.close();
|
||||
}
|
||||
|
||||
private static void applySecurityHeaders(Headers headers) {
|
||||
headers.set("X-Content-Type-Options", "nosniff");
|
||||
headers.set("X-Frame-Options", "DENY");
|
||||
headers.set("Referrer-Policy", "no-referrer");
|
||||
headers.set("Content-Security-Policy",
|
||||
"default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; connect-src 'self'");
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ApiOperation {
|
||||
ProxyResponse execute(Map<String, String> form, ApiConfig config);
|
||||
}
|
||||
|
||||
private static final class StaticHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
if (!exchange.getRequestMethod().equalsIgnoreCase("GET")) {
|
||||
exchange.getResponseHeaders().set("Allow", "GET");
|
||||
exchange.sendResponseHeaders(405, -1);
|
||||
exchange.close();
|
||||
return;
|
||||
}
|
||||
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
if (path.equals("/")) {
|
||||
path = "/index.html";
|
||||
}
|
||||
if (path.contains("..")) {
|
||||
exchange.sendResponseHeaders(400, -1);
|
||||
exchange.close();
|
||||
return;
|
||||
}
|
||||
|
||||
String resource = "web" + path;
|
||||
try (InputStream input = AppointmentWebServer.class.getClassLoader().getResourceAsStream(resource)) {
|
||||
if (input == null) {
|
||||
exchange.sendResponseHeaders(404, -1);
|
||||
exchange.close();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = input.readAllBytes();
|
||||
Headers headers = exchange.getResponseHeaders();
|
||||
applySecurityHeaders(headers);
|
||||
headers.set("Content-Type", contentType(path));
|
||||
headers.set("Cache-Control", path.endsWith("index.html") ? "no-cache" : "public, max-age=3600");
|
||||
exchange.sendResponseHeaders(200, data.length);
|
||||
exchange.getResponseBody().write(data);
|
||||
exchange.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static String contentType(String path) {
|
||||
if (path.endsWith(".html")) return "text/html; charset=utf-8";
|
||||
if (path.endsWith(".css")) return "text/css; charset=utf-8";
|
||||
if (path.endsWith(".js")) return "application/javascript; charset=utf-8";
|
||||
if (path.endsWith(".svg")) return "image/svg+xml";
|
||||
if (path.endsWith(".png")) return "image/png";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user