diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..6791ab4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,55 @@
+# Dependencies
+node_modules
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Build outputs
+dist
+.next
+out
+build
+coverage
+
+# IDE
+.vscode
+.idea
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Git
+.git
+.gitignore
+
+# Environment
+.env
+.env.local
+.env.*.local
+
+# Testing
+coverage
+.nyc_output
+
+# Logs
+logs
+*.log
+
+# Nx
+.nx
+
+# Docker
+Dockerfile
+docker-compose*.yml
+.dockerignore
+
+# Misc
+*.md
+!README.md
+.editorconfig
+.prettierrc
+.eslintrc
diff --git a/apps/hackathon/eslint.config.mjs b/apps/hackathon/eslint.config.mjs
new file mode 100644
index 0000000..e8e4590
--- /dev/null
+++ b/apps/hackathon/eslint.config.mjs
@@ -0,0 +1,12 @@
+import nx from '@nx/eslint-plugin';
+import baseConfig from '../../eslint.config.mjs';
+
+export default [
+ ...baseConfig,
+ ...nx.configs['flat/react'],
+ {
+ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
+ // Override or add rules here
+ rules: {},
+ },
+];
diff --git a/apps/hackathon/index.html b/apps/hackathon/index.html
new file mode 100644
index 0000000..41c9d20
--- /dev/null
+++ b/apps/hackathon/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Hackathon
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/hackathon/postcss.config.mjs b/apps/hackathon/postcss.config.mjs
new file mode 100644
index 0000000..63252e5
--- /dev/null
+++ b/apps/hackathon/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/hackathon/project.json b/apps/hackathon/project.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/apps/hackathon/project.json
@@ -0,0 +1 @@
+{}
diff --git a/apps/hackathon/src/app/404.tsx b/apps/hackathon/src/app/404.tsx
new file mode 100644
index 0000000..2db4df6
--- /dev/null
+++ b/apps/hackathon/src/app/404.tsx
@@ -0,0 +1,23 @@
+import { Link } from 'react-router-dom';
+
+export default function NotFoundPage() {
+ return (
+
+
+
404
+
+ Page Not Found
+
+
+ The page you are looking for doesn't exist or has been moved.
+
+
+ Go Back Home
+
+
+
+ );
+}
diff --git a/apps/hackathon/src/app/error.tsx b/apps/hackathon/src/app/error.tsx
new file mode 100644
index 0000000..3859b00
--- /dev/null
+++ b/apps/hackathon/src/app/error.tsx
@@ -0,0 +1,29 @@
+import { useRouteError, isRouteErrorResponse } from 'react-router-dom';
+
+export default function ErrorPage() {
+ const error = useRouteError();
+ let errorMessage: string;
+
+ if (isRouteErrorResponse(error)) {
+ errorMessage = error.statusText;
+ } else if (error instanceof Error) {
+ errorMessage = error.message;
+ } else if (typeof error === 'string') {
+ errorMessage = error;
+ } else {
+ console.error(error);
+ errorMessage = 'Unknown error';
+ }
+
+ return (
+
+
+
Oops!
+
+ Sorry, an unexpected error has occurred.
+
+
{errorMessage}
+
+
+ );
+}
diff --git a/apps/hackathon/src/app/layout.tsx b/apps/hackathon/src/app/layout.tsx
new file mode 100644
index 0000000..d7b3c11
--- /dev/null
+++ b/apps/hackathon/src/app/layout.tsx
@@ -0,0 +1,10 @@
+import { Outlet, ScrollRestoration } from 'react-router-dom';
+
+export default function RootLayout() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/apps/hackathon/src/app/page.tsx b/apps/hackathon/src/app/page.tsx
new file mode 100644
index 0000000..3faa6a0
--- /dev/null
+++ b/apps/hackathon/src/app/page.tsx
@@ -0,0 +1,19 @@
+export default function HomePage() {
+ return (
+
+
+
+ Hackathon App
+
+
+ Welcome to the Hackathon platform
+
+
+
+ This is a new Vite + React app with file-based routing
+
+
+
+
+ );
+}
diff --git a/apps/hackathon/src/index.css b/apps/hackathon/src/index.css
new file mode 100644
index 0000000..d4b5078
--- /dev/null
+++ b/apps/hackathon/src/index.css
@@ -0,0 +1 @@
+@import 'tailwindcss';
diff --git a/apps/hackathon/src/main.tsx b/apps/hackathon/src/main.tsx
new file mode 100644
index 0000000..ea6d2ba
--- /dev/null
+++ b/apps/hackathon/src/main.tsx
@@ -0,0 +1,45 @@
+import { createRoot } from 'react-dom/client';
+import { middleware } from './middleware';
+import { StrictMode } from 'react';
+import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router';
+import {
+ add404PageToRoutesChildren,
+ addErrorElementToRoutes,
+ convertPagesToRoute,
+ ModalLoginProvider,
+ QueryProvider,
+} from '@imphnen-frontend-service/utils';
+import { Toaster } from 'sonner';
+import './index.css';
+
+const files = import.meta.glob('./app/**/*(page|layout).tsx');
+const errorFiles = import.meta.glob('./app/**/*error.tsx');
+const notFoundFiles = import.meta.glob('./app/**/*404.tsx');
+const loadingFiles = import.meta.glob('./app/**/*loading.tsx');
+
+const routes = convertPagesToRoute(files, loadingFiles) as RouteObject;
+addErrorElementToRoutes(errorFiles, routes);
+add404PageToRoutesChildren(notFoundFiles, routes);
+
+const router = createBrowserRouter([
+ {
+ ...routes,
+ loader: middleware,
+ shouldRevalidate: () => true,
+ },
+]);
+
+const rootElement = document.getElementById('root');
+
+if (!rootElement) throw new Error('Failed to find the root element');
+
+createRoot(rootElement).render(
+
+
+
+
+
+
+
+
+);
diff --git a/apps/hackathon/src/middleware.ts b/apps/hackathon/src/middleware.ts
new file mode 100644
index 0000000..5e3533a
--- /dev/null
+++ b/apps/hackathon/src/middleware.ts
@@ -0,0 +1,59 @@
+import {
+ PERMISSIONS,
+ SessionToken,
+ SessionUser,
+} from '@imphnen-frontend-service/utils';
+import { LoaderFunctionArgs, redirect } from 'react-router';
+
+const mappingPublicRoutes = [
+ '/',
+];
+
+const mappingRoutePermissions = [
+ {
+ path: '/dashboard',
+ permissions: [],
+ },
+];
+
+const mappingPublicPrefixRoutes = [
+ '/hackathons',
+];
+
+export const middleware = async ({ request }: LoaderFunctionArgs) => {
+ const url = new URL(request.url);
+ const pathname = url.pathname;
+ const session = SessionUser.get();
+ const session_token = SessionToken.get();
+ const token = session_token?.token?.access_token;
+ const userPermissions =
+ session?.role?.permissions?.map?.((perm) => perm?.name) ?? [];
+
+ // Allow to access the hackathon pages without authentication
+ if (mappingPublicPrefixRoutes.some((prefix) => pathname.startsWith(prefix))) {
+ return null;
+ }
+
+ if (mappingPublicRoutes.includes(pathname)) {
+ if (token) return redirect('/dashboard');
+ return null;
+ }
+
+ if (!session) return redirect('/');
+
+ const matchedRoute = mappingRoutePermissions.find(
+ (route) => route.path === pathname
+ );
+
+ if (matchedRoute) {
+ const hasPermission =
+ !matchedRoute.permissions ||
+ matchedRoute.permissions.some((perm) => userPermissions.includes(perm));
+
+ if (!hasPermission) {
+ return '/dashboard';
+ }
+ }
+
+ return null;
+};
diff --git a/apps/hackathon/tsconfig.app.json b/apps/hackathon/tsconfig.app.json
new file mode 100644
index 0000000..6d35def
--- /dev/null
+++ b/apps/hackathon/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "types": [
+ "node",
+ "@nx/react/typings/cssmodule.d.ts",
+ "@nx/react/typings/image.d.ts",
+ "vite/client"
+ ]
+ },
+ "exclude": [
+ "src/**/*.spec.ts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.tsx",
+ "src/**/*.test.tsx",
+ "src/**/*.spec.js",
+ "src/**/*.test.js",
+ "src/**/*.spec.jsx",
+ "src/**/*.test.jsx",
+ "vite.config.ts",
+ "vite.config.mts",
+ "vitest.config.ts",
+ "vitest.config.mts"
+ ],
+ "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
+}
diff --git a/apps/hackathon/tsconfig.json b/apps/hackathon/tsconfig.json
new file mode 100644
index 0000000..fdfab6c
--- /dev/null
+++ b/apps/hackathon/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "allowJs": false,
+ "esModuleInterop": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "types": ["vite/client", "vitest"]
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ }
+ ],
+ "extends": "../../tsconfig.base.json"
+}
diff --git a/apps/hackathon/tsconfig.spec.json b/apps/hackathon/tsconfig.spec.json
new file mode 100644
index 0000000..9d3bb72
--- /dev/null
+++ b/apps/hackathon/tsconfig.spec.json
@@ -0,0 +1,30 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "types": [
+ "vitest/globals",
+ "vitest/importMeta",
+ "vite/client",
+ "node",
+ "vitest",
+ "@nx/react/typings/cssmodule.d.ts",
+ "@nx/react/typings/image.d.ts"
+ ]
+ },
+ "include": [
+ "vite.config.ts",
+ "vite.config.mts",
+ "vitest.config.ts",
+ "vitest.config.mts",
+ "src/**/*.test.ts",
+ "src/**/*.spec.ts",
+ "src/**/*.test.tsx",
+ "src/**/*.spec.tsx",
+ "src/**/*.test.js",
+ "src/**/*.spec.js",
+ "src/**/*.test.jsx",
+ "src/**/*.spec.jsx",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/apps/hackathon/vite.config.ts b/apps/hackathon/vite.config.ts
new file mode 100644
index 0000000..3c4d082
--- /dev/null
+++ b/apps/hackathon/vite.config.ts
@@ -0,0 +1,42 @@
+///
+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/hackathon',
+ server: {
+ port: 3004,
+ host: 'localhost',
+ },
+ preview: {
+ port: 3005,
+ host: 'localhost',
+ },
+ plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
+ // Uncomment this if you are using workers.
+ // worker: {
+ // plugins: [ nxViteTsPaths() ],
+ // },
+ build: {
+ outDir: '../../dist/apps/hackathon',
+ emptyOutDir: true,
+ reportCompressedSize: true,
+ commonjsOptions: {
+ transformMixedEsModules: true,
+ },
+ },
+ test: {
+ watch: false,
+ globals: true,
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ reporters: ['default'],
+ coverage: {
+ reportsDirectory: '../../coverage/apps/hackathon',
+ provider: 'v8' as const,
+ },
+ },
+}));
diff --git a/docker-compose-backoffice.yml b/docker-compose-backoffice.yml
index 4da19f9..738529e 100644
--- a/docker-compose-backoffice.yml
+++ b/docker-compose-backoffice.yml
@@ -1,10 +1,9 @@
-version: '3.8'
services:
backoffice:
build:
context: .
dockerfile: docker/backoffice.Dockerfile
ports:
- - "3001:80"
+ - '127.0.0.1:3001:80'
restart: unless-stopped
container_name: backoffice
diff --git a/docker-compose-dimentorin.yml b/docker-compose-dimentorin.yml
index 049c32a..ae6a152 100644
--- a/docker-compose-dimentorin.yml
+++ b/docker-compose-dimentorin.yml
@@ -1,10 +1,9 @@
-version: '3.8'
services:
dimentorin:
build:
context: .
dockerfile: docker/dimentorin.Dockerfile
ports:
- - "3002:80"
+ - '127.0.0.1:3002:80'
restart: unless-stopped
container_name: dimentorin
diff --git a/docker-compose-gacha.yml b/docker-compose-gacha.yml
index 6bec329..1f73d65 100644
--- a/docker-compose-gacha.yml
+++ b/docker-compose-gacha.yml
@@ -1,10 +1,9 @@
-version: '3.8'
services:
gacha:
build:
context: .
dockerfile: docker/gacha.Dockerfile
ports:
- - "3003:80"
+ - '127.0.0.1:3003:80'
restart: unless-stopped
container_name: gacha
diff --git a/docker-compose-hackathon.yml b/docker-compose-hackathon.yml
new file mode 100644
index 0000000..3890532
--- /dev/null
+++ b/docker-compose-hackathon.yml
@@ -0,0 +1,9 @@
+services:
+ hackathon:
+ build:
+ context: .
+ dockerfile: docker/hackathon.Dockerfile
+ ports:
+ - '127.0.0.1:3004:80'
+ restart: unless-stopped
+ container_name: hackathon
diff --git a/docker-compose-landing.yml b/docker-compose-landing.yml
index 34feeca..04d8f86 100644
--- a/docker-compose-landing.yml
+++ b/docker-compose-landing.yml
@@ -1,10 +1,9 @@
-version: '3.8'
services:
landing:
build:
context: .
dockerfile: docker/landing.Dockerfile
ports:
- - "3000:3000"
+ - '127.0.0.1:3005:3000'
restart: unless-stopped
container_name: landing
diff --git a/docker/hackathon.Dockerfile b/docker/hackathon.Dockerfile
new file mode 100644
index 0000000..f39fbbf
--- /dev/null
+++ b/docker/hackathon.Dockerfile
@@ -0,0 +1,16 @@
+FROM node:22-alpine AS deps
+WORKDIR /app
+COPY package.json package-lock.json ./
+RUN npm install
+
+FROM node:22-alpine AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+RUN npm run hackathon:build
+
+FROM nginx:alpine AS runner
+RUN rm -rf /usr/share/nginx/html/*
+COPY --from=builder /app/dist/apps/hackathon /usr/share/nginx/html
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/package.json b/package.json
index 77ddfa9..880ae7c 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,10 @@
"dimentorin:test": "nx test dimentorin",
"dimentorin:build": "nx build dimentorin",
"dimentorin:prod": "serve ./dist/apps/dimentorin",
+ "hackathon:dev": "nx dev hackathon",
+ "hackathon:build": "nx build hackathon",
+ "hackathon:test": "nx test hackathon",
+ "hackathon:prod": "serve ./dist/apps/hackathon",
"landing:dev": "nx serve landing",
"landing:build": "nx build landing",
"landing:prod": "cd ./dist/apps/landing && npx next start",