mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
Compare commits
11 Commits
f9d02efe49
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
1245bf74c2
|
|||
|
908dd9b57b
|
|||
|
18f86125da
|
|||
|
f4ff9d8c68
|
|||
|
7994f79e02
|
|||
|
d11d360cbf
|
|||
|
573d063d55
|
|||
|
2dd006f14e
|
|||
|
072e24b227
|
|||
|
0795daba4b
|
|||
|
f139449d9a
|
@@ -0,0 +1,59 @@
|
||||
name: Production Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
GH_USER: therealowenrees
|
||||
IMAGE_NAME: therealowenrees/chess-scribe:latest
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: publish image
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
run: echo "${{ secrets.GHCR_SECRET }}" | docker login ghcr.io -u ${{ env.GH_USER }} --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build image and publish with Cache
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VITE_API_BASE_URL=https://api.chess-scribe.org/api/v1
|
||||
VITE_MATOMO_URL=https://analytics.owenrees.eu
|
||||
VITE_MATOMO_SITE_ID="3"
|
||||
VITE_MAINTENANCE_MODE=
|
||||
VITE_MAINTENANCE_MODE_MESSAGE="ChessScribe is currently undergoing maintenance. Some features may be unavailable."
|
||||
|
||||
deploy:
|
||||
needs: publish
|
||||
name: deploy image
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Connect and pull image from GHCR
|
||||
uses: appleboy/ssh-action@v1.2.5
|
||||
with:
|
||||
host: ${{ secrets.HOST }}
|
||||
username: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
port: ${{ secrets.PORT }}
|
||||
script: |
|
||||
cd /home/${{ secrets.USERNAME }}
|
||||
echo "${{ secrets.GHCR_SECRET }}" | docker login ghcr.io -u ${{ env.GH_USER }} --password-stdin
|
||||
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
docker compose up -d
|
||||
+16
-1
@@ -1,6 +1,21 @@
|
||||
# --- Stage 1: Build Stage ---
|
||||
FROM node:24-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 1. Declare the build-time arguments Vite needs
|
||||
ARG VITE_API_BASE_URL
|
||||
ARG VITE_MATOMO_URL
|
||||
ARG VITE_MATOMO_SITE_ID
|
||||
ARG VITE_MAINTENANCE_MODE
|
||||
ARG VITE_MAINTENANCE_MODE_MESSAGE
|
||||
|
||||
# 2. Assign them to environment variables so Vite detects them
|
||||
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL
|
||||
ENV VITE_MATOMO_URL=$VITE_MATOMO_URL
|
||||
ENV VITE_MATOMO_SITE_ID=$VITE_MATOMO_SITE_ID
|
||||
ENV VITE_MAINTENANCE_MODE=$VITE_MAINTENANCE_MODE
|
||||
ENV VITE_MAINTENANCE_MODE_MESSAGE=$VITE_MAINTENANCE_MODE_MESSAGE
|
||||
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
RUN corepack enable pnpm && pnpm i --frozen-lockfile
|
||||
COPY . .
|
||||
@@ -20,4 +35,4 @@ COPY --from=builder /app/.output ./.output
|
||||
EXPOSE 3000
|
||||
|
||||
# Run the compiled Nitro server
|
||||
CMD ["node", ".output/server/index.mjs"]
|
||||
CMD ["node", ".output/server/index.mjs"]
|
||||
|
||||
@@ -10,6 +10,4 @@
|
||||
- react query on for study and chapter load, to allow caching
|
||||
- https://specification.website/checklist/
|
||||
|
||||
- Custom headers do not work - the subtitle is appearing above every thing on first page, and the game starts 2nd page
|
||||
|
||||
- react query to cache the study and chapters
|
||||
@@ -1,17 +1,26 @@
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Image } from "@unpic/react";
|
||||
import { z } from "zod";
|
||||
import MaintenanceModeBanner from "#/components/MaintenanceModeBanner.tsx";
|
||||
import { env } from "#/env.ts";
|
||||
import logo from "@/assets/images/logo.svg?url";
|
||||
|
||||
export default function Header() {
|
||||
const maintenanceModeMessage = env.VITE_MAINTENANCE_MODE
|
||||
? z
|
||||
.string()
|
||||
.transform((value) => (value.trim() === "" ? null : value))
|
||||
.parse(env.VITE_MAINTENANCE_MODE_MESSAGE)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<header>
|
||||
{env.VITE_MAINTENANCE_MODE ? (
|
||||
{maintenanceModeMessage ? (
|
||||
<div className="absolute w-full">
|
||||
<MaintenanceModeBanner message={env.VITE_MAINTENANCE_MODE_MESSAGE} />
|
||||
<MaintenanceModeBanner message={maintenanceModeMessage} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<nav className="w-full max-w-screen-2xl place-self-center p-8">
|
||||
<div className="container mx-auto flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -28,6 +28,8 @@ const LichessStudyLinkInput = ({ setSelectedStudyId }: IProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(result.data);
|
||||
|
||||
setSelectedStudyId(result.data);
|
||||
toast.success("Study loaded — pick a chapter");
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@ import {
|
||||
type Dispatch,
|
||||
useActionState,
|
||||
useEffect,
|
||||
useRef,
|
||||
// useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { useClickOutside } from "#/hooks/useClickOutside.ts";
|
||||
// import { useClickOutside } from "#/hooks/useClickOutside.ts";
|
||||
import { useLichess } from "#/hooks/useLichess.ts";
|
||||
import type { IUserStudyChapter } from "#/interfaces.ts";
|
||||
import type { GameAction } from "#/reducers/gameReducer.ts";
|
||||
@@ -31,7 +31,7 @@ const initialChapterState: ChapterState = {
|
||||
const SelectStudyChapter = ({ selectedStudyId, gameDispatch }: IProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
// const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { getLichessStudyChapters, loadLichessStudyChapter } = useLichess();
|
||||
|
||||
@@ -47,6 +47,9 @@ const SelectStudyChapter = ({ selectedStudyId, gameDispatch }: IProps) => {
|
||||
const result = await getLichessStudyChapters({
|
||||
studyId: selectedStudyId,
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
||||
if (result.ok) {
|
||||
return {
|
||||
status: "success",
|
||||
@@ -54,6 +57,7 @@ const SelectStudyChapter = ({ selectedStudyId, gameDispatch }: IProps) => {
|
||||
error: null,
|
||||
} satisfies ChapterState;
|
||||
}
|
||||
|
||||
return {
|
||||
status: "error",
|
||||
chapters: [],
|
||||
@@ -67,7 +71,7 @@ const SelectStudyChapter = ({ selectedStudyId, gameDispatch }: IProps) => {
|
||||
chapter.name.toLowerCase().includes(search.toLowerCase()),
|
||||
);
|
||||
|
||||
useClickOutside({ isOpen, setIsOpen, setSearch, ref: dropdownRef });
|
||||
// useClickOutside({ isOpen, setIsOpen, setSearch, ref: dropdownRef });
|
||||
|
||||
useEffect(() => {
|
||||
if (isPending || chapterState.status === "success") {
|
||||
|
||||
+4
-2
@@ -1,8 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const clientSchema = z.object({
|
||||
VITE_API_BASE_URL: z.string("Invalid API Base URL format"),
|
||||
VITE_MATOMO_URL: z.string("Invalid URL format"),
|
||||
VITE_API_BASE_URL: z.url("Invalid API Base URL format"),
|
||||
VITE_MATOMO_URL: z.url("Invalid URL format"),
|
||||
VITE_MATOMO_SITE_ID: z.string("Invalid Site ID"),
|
||||
VITE_MAINTENANCE_MODE: z.string("Invalid Maintenance Mode"),
|
||||
VITE_MAINTENANCE_MODE_MESSAGE: z.string().optional(),
|
||||
@@ -27,6 +27,7 @@ const getEnv = () => {
|
||||
const _env = fullSchema.safeParse(rawEnv);
|
||||
if (!_env.success) {
|
||||
console.error("❌ Invalid Server Environment Variables:");
|
||||
console.error(JSON.stringify(_env.error.format(), null, 2));
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
return _env.data;
|
||||
@@ -34,6 +35,7 @@ const getEnv = () => {
|
||||
const _env = clientSchema.safeParse(rawEnv);
|
||||
if (!_env.success) {
|
||||
console.error("❌ Invalid Client Environment Variables:");
|
||||
console.error(JSON.stringify(_env.error.format(), null, 2));
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
return _env.data as z.infer<typeof fullSchema>;
|
||||
|
||||
@@ -65,7 +65,10 @@ const Chessboard = () => {
|
||||
/>
|
||||
) : null}
|
||||
{user ? (
|
||||
<LichessStudyLinkInput setSelectedStudyId={setSelectedStudyId} />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-center">or</p>
|
||||
<LichessStudyLinkInput setSelectedStudyId={setSelectedStudyId} />
|
||||
</div>
|
||||
) : null}
|
||||
</Section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user