added apiKey
This commit is contained in:
parent
04f3ef22e8
commit
6d0a22a314
@ -12,6 +12,10 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HttpServerApp {
|
public class HttpServerApp {
|
||||||
|
|
||||||
|
// ✅ Statický API klíč
|
||||||
|
private static final String API_KEY = "JustSomeRandomText";
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
TransmissionService service = new TransmissionService();
|
TransmissionService service = new TransmissionService();
|
||||||
service.loadNextDays(10);
|
service.loadNextDays(10);
|
||||||
@ -23,6 +27,8 @@ public class HttpServerApp {
|
|||||||
server.createContext("/transmissions", exchange -> {
|
server.createContext("/transmissions", exchange -> {
|
||||||
try {
|
try {
|
||||||
setCors(exchange);
|
setCors(exchange);
|
||||||
|
if (!checkApiKey(exchange)) return;
|
||||||
|
|
||||||
List<Transmission> all = service.getAll();
|
List<Transmission> all = service.getAll();
|
||||||
respondJson(exchange, mapper.writeValueAsString(all));
|
respondJson(exchange, mapper.writeValueAsString(all));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -35,6 +41,8 @@ public class HttpServerApp {
|
|||||||
server.createContext("/search", exchange -> {
|
server.createContext("/search", exchange -> {
|
||||||
try {
|
try {
|
||||||
setCors(exchange);
|
setCors(exchange);
|
||||||
|
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) {
|
||||||
@ -53,10 +61,12 @@ public class HttpServerApp {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ NEW: /refresh endpoint
|
// /refresh endpoint
|
||||||
server.createContext("/refresh", exchange -> {
|
server.createContext("/refresh", exchange -> {
|
||||||
try {
|
try {
|
||||||
setCors(exchange);
|
setCors(exchange);
|
||||||
|
if (!checkApiKey(exchange)) return;
|
||||||
|
|
||||||
if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
service.reloadDataAsync();
|
service.reloadDataAsync();
|
||||||
respondJson(exchange, "{\"status\":\"ok\",\"message\":\"Data se obnovují na pozadí.\"}");
|
respondJson(exchange, "{\"status\":\"ok\",\"message\":\"Data se obnovují na pozadí.\"}");
|
||||||
@ -96,17 +106,44 @@ public class HttpServerApp {
|
|||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
System.out.println("🚀 HTTP server běží na http://localhost:8080");
|
System.out.println("🚀 HTTP server běží na http://localhost:8080");
|
||||||
System.out.println(" ➜ /transmissions (všechny přenosy JSON)");
|
System.out.println(" ➜ /transmissions (všechny přenosy JSON, vyžaduje X-API-KEY)");
|
||||||
System.out.println(" ➜ /search?q=Brno (vyhledávání JSON)");
|
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)");
|
System.out.println(" ➜ /refresh (spustí opětovné načtení dat, vyžaduje X-API-KEY)");
|
||||||
System.out.println(" ➜ / (web UI)");
|
System.out.println(" ➜ / (web UI)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======= API Key ochrana =======
|
||||||
|
private static boolean checkApiKey(HttpExchange exchange) throws IOException {
|
||||||
|
String query = exchange.getRequestURI().getQuery(); // např. apiKey=xxxx
|
||||||
|
String key = null;
|
||||||
|
|
||||||
|
if (query != null) {
|
||||||
|
for (String part : query.split("&")) {
|
||||||
|
if (part.startsWith("apiKey=")) {
|
||||||
|
key = java.net.URLDecoder.decode(part.substring(7), StandardCharsets.UTF_8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!API_KEY.equals(key)) {
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
byte[] bytes = "{\"error\":\"Unauthorized\"}".getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.sendResponseHeaders(401, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= CORS =======
|
||||||
private static void setCors(HttpExchange exchange) {
|
private static void setCors(HttpExchange exchange) {
|
||||||
Headers h = exchange.getResponseHeaders();
|
Headers h = exchange.getResponseHeaders();
|
||||||
h.set("Access-Control-Allow-Origin", "*");
|
h.set("Access-Control-Allow-Origin", "*");
|
||||||
h.set("Access-Control-Allow-Methods", "GET, OPTIONS");
|
h.set("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||||
h.set("Access-Control-Allow-Headers", "Content-Type");
|
h.set("Access-Control-Allow-Headers", "Content-Type, X-API-KEY");
|
||||||
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
|
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
try {
|
try {
|
||||||
exchange.sendResponseHeaders(204, -1);
|
exchange.sendResponseHeaders(204, -1);
|
||||||
@ -116,6 +153,7 @@ public class HttpServerApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======= JSON odpověď =======
|
||||||
private static void respondJson(HttpExchange exchange, String json) throws IOException {
|
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 = json.getBytes(StandardCharsets.UTF_8);
|
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
|
||||||
@ -125,6 +163,7 @@ public class HttpServerApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======= Chyba =======
|
||||||
private static void sendError(HttpExchange exchange, int code, String message) throws IOException {
|
private static void sendError(HttpExchange exchange, int code, String message) throws IOException {
|
||||||
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
|
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
|
||||||
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
|
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
|
||||||
@ -134,6 +173,7 @@ public class HttpServerApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ======= Načtení resource =======
|
||||||
private static String readResource(String resourcePath) {
|
private static String readResource(String resourcePath) {
|
||||||
try (InputStream is = HttpServerApp.class.getResourceAsStream(resourcePath)) {
|
try (InputStream is = HttpServerApp.class.getResourceAsStream(resourcePath)) {
|
||||||
if (is == null) return null;
|
if (is == null) return null;
|
||||||
|
|||||||
@ -66,6 +66,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const apiKey = urlParams.get('apiKey') || '';
|
||||||
|
|
||||||
const API = '/transmissions';
|
const API = '/transmissions';
|
||||||
let all = [];
|
let all = [];
|
||||||
let filtered = [];
|
let filtered = [];
|
||||||
@ -82,18 +85,20 @@
|
|||||||
const refreshBtn = document.getElementById('refreshBtn');
|
const refreshBtn = document.getElementById('refreshBtn');
|
||||||
|
|
||||||
function fetchAll() {
|
function fetchAll() {
|
||||||
fetch(API).then(r => r.json()).then(data => {
|
fetch(API + '?apiKey=' + encodeURIComponent(apiKey))
|
||||||
all = data || [];
|
.then(r => r.json())
|
||||||
populateFilters();
|
.then(data => {
|
||||||
applyFilters();
|
all = data || [];
|
||||||
}).catch(err => {
|
populateFilters();
|
||||||
console.error(err);
|
applyFilters();
|
||||||
alert('Chyba při načítání dat: ' + err);
|
})
|
||||||
});
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
alert('Chyba při načítání dat: ' + err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateFilters() {
|
function populateFilters() {
|
||||||
// Sport filter
|
|
||||||
const sports = Array.from(new Set(all.map(x => x.sport).filter(Boolean))).sort();
|
const sports = Array.from(new Set(all.map(x => x.sport).filter(Boolean))).sort();
|
||||||
sportFilter.innerHTML = '<option value="">— Všechny sporty —</option>';
|
sportFilter.innerHTML = '<option value="">— Všechny sporty —</option>';
|
||||||
sports.forEach(s => {
|
sports.forEach(s => {
|
||||||
@ -102,7 +107,6 @@
|
|||||||
|
|
||||||
populateLeagueFilter();
|
populateLeagueFilter();
|
||||||
|
|
||||||
// Date filter
|
|
||||||
const dates = Array.from(new Set(all.map(x => x.date).filter(Boolean))).sort();
|
const dates = Array.from(new Set(all.map(x => x.date).filter(Boolean))).sort();
|
||||||
dateFilter.innerHTML = '<option value="">— Všechna data —</option>';
|
dateFilter.innerHTML = '<option value="">— Všechna data —</option>';
|
||||||
dates.forEach(d => {
|
dates.forEach(d => {
|
||||||
@ -121,33 +125,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeText(s) {
|
function normalizeText(s) {
|
||||||
if (!s) return '';
|
if (!s) return '';
|
||||||
// odstranění diakritiky a převod na malá písmena
|
return s.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase();
|
||||||
return s.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function applyFilters() {
|
function applyFilters() {
|
||||||
const q = normalizeText(searchBox.value.trim());
|
const q = normalizeText(searchBox.value.trim());
|
||||||
const sport = sportFilter.value;
|
const sport = sportFilter.value;
|
||||||
const league = leagueFilter.value;
|
const league = leagueFilter.value;
|
||||||
const date = dateFilter.value;
|
const date = dateFilter.value;
|
||||||
|
|
||||||
filtered = all.filter(t => {
|
filtered = all.filter(t => {
|
||||||
if (sport && t.sport !== sport) return false;
|
if (sport && t.sport !== sport) return false;
|
||||||
if (league && t.league !== league) return false;
|
if (league && t.league !== league) return false;
|
||||||
if (date && t.date !== date) return false;
|
if (date && t.date !== date) return false;
|
||||||
if (!q) return true;
|
if (!q) return true;
|
||||||
|
const text = normalizeText((t.title||'') + ' ' + (t.sport||'') + ' ' + (t.league||''));
|
||||||
|
return text.indexOf(q) !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
// spojíme text a normalizujeme
|
sortFiltered();
|
||||||
const text = normalizeText((t.title||'') + ' ' + (t.sport||'') + ' ' + (t.league||''));
|
currentPage = 1;
|
||||||
return text.indexOf(q) !== -1;
|
renderTable();
|
||||||
});
|
renderPager();
|
||||||
|
}
|
||||||
sortFiltered();
|
|
||||||
currentPage = 1;
|
|
||||||
renderTable();
|
|
||||||
renderPager();
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortFiltered() {
|
function sortFiltered() {
|
||||||
const ord = sortOrder.value;
|
const ord = sortOrder.value;
|
||||||
@ -221,33 +222,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let debounceTimer;
|
let debounceTimer;
|
||||||
searchBox.addEventListener('input', () => {
|
searchBox.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(applyFilters, 250); });
|
||||||
clearTimeout(debounceTimer);
|
|
||||||
debounceTimer = setTimeout(applyFilters, 250);
|
|
||||||
});
|
|
||||||
sportFilter.addEventListener('change', () => { populateLeagueFilter(); applyFilters(); });
|
sportFilter.addEventListener('change', () => { populateLeagueFilter(); applyFilters(); });
|
||||||
leagueFilter.addEventListener('change', applyFilters);
|
leagueFilter.addEventListener('change', applyFilters);
|
||||||
dateFilter.addEventListener('change', applyFilters);
|
dateFilter.addEventListener('change', applyFilters);
|
||||||
sortOrder.addEventListener('change', () => { sortFiltered(); renderTable(); });
|
sortOrder.addEventListener('change', () => { sortFiltered(); renderTable(); });
|
||||||
|
|
||||||
refreshBtn.addEventListener('click', async () => {
|
function refreshData() {
|
||||||
try {
|
refreshBtn.disabled = true;
|
||||||
refreshBtn.disabled = true;
|
refreshBtn.textContent = "Načítám...";
|
||||||
refreshBtn.textContent = "Načítám...";
|
fetch('/refresh?apiKey=' + encodeURIComponent(apiKey))
|
||||||
const resp = await fetch('/refresh');
|
.then(r => r.json())
|
||||||
await resp.json();
|
.then(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fetchAll();
|
fetchAll();
|
||||||
|
refreshBtn.disabled = false;
|
||||||
|
refreshBtn.textContent = "Refresh";
|
||||||
|
}, 7000);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
alert('Chyba při obnově dat: ' + err);
|
||||||
refreshBtn.disabled = false;
|
refreshBtn.disabled = false;
|
||||||
refreshBtn.textContent = "Refresh";
|
refreshBtn.textContent = "Refresh";
|
||||||
}, 7000);
|
});
|
||||||
} catch (err) {
|
}
|
||||||
console.error(err);
|
|
||||||
alert('Chyba při obnově dat: ' + err);
|
refreshBtn.addEventListener('click', refreshData);
|
||||||
refreshBtn.disabled = false;
|
|
||||||
refreshBtn.textContent = "Refresh";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fetchAll();
|
fetchAll();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user