aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.env.example1
-rw-r--r--backend/admin/handler.go42
-rw-r--r--backend/admin/templates/base.html6
-rw-r--r--backend/admin/templates/dashboard.html10
-rw-r--r--backend/admin/templates/game_edit.html4
-rw-r--r--backend/admin/templates/games.html2
-rw-r--r--backend/admin/templates/online_qualifying_ranking.html2
-rw-r--r--backend/admin/templates/user_edit.html2
-rw-r--r--backend/admin/templates/users.html2
-rw-r--r--backend/config/config.go (renamed from backend/config.go)32
-rw-r--r--backend/main.go21
-rw-r--r--compose.prod.yaml1
-rw-r--r--docs/DEV.md3
-rw-r--r--frontend/Dockerfile2
-rw-r--r--frontend/app/api/client.ts5
-rw-r--r--frontend/app/components/UserIcon.tsx6
-rw-r--r--frontend/app/config.ts2
-rw-r--r--frontend/app/root.tsx3
-rw-r--r--frontend/app/routes/_index.tsx3
-rw-r--r--frontend/app/routes/dashboard.tsx5
-rw-r--r--frontend/react-router.config.ts2
-rw-r--r--frontend/vite.config.ts2
22 files changed, 89 insertions, 69 deletions
diff --git a/.env.example b/.env.example
index f8186e1..c249872 100644
--- a/.env.example
+++ b/.env.example
@@ -1,2 +1,3 @@
+ALBATROSS_BASE_PATH=/phperkaigi/2025/code-battle/
ALBATROSS_JWT_SECRET=[your_secret_key]
ALBATROSS_COOKIE_SECRET=[your_secret_key]
diff --git a/backend/admin/handler.go b/backend/admin/handler.go
index 81ea2b7..f7fd63e 100644
--- a/backend/admin/handler.go
+++ b/backend/admin/handler.go
@@ -14,33 +14,31 @@ import (
"github.com/nsfisis/phperkaigi-2025-albatross/backend/account"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/auth"
+ "github.com/nsfisis/phperkaigi-2025-albatross/backend/config"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/db"
)
-const (
- basePath = "/phperkaigi/2025/code-battle"
-)
-
var jst = time.FixedZone("Asia/Tokyo", 9*60*60)
type Handler struct {
- q *db.Queries
+ q *db.Queries
+ conf *config.Config
}
-func NewHandler(q *db.Queries) *Handler {
- return &Handler{q: q}
+func NewHandler(q *db.Queries, conf *config.Config) *Handler {
+ return &Handler{q: q, conf: conf}
}
-func newAdminMiddleware() echo.MiddlewareFunc {
+func (h *Handler) newAdminMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
jwt, err := c.Cookie("albatross_token")
if err != nil {
- return c.Redirect(http.StatusSeeOther, basePath+"/login")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"login")
}
claims, err := auth.ParseJWT(jwt.Value)
if err != nil {
- return c.Redirect(http.StatusSeeOther, basePath+"/login")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"login")
}
if !claims.IsAdmin {
return echo.NewHTTPError(http.StatusForbidden)
@@ -52,7 +50,7 @@ func newAdminMiddleware() echo.MiddlewareFunc {
func (h *Handler) RegisterHandlers(g *echo.Group) {
g.Use(newAssetsMiddleware())
- g.Use(newAdminMiddleware())
+ g.Use(h.newAdminMiddleware())
g.GET("/dashboard", h.getDashboard)
g.GET("/users", h.getUsers)
@@ -69,7 +67,7 @@ func (h *Handler) RegisterHandlers(g *echo.Group) {
func (h *Handler) getDashboard(c echo.Context) error {
return c.Render(http.StatusOK, "dashboard", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "Dashboard",
})
}
@@ -91,7 +89,7 @@ func (h *Handler) getUsers(c echo.Context) error {
}
return c.Render(http.StatusOK, "users", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "Users",
"Users": users,
})
@@ -111,7 +109,7 @@ func (h *Handler) getUserEdit(c echo.Context) error {
}
return c.Render(http.StatusOK, "user_edit", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "User Edit",
"User": echo.Map{
"UserID": row.UserID,
@@ -155,7 +153,7 @@ func (h *Handler) postUserEdit(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.Redirect(http.StatusSeeOther, basePath+"/admin/users")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/users")
}
func (h *Handler) postUserFetchIcon(c echo.Context) error {
@@ -177,7 +175,7 @@ func (h *Handler) postUserFetchIcon(c echo.Context) error {
// The failure is intentionally ignored. Retry manually if needed.
}
}()
- return c.Redirect(http.StatusSeeOther, basePath+"/admin/users")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/users")
}
func (h *Handler) getGames(c echo.Context) error {
@@ -203,7 +201,7 @@ func (h *Handler) getGames(c echo.Context) error {
}
return c.Render(http.StatusOK, "games", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "Games",
"Games": games,
})
@@ -257,7 +255,7 @@ func (h *Handler) getGameEdit(c echo.Context) error {
}
return c.Render(http.StatusOK, "game_edit", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "Game Edit",
"Game": echo.Map{
"GameID": row.GameID,
@@ -367,7 +365,7 @@ func (h *Handler) postGameEdit(c echo.Context) error {
}
}
- return c.Redirect(http.StatusSeeOther, basePath+"/admin/games")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/games")
}
func (h *Handler) postGameStart(c echo.Context) error {
@@ -389,7 +387,7 @@ func (h *Handler) postGameStart(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- return c.Redirect(http.StatusSeeOther, basePath+"/admin/games")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/games")
}
func (h *Handler) getOnlineQualifyingRanking(c echo.Context) error {
@@ -424,7 +422,7 @@ func (h *Handler) getOnlineQualifyingRanking(c echo.Context) error {
}
}
return c.Render(http.StatusOK, "online_qualifying_ranking", echo.Map{
- "BasePath": basePath,
+ "BasePath": h.conf.BasePath,
"Title": "Online Qualifying Ranking",
"Entries": entries,
})
@@ -464,5 +462,5 @@ func (h *Handler) postFix(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
}
- return c.Redirect(http.StatusSeeOther, basePath+"/admin/dashboard")
+ return c.Redirect(http.StatusSeeOther, h.conf.BasePath+"admin/dashboard")
}
diff --git a/backend/admin/templates/base.html b/backend/admin/templates/base.html
index 08c7dc5..653acc6 100644
--- a/backend/admin/templates/base.html
+++ b/backend/admin/templates/base.html
@@ -2,9 +2,9 @@
<html>
<head>
<title>ADMIN {{ .Title }} | PHPerKaigi 2025 Albatross</title>
- <link rel="icon" href="{{ .BasePath }}/favicon.svg">
- <link rel="stylesheet" href="{{ .BasePath }}/admin/css/normalize.css">
- <link rel="stylesheet" href="{{ .BasePath }}/admin/css/sakura.css">
+ <link rel="icon" href="{{ .BasePath }}favicon.svg">
+ <link rel="stylesheet" href="{{ .BasePath }}admin/css/normalize.css">
+ <link rel="stylesheet" href="{{ .BasePath }}admin/css/sakura.css">
</head>
<body>
<section>
diff --git a/backend/admin/templates/dashboard.html b/backend/admin/templates/dashboard.html
index 92133b6..dcc71ba 100644
--- a/backend/admin/templates/dashboard.html
+++ b/backend/admin/templates/dashboard.html
@@ -2,18 +2,18 @@
{{ define "content" }}
<p>
- <a href="{{ .BasePath }}/admin/users">Users</a>
+ <a href="{{ .BasePath }}admin/users">Users</a>
</p>
<p>
- <a href="{{ .BasePath }}/admin/games">Games</a>
+ <a href="{{ .BasePath }}admin/games">Games</a>
</p>
<p>
- <a href="{{ .BasePath }}/admin/online-qualifying-ranking?game_1=7&game_2=8">Online Qualifying Ranking</a>
+ <a href="{{ .BasePath }}admin/online-qualifying-ranking?game_1=7&game_2=8">Online Qualifying Ranking</a>
</p>
-<form method="post" action="{{ .BasePath }}/admin/fix">
+<form method="post" action="{{ .BasePath }}admin/fix">
<button type="submit">fix</button>
</form>
-<form method="post" action="{{ .BasePath }}/logout">
+<form method="post" action="{{ .BasePath }}logout">
<button type="submit">Logout</button>
</form>
{{ end }}
diff --git a/backend/admin/templates/game_edit.html b/backend/admin/templates/game_edit.html
index 0d92c95..2d769c4 100644
--- a/backend/admin/templates/game_edit.html
+++ b/backend/admin/templates/game_edit.html
@@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "breadcrumb" }}
-<a href="{{ .BasePath }}/admin/dashboard">Dashboard</a> | <a href="{{ .BasePath }}/admin/games">Games</a>
+<a href="{{ .BasePath }}admin/dashboard">Dashboard</a> | <a href="{{ .BasePath }}admin/games">Games</a>
{{ end }}
{{ define "content" }}
@@ -59,7 +59,7 @@
<button type="submit">Save</button>
</div>
<div>
- <button type="submit" formaction="{{ .BasePath }}/admin/games/{{ .Game.GameID }}/start">Start</button>
+ <button type="submit" formaction="{{ .BasePath }}admin/games/{{ .Game.GameID }}/start">Start</button>
</div>
</form>
{{ end }}
diff --git a/backend/admin/templates/games.html b/backend/admin/templates/games.html
index 3be6726..b5c512a 100644
--- a/backend/admin/templates/games.html
+++ b/backend/admin/templates/games.html
@@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "breadcrumb" }}
-<a href="{{ .BasePath }}/admin/dashboard">Dashboard</a>
+<a href="{{ .BasePath }}admin/dashboard">Dashboard</a>
{{ end }}
{{ define "content" }}
diff --git a/backend/admin/templates/online_qualifying_ranking.html b/backend/admin/templates/online_qualifying_ranking.html
index 039687e..663b68e 100644
--- a/backend/admin/templates/online_qualifying_ranking.html
+++ b/backend/admin/templates/online_qualifying_ranking.html
@@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "breadcrumb" }}
-<a href="{{ .BasePath }}/admin/dashboard">Dashboard</a>
+<a href="{{ .BasePath }}admin/dashboard">Dashboard</a>
{{ end }}
{{ define "content" }}
diff --git a/backend/admin/templates/user_edit.html b/backend/admin/templates/user_edit.html
index 6942204..4c1016b 100644
--- a/backend/admin/templates/user_edit.html
+++ b/backend/admin/templates/user_edit.html
@@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "breadcrumb" }}
-<a href="{{ .BasePath }}/admin/dashboard">Dashboard</a> | <a href="{{ .BasePath }}/admin/users">Users</a>
+<a href="{{ .BasePath }}admin/dashboard">Dashboard</a> | <a href="{{ .BasePath }}admin/users">Users</a>
{{ end }}
{{ define "content" }}
diff --git a/backend/admin/templates/users.html b/backend/admin/templates/users.html
index 36fae0d..4b8e6dc 100644
--- a/backend/admin/templates/users.html
+++ b/backend/admin/templates/users.html
@@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "breadcrumb" }}
-<a href="{{ .BasePath }}/admin/dashboard">Dashboard</a>
+<a href="{{ .BasePath }}admin/dashboard">Dashboard</a>
{{ end }}
{{ define "content" }}
diff --git a/backend/config.go b/backend/config/config.go
index 99a6d54..8211bfd 100644
--- a/backend/config.go
+++ b/backend/config/config.go
@@ -1,4 +1,4 @@
-package main
+package config
import (
"fmt"
@@ -6,12 +6,13 @@ import (
)
type Config struct {
- dbHost string
- dbPort string
- dbUser string
- dbPassword string
- dbName string
- isLocal bool
+ DBHost string
+ DBPort string
+ DBUser string
+ DBPassword string
+ DBName string
+ BasePath string
+ IsLocal bool
}
func NewConfigFromEnv() (*Config, error) {
@@ -35,14 +36,19 @@ func NewConfigFromEnv() (*Config, error) {
if !exists {
return nil, fmt.Errorf("ALBATROSS_DB_NAME not set")
}
+ basePath, exists := os.LookupEnv("ALBATROSS_BASE_PATH")
+ if !exists {
+ return nil, fmt.Errorf("ALBATROSS_BASE_PATH not set")
+ }
isLocalStr, exists := os.LookupEnv("ALBATROSS_IS_LOCAL")
isLocal := exists && isLocalStr == "1"
return &Config{
- dbHost: dbHost,
- dbPort: dbPort,
- dbUser: dbUser,
- dbPassword: dbPassword,
- dbName: dbName,
- isLocal: isLocal,
+ DBHost: dbHost,
+ DBPort: dbPort,
+ DBUser: dbUser,
+ DBPassword: dbPassword,
+ DBName: dbName,
+ BasePath: basePath,
+ IsLocal: isLocal,
}, nil
}
diff --git a/backend/main.go b/backend/main.go
index 06d37c3..c13dea4 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -13,6 +13,7 @@ import (
"github.com/nsfisis/phperkaigi-2025-albatross/backend/admin"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/api"
+ "github.com/nsfisis/phperkaigi-2025-albatross/backend/config"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/db"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/game"
"github.com/nsfisis/phperkaigi-2025-albatross/backend/taskqueue"
@@ -33,19 +34,19 @@ func connectDB(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
func main() {
var err error
- config, err := NewConfigFromEnv()
+ conf, err := config.NewConfigFromEnv()
if err != nil {
log.Fatalf("Error loading env %v", err)
}
- openAPISpec, err := api.GetSwaggerWithPrefix("/phperkaigi/2025/code-battle/api")
+ openAPISpec, err := api.GetSwaggerWithPrefix(conf.BasePath + "api")
if err != nil {
log.Fatalf("Error loading OpenAPI spec\n: %s", err)
}
ctx := context.Background()
- dbDSN := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.dbHost, config.dbPort, config.dbUser, config.dbPassword, config.dbName)
+ dbDSN := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", conf.DBHost, conf.DBPort, conf.DBUser, conf.DBPassword, conf.DBName)
connPool, err := connectDB(ctx, dbDSN)
if err != nil {
log.Fatalf("Error connecting to db %v", err)
@@ -65,27 +66,27 @@ func main() {
gameHub := game.NewGameHub(queries, taskQueue, workerServer)
- apiGroup := e.Group("/phperkaigi/2025/code-battle/api")
+ apiGroup := e.Group(conf.BasePath + "api")
apiGroup.Use(oapimiddleware.OapiRequestValidator(openAPISpec))
apiHandler := api.NewHandler(queries, gameHub)
api.RegisterHandlers(apiGroup, api.NewStrictHandler(apiHandler, nil))
- adminHandler := admin.NewHandler(queries)
- adminGroup := e.Group("/phperkaigi/2025/code-battle/admin")
+ adminHandler := admin.NewHandler(queries, conf)
+ adminGroup := e.Group(conf.BasePath + "admin")
adminHandler.RegisterHandlers(adminGroup)
- if config.isLocal {
- filesGroup := e.Group("/phperkaigi/2025/code-battle/files")
+ if conf.IsLocal {
+ filesGroup := e.Group(conf.BasePath + "files")
filesGroup.Use(middleware.StaticWithConfig(middleware.StaticConfig{
Root: "/",
Filesystem: http.Dir("/data/files"),
IgnoreBase: true,
}))
- e.GET("/phperkaigi/2025/code-battle/*", func(c echo.Context) error {
+ e.GET(conf.BasePath+"*", func(c echo.Context) error {
return c.Redirect(http.StatusPermanentRedirect, "http://localhost:5173"+c.Request().URL.Path)
})
- e.POST("/phperkaigi/2025/code-battle/*", func(c echo.Context) error {
+ e.POST(conf.BasePath+"*", func(c echo.Context) error {
return c.Redirect(http.StatusPermanentRedirect, "http://localhost:5173"+c.Request().URL.Path)
})
diff --git a/compose.prod.yaml b/compose.prod.yaml
index 1da939b..5e12083 100644
--- a/compose.prod.yaml
+++ b/compose.prod.yaml
@@ -37,6 +37,7 @@ services:
context: ./frontend
args:
ALBATROSS_HOST: localhost
+ ALBATROSS_BASE_PATH: $ALBATROSS_BASE_PATH
expose:
- 80
env_file: [.env]
diff --git a/docs/DEV.md b/docs/DEV.md
index 7f79840..2a44f4a 100644
--- a/docs/DEV.md
+++ b/docs/DEV.md
@@ -18,6 +18,9 @@
1. Clone the repository.
1. `cd path/to/the/repo`
+1. Copy `.env.example` to `.env` and configure:
+ * `ALBATROSS_JWT_SECRET`: Secret key for JWT tokens
+ * `ALBATROSS_COOKIE_SECRET`: Secret key for cookies
1. `make init`
1. `make up`
1. Access to http://localhost:5173/phperkaigi/2025/code-battle/.
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index 0e6499c..2659296 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -1,4 +1,5 @@
ARG ALBATROSS_HOST
+ARG ALBATROSS_BASE_PATH
FROM node:22.14 AS builder
@@ -9,6 +10,7 @@ COPY package.json package-lock.json .
RUN npm install --include=dev
COPY . .
+ENV ALBATROSS_BASE_PATH="$ALBATROSS_BASE_PATH"
RUN npm run build
################################################################################
diff --git a/frontend/app/api/client.ts b/frontend/app/api/client.ts
index ee5437d..6fa784f 100644
--- a/frontend/app/api/client.ts
+++ b/frontend/app/api/client.ts
@@ -1,12 +1,13 @@
import createClient from "openapi-fetch";
import { createContext } from "react";
+import { API_BASE_PATH } from "../config";
import type { paths } from "./schema";
const client = createClient<paths>({
baseUrl:
process.env.NODE_ENV === "development"
- ? "http://localhost:8003/phperkaigi/2025/code-battle/api/"
- : "https://t.nil.ninja/phperkaigi/2025/code-battle/api/",
+ ? `http://localhost:8003${API_BASE_PATH}`
+ : `https://t.nil.ninja${API_BASE_PATH}`,
});
export async function apiLogin(username: string, password: string) {
diff --git a/frontend/app/components/UserIcon.tsx b/frontend/app/components/UserIcon.tsx
index e14a571..8002c6f 100644
--- a/frontend/app/components/UserIcon.tsx
+++ b/frontend/app/components/UserIcon.tsx
@@ -1,3 +1,5 @@
+import { BASE_PATH } from "../config";
+
type Props = {
iconPath: string;
displayName: string;
@@ -9,8 +11,8 @@ export default function UserIcon({ iconPath, displayName, className }: Props) {
<img
src={
process.env.NODE_ENV === "development"
- ? `http://localhost:8003/phperkaigi/2025/code-battle${iconPath}`
- : `/phperkaigi/2025/code-battle${iconPath}`
+ ? `http://localhost:8003${BASE_PATH}${iconPath}`
+ : `${BASE_PATH}${iconPath}`
}
alt={`${displayName} のアイコン`}
className={`rounded-full border-4 border-white ${className}`}
diff --git a/frontend/app/config.ts b/frontend/app/config.ts
new file mode 100644
index 0000000..3e6e642
--- /dev/null
+++ b/frontend/app/config.ts
@@ -0,0 +1,2 @@
+export const BASE_PATH = import.meta.env.VITE_ALBATROSS_BASE_PATH || "/";
+export const API_BASE_PATH = `${BASE_PATH}api/`;
diff --git a/frontend/app/root.tsx b/frontend/app/root.tsx
index f1b5c54..7d08061 100644
--- a/frontend/app/root.tsx
+++ b/frontend/app/root.tsx
@@ -4,11 +4,12 @@ import type { LinksFunction } from "react-router";
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
import "./tailwind.css";
import "./shiki.css";
+import { BASE_PATH } from "./config";
config.autoAddCss = false;
export const links: LinksFunction = () => [
- { rel: "icon", href: "/phperkaigi/2025/code-battle/favicon.svg" },
+ { rel: "icon", href: `${BASE_PATH}code-battle/favicon.svg` },
];
export function Layout({ children }: { children: React.ReactNode }) {
diff --git a/frontend/app/routes/_index.tsx b/frontend/app/routes/_index.tsx
index ef54c84..9767985 100644
--- a/frontend/app/routes/_index.tsx
+++ b/frontend/app/routes/_index.tsx
@@ -2,6 +2,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "react-router";
import { ensureUserNotLoggedIn } from "../.server/auth";
import BorderedContainer from "../components/BorderedContainer";
import NavigateLink from "../components/NavigateLink";
+import { BASE_PATH } from "../config";
export const meta: MetaFunction = () => [
{ title: "PHPerKaigi 2025 Albatross" },
@@ -16,7 +17,7 @@ export default function Index() {
return (
<div className="min-h-screen bg-sky-600 flex flex-col items-center justify-center gap-y-6">
<img
- src="/phperkaigi/2025/code-battle/logo.svg"
+ src={`${BASE_PATH}logo.svg`}
alt="PHPerKaigi 2025"
className="w-64 h-64"
/>
diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx
index b7bfb01..75e809b 100644
--- a/frontend/app/routes/dashboard.tsx
+++ b/frontend/app/routes/dashboard.tsx
@@ -5,6 +5,7 @@ import { createApiClient } from "../api/client";
import BorderedContainerWithCaption from "../components/BorderedContainerWithCaption";
import NavigateLink from "../components/NavigateLink";
import UserIcon from "../components/UserIcon";
+import { BASE_PATH } from "../config";
export const meta: MetaFunction = () => [
{ title: "Dashboard | PHPerKaigi 2025 Albatross" },
@@ -76,8 +77,8 @@ export default function Dashboard() {
<a
href={
process.env.NODE_ENV === "development"
- ? "http://localhost:8003/phperkaigi/2025/code-battle/admin/dashboard"
- : "/phperkaigi/2025/code-battle/admin/dashboard"
+ ? `http://localhost:8003${BASE_PATH}admin/dashboard`
+ : `${BASE_PATH}admin/dashboard`
}
className="text-lg text-white bg-sky-600 px-4 py-2 rounded-sm transition duration-300 hover:bg-sky-500 focus:ring-3 focus:ring-sky-400 focus:outline-hidden"
>
diff --git a/frontend/react-router.config.ts b/frontend/react-router.config.ts
index cade2ea..10392e4 100644
--- a/frontend/react-router.config.ts
+++ b/frontend/react-router.config.ts
@@ -1,5 +1,5 @@
import type { Config } from "@react-router/dev/config";
export default {
- basename: "/phperkaigi/2025/code-battle/",
+ basename: process.env.ALBATROSS_BASE_PATH || "/",
} satisfies Config;
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index aab20f4..b9b2873 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -4,6 +4,6 @@ import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
- base: "/phperkaigi/2025/code-battle/",
+ base: process.env.ALBATROSS_BASE_PATH || "/",
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
});