Skip to content

Commit 8902faf

Browse files
committed
Publishing - deploying to porter
1 parent f973073 commit 8902faf

File tree

6 files changed

+85
-33
lines changed

6 files changed

+85
-33
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"on":
2+
push:
3+
branches:
4+
- new-template
5+
name: Deploy to camera-photo-example
6+
jobs:
7+
porter-deploy:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v4
12+
- name: Set Github tag
13+
id: vars
14+
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
15+
- name: Setup porter
16+
uses: porter-dev/[email protected]
17+
- name: Deploy stack
18+
timeout-minutes: 30
19+
run: exec porter apply -f ./porter.yaml
20+
env:
21+
PORTER_APP_NAME: mentra-live-template-app
22+
PORTER_CLUSTER: "4689"
23+
PORTER_DEPLOYMENT_TARGET_ID: 4a24a192-04c8-421f-8fc2-22db1714fdc0
24+
PORTER_HOST: https://dashboard.porter.run
25+
PORTER_PR_NUMBER: ${{ github.event.number }}
26+
PORTER_PROJECT: "15081"
27+
PORTER_REPO_NAME: ${{ github.event.repository.name }}
28+
PORTER_TAG: ${{ steps.vars.outputs.sha_short }}
29+
PORTER_TOKEN: ${{ secrets.PORTER_APP_15081_4689 }}

docker/Dockerfile

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
FROM oven/bun:latest
22
WORKDIR /app
3-
# Copy package files
3+
4+
# Install Node.js and npm (required for frontend build)
5+
RUN apt-get update && apt-get install -y nodejs npm && rm -rf /var/lib/apt/lists/*
6+
7+
# Copy root package files
48
COPY package.json bun.lock* ./
5-
# Install dependencies (including dev dependencies for build)
9+
# Install root dependencies
610
RUN bun install
7-
# Copy the application code
11+
12+
# Copy frontend package files
13+
COPY src/frontend/package*.json ./src/frontend/
14+
# Install frontend dependencies
15+
RUN cd src/frontend && npm install
16+
17+
# Copy the entire application code
818
COPY . .
9-
# Expose the port
19+
20+
# Build the frontend (creates src/frontend/dist)
21+
RUN cd src/frontend && npm run build
22+
23+
# Expose the port (Porter uses 80)
1024
EXPOSE 80

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"scripts": {
66
"start": "bun src/mentra-app/index.ts",
77
"dev": "bun --hot src/mentra-app/index.ts",
8+
"build:frontend": "cd src/frontend && npm run build",
9+
"build": "npm run build:frontend",
10+
"start:prod": "npm run build && npm run start",
811
"test": "echo \"Error: no test specified\" && exit 1"
912
},
1013
"author": "Aryan Farhang",

porter.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build:
66
context: .
77
dockerfile: ./docker/Dockerfile
88
services:
9-
- name: camera-photo-app
9+
- name: mentra-live-template-app
1010
type: web
1111
run: bun run start
1212
port: 80
@@ -16,3 +16,4 @@ services:
1616
NODE_ENV: ${NODE_ENV}
1717
PORTER_APP_NAME: ${PORTER_APP_NAME}
1818
HOST: "0.0.0.0"
19+
PORT: "80"

src/mentra-app/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,29 @@ class ExampleMentraOSApp extends AppServer {
7373
const publicPath = path.join(process.cwd(), "src", "public");
7474
this.getExpressApp().use("/assets", express.static(publicPath + "/assets"));
7575

76+
// Serve the built React frontend from the same port
77+
const frontendDistPath = path.join(process.cwd(), "src", "frontend", "dist");
78+
this.getExpressApp().use(express.static(frontendDistPath));
79+
7680
// Set up all web routes (pass our photos map)
7781
setupWebviewRoutes(this.getExpressApp(), this.photosMap);
82+
83+
// SPA fallback - serve index.html for all non-API routes
84+
// This must be AFTER setupWebviewRoutes to not interfere with API routes
85+
this.getExpressApp().get('*', (req: any, res: any) => {
86+
// Don't intercept API routes or assets
87+
if (req.path.startsWith('/api') || req.path.startsWith('/assets')) {
88+
return;
89+
}
90+
91+
// Serve the React app's index.html for all other routes
92+
res.sendFile(path.join(frontendDistPath, 'index.html'), (err: any) => {
93+
if (err) {
94+
console.error('Error serving index.html:', err);
95+
res.status(500).send('Frontend build not found. Please run: npm run build:frontend');
96+
}
97+
});
98+
});
7899
}
79100

80101
// Session Lifecycle - Called when a user opens/closes the app

src/mentra-app/routes/routes.ts

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@
33
* Web Routes Module
44
* =============================================================================
55
*
6-
* This module contains all API endpoints and web routes for the photo viewer.
6+
* This module contains all API endpoints for the camera application.
77
*
88
* Routes included:
9+
* - GET /api/photo-stream - SSE endpoint for real-time photo updates
10+
* - GET /api/transcription-stream - SSE endpoint for real-time transcriptions
11+
* - POST /api/play-audio - Play audio to MentraOS glasses
12+
* - POST /api/speak - Text-to-speech to MentraOS glasses
13+
* - POST /api/stop-audio - Stop audio playback
14+
* - GET /api/theme-preference - Get user's theme preference from Simple Storage
15+
* - POST /api/theme-preference - Set user's theme preference in Simple Storage
916
* - GET /api/latest-photo - Get metadata for the latest photo
1017
* - GET /api/photo/:requestId - Get the actual photo image data
1118
* - GET /api/photo-base64/:requestId - Get photo as base64 JSON
12-
* - GET /api/photo-stream - SSE endpoint for real-time photo updates
13-
* - GET /webview - Main photo viewer web interface
19+
*
20+
* Note: The React frontend is served from the root route by index.ts
1421
*
1522
* =============================================================================
1623
*/
1724

1825
import { Express, Response } from 'express';
19-
import { AuthenticatedRequest } from '@mentra/sdk';
20-
import * as ejs from 'ejs';
21-
import * as path from 'path';
2226
import { getThemePreference, setThemePreference } from '../modules/simple-storage';
2327

2428
// Store SSE clients with userId mapping
@@ -473,26 +477,6 @@ export function setupWebviewRoutes(
473477
});
474478
});
475479

476-
477-
// Route 4: Main photo viewer web interface
478-
app.get('/webview', async (req: any, res: any) => {
479-
const userId = (req as AuthenticatedRequest).authUserId;
480-
481-
if (!userId) {
482-
res.status(401).send(`
483-
<html>
484-
<head><title>Photo Viewer - Not Authenticated</title></head>
485-
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
486-
<h1>Please open this page from the MentraOS app</h1>
487-
</body>
488-
</html>
489-
`);
490-
return;
491-
}
492-
493-
const templatePath = path.join(process.cwd(), 'views', 'photo-viewer.ejs');
494-
const html = await ejs.renderFile(templatePath, {});
495-
496-
res.send(html);
497-
});
480+
// Note: The /webview EJS route has been removed.
481+
// The React frontend is now served from the root route (/) by the SPA fallback in index.ts
498482
}

0 commit comments

Comments
 (0)