Commit Graph
60 Commits
Author SHA1 Message Date
Rotimi Best ddba364bd2 perf(ci): parallelize Railway preview deploys (#909) 2026-08-15 23:31:41 +01:00
Abdulmuizz Hamzatgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>Rotimi Best
d3a0e01f2b fix: flakiness in railway deployment logs (#896)
* fix: handle flakiness in railway deployment logs by verifying actual deployment status

* fix: improve handling of railway deployment log-streaming errors to ensure accurate deployment status

* fix: enhance deployment status verification to address log-streaming glitches in railway

* fix: allow running of railway workflow for forked version

* Apply suggestions from code review

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-08-14 13:48:58 +01:00
Cursor AgentandRotimi Best 9adc38bd8a chore(ci): harden supply-chain controls before reopening PRs
Isolate npm publish from PR CI, enforce frozen lockfiles and a 24h
minimumReleaseAge, and document safe pull_request_target usage so fork
PRs cannot reach publish credentials.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-08-10 21:00:29 +01:00
Abdulmuizz Hamzat 05936e788d chore: update-open-api-spec (#813)
* chore: update-open-api-spec

* fix: address comment
2026-08-05 08:37:41 +01:00
Abdulmuizz Hamzat 8af0675895 fix: minio bucket seed race (#820)
* fix(workflows): retry MinIO bucket creation to survive post-boot format window

A freshly duplicated cio-minio's HTTP listener accepts connections
before it finishes formatting its storage backend (confirmed via its
own boot logs: "Formatting 1st pool, 1 set(s), 1 drives per set."),
so a bucket-creation call landing in that window can transiently fail
with a misleading "specified bucket does not exist" response even
though auth and connectivity (mc alias set) already succeeded. Retry
bucket creation up to 10 times with a 5s backoff instead of failing
the whole run on what's usually just a not-ready-yet response.

* fix(workflows): truncate env name slug to fit Railway's length limit

railway environment new failed with "Error in name - Invalid input"
for a 48-char name (pr-cursor-notes-workspace-feedback-6d09-0e6aa7c9)
derived from a long auto-generated branch name. Railway docs cite a
32-char limit for service names; environment names hit the same wall.

The collision-resistant hash suffix (added for the earlier CodeRabbit
finding) is what actually guarantees uniqueness -- the slug is only
there for readability -- so cap the slug at 18 chars instead of
dropping the hash. pr-<slug><=18>-<hash8> stays at or under 30 chars.

* fix(workflows): retry mc anonymous set for the same boot-race window

The mb retry loop worked as intended in the latest test run (attempt
1 failed on all three buckets during cio-minio's post-boot format
window, attempt 2 succeeded), but the very next call -- anonymous set
download on cio/media -- hit the identical "specified bucket does not
exist" error right after mb had just succeeded. Same underlying race,
one command later. Wrap it in the same retry pattern.

* update workflow

* fix: silen error on db:seed

* update workflow

* update workflow

* update workflow
2026-08-03 02:05:10 +01:00
Abdulmuizz HamzatandRotimi Best 73f39da54a feat(workflows): add Railway preview environment and teardown workflows (#766)
* feat(workflows): add Railway preview environment and teardown workflows

* fix(workflows): enhance security by binding branch names to env variables in Railway workflows

* fix(workflows): ensure preview URLs are live by blocking on service healthchecks during deployment

* temp: auto-run railway-preview.yml on push for demo recording

workflow_dispatch requires the file on the default branch before "Run
workflow" is available; add a scoped push trigger + input fallbacks so
this branch can produce a real run to record. Revert before merge.

* fix(workflows): pass --workspace to railway link for non-interactive CI

Railway CLI now requires --workspace when running `railway link`
non-interactively; without it the preview workflow fails at the link
step. Defaults to the "cio projects" workspace the staging project
lives in, overridable via a RAILWAY_WORKSPACE repo variable.

* fix(workflows): read RAILWAY_PROJECT_ID from secrets, not vars

It's configured as a repository secret (has been for weeks), but the
workflow read it via the vars context, which is a separate namespace
and always resolved empty -> railway link failed with Project "" not
found. Read it via secrets to match how it's actually stored.

* fix(workflows): pass --workspace to environment new/delete, stop masking failures

railway environment new needed --workspace in non-interactive mode too
(same as railway link), but its failure was unconditionally swallowed
by `|| echo`, so the workflow proceeded to "Link" as if the preview
environment existed when it never got created. Add --workspace to
both environment new and environment delete, and only swallow the
"already exists" case -- any other failure now fails the run where it
actually happened instead of surfacing as a confusing error two steps
later.

* fix(workflows): stop passing --project/--workspace to railway environment new/delete

Verified against the actual CLI (railway environment new --help / delete
--help): those subcommands take NO --project/--workspace/--environment
flags at all, and were silently rejecting them, which is why environment
creation kept failing and getting masked by the fallback echo. Only
`railway link` accepts --project/--workspace/--environment, so link to
the base project first, then create/delete environments by name, then
re-link to switch into the new one for deploy.

* fix(workflows): force path-style addressing for mc against Railway's MinIO domain

mc alias set defaults to --path auto, which can pick virtual-host-style
bucket addressing (bucket.host/key). Railway's edge only routes the
exact configured domain, not wildcard bucket subdomains, so mb then
fails with a misleading "specified bucket does not exist" even though
the alias/auth check (a GET) succeeds. Force --path on, matching the
app's own OBJECT_STORAGE_FORCE_PATH_STYLE=true for this same endpoint.

* chore: retrigger preview workflow after fixing cio-minio start command

staging's cio-minio now has the correct Custom Start Command, and the
stale pr-chore-staging-setup preview env (duplicated before that fix)
has been deleted. Empty commit to trigger a fresh deploy run.

* fix(workflows): generate cio-minio's public domain in the preview env

Confirmed staging's cio-minio has its own domain, but environment
duplication doesn't carry it to the new preview environment, so
RAILWAY_PUBLIC_DOMAIN came back empty there and produced an invalid
"https://" alias URL. Generate one in CI, same as the existing
cio-dashboard domain step, pinned to port 9000 since the minio/minio
image exposes both the S3 API (9000) and console (9001).

* chore(workflows): revert temp push-trigger testing scaffold, trim comments

Remove the push trigger and || fallback expressions added solely to
get a run before this file exists on main (workflow_dispatch requires
that for the "Run workflow" UI). Back to workflow_dispatch-only, with
comments trimmed from a debugging narrative down to the durable facts
future readers need. All real fixes found during testing (link
--workspace, secrets vs vars, environment new/delete flag handling,
mc --path on, cio-minio domain generation) are unchanged.

* fix(workflows): fix teardown link/secrets bugs, harden domain reads

railway-preview-teardown.yml had the same class of bugs already found
and fixed in railway-preview.yml during testing:
- RAILWAY_PROJECT_ID read from vars.* while the deploy workflow (and
  the actual GitHub config) uses secrets.* — always resolved empty.
- environment delete was called without linking first, and passed an
  unsupported --project flag; the resulting CLI error was swallowed
  by `|| echo`, so teardown silently no-opped on every run and every
  preview environment leaked indefinitely.

Also hardens railway-preview.yml against a repeat of the cio-minio
domain bug: two other "generate domain, then read it back" spots
(cio-minio bucket seeding, cio-dashboard's final URL report) had the
same unguarded `|| true` swallow with no check that the resulting
host is non-empty. Both now fail loudly instead of silently
proceeding — the dashboard one matters most since it's the final
step and would otherwise report success with a broken link.

Resolves the now-verified SPIKE comment on environment duplication,
and trims a few comments that restated code instead of explaining
non-obvious behavior.

* fix(workflows): address CodeRabbit findings on preview lifecycle

- Unify concurrency groups between deploy and teardown (was
  railway-preview-<branch> vs railway-preview-teardown-<branch>, so
  they never coordinated). A PR-close teardown and a manual deploy
  for the same branch could otherwise race, letting a deploy recreate
  an environment right after teardown deleted it.
- Pin the Railway CLI to 5.30.1 (the version verified against all
  session) in both workflows, instead of an unpinned `npm install -g`
  that could silently pick up different undocumented CLI behavior
  later, same class of bug this session hit repeatedly.
- Append a short hash of the original branch string to the derived
  environment name in both workflows. The slug alone is lossy (/, _,
  and . all collapse to the same separator), so e.g. "foo/bar" and
  "foo_bar" previously collided onto the same environment name.

* fix(workflows): scope teardown job to same-repo PRs only

The header comment already documented that this only applies to
same-repo branches (secrets are withheld from fork PR runs), but
nothing enforced it -- the job ran on every closed PR including
forks, where the empty RAILWAY_API_TOKEN/RAILWAY_PROJECT_ID made
railway link fail with an auth error and show a red run for a PR
that never had a preview environment to clean up.

* docs: migrate RAILWAY_PREVIEW.md to PREVIEW_ENV.md

Replace docker/docs/RAILWAY_PREVIEW.md with PREVIEW_ENV.md at the repo
root, restructured per PR review feedback around the two things a
reader actually needs: how to deploy to staging, and how to deploy a
PR to a preview environment. Drops the one-time Railway project setup
section (not needed on this page, per review).

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
2026-08-01 21:07:31 +01:00
8c949ce2e4 ci: remove deploy-docs workflow (#811)
Remove the Cloudflare docs deployment workflow and the redeploy-docs
job in upload-openapi-spec that called it.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-07-31 16:35:36 +01:00
Abdulmuizz HamzatandRotimi Best b599f1a017 chore(docs): migrate the docs site from Fumadocs to Blume (#753)
* Update documentation for ClassroomIO: change Fumadocs to Blume, revise MDX format conventions, and clarify sidebar navigation requirements.

* docs: update README and scripts for improved preview and error handling

* docs: update Discord link in configuration and documentation

* docs: add token-auth route and update documentation formatting

* Fix formatting in auto-comment-issues.yml

* Fix JSON syntax by adding a comma

* fix(docs): declare Blume's Astro build deps so Cloudflare build resolves them

blume build generates an Astro project under apps/docs/.blume/ that imports
astro, @astrojs/mdx, @tailwindcss/vite, tailwindcss, @tailwindcss/typography,
@shikijs/twoslash and @scalar/astro. Under pnpm's isolated node_modules these
are nested inside blume and not resolvable from the docs app, so a clean
--frozen-lockfile build (Cloudflare) fails at 'Cannot find module astro/config'.
Declare them directly in @cio/docs, pinned to blume's resolved versions.

* refactor(docs): update API reference paths and documentation for clarity

* fix: change platofrm url to docs home page

* Refactor documentation for self-hosting ClassroomIO

- Removed the Supabase Cloud setup guide as it is no longer needed.
- Updated the Enterprise SSO documentation to include a reference link for SSO & licensing.
- Added new documentation for Content Security Policy (CSP) configuration.
- Created a new section for configuring SMTP for email notifications.
- Introduced a comprehensive configuration index for self-hosted ClassroomIO, detailing environment variables and their usage.
- Added documentation for optional integrations including Google OAuth, OpenAI, and Unsplash.
- Documented the generation and usage of required secrets for ClassroomIO.
- Created a section for SSO & licensing, detailing the requirements for Enterprise features.
- Added object storage configuration documentation, covering both bundled MinIO and external S3-compatible services.
- Documented versioning and upgrade processes for ClassroomIO.
- Updated the Coolify and Docker self-hosting guides to streamline configuration references and upgrade instructions.
- Enhanced the Railway self-hosting guide with links to relevant configuration sections.
- Updated token authentication documentation to reference SSO & licensing.

* Add comprehensive guides for ClassroomIO features and functionalities

- Introduced a new guide for the Student Dashboard, detailing navigation and features available to students.
- Added a guide on using Tags for organizing and filtering courses within ClassroomIO.
- Created a guide on how to take attendance in courses, including marking attendance and related functionalities.
- Developed a Terminology guide to clarify key terms used throughout ClassroomIO.
- Implemented a Token Auth guide for signing users into ClassroomIO from external applications using JWT.
- Added instructions for using math in the editor with LaTeX support.
- Created a guide for customizing the welcome email sent to students upon enrollment in courses.
- Added a guide explaining what ClassroomIO is and its key features.
- Updated the home documentation to provide a clearer overview of ClassroomIO and its resources.
- Enhanced self-hosted configuration documentation with a new guide for deploying ClassroomIO on Dokploy.
- Updated links in the self-hosted configuration documentation for better navigation.
- Added new images to support the Dokploy deployment guide.
- Updated asset packaging script to handle redirects for the new guide structure.
- Fixed footer link to point to the correct Getting Started guide.

* chore: uprage version

* feat(docs): add DigitalOcean deployment guide and flatten guides structure

- Add a guide for deploying ClassroomIO on DigitalOcean using Docker Compose
  and Caddy for automatic HTTPS.
- List DigitalOcean as a deployment option in the self-hosting index.
- Move guides out of content/docs/guides into content/docs (student-dashboard,
  tags, terminology, token-auth, what-is-classroomio, audience, cohorts,
  community, course, enterprise-sso, mcp, organization, quickstart/*).
- Rename home.mdx to index.mdx.
- Update package-assets script to emit redirects for the old guides paths.

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
2026-07-22 22:30:10 +01:00
ca3fdd5b6a fix(dashboard): lesson nav build error and add CI build check (#765)
* fix(dashboard): repair lesson nav build and improve locked feedback

Replace stale $isOrgStudent reference with isCourseLearnerView to fix
the Svelte build error. Disable prev/next when the adjacent item is
locked, show lock tooltips, and surface a snackbar if navigation is
blocked (including keyboard shortcuts).

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* ci: add package build check and AGENTS.md verification steps

Document required dashboard/API build commands for agents and add a
GitHub Actions workflow that runs those builds on PRs touching apps or
packages. Copy dashboard .env.example in CI so vite build has required
public env vars.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-07-16 12:34:03 +01:00
Abdulmuizz Hamzat 2e402efa78 chore(ci): publish latest from main and retire the edge tag (#711)
* fix(docs): update versioning strategy and tag policies for Docker images

* fix(docs): clarify versioning strategy in environment configuration
fix(docker): update docker-push script to remove local latest tag handling

* fix(docs): clarify VERSION variable usage in Docker push script
2026-07-07 12:59:33 +01:00
efdcfba68d feat(api): purge Cloudflare CDN before and after OpenAPI spec upload (#704)
* feat(api): purge Cloudflare CDN before and after OpenAPI spec upload

Purge api.cdn.clsrio.com edge cache for openapi-latest and the dated
spec URL so deploys are not blocked by the 1h Cache-Control TTL on R2
objects. Uses the same CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID
pattern as embed uploads; skips when credentials are unset.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(api): purge OpenAPI CDN cache only after upload

Match the embeds upload pattern: update R2 first, then invalidate
edge cache so clients refetch the new spec.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(api): treat Cloudflare purge 2xx as success when body is unparseable

Avoid false upload failures when the purge API returns 200 with a
non-JSON body. Addresses Greptile review on #704.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-07-02 12:11:00 +01:00
fe9f8c441f feat(api): public API plan gating, docs auth, and dashboard snippets (#701)
* fix(automation): align public API plan gating across cloud and self-hosted

Add shared canUsePublicApi helper so cloud allows Early Adopter and
Enterprise while self-hosted requires Enterprise. Update the API key
dashboard page and backend key creation checks to use the same rules.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* docs(api): document bearer auth on public API reference

Add shared OpenAPI enrichment with bearerAuth security scheme and
authentication instructions. The interactive reference now loads an
enriched spec with Authorize support, and the API guide explains how
to create and use org API keys.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* feat(dashboard): add API quick-start code snippets on automation page

Mirror the MCP client setup section with cURL, JavaScript, and Python
examples that show how to authenticate public API requests with org keys.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* ci(openapi): republish spec when public API OpenAPI helpers change

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* feat(dashboard): add Go snippet to API quick-start examples

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* docs(api): use /org/*/api and clarify dashboard key generation path

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

* fix(api): escape backticks in OpenAPI description template literal

Greptile flagged that unescaped backticks around * in
PUBLIC_API_OPENAPI_DESCRIPTION evaluated to NaN and dropped auth docs
from the Scalar spec. Also use plan-specific snackbar message on upgrade.

Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
2026-07-02 11:24:33 +01:00
Abdulmuizz HamzatandRotimi Best 6d594b4296 feat: harden self-hosting + add pull-based deploy, Coolify/Railway (#666)
* feat: enhance self-hosting support and CI/CD workflows

- Updated .env.example to clarify auth secret generation and self-hosting mode.
- Added CI workflow for build and lint checks on PRs and main branch pushes.
- Improved docker-publish workflow to trigger on relevant file changes and added multi-arch support.
- Enhanced upload-openapi-spec workflow with caching for dependencies.
- Removed obsolete Dockerfile for API service and added jobs worker to Docker setup.
- Updated self-host documentation to reflect new jobs worker requirement.
- Modified Dockerfiles for API and Dashboard to optimize dependency fetching and installation.
- Enhanced docker-compose.yaml with health checks and jobs worker configuration.
- Updated docker-push.sh to support multi-architecture builds and improved logging.
- Revised documentation for publishing images and self-hosting to include new features and requirements.

* feat(self-host): safe versioned releases + pull-based image pipeline

Builds on the pull-based images compose (docker-compose.images.yaml) to make
pulling pre-built images safe for self-hosters:

- docker-publish: stable-releases + edge tag policy. Git tag vX.Y.Z publishes
  X.Y.Z/X.Y/X/latest; a push to main publishes only :edge (latest never moves
  on a routine merge).
- Pre-publish smoke-test gate: each image is booted against ephemeral
  postgres+redis and healthchecked before any push (docker/smoke-test.sh).
- CIO_VERSION pinning in .env.example; run-docker-full-stack.sh --images flag
  to pull instead of build.
- Docs: SELF_HOST pull-from-images + versioning/upgrade/rollback sections,
  PUBLISHING_IMAGES tag policy + pnpm release flow, self-hosting.mdx quickstart.

* feat: enhance self-hosting support and CI/CD workflows

- Updated .env.example to clarify auth secret generation and self-hosting mode.
- Added CI workflow for build and lint checks on PRs and main branch pushes.
- Improved docker-publish workflow to trigger on relevant file changes and added multi-arch support.
- Enhanced upload-openapi-spec workflow with caching for dependencies.
- Removed obsolete Dockerfile for API service and added jobs worker to Docker setup.
- Updated self-host documentation to reflect new jobs worker requirement.
- Modified Dockerfiles for API and Dashboard to optimize dependency fetching and installation.
- Enhanced docker-compose.yaml with health checks and jobs worker configuration.
- Updated docker-push.sh to support multi-architecture builds and improved logging.
- Revised documentation for publishing images and self-hosting to include new features and requirements.

* ci: run build-only on PRs, defer lint until repo-wide format

* docs: point Railway template link at the new template URL

* fix: address Greptile review + dashboard CI build env

- docker-push.sh: only also-tag :latest for real semver releases, so
  VERSION=edge/main/dev can't clobber the stable latest pointer (P1).
- run-docker-full-stack.sh: stop deriving OBJECT_STORAGE_PUBLIC_ENDPOINT from
  the dashboard origin (broke presigned URLs for videos/documents buckets);
  derive only the public media base URL and document the S3-API route.
- ci.yml: provide PUBLIC_IS_SELFHOSTED at build time (dashboard reads it from
  SvelteKit's strict $env/static/public); bump cache actions v3 -> v4.
- upload-openapi-spec.yml: bump cache actions v3 -> v4.
- smoke-test.sh: dashboard gate now fails on 5xx instead of accepting any response.

* ci: scope PR build gate to the api + dashboard closures

Building every package surfaced pre-existing rot in apps that aren't part of
this PR (storybook's missing @lucide/svelte import, course-app prettier debt).
The gate's purpose is to verify the two Docker-image apps; the rest are built on
PRs by their own Cloudflare Workers builds. Filter to @cio/api... and
@cio/dashboard... so the gate covers exactly what it should.

* fix(docker-push): make latest_arg always return 0

Use an `if` instead of `is_release_version && printf` so the helper returns 0
even when it emits no tag. A non-zero return inside the $(latest_arg ...) build
argument is fragile under set -e (and would abort with inherit_errexit), which
would break the edge/main builds the policy explicitly supports.

* fix(docker): copy patches/ into jobs image so pnpm install resolves patchedDependencies

The jobs Dockerfile ran `pnpm install` without copying patches/, so pnpm
failed hashing package.json#pnpm.patchedDependencies (layerchart patch),
breaking the self-host full-stack build. api/dashboard already copy patches/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(docker): build jobs worker deps in topological order

The jobs Dockerfile built workspace packages with a hand-written list that
omitted transitive deps (e.g. @cio/question-types, consumed by @cio/utils),
so `tsc` failed with TS2307 during the self-host build. Use the recursive
`pnpm -r --filter @cio/jobs-worker... build` like the api/dashboard images,
which builds the full dependency closure in the correct order.

* refactor(docker): move jobs Dockerfile to docker/Dockerfile.jobs and align dep install

Move apps/jobs/Dockerfile -> docker/Dockerfile.jobs so all three image recipes
live together in docker/, and update every reference (docker-compose.yaml,
docker-push.sh, docker-publish.yml, railway.jobs.json, self-hosting.mdx).

Also align its dependency install with the api/dashboard images: replace the
hand-maintained list of workspace package.json copies (which silently missed
transitive deps like @cio/core / @cio/ai-assistant, leaving their node_modules
uninstalled and breaking the build with TS2307) with the proven pnpm-fetch
pattern: fetch the full lockfile into a cached store, COPY . ., then an offline
filtered install of @cio/jobs-worker... This installs the complete dependency
closure regardless of how the workspace graph grows.

* refactor(docker): move docker-compose.yaml to repo root for Coolify compatibility

Coolify's Docker Compose build pack forces Base Directory to the repo root and
resolves build contexts from there, so the compose's `context: ..` (needed when
the file lived in docker/) climbed above the repo and failed with
`lstat /artifacts/docker: no such file or directory`.

Move docker/docker-compose.yaml -> ./docker-compose.yaml and change the build
contexts from `..` to `.` so the context is the repo root with no upward path.
This works identically for run-docker-full-stack.sh, manual `docker compose up`,
and Coolify. Dockerfiles stay referenced as docker/Dockerfile.*. Update the
references in run-docker-full-stack.sh and the docs accordingly. The images
compose (docker-compose.images.yaml) is unaffected (no build context) and stays
in docker/.

* Add self-hosting documentation for Coolify and Railway

- Created new documentation for deploying ClassroomIO on Coolify using a pull-based Docker Compose stack.
- Added comprehensive guide for self-hosting ClassroomIO with Docker, covering both pre-built images and building from source.
- Introduced a new meta.json file to organize self-hosting documentation pages.
- Developed a Railway deployment guide with environment variable configurations for API, dashboard, and jobs worker.
- Updated self-hosting blog post to link directly to the new Railway guide.
- Removed obsolete Railway configuration files (railway.api.json, railway.dashboard.json, railway.jobs.json).
- Enhanced run-docker-full-stack.sh script to improve MinIO credential generation and service readiness checks.
- Updated docker-compose files to enforce strong secret requirements for authentication.
- Adjusted self-hosting references in the Docker documentation to include new guides.

* fix(docker): point references at root-level docker-compose.images.yaml

The pull-based compose file moved to the repo root (alongside docker-compose.yaml).
Update IMAGES_COMPOSE_FILE in run-docker-full-stack.sh, the file's own header
example, and the docs (docker.mdx curl URL, coolify.mdx path + blob link,
SELF_HOST.md relative link + curl URL).

* fix(self-host): address review round 2 — drop unused AUTH_BEARER_TOKEN, pull-by-default, doc fixes

- Remove AUTH_BEARER_TOKEN everywhere (unused in app code; PRIVATE_SERVER_KEY is the real
  dashboard<->api key). Simplifies run-docker-full-stack.sh secret generation.
- run-docker-full-stack.sh now defaults to PULLING pre-built images; add --build for source.
- docs: recommend MinIO, 'a way' (not 'recommended way') to self-host, remove Direct API Access
  section, trim Common Commands, SMTP vars per-line, fix PRIVATE_SERVER_KEY wording.

* feat(db): versioned migrations for self-host with auto-baseline

- Regenerate a single clean baseline migration; db:setup now runs drizzle-kit migrate (not push).
- Add db:generate / db:migrate / db:baseline scripts.
- baselineMigrationsIfNeeded(): adopt an existing (push/supabase) schema into drizzle's journal so
  migrate won't try to recreate existing objects ("type/table already exists"). No-ops on fresh
  and already-tracked databases; runs automatically in db:setup before migrate.

* ci(openapi): drop stale ci.yml reference in turbo-cache comment

ci.yml was removed earlier in this review; the openapi workflow's turbo cache still
warms from its own prior runs via restore-keys, so this is a comment-only fix.

* fix(db): make baseline squash-safe for migrate-tracked databases

baselineMigrationsIfNeeded returned early whenever drizzle.__drizzle_migrations
had any rows. After a migration squash, an instance tracked under the old
journal (pre-squash hashes) would then let `drizzle-kit migrate` re-run the
consolidated baseline — whose journal `when` is newer than every old created_at
— and crash on `CREATE TYPE/TABLE "..." already exists` at startup.

Now, when history exists but the consolidated baseline hash is absent, insert
that hash (marking it already applied) so migrate skips it. Only the baseline
entry (idx 0) is reconciled, so genuinely pending future migrations still run.

Verified end-to-end against Postgres: reproduces the original crash, confirms
the fix no-ops migrate on a pre-squash DB, and confirms a later 0001 migration
still applies. Fresh-DB and already-adopted paths unchanged.

* fix(docker): add explicit id to pnpm-store cache mounts

Railway's Metal builder rejects BuildKit cache mounts that omit an
explicit id (it does not default id to target like standard Docker/
BuildKit do), failing the build at parse time with:

  dockerfile invalid: flag '--mount=type=cache,target=/pnpm/store'
  is missing an id argument

Add id=pnpm-store to all six cache mounts across the api, dashboard,
and jobs images. Harmless on Docker Desktop / CI / GitHub Actions, and
required for the Railway self-host build path.

* fix(docker): drop BuildKit cache mounts for Railway compatibility

Railway's builder rejects cache-mount ids not prefixed with its
per-service cacheKey, which can't be hardcoded in a Dockerfile shared
across CI, local Docker, and three Railway services. Remove the
--mount=type=cache flag (keep --store-dir /pnpm/store); the store is
baked into the fetch layer and reused by the offline install, so
behaviour is unchanged. Supersedes 9c4633f2c.

* fix(docker): force LF for shell scripts and Dockerfiles via .gitattributes

cio-api crash-looped on Railway with 'exec /app/entrypoint.sh: No such
file or directory' because the entrypoint shipped with CRLF endings
(Windows checkout, core.autocrlf=true) — the '#!/bin/sh\r' shebang is
unresolvable in the container. Add .gitattributes pinning *.sh and
Dockerfile* to LF so container-executed files are never CRLF regardless
of contributor OS.

* fix(db): serialize db-setup with an advisory lock to prevent baseline double-insert

Concurrent db:setup runs (e.g. multiple API replicas booting together) could both
pass the baseline 'already present?' check and insert duplicate rows into
drizzle.__drizzle_migrations, which has no unique constraint on hash. Wrap baseline
+ migrate in a pg_advisory_lock held on a dedicated connection so concurrent
startups serialize: the loser blocks, then sees the baseline applied and no-ops.

* docs(railway): update template deploy URL to classroomio-1

---------
Co-authored-by: Rotimi Best <best@classroomio.com>
2026-06-23 10:17:09 +01:00
Rotimi Best 367ddcddd7 feat(embeds): add embed.classroomio.com public widget host
Serve widgets from short public URLs via tenant-router with R2-backed
assets and runtime API_URL injection, so copy-paste snippets hide infra
paths. Update widget payloads, CSP, upload purge URLs, and dev-only API
inject; document format/stage workflow in AGENTS.md and fix ui-prefix-check
YAML syntax.
2026-06-12 02:06:37 +01:00
Rotimi Best 899a9f2b51 fix: apply ui: prefix to all packages/ui components and automate it 2026-06-11 23:18:40 +01:00
Rotimi Best 2f1b6376ff ci: use pnpm/action-setup@v4 so CI reads the pinned pnpm version
The openapi/storybook/embeds workflows pinned pnpm 8 and cypress pinned
pnpm 6 via action-setup@v2. The repo's lockfile is lockfileVersion 9.0
(written by the pinned packageManager pnpm@10.19.0), which pnpm 8/6 cannot
read — it ignored the lockfile and --frozen-lockfile failed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. Drop the explicit version and let
action-setup@v4 resolve pnpm from the packageManager field, matching the
already-working publish-mcp / publish-course-app workflows.
2026-06-07 19:05:15 +01:00
Rotimi Best f0fad9512c chore: force pnpm i in upload api spec workflow 2026-05-21 22:59:55 +01:00
Rotimi Best fbbce6b7c0 fix: ci fixes, tenant router debug, select width improvement 2026-05-21 17:52:28 +01:00
Rotimi Best 2f710e1739 chore: remove supabase ci/cd 2026-05-19 04:44:19 +01:00
Rotimi Best d6e812d08b feat: enhance API and middleware functionality: add public API routes, implement program and course member checks, and improve rate limiting for invite acceptance. Update OpenAPI spec generation to focus on public API endpoints and add new middleware for automation key scopes. Include new documentation for lesson AI features and public API tests. 2026-04-12 08:58:41 +01:00
Rotimi Best 7caf6dd995 chore: update MCP package build process and configuration defaults 2026-03-14 08:43:39 +01:00
Rotimi Best dd8c8137a0 chore: update publish workflow to trigger on main branch and refine slug generation in course settings 2026-03-13 20:18:46 +01:00
Rotimi Best 492f8dc5ed feat: implement automation key management with middleware and routes for organization API keys, invite improvements 2026-03-13 18:40:07 +01:00
Rotimi Best 77a1188573 Publish @classroomio/mcp package 2026-03-13 18:01:46 +01:00
Rotimi Best c7854efae3 feat: redo settings and all org pages. (#601)
* feat: new components, deploy storybook

* refactor: simplify component field definitions and enhance button loading state

- Removed unnecessary type imports and simplified the FIELDS array definitions across multiple components in Storybook.
- Updated button component to use a loading state instead of isLoading, improving clarity and consistency in props.
- Adjusted button stories to reflect the new loading prop and enhanced the button's loading visual feedback.
- Minor CSS adjustments for better layout and styling consistency.

* chore: add field, card, password components

* feat: improve page component & update AuthUI path

* chore: update bits-ui to version 2.14.4 and all org related UI's

- Updated bits-ui dependency in pnpm-lock.yaml and package.json to version 2.14.4.
- Refactored imports for RoleBasedSecurity and UpgradeBanner components to use the new ui feature directory.
- Removed unused Course components and related files to clean up the codebase.
- Enhanced various components to improve consistency and maintainability.

* chore: move all setting buttons to the top and update auth ui styles

* chore: remove svelte-loading-spinners and replace with @cio/ui/base/spinner

- Removed all instances of svelte-loading-spinners from the project.
- Updated components to use the new Spinner from @cio/ui/base/spinner.
- Cleaned up pnpm-lock.yaml and package.json to reflect the removal of svelte-loading-spinners.
- Added new CSS animations for rocket launch effects in app.css.
- Enhanced email change flow with updated verification messages in translations.

* chore: remove supabase file upload from dashboard and move to r2

* chore: update organization API and UI components

- Added new API methods for organization plan operations and team management.
- Introduced middleware for API key authentication and organization admin checks.
- Refactored organization-related components to utilize the new API structure.
- Updated various UI components to improve consistency and maintainability.
- Enhanced error handling and validation for organization-related actions.
- Removed deprecated services and streamlined the codebase.
2025-12-02 09:54:38 +01:00
Rotimi Best 9c9f049917 chore: clean up docker-publish.yml by removing unused tag types 2025-10-11 14:41:47 +01:00
Rotimi Best 775e616d26 chore: update compose to use latest image 2025-10-11 14:33:26 +01:00
Rotimi Best 168866c81b feat: optimize docker compose for dashboard and api (#578)
* chore: use turbo for build

* fix docker compose file and remove backend

* chore: update .dockerignore to include additional files and directories for improved build management

* copy everything in both docke filed

* refactor: streamline Dockerfile to copy entire dashboard app and remove unnecessary node_modules references

* update api to use turbo to start

* run ls la for docker files

* update cmd commands

* update cmds to use node

* fix: add redis url to docker compose

* fix: reduce memory allocation for dashboard build in Dockerfile

* debug dashboard build

* update api docker file

* update dashboard

* add tsconfig

* chore: add echo to dockerfile

* fix: change echo to run

* chore: install pnpm before switching to non-root user in Dockerfile

* refactor: update start commands in package.json and Dockerfiles to use pnpm

* install turbo

* use turbo 1.10.16

* update turbo config

* fix: update CMD in Dockerfile to run the API directly using Node.js

* change

* add commands to turbo.json

* copy over turbo

* add workspace

* fix: update start commands in package.json to specify ports for dashboard and API

* publish docker images

* add dev branch

* update compose

* build only for amd64 platform

* refactor: update docker-compose to use pre-built images and remove build context

* coolify-dockertest

* point to latest

* chore: update docker-compose to use specific version tags for API and dashboard images

* remove envs for docker file

* 0.1.3

* use dynamic env

* 0.1.4

* add PUBLIC_IS_SELFHOSTED=true

* chore: update docker-compose to use version 0.1.5 for API and dashboard images

* use static for PUBLIC_IS_SELFHOSTED

* disable cache for tag

* use 0.1.7

* add PUBLIC_IS_SELFHOSTED=true

* comment out caching

* update docker-compose to use version 0.1.8 for API and dashboard images

* update api port

* use tag 0.1.9

* add cache

* use latest tag in compose file
2025-10-11 13:27:50 +01:00
Rotimi Best 6d68351a46 feat: end to end api type safety with redis rate limiting (#573)
* feat: support hono rpc

* add wrangler file

* update name of wrangler file

* add wrangler as a dependency

* add compatibility_flags

* trigger ci

* core: add health check to docker file

* expose port 80

* update custom healthcheck

* feat: implement openapi

* install dependencies in root

* debug yml

* update yml

* update port

* update dockerfile with envs

* completely move away from apiClient

* rename backend to api

* refactor: update server descriptions and comment out local spec saving

* update lock file

* use api.classroomio.com

* add support for redis

* fix: add missing line break in WelcomeModal.svelte

* refactor: remove wrangler configuration and update Vite config for API integration
2025-09-21 19:35:41 +01:00
Rotimi Best f87a8beb17 chore: change node version to 20.19.3 2025-09-10 17:24:35 +01:00
Rotimi Best 733e24ae75 chore: use node v20.19.4 2025-09-07 16:30:57 +01:00
Rotimi Best 11a65783e7 chore: upgrade to node 22.12 2025-09-07 15:04:09 +01:00
Rotimi Best 5737765cb9 fix: update node version from 18.17 to 18.18 (#563)
* fix: update node version from 18.17 to 18.18

* update nvmrc

* add nvmrc
2025-07-17 12:23:56 +01:00
Rotimi Bestandemmanuel 3ee7cf10e3 feat: course-app v0.0.11 (#523)
* chore: remove bin

* fix: restore bin

* change dist to bin

* publish only dist folder

* publish with src folder.

* fix ci

* merge steps in CI

* update workflow

* deploy with src

* update to v9

* delete unused code

* complete landing page

* complete landing page

* feat: webflow template (#518)

* created a new branch for webflow from debug/course-app

* added webflow and reverted neccessary changes

* fixed commit issues

* clean up posthog

* clean up classic

* clean up minimal

* clean up posthog

* clean up webflow

* v0.0.10

* markdown lint

* feat: add prep template (#522)

* working on the ielts template

* working on the ielts template

* finished the ielts template

---------

Co-authored-by: emmanuel <chifez1@gmail.com>

* clean up examprep

* v0.0.11

---------

Co-authored-by: Em_Dev <Chifez1@gmail.com>
2024-12-17 04:06:03 +01:00
Rotimi Best 5c83034a01 chore: make course-app public 2024-12-14 04:01:44 +01:00
Rotimi Best f1de9e1d03 fix: publish course app 2024-12-14 03:52:27 +01:00
Rotimi Best ac21be42b0 chore: update @classroomio/course-app CI trigger 2024-12-14 03:49:06 +01:00
Rotimi Bestandemmanuel 26700dccd0 feat: introducing @classroomio/course-app (#490)
* feat: v1 templates

* update nvm

* working on the displaying the course content

* working on the displaying the course content

* displayed the content

* finished with the logic for rendering the content for all the courses and lessons

* post hog template UI is good to go

* classic and minimalistic template UI is good to go

* some refactoring

* some refactoring

* layout adjustments

* update tsconfig

* trying to get the filter to work well

* this is working now

* cleanup

* cleanup

* UI updates

* course page UI for posthog

* navbar and sidebar for course page in all templates complete

* all templates course page showing lesson content from md file with mdsvex
configured

* wokring on sections

* posthog lesson content done

* all templates fetches the lesson content from the section

* all templates properly typed

* scaffolding the new template

* finished the webflow-saas

* done with the webflow saas UI

* new calcom template

* new calcom template

* masonry layout for testitomonail section layout done

* calcom UI done

* UI all done

* cleanup

* trying to fix dark mode for the course page and home page

* done with this

* done with this

* working on tests

* migrated minimalistic template to svelte 5

* updated the syntax to svelte 5 for webflow

* migrated classic to svelte 5

* migrated posthog to svelte 5

* used svelte 5 syntax in calcom

* cli tool woks locally

* rename to course-sites

* use pnpm in root

* calcom refactoring

* remove unused files

* chore: biggest refactoring in my life

* add basic readme

* refactoring on course page

* restructure how courses are ordered

* proper state propagation

* add cf adapter

* fix pnpm i issue

* fix folder icon size

* use 18.17 version

* update npmrc

* add engine to packagejson

* pnpm install

* add engine to cio-com

* make it 18.x

* remove sequence

* move mdsvex to dependencies

* update lock

* mdsvex please work

* replace fs with import from vite

* add lock

* styling improvements

* fix styling

* sidebar style improvements

* fix: mobile concerns

* fix spacing of course header on desktop

* update header

* top nav fix

* feat: make cal template

* setup another template

* classic basic UI

* fixed the /courses fetching wrong page content

* fix breaking template cause of logo

* adding some updates

* improve styles theming

* finished updates from reviews

* updated the custom styling css

* clean up posthog template

* cli tool done, needs some cleaning though

* moved components to home

* improved the UI of the minimal template

* Add CLI tool and course app site

* prettier lint

* improve CLI

* ignore sveltekit folder

* publish course app

* fix tailwing theme + improve README

* finish up course-app site

* update README.md

---------

Co-authored-by: emmanuel <chifez1@gmail.com>
2024-12-14 03:40:47 +01:00
Rotimi Best 5e7726c6ad fix: update supabase prod ci 2024-06-08 05:03:55 +01:00
Rotimi Best b8554cd0d7 fix: deploy supabase prod 2024-06-08 05:03:28 +01:00
Rotimi Best 1591b5be2c chore: temp disable ci to supabase staging 2024-06-06 05:41:58 +01:00
Adeyemi AdetunjiandRotimi Best 32e10302cb feat: teachers page (#275)
* feat: add a teacher route

* refactor

* PR Fixes

* text-md fix

* remove root

* fixes

* update content

* Update Register behavior

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
2024-04-22 22:17:33 +01:00
Em_DevandRotimi Best 5680ca45fa chore: new logo (#268)
* updated the logo

* used a higer resolution of the 512 logo

* used a higer resolution of the 512 logo

* remove logo svg

* update logos

* stop cyprus

* delete unused files

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
2024-04-22 18:25:14 +01:00
Rotimi Best a95be6d620 ci: cypres test setup(#274)
* fix: font size broken

* add first cypress test

* save onboarding language

* load language

* get first test to work

* update command

* install pnpm

* update workflow

* install

* update

* test

* increase timeout

* another update

* update 2

* update 3

* upsdada 4

* sfgsfgf 5

* add def supabase url

* fix supabase issue

* add ci

* fix ci

* update .env.example

* update port

* tired

* prepare

* preview

* this is it

* ?????

* 99999900

* fix log issue

* ci

* nkkh

* =====

* 200000
2024-04-20 04:39:16 +01:00
Rotimi Best a626a18221 feat: deploy backend on push 2024-04-12 14:59:40 +01:00
Rotimi Best a94205a0ed fix: restore fly.io 2024-04-12 14:25:51 +01:00
Rotimi Best 085d7a2600 chore: remove fly.io github action 2024-04-09 21:44:18 +01:00
Rotimi Best 62a32a6204 feat: make video upload speed down to 5min for <500mb (#209)
* feat: make video upload down to 5min for <500mb

* update to 15 minutes timeout
2024-03-16 17:35:23 +01:00
Rotimi Best af909d1dd2 chore: make backend deploy, trigger only 2024-03-16 06:44:22 +01:00
Rotimi Best a5ab4ca49f chore: cd into backend before deploy 2024-03-16 06:28:48 +01:00