TutorLoom
The learning platform for companies, colleges and training institutes. Free and open source, with an Enterprise edition.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 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.