API change
This commit is contained in:
parent
c2d5d54592
commit
062c659cde
@ -18,7 +18,10 @@ func NewRouter(svc service.EpisodeService) *gin.Engine {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
})
|
||||
|
||||
r.GET("/time", func(c *gin.Context) {
|
||||
api := r.Group("/api")
|
||||
v1 := api.Group("/v1")
|
||||
|
||||
v1.GET("/time", func(c *gin.Context) {
|
||||
now := time.Now().UTC().UnixMilli()
|
||||
|
||||
c.Header("Cache-Control", "no-store, max-age=0, must-revalidate")
|
||||
@ -27,8 +30,6 @@ func NewRouter(svc service.EpisodeService) *gin.Engine {
|
||||
})
|
||||
})
|
||||
|
||||
v1 := r.Group("/v1")
|
||||
|
||||
// GET /v1/current
|
||||
v1.GET("/current", func(c *gin.Context) {
|
||||
cur, err := svc.GetCurrent(c.Request.Context())
|
||||
|
||||
@ -2,24 +2,23 @@ server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# Static files for the SPA
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# (Optional) redirect bare root to /watch-party/
|
||||
# Helps avoid loading "/" by mistake (your log shows that happened once)
|
||||
# Redirect bare root to /watch-party/
|
||||
location = / { return 302 /watch-party/; }
|
||||
|
||||
# Redirect no-trailing-slash to trailing slash
|
||||
# Ensure trailing slash
|
||||
location = /watch-party { return 302 /watch-party/; }
|
||||
|
||||
# Serve the SPA under /watch-party/ using alias
|
||||
# /watch-party/... -> /usr/share/nginx/html/...
|
||||
# Serve SPA under /watch-party/
|
||||
location /watch-party/ {
|
||||
alias /usr/share/nginx/html/;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Proxy API unchanged (your React app should request /api/current)
|
||||
# Backend API (preserve /api/v1/… path)
|
||||
location /api/ {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
@ -27,6 +26,6 @@ server {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Connection "";
|
||||
proxy_pass ${BACKEND_ORIGIN}/;
|
||||
proxy_pass ${BACKEND_ORIGIN}; # no trailing slash!
|
||||
}
|
||||
}
|
||||
7
frontend/src/api/endpoint.ts
Normal file
7
frontend/src/api/endpoint.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const API_ENDPOINT = {
|
||||
v1: {
|
||||
CURRENT: `/api/v1/current`,
|
||||
SHOWS: `/api/v1/shows`,
|
||||
TIME: `/api/v1/time`
|
||||
},
|
||||
} as const;
|
||||
@ -1,9 +1,10 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useServerClock } from "../hooks/useServerClock";
|
||||
import { API_ENDPOINT } from "../api/endpoint";
|
||||
|
||||
// ===== Config & fallbacks =====
|
||||
const TIMEZONE = "Asia/Tokyo"; // JST (UTC+09)
|
||||
const API_URL = "/api/v1/current";
|
||||
const API_URL_CURRENT = API_ENDPOINT.v1.CURRENT
|
||||
const FALLBACK_START_HOUR = 19;
|
||||
const FALLBACK_START_MINUTE = 25;
|
||||
const FALLBACK_END_SECONDS = 300;
|
||||
@ -74,7 +75,7 @@ function jstToUtcMs(y: number, m: number, d: number, hh: number, mm: number, ss
|
||||
}
|
||||
|
||||
async function loadSchedule(signal?: AbortSignal) {
|
||||
const res = await fetch(API_URL, { cache: "no-store", signal });
|
||||
const res = await fetch(API_URL_CURRENT, { cache: "no-store", signal });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return (await res.json()) as ApiSchedule;
|
||||
}
|
||||
@ -97,7 +98,6 @@ export default function Timer() {
|
||||
const [untilStart, setUntilStart] = useState(0);
|
||||
const timerRef = useRef<number | null>(null);
|
||||
const { nowMs, ready, error: timeError } = useServerClock({
|
||||
endpoint: `${import.meta.env.BASE_URL}api/time`, // works under /watch-party/ in prod, /api in dev
|
||||
refreshMs: TIME_SYNC_INTERVAL,
|
||||
});
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { API_ENDPOINT } from "../api/endpoint";
|
||||
|
||||
/** Uses /api/time => { now: <ms UTC> } and returns a server-correct "nowMs()" */
|
||||
const TIME_URL_ENDPOINT = API_ENDPOINT.v1.TIME;
|
||||
|
||||
export function useServerClock(opts?: {
|
||||
endpoint?: string;
|
||||
refreshMs?: number;
|
||||
enabled?: boolean;
|
||||
}) {
|
||||
const {
|
||||
endpoint = "/api/time",
|
||||
refreshMs = 60_000,
|
||||
enabled = true,
|
||||
} = opts || {};
|
||||
@ -20,7 +21,7 @@ export function useServerClock(opts?: {
|
||||
try {
|
||||
setError(null);
|
||||
const t0 = Date.now();
|
||||
const res = await fetch(endpoint, { cache: "no-store" });
|
||||
const res = await fetch(TIME_URL_ENDPOINT, { cache: "no-store" });
|
||||
const t1 = Date.now();
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const data = await res.json();
|
||||
@ -31,7 +32,7 @@ export function useServerClock(opts?: {
|
||||
} catch (e: any) {
|
||||
setError(e?.message || "time sync failed");
|
||||
}
|
||||
}, [endpoint]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { API_ENDPOINT } from "../api/endpoint";
|
||||
|
||||
type Show = {
|
||||
id: number;
|
||||
@ -10,8 +11,8 @@ type Show = {
|
||||
date_created: string;
|
||||
};
|
||||
|
||||
const GET_URL = "/api/v1/shows";
|
||||
const POST_URL = "/api/v1/current";
|
||||
const GET_URL = API_ENDPOINT.v1.SHOWS;
|
||||
const POST_URL = API_ENDPOINT.v1.CURRENT;
|
||||
|
||||
const HHMM = /^(\d{1,2}):([0-5]\d)$/;
|
||||
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
// Use PUBLIC_BASE_PATH="/watch-party/" for prod container builds.
|
||||
// In dev, keep "/" so local paths are simple.
|
||||
export default defineConfig(({ mode }) => {
|
||||
const base = mode === "development" ? "/" : process.env.PUBLIC_BASE_PATH || "/";
|
||||
const base =
|
||||
mode === "development"
|
||||
? "/"
|
||||
: process.env.PUBLIC_BASE_PATH || "/watch-party/";
|
||||
|
||||
return {
|
||||
base,
|
||||
plugins: [react()],
|
||||
|
||||
// DEV: proxy /api/* directly to Go backend
|
||||
server: {
|
||||
port: 5173,
|
||||
open: true,
|
||||
@ -13,7 +21,16 @@ export default defineConfig(({ mode }) => {
|
||||
"/api": {
|
||||
target: "http://localhost:8082",
|
||||
changeOrigin: true,
|
||||
rewrite: p => p.replace(/^\/api/, ""),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// For `vite preview`, keep the same proxy
|
||||
preview: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8082",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user