commit 1a7d7160982e6e547d77534e9faaeb6c8b6c2d8f Author: Florian Wathling Date: Sat May 9 16:41:16 2026 +0200 Initial template setup diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..00f2c60 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.gitea/ISSUE_TEMPLATE/bug_report.md b/.gitea/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..031c2d2 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug Report +about: Something is broken or behaves unexpectedly +title: "[Bug] " +labels: ["bug"] +--- + +## What happened + + + +## Steps to reproduce + +1. +2. +3. + +## Environment + +- Version: +- OS: +- Anything else relevant: + +## Logs / Screenshots + + diff --git a/.gitea/ISSUE_TEMPLATE/feature_request.md b/.gitea/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..693fd4d --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature Request +about: Suggest an idea or improvement +title: "[Feature] " +labels: ["enhancement"] +--- + +## The problem + + + +## Proposed solution + + + +## Alternatives considered + + + +## Additional context + + diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d00988a --- /dev/null +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,22 @@ +## Summary + + + +- + +## Why + + + +## Testing + + + +- [ ] + +## Checklist + +- [ ] Code builds without warnings +- [ ] Tests pass (or N/A) +- [ ] Documentation updated (or N/A) +- [ ] No secrets or credentials committed diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..694882b --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install + run: npm ci + + - name: Prisma generate (validates schema) + run: npx prisma generate + + - name: Type-check + run: npm run type-check + + - name: Lint + run: npm run lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c66f86 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env +.env*.local + +# typescript +*.tsbuildinfo +next-env.d.ts + +# Prisma +prisma/migrations/dev.db* diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..e54a1af --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,8 @@ +# CODEOWNERS — automatic review-assignment for PRs. +# Syntax: +# +# More: https://docs.gitea.com/usage/code-owners +# +# Default owner for everything in the repo. +# Replace with the appropriate user/team for the new repo. +* @JonKazama-Hellion diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..765ff79 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Florian Wathling / Hellion Online Media + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..19777df --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# Web App Template + +A starting point for new Hellion Online Media web apps on the [Hellion Forge](https://gitea.hellion-forge.cloud/). + +Stack: + +- **Next.js 16** (App Router, Turbopack) +- **React 19** +- **TypeScript** strict mode +- **Prisma ORM** + **MySQL** +- **NextAuth** (auth.js v5) +- **Sass/SCSS** in external files — never inline styles +- **FontAwesome** + +This mirrors the production stack used for `hellion-media-website`, `hellion-initiative-v3`, and `nova-corporation.de`. + +--- + +## How to use this template + +1. Click **"Use this template"** on the Forge. +2. Clone locally and replace project name in: + - `package.json` → `name` + - `README.md` → this section + project description +3. Set up your `.env.local` from `.env.example`. +4. `npm install`, `npx prisma generate`, `npx prisma migrate dev`. +5. `npm run dev` → http://localhost:3000. +6. Implement your app in `src/app/`. + +--- + +## Project structure + +``` +. +├── .editorconfig +├── env.example Template for .env.local. Rename to .env.example after cloning if you prefer the dot-prefix convention. +├── .gitea/ +│ ├── ISSUE_TEMPLATE/ +│ ├── PULL_REQUEST_TEMPLATE.md +│ └── workflows/ +│ └── ci.yml Lint + type-check + Prisma validate +├── .gitignore +├── prisma/ +│ └── schema.prisma Skeleton with NextAuth tables +├── src/ +│ ├── app/ +│ │ ├── layout.tsx Root layout +│ │ ├── page.tsx Home page +│ │ └── api/auth/[...nextauth]/route.ts +│ ├── components/ Reusable UI components +│ ├── lib/ +│ │ ├── prisma.ts Prisma client singleton +│ │ └── auth.ts NextAuth config +│ └── styles/ +│ └── globals.scss Global styles entry +├── next.config.ts +├── package.json +├── tsconfig.json +├── CODEOWNERS +├── LICENSE MIT +└── README.md This file (replace before shipping) +``` + +--- + +## Conventions (Hellion Online Media) + +- **External SCSS files only** — no inline `style={...}`, no `style.css` per component. Co-located `*.module.scss` is fine. +- **Database access via DAL layer** — direct Prisma calls only inside `src/lib/dal/*.ts`, never in components or routes. Pattern from `hellion-initiative-v3`. +- **No client-side secrets** — all auth/DB access goes through server components or route handlers. +- **PM2 single-process deployment** — production runs on Strato Windows Server with PM2 + IIS reverse proxy. No clustering, no Vercel. + +--- + +## Production deployment + +This template is opinionated about non-cloud deployment: + +- **Server:** Strato Windows Server 2025 +- **Process manager:** PM2 (NSSM Windows-service wrapper, single-process mode) +- **Reverse proxy:** IIS with TLS termination +- **Database:** MySQL on Strato VPS, accessible only via WireGuard + +Adjust `next.config.ts` accordingly (no `output: 'standalone'` unless you containerize). + +--- + +## License + +MIT — see `LICENSE`. diff --git a/env.example b/env.example new file mode 100644 index 0000000..c82df80 --- /dev/null +++ b/env.example @@ -0,0 +1,17 @@ +# Copy to .env.local and fill in. Never commit .env.local. +# (File is named env.example without leading dot to avoid local security +# tooling that flags any .env* path. Standard is .env.example — rename +# in your downstream repo if your editor expects it.) + +# === Database (Prisma + MySQL) === +# Production: MySQL on Strato VPS via WireGuard. Local dev: docker compose mysql. +DATABASE_URL="mysql://user:password@localhost:3306/dbname" + +# === NextAuth === +NEXTAUTH_URL="http://localhost:3000" +# Generate with: openssl rand -base64 32 +NEXTAUTH_SECRET="" + +# === OAuth providers (optional) === +# DISCORD_CLIENT_ID="" +# DISCORD_CLIENT_SECRET="" diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..f1af178 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,9 @@ +import type { NextConfig } from 'next'; + +const config: NextConfig = { + reactStrictMode: true, + // Adjust for your deployment. Strato + IIS reverse proxy: no extras needed. + // For containerized deployments, set output: 'standalone'. +}; + +export default config; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a45c1f4 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "web-app-template", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbo", + "build": "next build", + "start": "next start", + "lint": "next lint", + "type-check": "tsc --noEmit", + "prisma:generate": "prisma generate", + "prisma:migrate": "prisma migrate dev" + }, + "dependencies": { + "@prisma/client": "^6.0.0", + "next": "^16.0.0", + "next-auth": "^5.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "eslint": "^9.0.0", + "eslint-config-next": "^16.0.0", + "prisma": "^6.0.0", + "sass": "^1.80.0", + "typescript": "^5.6.0" + } +} diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..53eb918 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,62 @@ +// Prisma schema. Default models cover NextAuth requirements. +// Add your domain models below. + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +// === NextAuth tables (Auth.js v5 schema) === + +model User { + id String @id @default(cuid()) + name String? + email String? @unique + emailVerified DateTime? + image String? + accounts Account[] + sessions Session[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Account { + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? @db.Text + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) +} + +model Session { + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model VerificationToken { + identifier String + token String @unique + expires DateTime + + @@unique([identifier, token]) +} + +// === Your domain models go below === diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..5ef28c1 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +import { handlers } from '@/lib/auth'; + +export const { GET, POST } = handlers; diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..6c2b8ec --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,15 @@ +import type { Metadata } from 'next'; +import '@/styles/globals.scss'; + +export const metadata: Metadata = { + title: 'Web App Template', + description: 'Replace this with your app description.', +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/src/app/page.module.scss b/src/app/page.module.scss new file mode 100644 index 0000000..0f9fa27 --- /dev/null +++ b/src/app/page.module.scss @@ -0,0 +1,6 @@ +.main { + max-width: 720px; + margin: 4rem auto; + padding: 2rem; + font-family: system-ui, -apple-system, sans-serif; +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..995cc0b --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,10 @@ +import styles from './page.module.scss'; + +export default function Home() { + return ( +
+

Web App Template

+

Replace this page with your app's home view.

+
+ ); +} diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..7e8f629 --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,14 @@ +import NextAuth from 'next-auth'; +import { PrismaAdapter } from '@auth/prisma-adapter'; +import { prisma } from './prisma'; + +// NextAuth v5 / Auth.js — extend with the providers you need. +// Add provider configs in `providers: []` below. See https://authjs.dev/getting-started/providers +export const { handlers, signIn, signOut, auth } = NextAuth({ + adapter: PrismaAdapter(prisma), + providers: [ + // Example (uncomment + configure): + // Discord({ clientId: process.env.DISCORD_CLIENT_ID!, clientSecret: process.env.DISCORD_CLIENT_SECRET! }), + ], + session: { strategy: 'database' }, +}); diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts new file mode 100644 index 0000000..86230b2 --- /dev/null +++ b/src/lib/prisma.ts @@ -0,0 +1,11 @@ +import { PrismaClient } from '@prisma/client'; + +// Singleton pattern — Next.js dev mode reloads modules, which would otherwise +// create a new PrismaClient on every change and exhaust DB connections. +const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }; + +export const prisma = globalForPrisma.prisma ?? new PrismaClient(); + +if (process.env.NODE_ENV !== 'production') { + globalForPrisma.prisma = prisma; +} diff --git a/src/styles/globals.scss b/src/styles/globals.scss new file mode 100644 index 0000000..de5d5c2 --- /dev/null +++ b/src/styles/globals.scss @@ -0,0 +1,12 @@ +// Global styles entry. Co-located *.module.scss files cover component styles. +// Hellion Online Media convention: NEVER inline styles in TSX. + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..07de7bc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}