added icon

This commit is contained in:
Radek Davidek 2025-11-01 13:37:05 +01:00
parent dd14b7ba96
commit 2f3eeebe72
3 changed files with 243 additions and 205 deletions

View File

@ -13,219 +13,257 @@ import java.util.List;
public class HttpServerApp { public class HttpServerApp {
// Statický API klíč // Statický API klíč
private static final String API_KEY = "6666-1234"; private static final String API_KEY = "6666-1234";
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
TransmissionService service = new TransmissionService(); TransmissionService service = new TransmissionService();
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
// /transmissions -> all data nebo jen pro konkrétní datum // /transmissions -> all data nebo jen pro konkrétní datum
server.createContext("/transmissions", exchange -> { server.createContext("/transmissions", exchange -> {
try { try {
setCors(exchange); setCors(exchange);
if (!checkApiKey(exchange)) return; if (!checkApiKey(exchange))
return;
URI uri = exchange.getRequestURI(); URI uri = exchange.getRequestURI();
String query = uri.getQuery(); String query = uri.getQuery();
String date = null; String date = null;
if (query != null) { if (query != null) {
for (String part : query.split("&")) { for (String part : query.split("&")) {
if (part.startsWith("date=")) { if (part.startsWith("date=")) {
date = java.net.URLDecoder.decode(part.substring(5), StandardCharsets.UTF_8); date = java.net.URLDecoder.decode(part.substring(5), StandardCharsets.UTF_8);
} }
} }
} }
List<Transmission> result; List<Transmission> result;
if (date != null && !date.isBlank()) { if (date != null && !date.isBlank()) {
result = service.getByDateLazy(date); result = service.getByDateLazy(date);
} else { } else {
result = service.getAll(); result = service.getAll();
} }
respondJson(exchange, mapper.writeValueAsString(result)); respondJson(exchange, mapper.writeValueAsString(result));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
sendError(exchange, 500, e.getMessage()); sendError(exchange, 500, e.getMessage());
} }
}); });
// /search?q=... // /search?q=...
server.createContext("/search", exchange -> { server.createContext("/search", exchange -> {
try { try {
setCors(exchange); setCors(exchange);
if (!checkApiKey(exchange)) return; if (!checkApiKey(exchange))
return;
URI uri = exchange.getRequestURI(); URI uri = exchange.getRequestURI();
String query = null; String query = null;
if (uri.getQuery() != null) { if (uri.getQuery() != null) {
String fullq = uri.getQuery(); String fullq = uri.getQuery();
for (String part : fullq.split("&")) { for (String part : fullq.split("&")) {
if (part.startsWith("q=")) { if (part.startsWith("q=")) {
query = java.net.URLDecoder.decode(part.substring(2), StandardCharsets.UTF_8.name()); query = java.net.URLDecoder.decode(part.substring(2), StandardCharsets.UTF_8.name());
} }
} }
} }
List<Transmission> results = service.search(query); List<Transmission> results = service.search(query);
respondJson(exchange, mapper.writeValueAsString(results)); respondJson(exchange, mapper.writeValueAsString(results));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
sendError(exchange, 500, e.getMessage()); sendError(exchange, 500, e.getMessage());
} }
}); });
// /refresh endpoint // /refresh endpoint
server.createContext("/refresh", exchange -> { server.createContext("/refresh", exchange -> {
try { try {
setCors(exchange); setCors(exchange);
if (!checkApiKey(exchange)) return; if (!checkApiKey(exchange))
return;
if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) { if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) {
// přečti volitelné datum z query stringu // přečti volitelné datum z query stringu
String dateParam = null; String dateParam = null;
String query = exchange.getRequestURI().getQuery(); String query = exchange.getRequestURI().getQuery();
if (query != null) { if (query != null) {
for (String part : query.split("&")) { for (String part : query.split("&")) {
if (part.startsWith("date=")) { if (part.startsWith("date=")) {
dateParam = java.net.URLDecoder.decode(part.substring(5), StandardCharsets.UTF_8); dateParam = java.net.URLDecoder.decode(part.substring(5), StandardCharsets.UTF_8);
break; break;
} }
} }
} }
// pokud není zadané, použij dnešní den // pokud není zadané, použij dnešní den
String dateStr = (dateParam != null && !dateParam.isBlank()) String dateStr = (dateParam != null && !dateParam.isBlank()) ? dateParam
? dateParam : java.time.LocalDate.now().toString();
: java.time.LocalDate.now().toString();
// načti data pro konkrétní den // načti data pro konkrétní den
System.out.println("🔄 Obnovuji data pro den: " + dateStr); System.out.println("🔄 Obnovuji data pro den: " + dateStr);
service.reloadDayAsync(dateStr); service.reloadDayAsync(dateStr);
respondJson(exchange, "{\"status\":\"ok\",\"message\":\"Data pro den " + dateStr + " se obnovují.\"}"); respondJson(exchange,
} else { "{\"status\":\"ok\",\"message\":\"Data pro den " + dateStr + " se obnovují.\"}");
exchange.sendResponseHeaders(405, -1); } else {
} exchange.sendResponseHeaders(405, -1);
} catch (Exception e) { }
sendError(exchange, 500, e.getMessage()); } catch (Exception e) {
} sendError(exchange, 500, e.getMessage());
}); }
});
// serve /index.html // serve /index.html
server.createContext("/", exchange -> { server.createContext("/", exchange -> {
try { try {
setCors(exchange); setCors(exchange);
if (!checkApiKey(exchange)) return; String path = exchange.getRequestURI().getPath();
String path = exchange.getRequestURI().getPath(); if (path.equals("/icon.png")) {
if ("/".equals(path) || path.isEmpty() || path.equals("/index.html")) { byte[] data = readResourceAsBytes("/icon.png");
String html = readResource("/index.html"); if (data == null) {
if (html == null) { sendError(exchange, 404, "index.html not found in resources");
sendError(exchange, 404, "index.html not found in resources"); return;
return; }
} exchange.getResponseHeaders().set("Content-Type", "image/png");
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=utf-8"); exchange.sendResponseHeaders(200, data.length);
byte[] bytes = html.getBytes(StandardCharsets.UTF_8); try (OutputStream os = exchange.getResponseBody()) {
exchange.sendResponseHeaders(200, bytes.length); os.write(data);
try (OutputStream os = exchange.getResponseBody()) { os.flush();
os.write(bytes); }
} return;
} else { }
sendError(exchange, 404, "Not found");
}
} catch (Exception e) {
e.printStackTrace();
sendError(exchange, 500, e.getMessage());
}
});
server.start(); if (!checkApiKey(exchange))
System.out.println("🚀 HTTP server běží na http://localhost:8080"); return;
System.out.println(" ➜ /transmissions (všechny přenosy JSON, vyžaduje X-API-KEY)"); if ("/".equals(path) || path.isEmpty() || path.equals("/index.html")) {
System.out.println(" ➜ /search?q=Brno (vyhledávání JSON, vyžaduje X-API-KEY)"); String html = readResource("/index.html");
System.out.println(" ➜ /refresh (spustí opětovné načtení dat, vyžaduje X-API-KEY)"); if (html == null) {
System.out.println(" ➜ / (web UI)"); sendError(exchange, 404, "index.html not found in resources");
} return;
}
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=utf-8");
byte[] bytes = html.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
os.flush();
}
} else {
sendError(exchange, 404, "Not found");
}
} catch (Exception e) {
e.printStackTrace();
sendError(exchange, 500, e.getMessage());
}
});
// ======= API Key ochrana ======= server.start();
private static boolean checkApiKey(HttpExchange exchange) throws IOException { System.out.println("🚀 HTTP server běží na http://localhost:8080");
String query = exchange.getRequestURI().getQuery(); // např. apiKey=xxxx System.out.println(" ➜ /transmissions (všechny přenosy JSON, vyžaduje X-API-KEY)");
String key = null; System.out.println(" ➜ /search?q=Brno (vyhledávání JSON, vyžaduje X-API-KEY)");
System.out.println(" ➜ /refresh (spustí opětovné načtení dat, vyžaduje X-API-KEY)");
System.out.println(" ➜ / (web UI)");
}
if (query != null) { private static byte[] readResourceAsBytes(String string) {
for (String part : query.split("&")) { try (InputStream is = HttpServerApp.class.getResourceAsStream(string)) {
if (part.startsWith("apiKey=")) { if (is == null)
key = java.net.URLDecoder.decode(part.substring(7), StandardCharsets.UTF_8); return null;
break; ByteArrayOutputStream buffer = new ByteArrayOutputStream();
} byte[] data = new byte[16384];
} int nRead;
} while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
if (!API_KEY.equals(key)) { // ======= API Key ochrana =======
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8"); private static boolean checkApiKey(HttpExchange exchange) throws IOException {
byte[] bytes = "{\"error\":\"Unauthorized\"}".getBytes(StandardCharsets.UTF_8); String query = exchange.getRequestURI().getQuery(); // např. apiKey=xxxx
exchange.sendResponseHeaders(401, bytes.length); String key = null;
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
return false;
}
return true;
}
// ======= CORS ======= if (query != null) {
private static void setCors(HttpExchange exchange) { for (String part : query.split("&")) {
Headers h = exchange.getResponseHeaders(); if (part.startsWith("apiKey=")) {
h.set("Access-Control-Allow-Origin", "*"); key = java.net.URLDecoder.decode(part.substring(7), StandardCharsets.UTF_8);
h.set("Access-Control-Allow-Methods", "GET, OPTIONS"); break;
h.set("Access-Control-Allow-Headers", "Content-Type, X-API-KEY"); }
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) { }
try { }
exchange.sendResponseHeaders(204, -1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// ======= JSON odpověď ======= if (!API_KEY.equals(key)) {
private static void respondJson(HttpExchange exchange, String json) throws IOException { exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8"); byte[] bytes = "{\"error\":\"Unauthorized\"}".getBytes(StandardCharsets.UTF_8);
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); exchange.sendResponseHeaders(401, bytes.length);
exchange.sendResponseHeaders(200, bytes.length); try (OutputStream os = exchange.getResponseBody()) {
try (OutputStream os = exchange.getResponseBody()) { os.write(bytes);
os.write(bytes); }
} return false;
} }
return true;
}
// ======= Chyba ======= // ======= CORS =======
private static void sendError(HttpExchange exchange, int code, String message) throws IOException { private static void setCors(HttpExchange exchange) {
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8"); Headers h = exchange.getResponseHeaders();
byte[] bytes = message.getBytes(StandardCharsets.UTF_8); h.set("Access-Control-Allow-Origin", "*");
exchange.sendResponseHeaders(code, bytes.length); h.set("Access-Control-Allow-Methods", "GET, OPTIONS");
try (OutputStream os = exchange.getResponseBody()) { h.set("Access-Control-Allow-Headers", "Content-Type, X-API-KEY");
os.write(bytes); if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
} try {
} exchange.sendResponseHeaders(204, -1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// ======= Načtení resource ======= // ======= JSON odpověď =======
private static String readResource(String resourcePath) { private static void respondJson(HttpExchange exchange, String json) throws IOException {
try (InputStream is = HttpServerApp.class.getResourceAsStream(resourcePath)) { exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
if (is == null) return null; byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { exchange.sendResponseHeaders(200, bytes.length);
StringBuilder sb = new StringBuilder(); try (OutputStream os = exchange.getResponseBody()) {
String line; os.write(bytes);
while ((line = br.readLine()) != null) { }
sb.append(line).append("\n"); }
}
return sb.toString(); // ======= Chyba =======
} private static void sendError(HttpExchange exchange, int code, String message) throws IOException {
} catch (IOException e) { exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
e.printStackTrace(); byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
return null; exchange.sendResponseHeaders(code, bytes.length);
} try (OutputStream os = exchange.getResponseBody()) {
} os.write(bytes);
}
}
// ======= Načtení resource =======
private static String readResource(String resourcePath) {
try (InputStream is = HttpServerApp.class.getResourceAsStream(resourcePath)) {
if (is == null)
return null;
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
} }

BIN
src/main/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -2,6 +2,7 @@
<html lang="cs"> <html lang="cs">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TVCOM Přenosy</title> <title>TVCOM Přenosy</title>
<style> <style>
@ -74,7 +75,7 @@ input, select, button {
margin-top: 30px; margin-top: 30px;
} }
@media (max-width: 500px) { @media ( max-width : 500px) {
.card img { .card img {
height: 120px; height: 120px;
} }
@ -82,21 +83,20 @@ input, select, button {
</style> </style>
</head> </head>
<body> <body>
<h1>📺 TVCOM Přenosy</h1> <h1>📺 TVCOM Přenosy</h1>
<div class="filters"> <div class="filters">
<input id="search" type="text" placeholder="Hledat přenos..." /> <input id="search" type="text" placeholder="Hledat přenos..." /> <input
<input id="datePicker" type="date" /> id="datePicker" type="date" /> <select id="sportFilter">
<select id="sportFilter"> <option value="">Všechny sporty</option>
<option value="">Všechny sporty</option> </select>
</select> <button id="refreshBtn">♻️ Obnovit data</button>
<button id="refreshBtn">♻️ Obnovit data</button> </div>
</div>
<div id="loading">Načítám data...</div> <div id="loading">Načítám data...</div>
<div id="list" class="grid" style="display: none"></div> <div id="list" class="grid" style="display: none"></div>
<script> <script>
const API_BASE = ""; const API_BASE = "";
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const apiKey = urlParams.get('apiKey'); const apiKey = urlParams.get('apiKey');