chore: production setup — docker-compose, remove zrok refs, exclude local pb data, thob.studio domains

This commit is contained in:
rajun 2026-07-20 16:42:05 +05:30
parent 979516b681
commit 1cbee8ceaf
9 changed files with 112 additions and 6 deletions

8
.env.example Normal file
View File

@ -0,0 +1,8 @@
# AR Test — Environment Variables
# Copy this to .env on the droplet and fill in the values
# PocketBase public URL (the Traefik-routed domain)
VITE_POCKETBASE_URL=https://pb.thob.studio
# Frontend public URL
VITE_FRONTEND_URL=https://ar.thob.studio

8
.gitignore vendored
View File

@ -5,3 +5,11 @@
# React Router # React Router
/.react-router/ /.react-router/
/build/ /build/
# Local PocketBase dev data — never commit to production
/pocketbase/pb_data/
/pocketbase/pb_migrations/
/pocketbase/pocketbase
# Keep the root pb_migrations folder tracked (empty placeholder for production)
!/pb_migrations/.gitkeep

View File

@ -12,6 +12,11 @@ FROM node:20-alpine AS build-env
COPY . /app/ COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app WORKDIR /app
# VITE_ vars are baked into the bundle at build time — must be passed as ARGs
ARG VITE_POCKETBASE_URL
ARG VITE_FRONTEND_URL
ENV VITE_POCKETBASE_URL=$VITE_POCKETBASE_URL
ENV VITE_FRONTEND_URL=$VITE_FRONTEND_URL
RUN npm run build RUN npm run build
FROM node:20-alpine FROM node:20-alpine

View File

@ -69,8 +69,10 @@ export default function ARViewer() {
useEffect(() => { useEffect(() => {
if (!id) return; if (!id) return;
const loadAsset = async () => { const loadAsset = async () => {
// const response = await fetch(`https://thobapi.bopconsultancy.com/v2/ar-assets/${id}`);
const data = await pb.collection('ar_assets').getOne(id); const data = await pb.collection('ar_assets').getOne(id);
if (data) setAsset(data); // const data = await response.json();
setAsset(data);
}; };
loadAsset(); loadAsset();
}, [id]); }, [id]);
@ -177,12 +179,12 @@ export default function ARViewer() {
<div className="absolute top-0 left-0 right-0 flex items-start justify-between px-5 pt-5 pb-5 pointer-events-none"> <div className="absolute top-0 left-0 right-0 flex items-start justify-between px-5 pt-5 pb-5 pointer-events-none">
{asset?.name && ( {asset?.name && (
<span className="text-white text-sm font-bold tracking-widest uppercase"> <span className="text-black text-sm font-bold tracking-widest uppercase">
{asset.name} {asset.name}
</span> </span>
)} )}
<div className="pointer-events-auto w-9 h-9 bg-black border border-orange-500 rounded-lg flex items-center justify-center shrink-0 ml-3"> <div className="pointer-events-auto w-9 h-9 bg-black border rounded-lg flex items-center justify-center shrink-0 ml-3">
<span className="text-orange-500 font-black text-sm leading-none">t.</span> <span className="text-orange-500 font-semibold text-2xl pl-1">t.</span>
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@ import type { Route } from "./+types/root";
import "./app.css"; import "./app.css";
export const links: Route.LinksFunction = () => [ export const links: Route.LinksFunction = () => [
{ rel: "icon", href: "/favicon.svg", type: "image/svg+xml" },
{ rel: "preconnect", href: "https://fonts.googleapis.com" }, { rel: "preconnect", href: "https://fonts.googleapis.com" },
{ {
rel: "preconnect", rel: "preconnect",

View File

@ -142,7 +142,7 @@ export default function Dashboard() {
{assets.map((asset) => ( {assets.map((asset) => (
<div <div
key={asset.id} key={asset.id}
className='rounded-2xl bg-white p-5 shadow-sm transition hover:shadow-md' className='rounded-2xl bg-white p-5 shadow-sm transition hover:shadow-md bg-radial-[at_0%_0%] to-70% from-black/20 to-transparent '
> >
<h3 className='mb-3 text-base font-semibold text-gray-900'> <h3 className='mb-3 text-base font-semibold text-gray-900'>
{asset?.name} {asset?.name}
@ -155,7 +155,13 @@ export default function Dashboard() {
className='mx-auto w-40 rounded-lg' className='mx-auto w-40 rounded-lg'
/> />
) : ( ) : (
<div className='mx-auto flex h-40 w-40 flex-col items-center justify-center space-y-2 rounded-lg bg-gray-50 text-gray-400'> asset?.qr_file ? (
<img
src={pb.files.getURL(asset, asset.qr_file)}
alt={asset?.name}
className='mx-auto w-40 rounded-lg'
/>
) : <div className='mx-auto flex h-40 w-40 flex-col items-center justify-center space-y-2 rounded-lg bg-gray-50 text-gray-400'>
<ImageOff size={32} strokeWidth={1.5} /> <ImageOff size={32} strokeWidth={1.5} />
<span className='text-xs font-medium'>No QR Image</span> <span className='text-xs font-medium'>No QR Image</span>
</div> </div>

72
docker-compose.yml Normal file
View File

@ -0,0 +1,72 @@
# =============================================================================
# AR TEST — Application services
# Deploy path on droplet: /projects/ar-test/
#
# Traefik runs in a separate compose (shared infra project) and owns ports 80/443.
# This project only joins the shared external network `traefik_public`.
# =============================================================================
services:
app:
container_name: ar-test-app
image: registry.digitalocean.com/bop/ar-test:latest
pull_policy: always
restart: unless-stopped
build:
context: .
dockerfile: Dockerfile
args:
- VITE_POCKETBASE_URL=${VITE_POCKETBASE_URL:-https://pb.thob.studio}
expose:
- "3000"
environment:
- VITE_POCKETBASE_URL=${VITE_POCKETBASE_URL:-https://pb.thob.studio}
- VITE_FRONTEND_URL=${VITE_FRONTEND_URL:-https://ar.thob.studio}
depends_on:
- pocketbase
networks:
- traefik_public
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.ar-test-app.rule=Host(`ar.thob.studio`)"
- "traefik.http.routers.ar-test-app.entrypoints=websecure"
- "traefik.http.routers.ar-test-app.tls.certresolver=defaultresolver"
- "traefik.http.services.ar-test-app.loadbalancer.server.port=3000"
- "traefik.http.routers.ar-test-app-http.rule=Host(`ar.thob.studio`)"
- "traefik.http.routers.ar-test-app-http.entrypoints=web"
- "traefik.http.routers.ar-test-app-http.middlewares=ar-test-app-https-redirect"
- "traefik.http.middlewares.ar-test-app-https-redirect.redirectscheme.scheme=https"
pocketbase:
container_name: ar-test-pb
image: ghcr.io/muchobien/pocketbase:latest
restart: unless-stopped
command:
- serve
- --http=0.0.0.0:8090
- --dir=/pb_data
- --migrationsDir=/pb_migrations
volumes:
- pb_data:/pb_data
- ./pb_migrations:/pb_migrations
networks:
- traefik_public
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_public"
- "traefik.http.routers.ar-test-pb.rule=Host(`pb.thob.studio`)"
- "traefik.http.routers.ar-test-pb.entrypoints=websecure"
- "traefik.http.routers.ar-test-pb.tls.certresolver=defaultresolver"
- "traefik.http.services.ar-test-pb.loadbalancer.server.port=8090"
- "traefik.http.routers.ar-test-pb-http.rule=Host(`pb.thob.studio`)"
- "traefik.http.routers.ar-test-pb-http.entrypoints=web"
- "traefik.http.routers.ar-test-pb-http.middlewares=ar-test-pb-https-redirect"
- "traefik.http.middlewares.ar-test-pb-https-redirect.redirectscheme.scheme=https"
networks:
traefik_public:
external: true
volumes:
pb_data:

0
pb_migrations/.gitkeep Normal file
View File

View File

@ -5,4 +5,8 @@ import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({ export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
server: {
port: 5173,
host: true, // allow external access (local dev only)
},
}); });