From 951db0f28aa0c660f8dfab17847f34de2bfd7bf3 Mon Sep 17 00:00:00 2001 From: maulanasdqn Date: Wed, 21 Jan 2026 20:22:27 +0700 Subject: [PATCH] feat: add infra app for infrastructure documentation --- apps/infra/eslint.config.mjs | 3 + apps/infra/index.html | 15 ++ apps/infra/postcss.config.mjs | 9 ++ apps/infra/project.json | 8 + apps/infra/public/logos/logo.svg | 9 ++ apps/infra/public/logos/simple.svg | 9 ++ apps/infra/src/app/App.tsx | 235 +++++++++++++++++++++++++++++ apps/infra/src/index.css | 58 +++++++ apps/infra/src/main.tsx | 14 ++ apps/infra/tsconfig.app.json | 9 ++ apps/infra/tsconfig.json | 18 +++ apps/infra/vite.config.ts | 27 ++++ flake.nix | 4 + 13 files changed, 418 insertions(+) create mode 100644 apps/infra/eslint.config.mjs create mode 100644 apps/infra/index.html create mode 100644 apps/infra/postcss.config.mjs create mode 100644 apps/infra/project.json create mode 100644 apps/infra/public/logos/logo.svg create mode 100644 apps/infra/public/logos/simple.svg create mode 100644 apps/infra/src/app/App.tsx create mode 100644 apps/infra/src/index.css create mode 100644 apps/infra/src/main.tsx create mode 100644 apps/infra/tsconfig.app.json create mode 100644 apps/infra/tsconfig.json create mode 100644 apps/infra/vite.config.ts diff --git a/apps/infra/eslint.config.mjs b/apps/infra/eslint.config.mjs new file mode 100644 index 0000000..b7f6277 --- /dev/null +++ b/apps/infra/eslint.config.mjs @@ -0,0 +1,3 @@ +import baseConfig from '../../eslint.config.mjs'; + +export default [...baseConfig]; diff --git a/apps/infra/index.html b/apps/infra/index.html new file mode 100644 index 0000000..eed77cf --- /dev/null +++ b/apps/infra/index.html @@ -0,0 +1,15 @@ + + + + + IMPHNEN Infrastructure + + + + + + +
+ + + diff --git a/apps/infra/postcss.config.mjs b/apps/infra/postcss.config.mjs new file mode 100644 index 0000000..63252e5 --- /dev/null +++ b/apps/infra/postcss.config.mjs @@ -0,0 +1,9 @@ +import { join } from 'path'; + +export default { + plugins: { + '@tailwindcss/postcss': { + base: join(import.meta.dirname, '../../'), + }, + }, +}; diff --git a/apps/infra/project.json b/apps/infra/project.json new file mode 100644 index 0000000..86bb3f7 --- /dev/null +++ b/apps/infra/project.json @@ -0,0 +1,8 @@ +{ + "name": "infra", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/infra/src", + "projectType": "application", + "tags": [], + "targets": {} +} diff --git a/apps/infra/public/logos/logo.svg b/apps/infra/public/logos/logo.svg new file mode 100644 index 0000000..ca0f36e --- /dev/null +++ b/apps/infra/public/logos/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/infra/public/logos/simple.svg b/apps/infra/public/logos/simple.svg new file mode 100644 index 0000000..0fd70f3 --- /dev/null +++ b/apps/infra/public/logos/simple.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/apps/infra/src/app/App.tsx b/apps/infra/src/app/App.tsx new file mode 100644 index 0000000..0ce8e76 --- /dev/null +++ b/apps/infra/src/app/App.tsx @@ -0,0 +1,235 @@ +const apps = [ + { name: 'Landing', domain: 'imphnen.dev', type: 'Next.js', color: 'blue' }, + { name: 'Gacha', domain: 'gacha.imphnen.dev', type: 'Vite', color: 'green' }, + { name: 'Backoffice', domain: 'backoffice.imphnen.dev', type: 'Vite', color: 'green' }, + { name: 'Dimentorin', domain: 'dimentorin.imphnen.dev', type: 'Vite', color: 'green' }, + { name: 'Hackathon', domain: 'hackathon.imphnen.dev', type: 'Vite', color: 'green' }, + { name: 'Infra', domain: 'infra.imphnen.dev', type: 'Vite', color: 'green' }, +]; + +const techStack = [ + { category: 'OS', items: ['NixOS 24.11'] }, + { category: 'Web Server', items: ['Nginx'] }, + { category: 'SSL', items: ['Let\'s Encrypt (ACME)'] }, + { category: 'Build', items: ['Nix Flakes', 'Nx Monorepo'] }, + { category: 'Frontend', items: ['React', 'Next.js', 'Vite', 'TailwindCSS'] }, + { category: 'Secrets', items: ['sops-nix'] }, +]; + +export default function App() { + return ( +
+ {/* Header */} +
+
+
+
+
+ I +
+ IMPHNEN Infrastructure +
+ + GitHub + +
+
+
+ +
+ {/* Hero */} +
+

+ Infrastructure Overview +

+

+ Declarative NixOS infrastructure powering IMPHNEN's frontend applications + on Hetzner Cloud. +

+
+ + {/* Architecture Diagram */} +
+

Architecture

+
+
+ {/* Internet */} +
+ 🌐 + Internet +
+
+ + {/* Cloudflare */} +
+ Cloudflare +

DNS + CDN + DDoS Protection

+
+
+ + {/* Hetzner VPS */} +
+
+ Hetzner Cloud VPS +

NixOS | 167.235.70.37

+
+ + {/* Nginx */} +
+
+ Nginx +

Reverse Proxy + Static Files + SSL Termination

+
+
+ + {/* Apps Grid */} +
+ {apps.map((app) => ( +
+

{app.name}

+

{app.domain}

+ + {app.type} + +
+ ))} +
+
+
+
+
+ + {/* Deployed Apps */} +
+

Deployed Applications

+
+ {apps.map((app) => ( + +
+
+

+ {app.name} +

+

{app.domain}

+
+ {app.type} +
+
+ ))} +
+
+ + {/* Tech Stack */} +
+

Technology Stack

+
+ {techStack.map((tech) => ( +
+

+ {tech.category} +

+
+ {tech.items.map((item) => ( + + {item} + + ))} +
+
+ ))} +
+
+ + {/* Deployment Flow */} +
+

Deployment Flow

+
+
+
+
+ 1 +
+

Push to GitHub

+

develop branch

+
+
+
+
+ 2 +
+

Update Flake

+

nix flake update

+
+
+
+
+ 3 +
+

Remote Build

+

nixos-rebuild

+
+
+
+
+ +
+

Live

+

Zero downtime

+
+
+
+
+ + {/* Repositories */} +
+

Repositories

+
+ +

imphnen-frontend-service

+

+ Nx monorepo containing all frontend applications +

+
+ +

imphnen-infrastructure

+

+ NixOS flake configuration for server deployment +

+
+
+
+
+ + {/* Footer */} +
+
+

IMPHNEN Infrastructure © {new Date().getFullYear()}

+
+
+
+ ); +} diff --git a/apps/infra/src/index.css b/apps/infra/src/index.css new file mode 100644 index 0000000..62299c4 --- /dev/null +++ b/apps/infra/src/index.css @@ -0,0 +1,58 @@ +@import 'tailwindcss'; + +@theme { + --color-primary: #3b82f6; + --color-primary-hover: #2563eb; +} + +@layer base { + * { + box-sizing: border-box; + margin: 0; + padding: 0; + } + + body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background-color: #0a0a0a; + color: #ffffff; + line-height: 1.6; + } +} + +@layer components { + .card { + background: #141414; + border: 1px solid #27272a; + border-radius: 12px; + padding: 1.5rem; + } + + .badge { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 500; + } + + .badge-blue { + background: rgba(59, 130, 246, 0.2); + color: #60a5fa; + } + + .badge-green { + background: rgba(34, 197, 94, 0.2); + color: #4ade80; + } + + .badge-purple { + background: rgba(168, 85, 247, 0.2); + color: #c084fc; + } + + .badge-orange { + background: rgba(249, 115, 22, 0.2); + color: #fb923c; + } +} diff --git a/apps/infra/src/main.tsx b/apps/infra/src/main.tsx new file mode 100644 index 0000000..808a90e --- /dev/null +++ b/apps/infra/src/main.tsx @@ -0,0 +1,14 @@ +import { createRoot } from 'react-dom/client'; +import { StrictMode } from 'react'; +import App from './app/App'; +import './index.css'; + +const rootElement = document.getElementById('root'); + +if (!rootElement) throw new Error('Failed to find the root element'); + +createRoot(rootElement).render( + + + +); diff --git a/apps/infra/tsconfig.app.json b/apps/infra/tsconfig.app.json new file mode 100644 index 0000000..4750700 --- /dev/null +++ b/apps/infra/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node", "vite/client"] + }, + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx"], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/apps/infra/tsconfig.json b/apps/infra/tsconfig.json new file mode 100644 index 0000000..fe609d7 --- /dev/null +++ b/apps/infra/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "types": ["vite/client"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ], + "extends": "../../tsconfig.base.json" +} diff --git a/apps/infra/vite.config.ts b/apps/infra/vite.config.ts new file mode 100644 index 0000000..8062251 --- /dev/null +++ b/apps/infra/vite.config.ts @@ -0,0 +1,27 @@ +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../node_modules/.vite/apps/infra', + server: { + port: 3006, + host: 'localhost', + }, + preview: { + port: 3007, + host: 'localhost', + }, + plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], + build: { + outDir: '../../dist/apps/infra', + emptyOutDir: true, + reportCompressedSize: true, + commonjsOptions: { + transformMixedEsModules: true, + }, + }, +})); diff --git a/flake.nix b/flake.nix index 8a1023a..cc3ed3d 100644 --- a/flake.nix +++ b/flake.nix @@ -32,6 +32,7 @@ gacha = mkViteApp { name = "gacha"; buildScript = "gacha:build"; }; dimentorin = mkViteApp { name = "dimentorin"; buildScript = "dimentorin:build"; }; hackathon = mkViteApp { name = "hackathon"; buildScript = "hackathon:build"; }; + infra = mkViteApp { name = "infra"; buildScript = "infra:build"; }; # Next.js app landing = landingApp; @@ -82,6 +83,7 @@ gacha = import ./nix/modules/static-app.nix { inherit self; } { appName = "gacha"; }; dimentorin = import ./nix/modules/static-app.nix { inherit self; } { appName = "dimentorin"; }; hackathon = import ./nix/modules/static-app.nix { inherit self; } { appName = "hackathon"; }; + infra = import ./nix/modules/static-app.nix { inherit self; } { appName = "infra"; }; # All-in-one module that enables all apps all = { config, lib, pkgs, ... }: { @@ -91,6 +93,7 @@ self.nixosModules.gacha self.nixosModules.dimentorin self.nixosModules.hackathon + self.nixosModules.infra ]; }; }; @@ -103,6 +106,7 @@ gacha = self.packages.${final.system}.gacha; dimentorin = self.packages.${final.system}.dimentorin; hackathon = self.packages.${final.system}.hackathon; + infra = self.packages.${final.system}.infra; # Function to build hackathon with custom environment variables (e.g., Supabase) mkHackathonWithEnv = envVars: import ./nix/mkViteApp.nix { pkgs = final;