Unraid deployment fixes and generator improvements

- Add Next.js rewrites to proxy API calls through same origin (fixes login/media on werkout.treytartt.com)
- Fix mediaUrl() in DayCard and ExerciseRow to use relative paths in production
- Add proxyTimeout for long-running workout generation endpoints
- Add CSRF trusted origin for treytartt.com
- Split docker-compose into production (Unraid) and dev configs
- Show display_name and descriptions on workout type cards
- Generator: rules engine improvements, movement enforcement, exercise selector updates
- Add new test files for rules drift, workout research generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-23 10:25:45 -06:00
parent 1c61b80731
commit 03681c532d
21 changed files with 2366 additions and 138 deletions

View File

@@ -84,14 +84,14 @@ export function WorkoutTypesStep({
isSelected ? "text-accent" : "text-zinc-100"
}`}
>
{wt.name}
{wt.display_name || wt.name}
</span>
<Badge variant={intensityVariant[wt.typical_intensity] || "default"}>
{wt.typical_intensity}
</Badge>
</div>
{wt.description && (
<p className="text-sm text-zinc-400 line-clamp-2">
<p className="text-sm text-zinc-400">
{wt.description}
</p>
)}

View File

@@ -63,7 +63,10 @@ function XIcon({ className = "" }: { className?: string }) {
function mediaUrl(path: string): string {
if (typeof window === "undefined") return path;
return `${window.location.protocol}//${window.location.hostname}:8001${path}`;
if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") {
return `${window.location.protocol}//${window.location.hostname}:8001${path}`;
}
return path;
}
function PlayIcon({ className = "" }: { className?: string }) {
@@ -301,6 +304,7 @@ export function DayCard({
focus_area: previewDay.focus_area,
workout_type_id: previewDay.workout_type_id,
date: previewDay.date,
plan_id: previewDay.plan_id,
});
onPreviewDayChange(previewDayIndex, newDay);
} catch (err) {
@@ -421,6 +425,17 @@ export function DayCard({
)}
</div>
{previewDay.warnings && previewDay.warnings.length > 0 && (
<div className="rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-2 text-xs text-yellow-200">
<p className="font-semibold mb-1">Warnings</p>
<ul className="list-disc list-inside space-y-0.5">
{previewDay.warnings.map((w, idx) => (
<li key={idx}>{w}</li>
))}
</ul>
</div>
)}
{/* Supersets */}
{spec && spec.supersets.length > 0 && (
<div className="flex flex-col gap-2">

View File

@@ -4,7 +4,10 @@ import type { SupersetExercise } from "@/lib/types";
function mediaUrl(path: string): string {
if (typeof window === "undefined") return path;
return `${window.location.protocol}//${window.location.hostname}:8001${path}`;
if (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") {
return `${window.location.protocol}//${window.location.hostname}:8001${path}`;
}
return path;
}
interface ExerciseRowProps {

View File

@@ -0,0 +1,7 @@
import nextVitals from "eslint-config-next/core-web-vitals";
const config = [
...nextVitals,
];
export default config;

View File

@@ -1,6 +1,10 @@
/** @type {import('next').NextConfig} */
// v2
const nextConfig = {
skipTrailingSlashRedirect: true,
experimental: {
proxyTimeout: 120000, // 2 minutes for long-running workout generation
},
images: {
remotePatterns: [
{
@@ -16,12 +20,24 @@ const nextConfig = {
],
},
async rewrites() {
return [
{
source: "/media/:path*",
destination: "http://localhost:8000/media/:path*",
},
const djangoUrl = process.env.DJANGO_INTERNAL_URL || "http://localhost:8000";
// Helper: for each Django prefix, create two rewrites:
// 1. with trailing slash preserved
// 2. without trailing slash → add it (Django requires trailing slashes)
const djangoPrefixes = [
"media", "registered_user", "exercise", "muscle",
"equipment", "workout", "generator", "videos", "admin",
];
return djangoPrefixes.flatMap((prefix) => [
{
source: `/${prefix}/:path*/`,
destination: `${djangoUrl}/${prefix}/:path*/`,
},
{
source: `/${prefix}/:path*`,
destination: `${djangoUrl}/${prefix}/:path*/`,
},
]);
},
};

View File

@@ -6,7 +6,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "eslint ."
},
"keywords": [],
"author": "",