4 Commits

Author SHA1 Message Date
jacobh2 5348bedb86 Update base path
Build Backend / build (push) Successful in 9m43s
Build Frontend / build (push) Failing after 10m48s
2026-06-23 22:37:23 +02:00
jacobh2 5bdc37ebcf Add base path 2026-06-23 22:13:38 +02:00
jacobh2 e042943bd3 test hook 2026-06-16 01:35:22 +02:00
jacobh2 1e705b822a feat(backend): add indexes to SleepSession start_time and end_time
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-12 23:08:39 +02:00
9 changed files with 55 additions and 14 deletions
+1 -1
View File
@@ -53,6 +53,7 @@ jobs:
--platform linux/arm64 \ --platform linux/arm64 \
--build-arg VITE_APP_VERSION=$(git describe --tags) \ --build-arg VITE_APP_VERSION=$(git describe --tags) \
--build-arg VITE_APP_GITSHA=$(git rev-parse --short HEAD) \ --build-arg VITE_APP_GITSHA=$(git rev-parse --short HEAD) \
--build-arg VITE_BASE_PATH=/api/ingress/timer \
-t gitea.lan/jacobh2/babymonitor-frontend:${{ env.VERSION }} \ -t gitea.lan/jacobh2/babymonitor-frontend:${{ env.VERSION }} \
./frontend \ ./frontend \
--push --push
@@ -60,4 +61,3 @@ jobs:
run: | run: |
git tag "$VERSION" git tag "$VERSION"
git push origin "$VERSION" git push origin "$VERSION"
@@ -0,0 +1,34 @@
"""add indexes to sleep_session
Revision ID: a3282e5bef40
Revises: 7a424a341b1d
Create Date: 2026-06-12 22:55:33.711961
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'a3282e5bef40'
down_revision: Union[str, Sequence[str], None] = '7a424a341b1d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_index(op.f('ix_sleep_session_end_time'), 'sleep_session', ['end_time'], unique=False)
op.create_index(op.f('ix_sleep_session_start_time'), 'sleep_session', ['start_time'], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_sleep_session_start_time'), table_name='sleep_session')
op.drop_index(op.f('ix_sleep_session_end_time'), table_name='sleep_session')
# ### end Alembic commands ###
+2 -1
View File
@@ -1,3 +1,4 @@
import os
from fastapi import FastAPI from fastapi import FastAPI
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -12,7 +13,7 @@ async def lifespan(app: FastAPI):
yield yield
app = FastAPI(lifespan=lifespan) app = FastAPI(lifespan=lifespan, root_path=os.getenv("ROOT_PATH", ""))
# CORS middleware # CORS middleware
app.add_middleware( app.add_middleware(
+2 -2
View File
@@ -7,7 +7,7 @@ class SleepSession(SQLModel, table=True):
__tablename__ = "sleep_session" __tablename__ = "sleep_session"
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
start_time: datetime = Field(nullable=False) start_time: datetime = Field(index=True, nullable=False)
end_time: Optional[datetime] = Field(default=None, nullable=True) end_time: Optional[datetime] = Field(default=None, nullable=True, index=True)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
+3
View File
@@ -8,12 +8,15 @@ services:
environment: environment:
- DATABASE_URL=sqlite+aiosqlite:///./data/sleep.db - DATABASE_URL=sqlite+aiosqlite:///./data/sleep.db
- CORS_ORIGINS=http://localhost - CORS_ORIGINS=http://localhost
- ROOT_PATH=/api/ingress/timer
restart: unless-stopped restart: unless-stopped
frontend: frontend:
build: build:
context: ./frontend context: ./frontend
platforms: [linux/arm64] platforms: [linux/arm64]
args:
- VITE_BASE_PATH=/api/ingress/timer
ports: ports:
- "8080:80" - "8080:80"
depends_on: depends_on:
+2
View File
@@ -2,8 +2,10 @@ FROM node:22-alpine AS build
ARG VITE_APP_VERSION=unknown ARG VITE_APP_VERSION=unknown
ARG VITE_APP_GITSHA=unknown ARG VITE_APP_GITSHA=unknown
ARG VITE_BASE_PATH
ENV VITE_APP_VERSION=$VITE_APP_VERSION ENV VITE_APP_VERSION=$VITE_APP_VERSION
ENV VITE_APP_GITSHA=$VITE_APP_GITSHA ENV VITE_APP_GITSHA=$VITE_APP_GITSHA
ENV VITE_BASE_PATH=$VITE_BASE_PATH
WORKDIR /app WORKDIR /app
# Copy package files first to leverage Docker cache # Copy package files first to leverage Docker cache
+1 -1
View File
@@ -9,7 +9,7 @@ import Footer from './components/Footer';
function App() { function App() {
return ( return (
<Router> <Router basename={import.meta.env.BASE_URL}>
<div className="min-h-screen bg-[#f8fafc] text-slate-900 font-sans antialiased"> <div className="min-h-screen bg-[#f8fafc] text-slate-900 font-sans antialiased">
<Navbar /> <Navbar />
<main className="container mx-auto px-4 sm:px-6 lg:px-8 mt-8"> <main className="container mx-auto px-4 sm:px-6 lg:px-8 mt-8">
+9 -8
View File
@@ -9,7 +9,8 @@ import type {
SummaryResponse SummaryResponse
} from '@/types'; } from '@/types';
const BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; const API_BASE = import.meta.env.BASE_URL.replace(/\/$/, '')
export const apiUrl = (path: string) => `${API_BASE}${path}`
/** /**
* Convert a local HH:MM time string to its UTC equivalent. * Convert a local HH:MM time string to its UTC equivalent.
@@ -33,7 +34,7 @@ export function localToUTC(localValue: string): string {
async function fetchStats<T>(endpoint: string, from_date: string, to_date: string, extraParams: Record<string, string> = {}): Promise<T> { async function fetchStats<T>(endpoint: string, from_date: string, to_date: string, extraParams: Record<string, string> = {}): Promise<T> {
const params = new URLSearchParams({ from_date, to_date, ...extraParams }); const params = new URLSearchParams({ from_date, to_date, ...extraParams });
const response = await fetch(`${BASE_URL}/api/stats/${endpoint}?${params.toString()}`); const response = await fetch(apiUrl(`/api/stats/${endpoint}?${params.toString()}`));
if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`); if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`);
return response.json(); return response.json();
} }
@@ -60,19 +61,19 @@ export async function fetchWakeSleepTimes(from_date: string, to_date: string, wa
// Session API functions // Session API functions
export async function fetchActiveSession(): Promise<SleepSessionRead | null> { export async function fetchActiveSession(): Promise<SleepSessionRead | null> {
const response = await fetch(`${BASE_URL}/api/sessions/active`); const response = await fetch(apiUrl('/api/sessions/active'));
if (!response.ok) return null; if (!response.ok) return null;
return response.json(); return response.json();
} }
export async function fetchSessions(): Promise<SessionListResponse> { export async function fetchSessions(): Promise<SessionListResponse> {
const response = await fetch(`${BASE_URL}/api/sessions`); const response = await fetch(apiUrl('/api/sessions'));
if (!response.ok) throw new Error('Failed to fetch sessions'); if (!response.ok) throw new Error('Failed to fetch sessions');
return response.json(); return response.json();
} }
export async function createSession(payload: SleepSessionCreate): Promise<SleepSessionRead> { export async function createSession(payload: SleepSessionCreate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions`, { const response = await fetch(apiUrl('/api/sessions'), {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), body: JSON.stringify(payload),
@@ -82,7 +83,7 @@ export async function createSession(payload: SleepSessionCreate): Promise<SleepS
} }
export async function endSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> { export async function endSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}/end`, { const response = await fetch(apiUrl(`/api/sessions/${sessionId}/end`), {
method: 'PATCH', method: 'PATCH',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), body: JSON.stringify(payload),
@@ -92,14 +93,14 @@ export async function endSession(sessionId: number, payload: SleepSessionUpdate)
} }
export async function deleteSession(sessionId: number): Promise<void> { export async function deleteSession(sessionId: number): Promise<void> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, { const response = await fetch(apiUrl(`/api/sessions/${sessionId}`), {
method: 'DELETE', method: 'DELETE',
}); });
if (!response.ok) throw new Error('Failed to delete session'); if (!response.ok) throw new Error('Failed to delete session');
} }
export async function updateSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> { export async function updateSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, { const response = await fetch(apiUrl(`/api/sessions/${sessionId}`), {
method: 'PATCH', method: 'PATCH',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload), body: JSON.stringify(payload),
+1 -1
View File
@@ -19,7 +19,7 @@ if (isDev) {
} }
export default defineConfig({ export default defineConfig({
base: './', base: process.env.VITE_BASE_PATH || '/',
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
define: { define: {
__APP_VERSION__: JSON.stringify(APP_VERSION), __APP_VERSION__: JSON.stringify(APP_VERSION),