Skip to content
Open
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
28 changes: 28 additions & 0 deletions tutorials/tutorial-silent-auth-backend/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';

export default defineConfig({
integrations: [
starlight({
title: 'silent-auth-tutorial',
social: {
github: 'https://github.com',
},
// Auto-generate sidebar from content files
sidebar: [
{
label: 'Tutorial',
autogenerate: { directory: 'docs' }
}
],
// Override Footer to add step navigation
components: {
Footer: './src/components/Footer.astro',
},
}),
],
server: {
port: 1234,
host: true, // Listen on all addresses (0.0.0.0) for Codespaces compatibility
},
});
16 changes: 16 additions & 0 deletions tutorials/tutorial-silent-auth-backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "tutorial-site",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.30.0",
"astro": "^5.0.0"
}
}
47 changes: 47 additions & 0 deletions tutorials/tutorial-silent-auth-backend/scripts/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash
# Don't use set -e here since we're running background processes
# We want the script to continue even if one process has issues starting

# Colors for output
GREEN='\x1b[0;32m'
BLUE='\x1b[0;34m'
YELLOW='\x1b[1;33m'
NC='\x1b[0m' # No Color

echo -e "$GREENStarting tutorial environment...$NC"
echo ""

# Function to handle cleanup on exit
cleanup() {
echo ""
echo -e "$YELLOWShutting down servers...$NC"
# Kill all background processes in this process group
kill 0 2>/dev/null || true
exit 0
}

trap cleanup SIGINT SIGTERM EXIT

# Start Astro tutorial server in background
echo -e "$BLUE[Tutorial]$NC Starting Astro server on port 1234..."
(npm run dev -- --host 0.0.0.0 --port 1234 2>&1 | sed "s/^/[Tutorial] /") &
ASTRO_PID=$!

# Wait a moment for Astro to start
sleep 2

# Start learner application server in background
echo -e "$BLUE[Learner]$NC Starting node server in workspace..."
(cd workspace && npm run dev 2>&1 | sed "s/^/[Learner] /") &
LEARNER_PID=$!

echo ""
echo -e "$GREEN✓ Both servers are running$NC"
echo -e "$GREEN✓ Tutorial available at http://localhost:1234$NC"
echo -e "$GREEN✓ Learner app available at http://localhost:3000$NC"
echo ""
echo "Press Ctrl+C to stop both servers"
echo ""

# Wait for both processes
wait
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
// Override Footer to add step navigation
import Default from '@astrojs/starlight/components/Footer.astro';
import StepNavigation from './StepNavigation.astro';
---

<Default {...Astro.props} />
<StepNavigation />
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
import { getCollection } from 'astro:content';

// Get all docs entries
const allDocs = await getCollection('docs');
const sortedDocs = allDocs.sort((a, b) => {
// Sort by step number if available, otherwise by slug
const aStep = a.data.step || (a.slug === 'index' ? 0 : 999);
const bStep = b.data.step || (b.slug === 'index' ? 0 : 999);
if (aStep !== bStep) return aStep - bStep;
return a.slug.localeCompare(b.slug);
});

// Find current page index
const currentPath = Astro.url.pathname.replace(/^\//, '').replace(/\/$/, '') || 'index';
const currentIndex = sortedDocs.findIndex((doc) => {
const docSlug = doc.slug === 'index' ? 'index' : doc.slug;
return docSlug === currentPath || `/${docSlug}/` === Astro.url.pathname;
});

const prevDoc = currentIndex > 0 ? sortedDocs[currentIndex - 1] : null;
const nextDoc = currentIndex >= 0 && currentIndex < sortedDocs.length - 1 ? sortedDocs[currentIndex + 1] : null;
---

{(prevDoc || nextDoc) && (
<nav class="step-navigation" aria-label="Step navigation">
{prevDoc ? (
<a href={`/${prevDoc.slug === 'index' ? '' : prevDoc.slug}/`} class="step-nav-button step-nav-prev">
<span class="step-nav-arrow">←</span>
<div class="step-nav-content">
<span class="step-nav-label">Previous</span>
<span class="step-nav-title">{prevDoc.data.title}</span>
</div>
</a>
) : (
<div class="step-nav-button step-nav-placeholder"></div>
)}
{nextDoc ? (
<a href={`/${nextDoc.slug === 'index' ? '' : nextDoc.slug}/`} class="step-nav-button step-nav-next">
<div class="step-nav-content">
<span class="step-nav-label">Next</span>
<span class="step-nav-title">{nextDoc.data.title}</span>
</div>
<span class="step-nav-arrow">→</span>
</a>
) : (
<div class="step-nav-button step-nav-placeholder"></div>
)}
</nav>
)}

<style>
.step-navigation {
display: flex;
justify-content: space-between;
gap: 1rem;
margin: 4rem 0 2rem;
}

.step-nav-button {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.25rem 1.5rem;
background: white !important;
border: 1px solid #e5e7eb;
border-radius: 8px;
text-decoration: none;
color: #1e293b;
transition: all 0.2s;
flex: 1;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.step-nav-button:hover {
background: white !important;
border-color: #3b82f6;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.step-nav-placeholder {
visibility: hidden;
pointer-events: none;
}

.step-nav-prev {
justify-content: flex-start;
}

.step-nav-next {
justify-content: flex-end;
}

.step-nav-content {
display: flex;
flex-direction: column;
gap: 0.25rem;
}

.step-nav-label {
font-size: 0.75rem;
font-weight: 600;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
}

.step-nav-title {
font-size: 1rem;
font-weight: 600;
color: #1e293b;
}

.step-nav-button:hover .step-nav-title {
color: #3b82f6;
}

.step-nav-arrow {
font-size: 1.25rem;
font-weight: 600;
color: #64748b;
flex-shrink: 0;
transition: color 0.2s;
}

.step-nav-button:hover .step-nav-arrow {
color: #3b82f6;
}

@media (max-width: 768px) {
.step-navigation {
flex-direction: column;
}

.step-nav-button {
width: 100%;
}

.step-nav-next {
justify-content: flex-start;
}
}
</style>
8 changes: 8 additions & 0 deletions tutorials/tutorial-silent-auth-backend/src/content.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineCollection } from 'astro:content';
import { docsSchema } from '@astrojs/starlight/schema';

export const collections = {
docs: defineCollection({
schema: docsSchema(),
}),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: "Project Setup"
description: "Create the server folder, initialise npm, install dependencies, and set up .gitignore."
step: 1
---

In this step you'll create the backend project.

---

## Install dependencies

Open a terminal in **GitHub Codespaces** and install the project dependencies:

```bash
npm install express cors dotenv @vonage/auth @vonage/verify2
```

Here's what each package does:

| Package | Purpose |
|---------|---------|
| `express` | HTTP web framework — handles routing and middleware |
| `cors` | Allows the Android app (a different origin) to call this API |
| `dotenv` | Loads environment variables from `.env` into `process.env` |
| `@vonage/auth` | Handles JWT-based authentication with the Vonage API |
| `@vonage/verify2` | The Vonage Verify v2 SDK — start verifications, check codes |

---

## Set up `.gitignore`

Two things must never end up in git: `node_modules` and your credentials.

Create a `.gitignore` file inside `workspace/` and add the following:

```
node_modules
.env
private.key
```

Or run these commands directly in the terminal:

```bash
echo "node_modules" >> .gitignore
echo ".env" >> .gitignore
echo "private.key" >> .gitignore
```

---

## Your project structure so far

```
workspace/
├── node_modules/ ← installed packages (ignored by git)
├── server.js
├── .gitignore
└── package.json
```

---

## Checkpoint

Before moving on, confirm:

- [ ] `workspace/package.json` exists
- [ ] `workspace/node_modules/` exists and contains `express`, `@vonage/auth`, `@vonage/verify2`
- [ ] `workspace/.gitignore` has `node_modules`, `.env`, and `private.key`

Run this to confirm the key packages are present:

```bash
ls node_modules | grep -E "express|vonage|dotenv|cors"
```

You should see entries for `express`, `cors`, `dotenv`, `@vonage` (as a scoped folder).
Loading