@@ -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
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: Something is broken or behaves unexpectedly
|
||||||
|
title: "[Bug] "
|
||||||
|
labels: ["bug"]
|
||||||
|
---
|
||||||
|
|
||||||
|
## What happened
|
||||||
|
|
||||||
|
<!-- Describe the bug. What did you do, what did you expect, what happened instead? -->
|
||||||
|
|
||||||
|
## Steps to reproduce
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
- Version:
|
||||||
|
- OS:
|
||||||
|
- Anything else relevant:
|
||||||
|
|
||||||
|
## Logs / Screenshots
|
||||||
|
|
||||||
|
<!-- Paste relevant log output or attach screenshots. Use ```code blocks``` for logs. -->
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: Feature Request
|
||||||
|
about: Suggest an idea or improvement
|
||||||
|
title: "[Feature] "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
---
|
||||||
|
|
||||||
|
## The problem
|
||||||
|
|
||||||
|
<!-- What are you trying to do? What's missing or annoying? -->
|
||||||
|
|
||||||
|
## Proposed solution
|
||||||
|
|
||||||
|
<!-- How would you solve it? Concrete behavior, not implementation details. -->
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
<!-- What else did you think about? Why didn't that work? -->
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
|
||||||
|
<!-- Screenshots, examples from other tools, related issues. -->
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
## Summary
|
||||||
|
|
||||||
|
<!-- What does this PR do? 1-3 bullet points. -->
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
<!-- Linked issue, motivation, or context. "Fixes #N" if applicable. -->
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
<!-- How did you verify this works? -->
|
||||||
|
|
||||||
|
- [ ]
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Code builds without warnings
|
||||||
|
- [ ] Tests pass (or N/A)
|
||||||
|
- [ ] Documentation updated (or N/A)
|
||||||
|
- [ ] No secrets or credentials committed
|
||||||
@@ -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
|
||||||
+40
@@ -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*
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# CODEOWNERS — automatic review-assignment for PRs.
|
||||||
|
# Syntax: <pattern> <user-or-team-handle>
|
||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -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.
|
||||||
@@ -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`.
|
||||||
+17
@@ -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=""
|
||||||
@@ -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;
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 ===
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { handlers } from '@/lib/auth';
|
||||||
|
|
||||||
|
export const { GET, POST } = handlers;
|
||||||
@@ -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 (
|
||||||
|
<html lang="de">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.main {
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 4rem auto;
|
||||||
|
padding: 2rem;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import styles from './page.module.scss';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className={styles.main}>
|
||||||
|
<h1>Web App Template</h1>
|
||||||
|
<p>Replace this page with your app's home view.</p>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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' },
|
||||||
|
});
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user