From f27ef6fa7f4fcad9eb3bf53d78bd81959f0b65bf Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 07:31:08 -0500 Subject: [PATCH 1/7] create dockerfile --- .dockerignore | 28 ++++++++++++++++++++++++++++ Dockerfile | 20 ++++++++++++++++++++ nginx.conf | 14 ++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..9a4de614 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# secrets +.env + +# misc +.vscode +.DS_Store +.env* +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..893d502e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:20 AS builder + +WORKDIR /app + +COPY package*.json . + +RUN npm install + +COPY . . + +RUN npm run build + +FROM nginx:1.27 + +WORKDIR /app + +COPY --from=builder /app/build . +COPY nginx.conf /etc/nginx/conf.d/default.conf + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 00000000..ac8b7a80 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,14 @@ +server { + listen 80; + + root /app; + index index.html index.htm; + + location / { + try_files $uri /index.html; + } + + location ~ ^/v/latest(.*) { + try_files $1 /index.html; + } +} \ No newline at end of file From 52d0b9f660d8b7075134afbb0213b57d6d8d1efa Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 07:33:37 -0500 Subject: [PATCH 2/7] start session even without access token --- src/components/navbars/topbar/apiDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/navbars/topbar/apiDropdown.tsx b/src/components/navbars/topbar/apiDropdown.tsx index 6ce8e5b1..7c81e8cc 100644 --- a/src/components/navbars/topbar/apiDropdown.tsx +++ b/src/components/navbars/topbar/apiDropdown.tsx @@ -60,7 +60,7 @@ export default function ApiDropdown(props) { useEffect(() => { if (mode === 'kiosk') { switchToScribeARServer(`ws://${serverAddress}/sourcesink`); - } else if (mode === 'student' && accessToken) { + } else if (mode === 'student') { console.log("Sending startSession POST with accessToken:", accessToken); fetch(`http://${serverAddress}/startSession`, { method: 'POST', From 105241e826ebbc6fc4d23a068eaac6b177e3d4f0 Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 07:52:48 -0500 Subject: [PATCH 3/7] get scribearURL from query params --- src/components/navbars/topbar/qrCodeScreen.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/navbars/topbar/qrCodeScreen.tsx b/src/components/navbars/topbar/qrCodeScreen.tsx index 01cca70d..3ba9692f 100644 --- a/src/components/navbars/topbar/qrCodeScreen.tsx +++ b/src/components/navbars/topbar/qrCodeScreen.tsx @@ -14,12 +14,17 @@ export default function QRCodeComponent() { const isKioskMode = urlParams.get('mode') === 'kiosk'; const serverAddress = urlParams.get('serverAddress'); const isStudentMode = urlParams.get('mode') === 'student'; + const scribearURLParam = urlParams.get('scribearURL'); useEffect(() => { if (!isKioskMode) return; setLoading(true); - setScribearURL(window.location.protocol + "//" + window.location.host + window.location.pathname); + if (scribearURLParam) { + setScribearURL(scribearURLParam); + } else { + setScribearURL(window.location.protocol + "//" + window.location.host + window.location.pathname); + } let updateAccessTokenTimeout; function updateAccessToken() { From 07f4d315cc5d97607dfc8a24af74a0cc05051940 Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:16:56 -0500 Subject: [PATCH 4/7] use sourceToken instead of localhoust auth --- src/components/navbars/topbar/apiDropdown.tsx | 24 ++++++++++--------- .../navbars/topbar/qrCodeScreen.tsx | 21 +++++++++++++--- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/components/navbars/topbar/apiDropdown.tsx b/src/components/navbars/topbar/apiDropdown.tsx index 7c81e8cc..76111c2e 100644 --- a/src/components/navbars/topbar/apiDropdown.tsx +++ b/src/components/navbars/topbar/apiDropdown.tsx @@ -26,22 +26,26 @@ export default function ApiDropdown(props) { const open = Boolean(anchorEl); const dispatch = useDispatch(); - const urlParams = new URLSearchParams(window.location.search); - const mode = urlParams.get('mode'); - const serverAddress = urlParams.get('serverAddress'); - const accessToken = urlParams.get('accessToken'); - const apiStatus = useSelector((state: RootState) => state.APIStatusReducer as ApiStatus); const scribearServerStatus = useSelector((state: RootState) => { return state.ScribearServerReducer as ScribearServerStatus }) - function switchToScribeARServer(scribearServerAddress: string) { + const urlParams = new URLSearchParams(window.location.search); + const mode = urlParams.get('mode'); + const kioskServerAddress = urlParams.get('kioskServerAddress'); + const serverAddress = urlParams.get('serverAddress'); + const accessToken = urlParams.get('accessToken'); + const sourceToken = urlParams.get('sourceToken'); + + // Automatically use scribear server as sink when in student mode or as sourcesink if in kiosk mode + useEffect(() => { + function switchToScribeARServer(scribearServerAddress: string) { // Set new scribear server address let copyScribearServerStatus = Object.assign({}, scribearServerStatus); copyScribearServerStatus.scribearServerAddress = scribearServerAddress - dispatch({type: 'CHANGE_SCRIBEAR_SERVER_ADDRESS', payload: copyScribearServerStatus}); + dispatch({type: 'CHANGE_SCRIBEAR_SERVER_ADDRESS', payload: {scribearServerAddress}}); // Switch to scribear server let copyStatus = Object.assign({}, apiStatus); @@ -56,10 +60,8 @@ export default function ApiDropdown(props) { dispatch({type: 'CHANGE_API_STATUS', payload: copyStatus}); } - // Automatically use scribear server as sink when in student mode or as sourcesink if in kiosk mode - useEffect(() => { if (mode === 'kiosk') { - switchToScribeARServer(`ws://${serverAddress}/sourcesink`); + switchToScribeARServer(`ws://${kioskServerAddress}/sourcesink?sourceToken=${sourceToken}`); } else if (mode === 'student') { console.log("Sending startSession POST with accessToken:", accessToken); fetch(`http://${serverAddress}/startSession`, { @@ -81,7 +83,7 @@ export default function ApiDropdown(props) { console.error('Error starting session:', error); }); } - }, [mode, serverAddress, accessToken]); + }, [accessToken, dispatch, mode, kioskServerAddress, serverAddress, sourceToken]); const isMobile = useMediaQuery(currTheme.breakpoints.down('sm')); diff --git a/src/components/navbars/topbar/qrCodeScreen.tsx b/src/components/navbars/topbar/qrCodeScreen.tsx index 3ba9692f..397f7aa3 100644 --- a/src/components/navbars/topbar/qrCodeScreen.tsx +++ b/src/components/navbars/topbar/qrCodeScreen.tsx @@ -12,9 +12,10 @@ export default function QRCodeComponent() { const urlParams = new URLSearchParams(window.location.search); const isKioskMode = urlParams.get('mode') === 'kiosk'; - const serverAddress = urlParams.get('serverAddress'); + const sourceServerAddress = urlParams.get('sourceServerAddress'); const isStudentMode = urlParams.get('mode') === 'student'; const scribearURLParam = urlParams.get('scribearURL'); + const sourceToken = urlParams.get('sourceToken'); useEffect(() => { if (!isKioskMode) return; @@ -28,9 +29,19 @@ export default function QRCodeComponent() { let updateAccessTokenTimeout; function updateAccessToken() { - fetch(`http://${serverAddress}/accessToken`) + fetch(`http://${sourceServerAddress}/accessToken`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ sourceToken }) + }) .then(response => response.json()) .then(data => { + if (!data.accessToken) { + throw Error('Unsuccessful request:' + JSON.stringify(data)); + } + setNodeServerAddress(data.serverAddress); setAccessToken(data.accessToken); setSuccessful(true); @@ -46,6 +57,10 @@ export default function QRCodeComponent() { .catch(error => { setSuccessful(false); console.error("Failed to fetch access token:", error) + + updateAccessTokenTimeout = setTimeout(() => { + updateAccessToken() + }, 30_000); }) .finally(() => setLoading(false)); } @@ -55,7 +70,7 @@ export default function QRCodeComponent() { return () => { clearTimeout(updateAccessTokenTimeout) } - }, []); + }, [isKioskMode, scribearURLParam, sourceServerAddress, sourceToken]); if (!isKioskMode) return <>; From 6faa04fc592aec072eae1a2df56350c825473695 Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:17:36 -0500 Subject: [PATCH 5/7] fix incorrect variable name --- src/components/navbars/topbar/qrCodeScreen.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/navbars/topbar/qrCodeScreen.tsx b/src/components/navbars/topbar/qrCodeScreen.tsx index 397f7aa3..040c07f7 100644 --- a/src/components/navbars/topbar/qrCodeScreen.tsx +++ b/src/components/navbars/topbar/qrCodeScreen.tsx @@ -12,7 +12,7 @@ export default function QRCodeComponent() { const urlParams = new URLSearchParams(window.location.search); const isKioskMode = urlParams.get('mode') === 'kiosk'; - const sourceServerAddress = urlParams.get('sourceServerAddress'); + const kioskServerAddress = urlParams.get('kioskServerAddress'); const isStudentMode = urlParams.get('mode') === 'student'; const scribearURLParam = urlParams.get('scribearURL'); const sourceToken = urlParams.get('sourceToken'); @@ -29,7 +29,7 @@ export default function QRCodeComponent() { let updateAccessTokenTimeout; function updateAccessToken() { - fetch(`http://${sourceServerAddress}/accessToken`, { + fetch(`http://${kioskServerAddress}/accessToken`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -70,7 +70,7 @@ export default function QRCodeComponent() { return () => { clearTimeout(updateAccessTokenTimeout) } - }, [isKioskMode, scribearURLParam, sourceServerAddress, sourceToken]); + }, [isKioskMode, scribearURLParam, kioskServerAddress, sourceToken]); if (!isKioskMode) return <>; From 96e0a9414585d51640f1db86adcf377417c73a40 Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Thu, 1 May 2025 21:08:42 -0500 Subject: [PATCH 6/7] create docker container --- .github/workflows/docker.yml | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..ad818545 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,66 @@ +name: node-server CI + +on: + push: + branches: + - master + - staging + tags: + - "v*.*.*" + pull_request: + workflow_dispatch: + +permissions: + actions: read + contents: read + +env: + DOCKERHUB_ORG: 'scribear' + +jobs: + build-container: + runs-on: ubuntu-latest + steps: + - name: Set up Git repository + uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.DOCKERHUB_ORG }}/frontend + tags: | + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: "linux/amd64,linux/arm64" + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=${{ env.DOCKERHUB_ORG }}/frontend:buildcache + cache-to: type=registry,ref=${{ env.DOCKERHUB_ORG }}/frontend:buildcache,mode=max + build-args: | + BRANCH=${{ steps.meta.outputs.version }} + BUILDNUMBER=${{ github.run_number }} + ARG GITSHA1=${{ github.sha }} \ No newline at end of file From 930018f9cc460c0ef6fde336f572c378fbc3cb5d Mon Sep 17 00:00:00 2001 From: Bennett Wu <57691028+bennettrwu@users.noreply.github.com> Date: Thu, 1 May 2025 21:09:54 -0500 Subject: [PATCH 7/7] update action name --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ad818545..985a2672 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,4 +1,4 @@ -name: node-server CI +name: build docker container on: push: