Skip to main content

daw.club — Gitea Frontend

Custom React frontend for browsing and managing Songbird projects hosted on the Gitea instance at git.daw.club. Live: https://daw.club (Cloudflare Pages) API backend: https://git.daw.club (Gitea 1.22.6 on GCE)

Stack

  • Vite + React 19 + TypeScript
  • Tailwind CSS v4 with @tailwindcss/vite plugin
  • Design tokens: Zinc (dark) + Emerald (accent) palette, Inter font — matches Songbird’s design system
  • shadcn/ui-style components (Button, Card, Input) using class-variance-authority
  • Lucide React for icons
  • React Router v7 for client-side routing

Pages

RoutePageDescription
/ExploreSearch and browse projects in a card grid
/loginLoginToken-based auth against Gitea API
/registerRegisterRedirects to Gitea’s native sign-up page
/:owner/:nameRepo DetailFile browser, edit name/description, clone URL, ZIP download
/:ownerUser ProfileList of a user’s repositories

Quick Start

cd server/gitea
npm install
npm run dev
The dev server starts at http://localhost:5173. By default it talks to https://git.daw.club.

Environment Variables

Copy .env.example to .env to configure:
# Gitea instance URL (without trailing slash)
VITE_GITEA_URL=https://git.daw.club

Build

npm run build     # TypeScript check + Vite production build -> dist/
npm run preview   # Preview the production build locally
npm run lint      # ESLint
Production build outputs to dist/ (~285 KB JS, ~90 KB gzipped).

Deploy to Cloudflare Pages

The frontend is deployed as a static site on Cloudflare Pages.

Prerequisites

  • Wrangler CLI: npm install -g wrangler
  • Cloudflare API token with Cloudflare Pages: Edit + Zone DNS: Edit permissions
  • Set environment variables:
    export CLOUDFLARE_API_TOKEN="<your-token>"
    export CLOUDFLARE_ACCOUNT_ID="8a0ff27d0ad9b0aa3e9ade38ad6be106"
    

Deploy

cd server/gitea
npm run build
wrangler pages deploy dist --project-name daw-club --branch main
The site is available at:

First-time setup (already done)

If setting up from scratch:
# Create the Pages project
wrangler pages project create daw-club --production-branch main

# Add custom domain via Cloudflare API
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/daw-club/domains" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "daw.club"}'

# Create CNAME DNS record: daw.club -> daw-club.pages.dev
curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"CNAME","name":"@","content":"daw-club.pages.dev","proxied":true,"ttl":1}'

API Layer

The API client lives in src/lib/api.ts. It wraps the Gitea REST API and handles:
  • Auth: Token creation via HTTP Basic Auth (POST /users/:username/tokens), stored in localStorage
  • Repos: Search, get, update, delete
  • Contents: File/directory listing, archive download (fetch-based, no token in URL)
  • Users: Get user profile, list user repos

Auth flow

  1. User enters username/password on /login
  2. Frontend creates an API token via Gitea’s basic auth endpoint
  3. Token stored in localStorage as gitea_token
  4. All subsequent API calls include Authorization: token <sha1> header
  5. Registration redirects to Gitea’s native sign-up page at git.daw.club/user/sign_up

CORS

Gitea must have CORS enabled in /etc/gitea/app.ini on the server:
[cors]
ENABLED = true
ALLOW_DOMAIN = *
ALLOW_SUBDOMAIN = true
METHODS = GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS

Project Structure

server/gitea/
├── src/
│   ├── App.tsx              # Router + Layout wrapper
│   ├── main.tsx             # Entry point
│   ├── index.css            # Tailwind v4 theme (HSL color tokens)
│   ├── components/
│   │   ├── Layout.tsx       # Header, footer, search bar
│   │   ├── Button.tsx       # CVA button variants
│   │   ├── Card.tsx         # Card component
│   │   └── Input.tsx        # Input component
│   ├── pages/
│   │   ├── Explore.tsx      # Project search + card grid
│   │   ├── Login.tsx        # Login form
│   │   ├── Register.tsx     # Registration redirect
│   │   ├── RepoDetail.tsx   # File browser + repo management
│   │   └── UserProfile.tsx  # User's repo list
│   └── lib/
│       ├── api.ts           # Gitea REST API client
│       └── utils.ts         # cn() utility (clsx + tailwind-merge)
├── index.html               # Entry HTML (Inter font, dark body fallback)
├── vite.config.ts           # Vite + React + Tailwind v4
├── .env.example             # Environment variable template
└── dist/                    # Production build output

Design Notes

  • Dark theme: Uses color-scheme: dark on :root plus !important on html/body/#root background to override Tailwind v4’s base layer cascade. Inline style on <body> in index.html as an additional fallback.
  • Inter font: Loaded from Google Fonts CDN. Falls back to system-ui if unavailable.
  • Auth state: Lives in module-level variables + localStorage, not React state. UI reactivity depends on navigation re-renders after login/logout.
  • No automated tests: All behavior is verified via manual E2E testing against the live Gitea instance.