ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
UniLink는 이집트 대학(공·사·특수) 정보를 한곳에 모아 전공·입학·학비·장학금을 안내합니다
UniLink is your comprehensive guide to private, public, and specialized universities in Egypt — programs, admission requirements, tuition fees, and scholarships in one place.
교육PLpgSQL · TypeScriptGitHub @Anu-bytes온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Web platform connecting students with universities and study-abroad programs.
• Next.js 16 (App Router, React 19) — apps/web
• Prisma 6 + PostgreSQL — database & ORM
• Auth.js (NextAuth v5) — credentials + Google OAuth
• next-intl — i18n with English & Arabic (RTL) locales
• Tailwind CSS v4 + shadcn / Base UI — styling & components
• npm workspaces — monorepo (apps/*)
• Node.js 20+
• Docker (for local PostgreSQL) — or a hosted Postgres connection string
Copy the example env files and fill in your own values:
Never commit .env files — they hold real credentials and are gitignored.README 전문 보기 ↗Web platform connecting students with universities and study-abroad programs.
apps/webapps/*)unilink-workspace/
├─ apps/
│ └─ web/ # Next.js app (frontend + API routes)
│ ├─ prisma/ # schema.prisma, migrations, seed/
│ ├─ messages/ # i18n translation files (en.json, ar.json)
│ ├─ public/ # static assets
│ └─ src/ # app code (app/, components/, lib/, i18n/)
├─ compose.yaml # local PostgreSQL via Docker
└─ package.json # workspace root scripts
npm install
Copy the example env files and fill in your own values:
cp .env.example .env # database creds for docker compose
cp apps/web/.env.example apps/web/.env # app config (DATABASE_URL, AUTH_SECRET, OAuth)
Generate an auth secret with:
npx auth secret
Never commit
.envfiles — they hold real credentials and are gitignored.
For hosted Supabase, use the two connection strings shown by Supabase Dashboard > Connect:
DATABASE_URL: transaction pooler on port 6543, used by the running appDIRECT_URL: session pooler on port 5432, used by Prisma migrationsKeeping these separate prevents schema migrations from running through transaction pooling. Use placeholders in committed files and keep real credentials only in apps/web/.env.
For the default local Compose database, replace both database URLs in
apps/web/.env with postgresql://postgres:postgres@localhost:5432/unilink?schema=public.
These are local development credentials only. If you customize root .env
credentials or POSTGRES_PORT, make the app URLs match those values.
npm run db:up
Skip this step when using Supabase.
npm run db:deploy # apply existing migrations without generating new ones
npm run db:seed # load the Egyptian university catalogue
The seed is idempotent and only touches the universities it owns (matched by
slug), so it is safe to re-run after editing apps/web/prisma/seed/data.ts.
Use db:migrate only when intentionally authoring a schema change. Prisma does
not model the raw-SQL trigram search indexes; do not accept generated migrations
that drop them during routine setup.
npm run dev
npm run build uses Next's supported Webpack production builder. The optional
npm run build:turbo --workspace=apps/web retains Turbopack for environments that
support its worker-port requirements. Development still uses Turbopack.
| Route | What it is |
|---|---|
/{locale}/universities | Public directory of published universities |
/{locale}/universities/[slug] | University profile: gallery hero plus the About / Faculties / Location / Admission Requirements / Admission Criteria / Minimum Scores / Tuition Fees tabs (?tab= is linkable) |
/{locale}/universities/[slug]/programs/[programSlug] | Program profile |
/{locale}/app/search | Signed-in program search: natural-language search bar, filters, match-scored result cards |
/{locale}/app/compare | Side-by-side comparison of up to four programs |
/{locale}/app/applications, /app/saved, /app/profile | Student workspace |
/{locale}/admin | Admin dashboard: catalogue, people, growth (see below) |
Everything under /{locale}/app requires a session; src/proxy.ts redirects
anonymous visitors to /login.
/{locale}/admin is the back office. It is bilingual and RTL-aware like the
rest of the site, but styled as its own surface — dark sidebar, light content —
so there is never a doubt about which side of the product you are on.
| Section | Route | What it manages |
|---|---|---|
| Overview | /admin | Counts across the catalogue, people and leads, plus the newest applications, sign-ups and partnership leads |
| Universities | /admin/universities | The university record and its gallery images, feature bullets, tab content blocks and minimum scores |
| Faculties | /admin/faculties | Faculties under each university |
| Programs | /admin/programs | Programs, their intakes and their English requirements |
| Users | /admin/users | Accounts, roles and the onboarding profile behind each one |
| Applications | /admin/applications | Every student application and its status |
| Leads | /admin/leads | Partnership requests submitted from /contact |
| Testimonials | /admin/testimonials | Home-page testimonials |
| Scholarships | /admin/scholarships | Scholarship listings |
User.role decides. Three layers guard the surface, because one is not enough:
src/proxy.ts sends anonymous visitors to /login. It only sees the session
cookie, so it cannot tell an admin from a student.[locale]/(admin)/admin calls requireAdminPage() as its
first statement, which re-reads the row. A signed-in non-admin gets a 404
rather than a "forbidden" page — nothing links here, so confirming the route
exists would only help someone guessing./api/admin/* handler independently calls requireAdmin().The admin layout checks too, so the shell is never built for the wrong person, but it is deliberately not what the gate rests on. Next.js re-executes a layout only when the incoming router state does not already match that segment, so on a client-side navigation between two admin pages the layout is skipped and only the page renders — and the router state arrives in a request header, so it can be forged. Authorization has to sit with the code that reads the data. Do not move these checks back up into the layout.
The role is also mirrored onto the session token for the navigation to read, but that copy is refreshed at most once a minute and is never the authority. Layers 2 and 3 read the database, so removing someone's access takes effect on their very next click.
Nobody can be promoted from inside the dashboard until somebody is already in it, so the first admin is made from the command line:
npm run db:seed:admin -- [email protected] # promote or create
npm run db:seed:admin -- [email protected] 'a-strong-password'
An existing account is promoted and its password left alone. A missing account is created, and a generated password is printed once. There is deliberately no default email or password baked into the repository.
src/app/api/admin/* follows the same shape as the existing routes
(api/saved, api/applications): a route.ts per resource exporting named HTTP
methods, zod for the body, NextResponse.json for the reply. The shared pieces
live in src/lib/admin.ts (the guard) and src/lib/admin-api.ts (pagination,
{ error, field } responses, Prisma error mapping).
Lists accept ?page, ?perPage, ?q, ?sort, ?order plus per-resource
filters, and answer with { items, page, perPage, total, totalPages }. Deletes
that cascade — a university, a faculty, a program, a user — require an explicit
?confirm=true, and answer 409 with the row counts at stake when it is missing.
Image uploads go to POST /api/admin/media (multipart, file + folder), which
validates the bytes by signature exactly as the avatar route does and stores them
in the media bucket. With Supabase unset, the image fields fall back to pasting
a URL.
npm run lint:admin
npm run test:admin
ADMIN_SMOKE_LOCAL=1 npm run test:admin:smoke
npm run build
The smoke probe requires a running local server and refuses remote app/database
targets. It creates disposable users, an academic profile, an application, and
catalogue/growth records, exercises role transitions and every admin API method,
checks all admin pages in English/Arabic as HTML and matching-layout RSC requests,
then deletes only its own exact fixture IDs. No existing account is promoted or
demoted. For interactive QA, run
ADMIN_SMOKE_LOCAL=1 npm run test:admin:smoke --workspace=apps/web -- --hold;
press Enter in that terminal after browser checks to clean up.
Real Supabase uploads still require valid SUPABASE_URL and
SUPABASE_SERVICE_ROLE_KEY, with the media and avatars buckets configured.
Missing configuration returns 503; no remote test is silently counted as passed.
Storage deletion only accepts canonical URLs from the configured project's own
bucket. Explicit media deletion surfaces upstream failures; cleanup after a
record update remains best effort.
Reordering currently uses multiple requests: failures stop the remaining writes and refresh the server order, but do not roll back earlier writes. Same-field concurrent edits remain last-write-wins. Atomic ordering and version-based edit conflicts are separate future hardening work, not guarantees of these tests.
| Command | Description |
|---|---|
npm run dev | Start the web dev server |
npm run build | Production build |
npm run start | Start the production server |
npm run lint | Lint the web app |
npm run lint:admin | Lint the admin release scope |
npm run test:admin | Run admin validation, ordering, and media unit tests |
ADMIN_SMOKE_LOCAL=1 npm run test:admin:smoke | Run disposable local integration checks |
npm run db:up | Start and health-check the local Compose database |
npm run db:deploy | Apply existing migrations without generating new ones |
npm run db:generate | Generate the Prisma client |
npm run db:migrate | Create & apply a dev migration |
npm run db:seed | Seed the university / faculty / program catalogue |
npm run db:seed:admin -- <email> [password] | Promote or create an administrator |
npm run db:studio | Open Prisma Studio |