| import com.sun.net.httpserver.Headers; |
| import com.sun.net.httpserver.HttpServer; |
|
|
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.InetSocketAddress; |
| import java.net.URI; |
| import java.net.http.HttpClient; |
| import java.net.http.HttpRequest; |
| import java.net.http.HttpResponse; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.time.Duration; |
|
|
| public class KshitijAIProxyServer { |
|
|
| private static final int PORT = 7860; |
|
|
| |
| private static final String BACKEND_URL = getenvOrThrow("BACKEND_URL"); |
| private static final String HF_TOKEN = getenvOrThrow("HF_TOKEN"); |
|
|
| public static void main(String[] args) throws Exception { |
| final Path baseDir = Paths.get(System.getProperty("user.dir")).toRealPath(); |
|
|
| HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0); |
|
|
| server.createContext("/health", exchange -> { |
| addCors(exchange.getResponseHeaders()); |
| if (!exchange.getRequestMethod().equalsIgnoreCase("GET")) { |
| exchange.sendResponseHeaders(405, -1); |
| return; |
| } |
| byte[] ok = "ok".getBytes(StandardCharsets.UTF_8); |
| exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8"); |
| exchange.sendResponseHeaders(200, ok.length); |
| try (OutputStream os = exchange.getResponseBody()) { os.write(ok); } |
| }); |
|
|
| |
| server.createContext("/api/chat", exchange -> { |
| Headers h = exchange.getResponseHeaders(); |
| addCors(h); |
|
|
| |
| if (exchange.getRequestMethod().equalsIgnoreCase("OPTIONS")) { |
| exchange.sendResponseHeaders(204, -1); |
| return; |
| } |
|
|
| if (!exchange.getRequestMethod().equalsIgnoreCase("POST")) { |
| exchange.sendResponseHeaders(405, -1); |
| return; |
| } |
|
|
| byte[] requestBytes = exchange.getRequestBody().readAllBytes(); |
|
|
| HttpClient client = HttpClient.newBuilder() |
| .connectTimeout(Duration.ofSeconds(10)) |
| .build(); |
|
|
| String target = BACKEND_URL.endsWith("/") |
| ? BACKEND_URL + "api/chat" |
| : BACKEND_URL + "/api/chat"; |
|
|
| HttpRequest req = HttpRequest.newBuilder() |
| .uri(URI.create(target)) |
| .timeout(Duration.ofSeconds(300)) |
| .header("Content-Type", "application/json") |
| .header("Authorization", "Bearer " + HF_TOKEN) |
| .POST(HttpRequest.BodyPublishers.ofByteArray(requestBytes)) |
| .build(); |
|
|
| HttpResponse<byte[]> resp; |
| try { |
| resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray()); |
| } catch (Exception e) { |
| byte[] err = ("Failed to reach backend at " + target + "\n" + e) |
| .getBytes(StandardCharsets.UTF_8); |
| h.set("Content-Type", "text/plain; charset=utf-8"); |
| exchange.sendResponseHeaders(502, err.length); |
| try (OutputStream os = exchange.getResponseBody()) { os.write(err); } |
| return; |
| } |
|
|
| h.set("Content-Type", "application/json; charset=utf-8"); |
| exchange.sendResponseHeaders(resp.statusCode(), resp.body().length); |
| try (OutputStream os = exchange.getResponseBody()) { os.write(resp.body()); } |
| }); |
|
|
| |
| server.createContext("/", exchange -> { |
| addCors(exchange.getResponseHeaders()); |
|
|
| if (!exchange.getRequestMethod().equalsIgnoreCase("GET")) { |
| exchange.sendResponseHeaders(405, -1); |
| return; |
| } |
|
|
| String urlPath = exchange.getRequestURI().getPath(); |
| if (urlPath == null || urlPath.isBlank() || urlPath.equals("/")) urlPath = "/index.html"; |
|
|
| String rel = urlPath.startsWith("/") ? urlPath.substring(1) : urlPath; |
| Path requested = baseDir.resolve(rel).normalize(); |
|
|
| if (!requested.startsWith(baseDir)) { exchange.sendResponseHeaders(403, -1); return; } |
| if (!Files.exists(requested) || Files.isDirectory(requested)) { exchange.sendResponseHeaders(404, -1); return; } |
|
|
| String ct = guessContentType(requested); |
| byte[] bytes = Files.readAllBytes(requested); |
|
|
| exchange.getResponseHeaders().set("Content-Type", ct); |
| exchange.sendResponseHeaders(200, bytes.length); |
| try (OutputStream os = exchange.getResponseBody()) { os.write(bytes); } |
| }); |
|
|
| server.start(); |
| System.out.println("Public UI + Proxy running on port " + PORT); |
| } |
|
|
| private static void addCors(Headers h) { |
| h.set("Access-Control-Allow-Origin", "*"); |
| h.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); |
| h.set("Access-Control-Allow-Headers", "Content-Type, Authorization"); |
| } |
|
|
| private static String guessContentType(Path file) throws IOException { |
| String name = file.getFileName().toString().toLowerCase(); |
| if (name.endsWith(".html")) return "text/html; charset=utf-8"; |
| if (name.endsWith(".css")) return "text/css; charset=utf-8"; |
| if (name.endsWith(".js")) return "application/javascript; charset=utf-8"; |
| String probe = Files.probeContentType(file); |
| return probe != null ? probe : "application/octet-stream"; |
| } |
|
|
| private static String getenvOrThrow(String key) { |
| String v = System.getenv(key); |
| if (v == null || v.isBlank()) throw new IllegalStateException("Missing env var: " + key); |
| return v; |
| } |
| } |
|
|