Skip to content

Commit b91c12a

Browse files
Matthew Valancyclaude
andcommitted
Add demo deployment configurations
Added demo-specific Dockerfile and nginx configuration for streamlined deployment. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9981b5b commit b91c12a

4 files changed

Lines changed: 135 additions & 6 deletions

File tree

packages/web/Dockerfile.demo

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM node:18-alpine AS builder
2+
3+
# Build arguments for demo configuration
4+
ARG VITE_GRAPHQL_URL
5+
ARG VITE_GRAPHQL_WS_URL
6+
ARG VITE_AUTO_GUEST_LOGIN=true
7+
8+
WORKDIR /app
9+
10+
# Copy package files
11+
COPY package.json package-lock.json turbo.json ./
12+
COPY packages/core/package.json ./packages/core/
13+
COPY packages/web/package.json ./packages/web/
14+
15+
# Install dependencies
16+
RUN npm ci
17+
18+
# Copy source code
19+
COPY packages/core ./packages/core
20+
COPY packages/web ./packages/web
21+
COPY tsconfig.json ./
22+
23+
# Set environment variables for Vite build
24+
ENV VITE_GRAPHQL_URL=${VITE_GRAPHQL_URL}
25+
ENV VITE_GRAPHQL_WS_URL=${VITE_GRAPHQL_WS_URL}
26+
ENV VITE_AUTO_GUEST_LOGIN=${VITE_AUTO_GUEST_LOGIN}
27+
28+
# Build the application
29+
RUN npm run build
30+
31+
# Convert absolute paths to relative paths for path-based routing
32+
# This allows the app to work when served under /demo/X/ prefix
33+
RUN sed -i 's|/assets/|assets/|g' /app/packages/web/dist/index.html && \
34+
sed -i 's|/favicon|favicon|g' /app/packages/web/dist/index.html && \
35+
sed -i 's|<head>|<head><base href="./">|' /app/packages/web/dist/index.html
36+
37+
# Production stage
38+
FROM nginx:alpine
39+
40+
# Copy built app to nginx
41+
COPY --from=builder /app/packages/web/dist /usr/share/nginx/html
42+
43+
# Copy demo-specific nginx configuration
44+
COPY packages/web/nginx.demo.conf /etc/nginx/conf.d/default.conf
45+
46+
EXPOSE 80
47+
48+
CMD ["nginx", "-g", "daemon off;"]

packages/web/nginx.demo.conf

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
server {
2+
listen 80;
3+
server_name localhost;
4+
root /usr/share/nginx/html;
5+
index index.html;
6+
7+
# Enable gzip compression
8+
gzip on;
9+
gzip_vary on;
10+
gzip_min_length 1024;
11+
gzip_types
12+
text/plain
13+
text/css
14+
text/xml
15+
text/javascript
16+
application/javascript
17+
application/xml+rss
18+
application/json;
19+
20+
# Handle client-side routing
21+
location / {
22+
try_files $uri $uri/ /index.html;
23+
}
24+
25+
# Cache static assets
26+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
27+
expires 1y;
28+
add_header Cache-Control "public, immutable";
29+
}
30+
31+
# Security headers
32+
add_header X-Frame-Options "SAMEORIGIN";
33+
add_header X-Content-Type-Options "nosniff";
34+
add_header X-XSS-Protection "1; mode=block";
35+
}

packages/web/src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ function AuthenticatedApp() {
8282
}
8383

8484
function App() {
85+
// Enable auto guest login in demo mode
86+
const autoGuestLogin = import.meta.env.VITE_AUTO_GUEST_LOGIN === 'true';
87+
8588
return (
86-
<AuthProvider>
89+
<AuthProvider autoGuestLogin={autoGuestLogin}>
8790
<AuthenticatedApp />
8891
</AuthProvider>
8992
);

packages/web/src/contexts/AuthContext.tsx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
2-
import { useLazyQuery, gql } from '@apollo/client';
2+
import { useLazyQuery, useMutation, gql } from '@apollo/client';
33
import { User, Team, AuthContextType } from '../types/auth';
44

55
const ME_QUERY = gql`
@@ -23,17 +23,41 @@ const ME_QUERY = gql`
2323
}
2424
`;
2525

26+
const GUEST_LOGIN_MUTATION = gql`
27+
mutation GuestLogin {
28+
guestLogin {
29+
token
30+
user {
31+
id
32+
email
33+
username
34+
name
35+
avatar
36+
role
37+
isActive
38+
isEmailVerified
39+
team {
40+
id
41+
name
42+
description
43+
}
44+
}
45+
}
46+
}
47+
`;
48+
2649
const AuthContext = createContext<AuthContextType | undefined>(undefined);
2750

2851
interface AuthProviderProps {
2952
children: ReactNode;
53+
autoGuestLogin?: boolean;
3054
}
3155

32-
export function AuthProvider({ children }: AuthProviderProps) {
56+
export function AuthProvider({ children, autoGuestLogin = false }: AuthProviderProps) {
3357
const [currentUser, setCurrentUser] = useState<User | null>(null);
3458
const [currentTeam, setCurrentTeam] = useState<Team | null>(null);
3559
const [isInitializing, setIsInitializing] = useState(true);
36-
60+
3761
const [getMe] = useLazyQuery(ME_QUERY, {
3862
onCompleted: (data) => {
3963
if (data.me) {
@@ -58,11 +82,27 @@ export function AuthProvider({ children }: AuthProviderProps) {
5882
}
5983
});
6084

85+
const [performGuestLogin] = useMutation(GUEST_LOGIN_MUTATION, {
86+
onCompleted: (data) => {
87+
if (data.guestLogin) {
88+
setCurrentUser(data.guestLogin.user);
89+
setCurrentTeam(data.guestLogin.user.team || null);
90+
localStorage.setItem('authToken', data.guestLogin.token);
91+
localStorage.setItem('currentUser', JSON.stringify(data.guestLogin.user));
92+
}
93+
setIsInitializing(false);
94+
},
95+
onError: (error) => {
96+
console.error('Auto guest login failed:', error);
97+
setIsInitializing(false);
98+
}
99+
});
100+
61101
// Load saved user from localStorage and validate token on mount
62102
useEffect(() => {
63103
const token = localStorage.getItem('authToken');
64104
const savedUser = localStorage.getItem('currentUser');
65-
105+
66106
if (token && savedUser) {
67107
try {
68108
JSON.parse(savedUser);
@@ -74,10 +114,13 @@ export function AuthProvider({ children }: AuthProviderProps) {
74114
localStorage.removeItem('currentUser');
75115
setIsInitializing(false);
76116
}
117+
} else if (autoGuestLogin) {
118+
// Auto-login as guest if no token exists and autoGuestLogin is enabled
119+
performGuestLogin();
77120
} else {
78121
setIsInitializing(false);
79122
}
80-
}, [getMe]);
123+
}, [getMe, autoGuestLogin, performGuestLogin]);
81124

82125
const login = (user: User, token?: string) => {
83126
setCurrentUser(user);

0 commit comments

Comments
 (0)