TutorLoom
기업·대학·교육기관을 위한 학습 플랫폼, 무료 오픈소스이며 엔터프라이즈 버전 제공
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Working codename. One core product, many schools. Not a portal per school.
Phase 0 is in progress. The foundations are built and proven: tenant isolation, the guard chain,
authentication, the design-system primitives and both app shells. No school-facing features yet —
that is Phase 1. Read docs/ before opening an editor; docs/20-phase-0-build-plan.md tracks what
is done.
| Order | Document | Why |
|---|---|---|
| 0 | docs/00-START-HERE.md | The entire plan in a 20-minute read. Start here. |
| 1 | docs/01-vision-and-scope.md | What we are building and what we deliberately are not |
| 2 | docs/02-feasibility-and-market.md | Is this viable, who else is in the market, what wins |
| 3 | docs/03-architecture.md | System shape, boundaries, request lifecycle |
| 4 | docs/04-multi-tenancy-and-security.md | The single most important document in this repo |
| 5 | docs/05-tech-stack.md | Every library, pinned, with a reason |
| 6 | docs/06-repo-structure.md | Turborepo layout and package boundaries |
| 7 | docs/07-data-model.md | Tables, keys, constraints, invariants |
| 8 | docs/08-rbac-and-roles.md | Roles, permissions, what each person actually sees |
| 9 | docs/09-page-inventory.md | Every screen in the school portal and what it does |
| 9b | docs/modules/ | Functional specification per domain |
| 10 | docs/10-ux-and-design-system.md | How we make it "next level" instead of a 20-item sidebar |
| 11 | docs/11-api-conventions.md | Contract rules between API and apps |
| 12 | docs/12-engineering-rules.md | Non-negotiables. Read before every PR |
| 13 | docs/13-infrastructure-and-deployment.md | Free-tier reality check and the path to production |
| 14 | docs/14-roadmap-and-phases.md | Phase plan with exit criteria |
| 15 | docs/15-risks-and-open-decisions.md | What can kill this, and what still needs a decision |
| 16 | docs/16-ui-principles.md | Binding rules for every piece of UI work. Read before writing a component |
| — | docs/adr/ | Architecture Decision Records |
A single NestJS API and two Next.js apps (school portal + super-admin console) in a Turborepo,
backed by one PostgreSQL database using shared-schema multi-tenancy with row-level security. Schools
are tenants. Every tenant row carries school_id, enforced simultaneously by an application-layer
query scope and a database RLS policy. The commercial engine is the fee module: configurable fee
heads, plans, discounts and a monthly voucher run that is idempotent, resumable and auditable.
Everything a school might want to customise is data, never a code branch.
Your previous portal became unmanageable because one school's requirements were allowed to become code. In this codebase, a school-specific requirement is either configuration, a feature flag, or it is not built.
There is no if (schoolId === 'x') in this repository. Ever. See docs/12-engineering-rules.md.
Requires Node 22 and pnpm 10 (see .nvmrc).
pnpm install
# Start PostgreSQL. Runs from node_modules — no Docker, no install, no admin.
# Leave it running in its own terminal.
pnpm db
cp .env.example .env
pnpm verify:rls-role # prove the app role cannot bypass RLS
pnpm --filter @ilm/db exec prisma migrate deploy
pnpm build
pnpm --filter @ilm/api dev # API :4000
pnpm --filter @ilm/portal dev # portal :3000
pnpm --filter @ilm/admin dev # platform :3001
There is no Docker in this project. pnpm db runs a real PostgreSQL from a binary in node_modules
for local development and tests. Deployed environments point DATABASE_URL at Supabase today and
Railway later (docs/13 §3) — Supabase is used as plain PostgreSQL only, no Auth and no client SDK,
which is what makes that move a pg_dump rather than a rewrite.
Tenants resolve from the hostname, so one process serves two sites:
| Address | What it is |
|---|---|
localhost:3000 | The public site — landing page, packages, signup, and sign-in |
{slug}.localhost:3000 | A school's portal |
localhost:3001 | The platform console |
Sign-in asks for an email and a password on both, and never for a school. On a school's address
the tenant is in the URL; on the apex it is resolved from the credentials after the password has
been verified, and only then does a person with accounts at several schools see them. A picker
offered before the password would hand the list of every school to anyone who loaded the page — see
docs/adr/0009-global-sign-in.md.
pnpm turbo run lint typecheck test build
The suites include the tenant-isolation suite and the structural RLS gate. Neither can skip when no database is present: a suite that silently skips is a green build that proves nothing, and what these two prove is the one thing that ends the company if it breaks.