Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Optional for the public site. Copy this file to .env.local when needed.
# Optional for the public site. Copy this file to .env at the repository root.
# All workspace commands load root .env files; process variables take precedence.

# Neon PostgreSQL connection string; leave blank until database features are added.
# Example: postgresql://USER:PASSWORD@HOST/DATABASE?sslmode=require
DATABASE_URL=

# Better Auth is installed for future use; no auth routes are configured yet.
# Shared auth factories are ready for future use; no auth routes are mounted.
# Generate a secret with: openssl rand -base64 32
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000
Expand All @@ -14,5 +15,5 @@ BETTER_AUTH_URL=http://localhost:3000
RESEND_API_KEY=
RESEND_FROM_EMAIL=

# reccomended as 5000 reqs can be sent per hour
# Optional GitHub token for a higher API rate limit.
GITHUB_TOKEN=
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Next.js checks
name: Workspace checks

on:
push:
Expand All @@ -21,4 +21,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm lint
- run: pnpm test
- run: pnpm build
- run: node scripts/prepare-vercel.mjs
- run: node scripts/verify-vercel.mjs
25 changes: 16 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules/
node_modules/
/.pnp
.pnp.js

# testing
/coverage
coverage/

# next.js
/.next/
/out/
# Astro
.astro/

# production
/build
build/

# misc
.DS_Store
Expand All @@ -30,15 +29,23 @@ yarn-error.log*
!.env.example

# React Email preview and exported templates
/.react-email/
/email-out/
.react-email/
email-out/

# vercel
.vercel

# typescript
*.tsbuildinfo
/next-env.d.ts

/.yarn
/.yarnrc.yml

# monorepo task and package outputs
.turbo/
dist/
.cache/

# Legacy local Next build artifacts
.next/
next-env.d.ts
14 changes: 11 additions & 3 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
build
node_modules
.next
**/node_modules
**/.next
**/.turbo
**/.react-email
**/build
**/dist
**/next-env.d.ts
**/*.tsbuildinfo
pnpm-lock.yaml
**/.astro
**/.vercel
10 changes: 0 additions & 10 deletions .prettierrc

This file was deleted.

98 changes: 59 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,90 +1,110 @@
# Manipal OSF website

The official Manipal OSF website is a single Next.js application at the repository root. It uses TypeScript, React, Tailwind CSS, and the App Router. Blog posts live in the repository as Markdown files.
The Manipal OSF website lives in a pnpm and Turborepo monorepo modeled on [create-t3-turbo](https://github.com/t3-oss/create-t3-turbo). The public website uses Astro components and TypeScript browser scripts, with Vercel ISR for live GitHub data.

## Development

Use Node.js 24 LTS (minimum 22) and PNPM 11.2.2. Run commands from the repository root:
Use Node.js 24 LTS (minimum 22.22 for Astro) and pnpm 11.2.2. Run commands from the repository root:

```sh
pnpm install
pnpm dev
```

Open http://localhost:3000. No environment variables or separate service are required for the existing site; an optional `GITHUB_TOKEN` raises the GitHub API limit for the home page statistics.

Development and production builds use Turbopack.
Open http://localhost:4321. The public website needs no service credentials. An optional `GITHUB_TOKEN` increases the GitHub API rate limit for statistics.

```sh
pnpm typecheck
pnpm lint
pnpm test
pnpm build # Vercel deployment output
pnpm build:local # Node production build
pnpm start # Serve the Node build on port 4321
pnpm format
```

CI runs the production build with `pnpm build`. A deployed Node.js application starts with `pnpm start`. Configure hosting to use the repository root, with `pnpm install --frozen-lockfile` as the install command and `pnpm build` as the build command.
`dev` starts workspaces under `apps/`. `build`, `lint`, `typecheck`, and `test` use Turbo's dependency graph and cache. Development servers and email previews are uncached. Turbo caches `dist/`, `.astro/`, and `.vercel/output/` build artifacts. Environment changes invalidate task caches.

See [website configuration](./WEBSITE_CONFIG.md) for the Markdown format, images, and publishing instructions. The blog initially displays “No posts yet.” Add your own content to `content/posts/`; existing hosted content is not copied automatically.
To target the website directly:

The `@next/mdx` component registry lives in `mdx-components.tsx` at the repository root, beside `app/`, as required by Next.js. Keep it there when adding MDX components. The filesystem blog continues to render Markdown using `MarkdownRenderer`.
```sh
pnpm --filter @apps/astro dev
pnpm build --filter=@apps/astro...
```

## UI components
## Workspace structure

shadcn/ui is configured with the Base UI Nova style, TypeScript, React Server Components, Lucide icons, and the `@/*` import alias pointing to the repository root. Its CSS variables extend the existing Nord light and Catppuccin dark themes, using the existing `data-theme` switch.
| Location | Package | Responsibility |
| ---------------- | ----------------- | ------------------------------------------------------------- |
| `apps/astro` | `@apps/astro` | Astro website, content, public assets, and site-specific code |
| `packages/env` | `@packages/env` | T3 Env validation and root environment loading |
| `packages/db` | `@packages/db` | Lazy Neon HTTP / Drizzle database clients |
| `packages/auth` | `@packages/auth` | Configurable Better Auth factory |
| `packages/ui` | `@packages/ui` | Astro avatar/badge components and class helpers |
| `packages/email` | `@packages/email` | React Email templates and local preview server |
| `tooling/*` | `@tooling/*` | ESLint, Prettier, TypeScript, and Tailwind configuration |

Import the initial Button from `@/components/ui/button` and shared class helpers from `@/lib/utils`. Add more components from the repository root:
Workspace imports use real package exports and `workspace:*` dependencies. The website's `@/*` alias refers to its `src/` directory. Shared dependencies are versioned centrally in the pnpm catalog.

```sh
pnpm exec shadcn add input
```

`components.json` points to `styles/globals.css`; Tailwind v4 does not need a separate Tailwind configuration file. Keep the existing palettes and `data-theme` convention when adding theme tokens.
See [website configuration](./WEBSITE_CONFIG.md) for posts, authors, images, and publishing. Content lives under `apps/astro/content/`. Astro collections validate Markdown posts and authors, and pages live under `apps/astro/src/pages/`. Public URLs continue to use post frontmatter IDs and author filenames.

## Environment variables

The public site runs without service credentials. When preparing backend features, copy the example file:
Keep environment files at the repository root:

```sh
cp .env.example .env.local
cp .env.example .env
```

`lib/env.ts` uses T3 Env and Zod to validate settings during development and builds. Service settings are optional, and blank values are treated as unset. Values supplied must be valid. Import `env` from `@/lib/env` in server code instead of reading `process.env` directly. All settings below are server variables; do not expose credentials using `NEXT_PUBLIC_`.
`with-env` loads root environment files before running Turbo or an application command. It uses `dotenv` and `dotenv-expand`, resolving the root from the loader's location rather than the current directory. Existing process variables win, followed by `.env.<mode>.local`, `.env.local`, `.env.<mode>`, and `.env`. Test mode skips `.env.local`. Vercel supplies process variables through project settings and needs no committed environment file.

Import `env` from `@packages/env` in server code. T3 Env and Zod validate supplied values, treat empty strings as unset, and prevent access to server variables in browser code. The filesystem loader is separate at `@packages/env/load`; do not import it into application runtime code.

| Variable | Purpose |
| -------------------- | -------------------------------------------------------------------------------------- |
| `DATABASE_URL` | Neon PostgreSQL connection string (`postgres://` or `postgresql://`). |
| `BETTER_AUTH_SECRET` | Future auth secret of at least 32 characters; generate with `openssl rand -base64 32`. |
| `BETTER_AUTH_URL` | Future auth server origin, such as `http://localhost:3000`. |
| `RESEND_API_KEY` | API key required when calling `getResend()`. |
| `RESEND_FROM_EMAIL` | Plain sender email address on a Resend-verified domain, for future sending features. |
| Variable | Purpose |
| -------------------- | --------------------------------------------------------------- |
| `DATABASE_URL` | Optional Neon PostgreSQL URL (`postgres://` or `postgresql://`) |
| `BETTER_AUTH_SECRET` | Optional auth secret, at least 32 characters |
| `BETTER_AUTH_URL` | Optional auth origin for a future app instance |
| `RESEND_API_KEY` | Required when calling the website's `getResend()` |
| `RESEND_FROM_EMAIL` | Optional sender address on a verified Resend domain |
| `GITHUB_TOKEN` | Optional higher GitHub API rate limit |

Real `.env*` files are ignored by Git; `.env.example` contains no credentials and is tracked.
All current variables are server-side. Keep `.env*` ignored except the credential-free example. When adding variables, update the shared validation schema, example file, and Turbo environment inputs together. Future apps can compose their own validation from this package as their requirements diverge.

## Database and authentication

Drizzle ORM, Drizzle Kit, the Neon serverless driver, Better Auth, and its Drizzle adapter are installed for later use. Database clients, application/auth schemas, migrations, auth routes, and login pages are deferred. The blog still uses repository Markdown files.
`@packages/db` exports `createDb(url, schema?)` and `getDb()`. Constructing a client does not execute a query. `getDb()` lazily creates a shared schema-less client from `env.DATABASE_URL`, with a clear error when it is unset. Use `createDb` when supplying a typed schema or a different database URL. These APIs are server-only. Neon HTTP supports individual queries and non-interactive batches; interactive transactions require a different driver.

When implementing these features, configure a Neon PostgreSQL database with the validated `DATABASE_URL`, use `drizzle-orm/neon-http` for HTTP queries (or `drizzle-orm/neon-serverless` when interactive transactions are needed), and connect Better Auth through `@better-auth/drizzle-adapter` with PostgreSQL provider `pg`. Generate the auth schema from the final auth configuration before creating Drizzle migrations. Add the auth Route Handler under `app/api/auth/[...all]/route.ts` and keep client auth code separate from server credentials.
`@packages/auth` exports `createAuth({ db, schema, options })`. Supply an explicit Drizzle auth schema and app-specific `secret`, `baseURL`, and `trustedOrigins`; `options` also accepts Better Auth providers, plugins, and session configuration. The factory preserves plugin and additional-field inference, uses the PostgreSQL Drizzle adapter, and disables interactive transactions. It does not establish a global auth singleton or shared cookie domain.

Drizzle Kit runs outside the Next.js runtime. Before adding its configuration, use `@next/env` to load the root `.env*` files so CLI commands follow the same environment loading rules as Next.js.
The website does not instantiate auth or mount auth endpoints. Auth tables, login methods, schema generation, migrations, and login pages remain deferred. When implementing them, generate the schema from the final auth configuration, keep tables and migrations in `packages/db`, and mount the framework-specific handler inside the consuming app. Set each app's URL and trusted origins explicitly. Drizzle Kit is owned by the database package; migration commands will be added with the actual schema.

## Email templates
## Shared UI and Tailwind

Import Astro components from `@packages/ui/components/avatar` and `@packages/ui/components/badge`, and `cn` from `@packages/ui/lib/utils`. The unused button and extra avatar components have been removed. There is no React/shadcn component-generation command.

`@tooling/tailwind/styles.css` provides Tailwind v4, animation imports, theme tokens, the `data-theme` dark variant, common base styles, and shared UI source registration. Applications import this CSS and register their own source directory with `@source`. The website retains its Nord light and Catppuccin dark palettes and self-hosted Cascadia Code font.

Resend and React Email are ready for future email features. Components and rendering utilities use the current `react-email` package; `@react-email/ui` provides local previews.
The website ships Astro HTML with small browser modules for theme switching, map controls, reading progress, and timeline effects. MapLibre is loaded near the viewport, with campus links available when the map cannot load. React remains in the email package for React Email templates.

## Email templates

```sh
pnpm email:dev
```

Open http://localhost:3001 to preview `emails/example.tsx`. Previews need no API key and send no email.
Open http://localhost:3001 to preview `packages/email/templates/example.tsx`. Previews need no API key and send no email. Apps can import the template from `@packages/email/example`.

The website retains its lazy `getResend()` helper at `@/lib/resend`. There are no sending endpoints or automatic email triggers.

Server code can import `getResend` from `@/lib/resend` to obtain a lazily initialized Resend client. It throws a clear error if `RESEND_API_KEY` is unset when called. When adding a sending feature, pass a verified sender, recipient, subject, and React Email template to `getResend().emails.send(...)`, and handle the returned `error`. There are no sending endpoints or automatic email triggers yet.
## Vercel and additional apps

## Server features
The root `vercel.json` deploys `@apps/astro` using a filtered Turbo build and the Vercel adapter's Build Output API artifacts. The deployment command copies `apps/astro/.vercel/output` into the root `.vercel/output` expected by Vercel, including functions, hourly ISR configuration, redirects, and PNG content types. Keep the Vercel project's Root Directory at the repository root, select Node.js 24, and enable Corepack (`ENABLE_EXPERIMENTAL_COREPACK=1`) to use the pinned pnpm version. Remove conflicting dashboard command overrides. No root framework dependency is needed.

Add server endpoints as Route Handlers in `app/**/route.ts`, using the Web `Request` and `Response` APIs or Next.js `NextRequest` and `NextResponse`. Keep private environment variables server-side and put reusable server logic in `lib/`. Async Server Components, post metadata, and `generateStaticParams` call the server-only filesystem blog loader directly; it does not make HTTP calls.
Content pages, feeds, metadata, and social preview PNGs are prerendered. Home and team pages render on demand and use a 3,600-second ISR expiration; after expiry, a request triggers regeneration. Failed GitHub loads return an uncached 503 page with offline messaging or the authored timeline so failed regeneration does not replace successful cached content. Actual Vercel cache hits and regeneration must be verified on a deployment; local Node runs do not emulate the platform cache.

Use a deployment that supports the Next.js Node.js runtime when adding Route Handlers or request-time server rendering.
For a local production server, run `pnpm build:local`, then `pnpm start`. The local build explicitly selects `@astrojs/node`; the default `pnpm build` selects `@astrojs/vercel`. You can override the local server's interface and port using `HOST` and `PORT`.

## Contributing
Add future applications under `apps/`, name them `@apps/<name>`, declare shared packages using `workspace:*`, and extend shared tooling. Each future app should have its own Vercel project, environment values, and app-specific deployment configuration. The root config remains specific to the current website; it does not deploy every app. For a project rooted in a new app directory, enable access to files outside that root so shared packages are available. Do not reuse the website's root build and output settings for another app.

React, Next.js, TypeScript, Tailwind CSS, and basic HTML knowledge are useful. Contributions to content and bug reports are welcome too.
GitHub Actions installs with the frozen lockfile and runs typecheck, lint, content/GitHub and service-package tests, the Vercel production build, and execution of the packaged server functions with GitHub offline. No live database, migration, email, or deployment is required for these checks.
Loading
Loading