Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Azure Static Web Apps CI/CD

# main -> Production environment; staging -> "staging" branch environment.
# No pull_request trigger: new environments copy Production's settings (incl. the live GITHUB_PAT).
on:
push:
branches:
- main
- staging

concurrency:
group: azure-swa-${{ github.ref_name }}
cancel-in-progress: false

jobs:
build_and_deploy_job:
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
lfs: false
- name: Stage categories.json for the API
run: cp categories.json api/categories.json
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_WONDERFUL_FIELD_0CD14C010 }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
production_branch: "main"
app_location: "/"
api_location: "api"
output_location: ""
skip_app_build: true # static site; api/ is still built by Oryx
30 changes: 19 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,24 @@ jobs:
run: npm ci --ignore-scripts

- name: Audit dependencies
# Production-only: this project ships zero runtime npm dependencies (the
# API functions use Node built-ins, the frontend loads Leaflet from a CDN).
# electron/electron-builder are devDependencies for the desktop packaging
# tool only — never shipped to the deployed map/admin/API — so their
# transitive vulnerabilities shouldn't block every unrelated PR.
# Production-only: the root project ships zero runtime npm dependencies
# (the public map loads Leaflet from a CDN). electron/electron-builder
# are devDependencies for the desktop packaging tool only — never
# shipped to the deployed map/admin/API — so their transitive
# vulnerabilities shouldn't block every unrelated PR.
run: npm audit --omit=dev --audit-level=high

- name: Install api/ dependencies
# The Azure Functions API has its own package.json (@azure/functions is
# its one real runtime dependency), separate from the root project.
run: cd api && npm ci --ignore-scripts

- name: Audit api/ dependencies
run: cd api && npm audit --omit=dev --audit-level=high

- name: Validate JSON files
run: |
for f in $(find . -name "*.json" -not -path "./node_modules/*" -not -path "./.git/*"); do
for f in $(find . -name "*.json" -not -path "./node_modules/*" -not -path "./api/node_modules/*" -not -path "./.git/*"); do
echo "Checking $f"
node -e "JSON.parse(require('fs').readFileSync('$f', 'utf8'))"
done
Expand All @@ -50,18 +58,18 @@ jobs:

- name: Check api/ function syntax
run: |
for f in api/*.js api/_lib/*.js; do
for f in api/_lib/*.js api/src/functions/*.js; do
echo "Checking $f"
node --check "$f"
done

- name: Check admin/ front-end syntax
run: |
node --check admin/admin.js
node --check admin/admin-auth.js
run: node --check admin/admin.js

- name: Run api/ unit tests (mocked Gist backend)
run: node test/api.test.js
run: |
cp categories.json api/categories.json
node test/api.test.js

- name: Run public map data tests
run: |
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ wheels/
*.pem
*.key

# Azure Functions local development settings (holds secrets like GITHUB_PAT,
# AZURE_CLIENT_SECRET when running `func start` locally against api/)
api/local.settings.json

# IDE
.vscode/
.idea/
Expand All @@ -56,6 +60,9 @@ yarn-error.log
dist/
out/

# Staged copy of the repo-root categories.json for the Azure Functions deploy (see workflow + pretest)
api/categories.json

# Local testing
*.log
.tmp-chrome/
Expand Down
30 changes: 0 additions & 30 deletions admin/admin-auth.js

This file was deleted.

27 changes: 4 additions & 23 deletions admin/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,10 @@ body {
color: #1f2937;
}

/* --- Login page --- */
.login-body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-card {
background: white;
padding: 32px;
border-radius: 10px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
width: 320px;
}
.login-card h1 { font-size: 20px; margin: 0 0 4px; }
.login-card .subtitle { margin: 0 0 20px; color: #6b7280; font-size: 14px; }
.login-card form { display: flex; flex-direction: column; gap: 8px; }
.login-card label { font-size: 13px; font-weight: 600; }
.login-card input { padding: 10px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; }
.login-card button { margin-top: 8px; padding: 10px; border: none; border-radius: 6px; background: #2563eb; color: white; font-weight: 600; cursor: pointer; }
.login-card button:hover { background: #1d4ed8; }

/* --- Dashboard --- */
/* --- Dashboard ---
The custom passphrase login page (and its .login-body/.login-card styles)
was removed — Microsoft Entra ID sign-in is handled entirely by Azure
Static Web Apps' built-in /.auth/login/aad, with no local login page. */
#admin-header {
display: flex;
justify-content: space-between;
Expand Down
17 changes: 9 additions & 8 deletions admin/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function hideFormError() {
async function loadData() {
const resp = await fetch('/api/gist', { credentials: 'same-origin' });
if (resp.status === 401) {
window.location.href = '/admin/index.html';
window.location.href = '/.auth/login/aad';
return;
}
if (!resp.ok) {
Expand All @@ -46,7 +46,7 @@ async function loadData() {
organizations = data.organizations;
categories = data.categories;
currentVersion = data.version;
lastUpdatedEl.textContent = `Last updated: ${new Date(currentVersion).toLocaleString()}`;
lastUpdatedEl.textContent = `Last updated: ${new Date(data.updatedAt).toLocaleString()}`;
renderOrgList();
}

Expand Down Expand Up @@ -229,7 +229,7 @@ document.getElementById('geocode-btn').addEventListener('click', async () => {
body: JSON.stringify({ address }),
});
if (resp.status === 401) {
window.location.href = '/admin/index.html';
window.location.href = '/.auth/login/aad';
return;
}
if (resp.status === 404) {
Expand Down Expand Up @@ -261,7 +261,7 @@ document.getElementById('delete-btn').addEventListener('click', async () => {
body: JSON.stringify({ mode: 'delete', orgId: editingOrgId, expectedVersion: currentVersion }),
});
const data = await resp.json();
if (resp.status === 401) { window.location.href = '/admin/index.html'; return; }
if (resp.status === 401) { window.location.href = '/.auth/login/aad'; return; }
if (resp.status === 409) {
showFormError(data.message + ' Reloading current data…');
await loadData();
Expand All @@ -279,9 +279,10 @@ document.getElementById('delete-btn').addEventListener('click', async () => {
}
});

document.getElementById('logout-btn').addEventListener('click', async () => {
await fetch('/api/logout', { method: 'POST' });
window.location.href = '/admin/index.html';
document.getElementById('logout-btn').addEventListener('click', () => {
// Azure Static Web Apps' built-in auth handles sign-out; there's no local
// session/cookie of our own to clear anymore.
window.location.href = '/.auth/logout';
});

form.addEventListener('submit', async (e) => {
Expand Down Expand Up @@ -329,7 +330,7 @@ form.addEventListener('submit', async (e) => {
});
const data = await resp.json();

if (resp.status === 401) { window.location.href = '/admin/index.html'; return; }
if (resp.status === 401) { window.location.href = '/.auth/login/aad'; return; }

if (resp.status === 409) {
showFormError(data.message);
Expand Down
132 changes: 0 additions & 132 deletions admin/dashboard.html

This file was deleted.

Loading
Loading