TutorLoom
기업·대학·교육기관을 위한 학습 플랫폼, 무료 오픈소스이며 엔터프라이즈 버전 제공
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Your medical school, all in one place — free study resources, exam practice, unit study groups, communities and messaging for medical students in Kenya.
Plain HTML/CSS/JS (no framework, no bundler).
| Concern | Service | Notes |
|---|---|---|
| Authentication | Clerk | Sign-up, sign-in, sessions, account management |
| Data + files | Supabase | Postgres with Row Level Security, Storage for uploads |
| Hosting | Vercel | Static deploy; scripts/build.mjs injects keys at build time |
Supabase never handles logins — it trusts Clerk session tokens through Clerk's official
Supabase integration. Inside the database the
signed-in user is auth.jwt() ->> 'sub' (their Clerk user ID), and every table is locked down with RLS.
pk_test_…).supabase/schema.sql → Run.
This creates all tables, security policies, the 4 starter communities, the resources and site-media
storage buckets, analytics + activity logging, roles and the admin functions.service_role / secret key).Project → Settings → Environment Variables, add:
| Name | Value |
|---|---|
CLERK_PUBLISHABLE_KEY | pk_test_… from Clerk |
SUPABASE_URL | https://<ref>.supabase.co |
SUPABASE_PUBLISHABLE_KEY | publishable / anon key |
Redeploy. vercel.json runs node scripts/build.mjs, which copies the site into dist/ and
writes dist/config.js from those variables.
Clerk development keys (
pk_test_…) work on any*.vercel.appURL — perfect for testing and sharing. Clerk production keys require a domain you own.
cp config.example.js config.js # then fill in the three values
npx serve . # or any static file server
app.js runtime: config checks, Clerk boot, Supabase client, all queries, shared UI
data.js static catalogue: Kenyan schools, countries, courses, units, interests, news
data/universities/ universities by country (one JSON file per country, loaded on demand)
theme.js light / dark theme (runs before first paint)
styles.css design system landing.css landing page only
admin.html/js/css admin dashboard (analytics, activity, users & roles, content, images)
img/ default site photos (Unsplash, see img/CREDITS.md) — replaceable from the admin page
*.html one file per page; each calls MedLink.run({ page }, async ({ api, me }) => …)
supabase/schema.sql database schema + RLS + storage bucket
scripts/build.mjs Vercel build: copies files to dist/ and writes config.js from env vars
data.js.admin.html)insert into public.user_roles (user_id, role)
select id, 'super_admin' from public.profiles where username = 'your_username';
After that, give other people roles from Admin → Users & roles.
| Role | Can |
|---|---|
| Super admin | Everything, including making admins / super admins and suspending staff |
| Admin | Site images, communities, edit profiles, suspend students, make moderators |
| Moderator | Analytics, activity, reports; edit or remove posts, comments and resources |
Every rule is enforced in the database (RLS + set_user_role, set_user_suspended, admin_stats,
admin_users in schema.sql), not just by hiding buttons.
?utm_source=whatsapp&utm_campaign=…).Admin → Users & roles → Add admin or moderator lets you search anyone on MedLink and give them a role.
To give a role to someone who hasn't joined yet, invite their email in the same window — they get the role the
next time they sign in. For that, Clerk must put the (verified) email in its session token:
Clerk dashboard → Sessions → Customize session token → add { "email": "{{user.primary_email_address}}" } → Save.
Admin → Exam bank:
Q: / A) … * format).Students pick their country during onboarding. Kenya shows a curated list of medical schools filtered by
programme (including Aga Khan University's Medical College and School of Nursing & Midwifery), with
"My school isn't listed" for anything else, e.g. KMTC. Every other country gets a searchable list from
data/universities/ — 10,000+ universities in 200 countries from the MIT-licensed
Hipo university list (refresh with node scripts/universities.mjs);
if a school is missing, students type its name.
Kept broad on purpose: one per course, one per country (created automatically the first time someone from that
country joins), a few topics, and MedLink Global for everyone. New students join their course, their country
and the global community automatically; year, course and university show on every post. Every
community has an invite link (join.html?c=…) with WhatsApp / Telegram / X / Facebook / email / SMS buttons —
new students are walked through sign-up and land inside the community. Each share is tagged
(utm_source=whatsapp etc.) so Admin → Traffic shows which channels bring people in.
app.js records page views, clicks on anything marked data-track, links to other sites, key actions
(sign-up, sign-in/out, uploads, downloads, views, saves, likes, comments, posts, follows, joins, searches,
messages sent, quiz starts/results), time actively spent on each page, and JavaScript errors — together with the
visit's traffic source (referrer + UTM), landing page, device, browser, OS, screen width, language and time zone.
Visitors get a random anonymous ID; signed-in events are linked to the user by the database itself.
Only staff can read the data. The landing-page footer tells visitors anonymous statistics are collected.
Re-run the whole of supabase/schema.sql in the SQL Editor — it only adds what's missing.