feat: homelab deployment - NPM + IONOS DNS + monitoring + NAS backup

- Restructured docker-compose for Nginx Proxy Manager (no custom nginx)
- Added domain wordly.art configuration
- Added Prometheus + Grafana monitoring stack with pre-configured dashboards
- Added PostgreSQL backup script to NAS (daily/weekly/monthly rotation)
- Added alert rules for backend, system, and Docker metrics
- Updated deployment guide for NPM + IONOS DNS homelab setup
- Added marketing plan document
- PDF translator and watermark support
- Enhanced middleware, routes, and translator modules

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 11:43:28 +02:00
parent 16ac7ca2b9
commit ce8e150a61
110 changed files with 6935 additions and 4301 deletions

View File

@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { useNotification } from '@/components/ui/notification';
import { useI18n } from '@/lib/i18n';
import { useLogin } from './useLogin';
export function LoginForm() {
@@ -17,11 +18,12 @@ export function LoginForm() {
const loginMutation = useLogin();
const { notify } = useNotification();
const { t } = useI18n();
useEffect(() => {
if (loginMutation.isError && loginMutation.error) {
notify({
title: 'Erreur de connexion',
title: t('login.errorTitle'),
description: loginMutation.error.message,
variant: 'destructive',
});
@@ -41,26 +43,26 @@ export function LoginForm() {
<Languages className="h-6 w-6" />
</div>
<span className="text-2xl font-semibold text-foreground">
Office Translator
{t('auth.brandName')}
</span>
</Link>
<CardTitle className="text-2xl font-bold">
Welcome back
{t('login.welcomeBack')}
</CardTitle>
<CardDescription>
Sign in to continue translating
{t('login.signInToContinue')}
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Label htmlFor="email">{t('login.email')}</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
placeholder={t('login.emailPlaceholder')}
value={email}
onChange={(e) => setEmail(e.target.value)}
leftIcon={<Mail className="h-4 w-4" />}
@@ -70,16 +72,16 @@ export function LoginForm() {
<div className="space-y-2">
<div className="flex justify-between items-center">
<Label htmlFor="password">Password</Label>
<Label htmlFor="password">{t('login.password')}</Label>
<Link href="/auth/forgot-password" className="text-sm text-primary hover:underline">
Forgot password?
{t('login.forgotPassword')}
</Link>
</div>
<div className="relative">
<Input
id="password"
type={showPassword ? 'text' : 'password'}
placeholder="••••••••"
placeholder={t('login.passwordPlaceholder')}
value={password}
onChange={(e) => setPassword(e.target.value)}
leftIcon={<Lock className="h-4 w-4" />}
@@ -102,22 +104,22 @@ export function LoginForm() {
>
{loginMutation.isPending ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Signing in...
<Loader2 className="me-2 h-4 w-4 animate-spin" />
{t('login.signingIn')}
</>
) : (
<>
Sign In
<ArrowRight className="ml-2 h-4 w-4" />
{t('login.signIn')}
<ArrowRight className="ms-2 h-4 w-4" />
</>
)}
</Button>
</form>
<p className="text-center text-sm text-muted-foreground">
Don&apos;t have an account?{' '}
{t('login.noAccount')}{' '}
<Link href="/auth/register" className="text-primary hover:underline">
Sign up for free
{t('login.signUpFree')}
</Link>
</p>
</CardContent>