feat: HLS streaming, transcripts, agent course generation & landing-page course previews (#663)
New features: - HLS video encoding/streaming (hls routes, hls-encoder) and a media transcripts pipeline (transcripts route + media-transcript queries) - AI assistant chat vs. run model: agent-course-generation worker/queue, run-monitor pane, chat-changes card, content ask-ai bar - Per-theme course-page previews across org landing-page themes (bold, classic, corporate, editorial, etc.) with a shared course token/component set - View-as-student mode and login-link / link-invite invite flows Refactor: - Extract packages/core consolidating config (env, storage, dashboard-url), services and utils previously duplicated under apps/api/src/config - Remove legacy agent service modules (chat-tools, chat-context, context-window, document, fetch-url, lesson-content) - Relax Cross-Origin-Resource-Policy to allow cross-origin media embedding - Add format script to tenant-router so turbo run format covers it
This commit is contained in:
+27
-11
@@ -15,18 +15,24 @@ import { RootProvider } from 'fumadocs-ui/provider/react-router';
|
||||
import { NotFound } from '@/components/not-found';
|
||||
import './app.css';
|
||||
|
||||
const DEFAULT_META_TITLE =
|
||||
'ClassroomIO Docs | Customer, Partner, and Employee Education';
|
||||
const DEFAULT_META_DESCRIPTION =
|
||||
'Learn how to create customer academies, partner certification programs, and employee training workflows in ClassroomIO.';
|
||||
const DEFAULT_OG_IMAGE_URL = 'https://brand.cdn.clsrio.com/og/classroomio-opengraph.png';
|
||||
const DEFAULT_OG_IMAGE_ALT = 'ClassroomIO documentation for customer, partner, and employee education';
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
{ rel: 'icon', href: '/docs/favicon.ico' }
|
||||
];
|
||||
|
||||
export const meta: MetaFunction = () => [
|
||||
{
|
||||
title: 'ClassroomIO | The Open Source Learning Management System for Companies'
|
||||
title: DEFAULT_META_TITLE
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'A flexible, user-friendly platform for creating, managing, and delivering courses for companies and training organisations'
|
||||
content: DEFAULT_META_DESCRIPTION
|
||||
},
|
||||
{
|
||||
property: 'og:url',
|
||||
@@ -38,12 +44,19 @@ export const meta: MetaFunction = () => [
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: 'ClassroomIO | The Open Source Learning Management System for Companies'
|
||||
content: DEFAULT_META_TITLE
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content:
|
||||
'A flexible, user-friendly platform for creating, managing, and delivering courses for companies and training organisations'
|
||||
content: DEFAULT_META_DESCRIPTION
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: DEFAULT_OG_IMAGE_URL
|
||||
},
|
||||
{
|
||||
property: 'og:image:alt',
|
||||
content: DEFAULT_OG_IMAGE_ALT
|
||||
},
|
||||
{
|
||||
property: 'og:image:type',
|
||||
@@ -59,7 +72,7 @@ export const meta: MetaFunction = () => [
|
||||
},
|
||||
{
|
||||
property: 'og:image:secure_url',
|
||||
content: 'https://brand.cdn.clsrio.com/og/classroomio-og.png'
|
||||
content: DEFAULT_OG_IMAGE_URL
|
||||
},
|
||||
{
|
||||
name: 'twitter:card',
|
||||
@@ -75,12 +88,11 @@ export const meta: MetaFunction = () => [
|
||||
},
|
||||
{
|
||||
name: 'twitter:title',
|
||||
content: 'ClassroomIO | The Open Source Learning Management System for Companies'
|
||||
content: DEFAULT_META_TITLE
|
||||
},
|
||||
{
|
||||
name: 'twitter:description',
|
||||
content:
|
||||
'A flexible, user-friendly platform for creating, managing, and delivering courses for companies and training organisations'
|
||||
content: DEFAULT_META_DESCRIPTION
|
||||
},
|
||||
{
|
||||
name: 'twitter:creator',
|
||||
@@ -88,7 +100,11 @@ export const meta: MetaFunction = () => [
|
||||
},
|
||||
{
|
||||
name: 'twitter:image',
|
||||
content: 'https://brand.cdn.clsrio.com/og/classroomio-og.png'
|
||||
content: DEFAULT_OG_IMAGE_URL
|
||||
},
|
||||
{
|
||||
name: 'twitter:image:alt',
|
||||
content: DEFAULT_OG_IMAGE_ALT
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: Welcome to ClassroomIO
|
||||
description: A flexible, user-friendly platform for creating, managing, and delivering courses for companies and training organisations.
|
||||
description: Learn how to create customer academies, partner certification programs, and employee training workflows in ClassroomIO.
|
||||
---
|
||||
|
||||
<img
|
||||
alt="A flexible, user-friendly platform for creating, managing, and delivering courses for companies and training organisations"
|
||||
src="https://brand.cdn.clsrio.com/og/classroomio-og.png"
|
||||
alt="ClassroomIO documentation for customer, partner, and employee education"
|
||||
src="https://brand.cdn.clsrio.com/og/classroomio-opengraph.png"
|
||||
/>
|
||||
|
||||
Welcome to the ClassroomIO documentation—your comprehensive guide to streamlining training for everyone. Our all-in-one platform empowers bootcamps, educators, and businesses to manage training programs easily. Run multiple classes and cohorts from a single UI with our mobile-first application, ensuring students can access all published content from any device.
|
||||
|
||||
@@ -31,7 +31,7 @@ cd classroomio
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Open `.env` and set `PUBLIC_SERVER_URL`, `TRUSTED_ORIGINS`, and `BETTER_AUTH_SECRET` for your domain. For local development the defaults work as-is.
|
||||
Open `.env` and set `DASHBOARD_ORIGIN` and `BETTER_AUTH_SECRET` for your domain. For local development the defaults work as-is.
|
||||
|
||||
3. Start the full stack:
|
||||
|
||||
@@ -65,8 +65,6 @@ Docker self-hosting uses a single root `.env` file. Every `docker compose` comma
|
||||
| `POSTGRES_DB` | Database name | `classroomio` |
|
||||
| `POSTGRES_USER` | Database user | `postgres` |
|
||||
| `POSTGRES_PASSWORD` | Database password | `postgres` |
|
||||
| `PUBLIC_SERVER_URL` | Public API URL (browser requests) | `http://localhost:3081` |
|
||||
| `TRUSTED_ORIGINS` | Comma-separated CORS origins | `http://localhost:3082` |
|
||||
| `BETTER_AUTH_SECRET` | Auth session signing secret | — |
|
||||
| `AUTH_BEARER_TOKEN` | API bearer token | Auto-generated |
|
||||
| `PRIVATE_SERVER_KEY` | Dashboard-to-API auth key (same in both) | Auto-generated |
|
||||
@@ -74,6 +72,15 @@ Docker self-hosting uses a single root `.env` file. Every `docker compose` comma
|
||||
| `PRIVATE_SERVER_URL` | Internal API URL for SSR (Docker network) | `http://api:3081` |
|
||||
| `DASHBOARD_ORIGIN` | Dashboard origin for invite links, email links, etc. | `http://localhost:3082` |
|
||||
|
||||
#### Optional — Direct API Access
|
||||
|
||||
Dashboard browser requests use same-origin `/proxy/*` and `/api/auth/*` paths, then the dashboard forwards them to `PRIVATE_SERVER_URL`. Set these only when browsers or third-party clients need to call the API origin directly.
|
||||
|
||||
| Variable | Description | Local Default |
|
||||
|----------|-------------|---------------|
|
||||
| `PUBLIC_SERVER_URL` | Canonical public API URL / direct API fallback | `http://localhost:3081` |
|
||||
| `TRUSTED_ORIGINS` | Comma-separated CORS origins for direct browser-to-API calls | — |
|
||||
|
||||
#### CSP Domains (Runtime)
|
||||
|
||||
CSP domain variables are **runtime** — set them in your `.env` or `docker-compose.yaml` `environment:` section. No image rebuild is needed when changing them.
|
||||
@@ -89,7 +96,7 @@ CSP domain variables are **runtime** — set them in your `.env` or `docker-comp
|
||||
| `CSP_MEDIA_SRC_DOMAINS` | Domains allowed in `img-src` and `media-src` |
|
||||
|
||||
<Callout type="warn" title="CSP Domains">
|
||||
When `PUBLIC_IS_SELFHOSTED=true`, the dashboard starts with only `'self'` allowed. Set `ALLOWED_EXTERNAL_DOMAINS` (or the per-directive vars) with your CDN, API, video, and font domains. Since these are runtime env vars, you can change them by restarting the container — no rebuild required.
|
||||
When `PUBLIC_IS_SELFHOSTED=true`, the dashboard starts with only `'self'` allowed. Set `ALLOWED_EXTERNAL_DOMAINS` (or the per-directive vars) with your CDN, video, and font domains. The API does not need to be added for normal dashboard traffic because requests are proxied same-origin. Since these are runtime env vars, you can change them by restarting the container — no rebuild required.
|
||||
</Callout>
|
||||
|
||||
#### Optional — Email
|
||||
@@ -127,7 +134,6 @@ Use `./run-docker-full-stack.sh --no-minio` to exclude MinIO if you do not need
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | Google OAuth |
|
||||
| `AUTH_COOKIE_DOMAIN` | Root domain for cross-subdomain cookies (e.g. `.yourdomain.com`) |
|
||||
| `PRIVATE_APP_HOST`, `PRIVATE_APP_SUBDOMAINS` | Multi-subdomain routing |
|
||||
| `UNSPLASH_API_KEY` | Unsplash image search |
|
||||
| `LICENSE_KEY` | Enterprise features (SSO, token-auth, no-tracking) |
|
||||
@@ -197,8 +203,6 @@ The quickest hosted deployment is the [Railway Template](https://railway.app/tem
|
||||
```zsh title="env"
|
||||
DATABASE_URL="${{db.DATABASE_URL}}"
|
||||
REDIS_URL="${{Redis.REDIS_URL}}"
|
||||
PUBLIC_SERVER_URL="https://api.yourdomain.com"
|
||||
TRUSTED_ORIGINS="https://api.yourdomain.com,https://app.yourdomain.com"
|
||||
BETTER_AUTH_SECRET="random-secure-key"
|
||||
AUTH_BEARER_TOKEN="some-key"
|
||||
PRIVATE_SERVER_KEY="same-key-for-api-and-dashboard"
|
||||
@@ -207,8 +211,10 @@ PUBLIC_IS_SELFHOSTED="true"
|
||||
# Dashboard URL for invite/email links (e.g. https://app.yourdomain.com)
|
||||
DASHBOARD_ORIGIN="https://app.yourdomain.com"
|
||||
|
||||
# Required for auth cookies when API and dashboard use different subdomains — set to your root domain, e.g. yourdomain.com
|
||||
AUTH_COOKIE_DOMAIN="yourdomain.com"
|
||||
# Optional direct public API URL. Normal dashboard auth/API calls use the
|
||||
# dashboard proxy and do not require API/dashboard cookie-domain matching.
|
||||
PUBLIC_SERVER_URL="https://api.yourdomain.com"
|
||||
TRUSTED_ORIGINS=
|
||||
|
||||
# Email (configure one)
|
||||
SMTP_HOST=
|
||||
@@ -225,21 +231,17 @@ CLOUDFLARE_BUCKET_DOMAIN=
|
||||
CLOUDFLARE_IMAGE_BUCKET_DOMAIN=
|
||||
```
|
||||
|
||||
<Callout type="warn" title="AUTH_COOKIE_DOMAIN (Railway)">
|
||||
When API and dashboard use different subdomains, set `AUTH_COOKIE_DOMAIN` to your root domain (e.g. `yourdomain.com`). Without it, Better Auth cookies will not be set and login will fail.
|
||||
</Callout>
|
||||
|
||||
Optional: add `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` for OAuth, `UNSPLASH_API_KEY` for image search, or `LICENSE_KEY` for enterprise features.
|
||||
|
||||
### cio-dashboard
|
||||
|
||||
```zsh title="env"
|
||||
ALLOWED_EXTERNAL_DOMAINS="https://*.yourdomain.com,https://api.yourdomain.com,https://fonts.googleapis.com"
|
||||
ALLOWED_EXTERNAL_DOMAINS="https://*.yourdomain.com,https://fonts.googleapis.com"
|
||||
PRIVATE_APP_HOST="https://app.yourdomain.com"
|
||||
PRIVATE_SERVER_KEY="same-key-for-api-and-dashboard"
|
||||
PUBLIC_IS_SELFHOSTED="true"
|
||||
PRIVATE_SERVER_URL="http://${{api.RAILWAY_PRIVATE_DOMAIN}}:8080"
|
||||
PUBLIC_SERVER_URL="https://api.yourdomain.com"
|
||||
ORIGIN="https://app.yourdomain.com"
|
||||
```
|
||||
|
||||
Optional: add `PUBLIC_APP_TITLE`, `PUBLIC_APP_DESCRIPTION`, `PUBLIC_OG_IMAGE_URL` for custom branding. You can also use per-directive CSP vars (`CSP_SCRIPT_SRC_DOMAINS`, `CSP_MEDIA_SRC_DOMAINS`, etc.) instead of `ALLOWED_EXTERNAL_DOMAINS` for finer control.
|
||||
@@ -258,4 +260,4 @@ For hosted deployments, Cloudflare R2 handles object storage. See the [Cloudflar
|
||||
|
||||
### SSO (Enterprise)
|
||||
|
||||
SSO requires a license key. Set `LICENSE_KEY` in your API environment and add your IdP domains to `TRUSTED_ORIGINS`. [Book a call](http://cal.com/classroomio/enterprise) to get a license.
|
||||
SSO requires a license key. Set `LICENSE_KEY` in your API environment. [Book a call](http://cal.com/classroomio/enterprise) to get a license.
|
||||
|
||||
Reference in New Issue
Block a user