forked from rajun/ar-test
chore: production setup — docker-compose, remove zrok refs, exclude local pb data, thob.studio domains
This commit is contained in:
parent
979516b681
commit
1cbee8ceaf
8
.env.example
Normal file
8
.env.example
Normal 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
8
.gitignore
vendored
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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
72
docker-compose.yml
Normal 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
0
pb_migrations/.gitkeep
Normal 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)
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user