A production-grade Learning Management System powered by SvelteKit 2, Tailwind, and Supabase. It ships responsive landing/auth flows, secure student dashboards, a teacher-only admin experience, file uploads, enrollment keys, and a gradebook pipeline backed by a relational schema.
//register, /login/student/dashboard/admin/dashboarduse:enhanceusername: Admin-user
password: Garden@2025!
Admins bypass Supabase auth with an admin-session cookie. Students must sign up with email + password (hashed via Node's scrypt) and use /student/* protected routes.
npm install
.env with:PUBLIC_SUPABASE_URL=your-project-url
PUBLIC_SUPABASE_ANON_KEY=your-anon-key
npm run dev -- --open
Create three (preferably private) buckets:
course-materials – PDFs, docs, slides, media uploaded by admins.assignment-assets – optional attachments bundled with assignments.student-submissions – files uploaded by students when submitting work.Run the SQL below (edit schema names if needed). profiles acts as the Users table queried by the app.
create table if not exists profiles (
id uuid primary key default gen_random_uuid(),
full_name text not null,
email text not null unique,
password_hash text not null,
role text not null default 'student',
created_at timestamptz default now()
);
create table if not exists courses (
id uuid primary key default gen_random_uuid(),
name text not null,
description text,
category text,
teacher_name text not null,
teacher_email text,
enrollment_key text not null unique,
hero_image text,
created_at timestamptz default now()
);
create table if not exists enrollments (
id uuid primary key default gen_random_uuid(),
user_id uuid references profiles(id) on delete cascade,
course_id uuid references courses(id) on delete cascade,
created_at timestamptz default now(),
unique (user_id, course_id)
);
create table if not exists materials (
id uuid primary key default gen_random_uuid(),
course_id uuid references courses(id) on delete cascade,
title text not null,
description text,
module_label text,
file_url text not null,
storage_path text,
created_at timestamptz default now()
);
create table if not exists assignments (
id uuid primary key default gen_random_uuid(),
course_id uuid references courses(id) on delete cascade,
title text not null,
instructions text not null,
due_date timestamptz not null,
max_score numeric,
attachment_url text,
attachment_path text,
created_at timestamptz default now()
);
create table if not exists submissions (
id uuid primary key default gen_random_uuid(),
assignment_id uuid references assignments(id) on delete cascade,
student_id uuid references profiles(id) on delete cascade,
note text,
file_url text,
submitted_at timestamptz default now()
);
create table if not exists grades (
id uuid primary key default gen_random_uuid(),
submission_id uuid references submissions(id) on delete cascade unique,
score text,
feedback text,
published boolean default false,
graded_at timestamptz default now()
);
create table if not exists announcements (
id uuid primary key default gen_random_uuid(),
course_id uuid references courses(id) on delete cascade,
title text not null,
message text not null,
created_at timestamptz default now()
);
Enable Row Level Security/policies to restrict insert/update/delete to trusted roles (service role or admin override) in production.
insert into courses (name, description, category, teacher_name, teacher_email, enrollment_key)
values ('Product Design 101', 'Intro course with weekly critiques.', 'Design', 'Avery Holt', '[email protected]', 'DESIGN-2025')
on conflict (enrollment_key) do nothing;
insert into assignments (course_id, title, instructions, due_date, max_score)
select id, 'Kickoff Brief', 'Upload research deck + notes.', now() + interval '5 days', 100
from courses where enrollment_key = 'DESIGN-2025'
on conflict do nothing;
insert into materials (course_id, title, description, module_label, file_url)
select id, 'Syllabus', 'Overview of expectations', 'Week 0', 'https://example.com/syllabus.pdf'
from courses where enrollment_key = 'DESIGN-2025'
on conflict do nothing;
Create student accounts via the /register page so passwords are hashed properly.
/admin/* routes.grades).npm run dev – local developmentnpm run build – production buildnpm run preview – preview production servernpm run check – Svelte + TypeScript diagnosticssrc/lib/server/submissions.js / storage.js for production.MIT