first commit

This commit is contained in:
Rutra committed 2026-10-12 14:25:22 +02:00
commit 3c0479faf0
169 files changed
+27617

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
/*
|--------------------------------------------------------------------------
| Environment variables service
|--------------------------------------------------------------------------
|
| The `Env.create` method creates an instance of the Env service. The
| service validates the environment variables and also cast values
| to JavaScript data types.
|
*/
import { Env } from '@adonisjs/core/env'
export default await Env.create(new URL('../', import.meta.url), {
// Node
NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
PORT: Env.schema.number(),
HOST: Env.schema.string({ format: 'host' }),
LOG_LEVEL: Env.schema.string(),
// App
APP_KEY: Env.schema.secret(),
APP_URL: Env.schema.string({ format: 'url', tld: false }),
// Session
SESSION_DRIVER: Env.schema.enum(['cookie', 'memory', 'database'] as const),
// Database (SQLite file holding users, history and the action log)
DB_PATH: Env.schema.string.optional(),
// VPS paths, as seen from inside the container
SRV_ROOT: Env.schema.string(),
HOST_PROC: Env.schema.string.optional(),
DISK_PATH: Env.schema.string.optional(),
BACKUP_DIR: Env.schema.string(),
// Docker
DOCKER_SOCKET: Env.schema.string.optional(),
CADDY_CONTAINER: Env.schema.string(),
CADDYFILE_PATH: Env.schema.string(),
CADDY_CONFIG_PATH: Env.schema.string(),
MARIADB_CONTAINER: Env.schema.string(),
// Projects
BASE_DOMAIN: Env.schema.string(),
// Backups ("HH:MM" in the server timezone, empty = no automatic backup)
BACKUP_DAILY_AT: Env.schema.string.optional(),
BACKUP_KEEP: Env.schema.number.optional(),
})
+52
View File
@@ -0,0 +1,52 @@
/*
|--------------------------------------------------------------------------
| HTTP kernel file
|--------------------------------------------------------------------------
|
| The HTTP kernel file is used to register the middleware with the server
| or the router.
|
*/
import router from '@adonisjs/core/services/router'
import server from '@adonisjs/core/services/server'
/**
* The error handler is used to convert an exception
* to a HTTP response.
*/
server.errorHandler(() => import('#exceptions/handler'))
/**
* The server middleware stack runs middleware on all the HTTP
* requests, even if there is no route registered for
* the request URL.
*
* Security headers come first so that the static files (the Angular
* build served from "public/") receive them too.
*/
server.use([
() => import('#middleware/security_headers_middleware'),
() => import('#middleware/force_json_response_middleware'),
() => import('#middleware/container_bindings_middleware'),
() => import('@adonisjs/static/static_middleware'),
])
/**
* The router middleware stack runs middleware on all the HTTP
* requests with a registered route.
*/
router.use([
() => import('@adonisjs/core/bodyparser_middleware'),
() => import('@adonisjs/session/session_middleware'),
() => import('@adonisjs/shield/shield_middleware'),
() => import('@adonisjs/auth/initialize_auth_middleware'),
])
/**
* Named middleware collection must be explicitly assigned to
* the routes or the routes group.
*/
export const middleware = router.named({
auth: () => import('#middleware/auth_middleware'),
})
+29
View File
@@ -0,0 +1,29 @@
/*
|--------------------------------------------------------------------------
| Background monitors
|--------------------------------------------------------------------------
|
| Started with the HTTP server only (not with the ace commands): host
| metrics, containers stats, site checks and the daily backup.
|
*/
import app from '@adonisjs/core/services/app'
import backupService from '#services/backup_service'
import dockerService from '#services/docker_service'
import metricsService from '#services/metrics_service'
import siteMonitor from '#services/site_monitor'
app.ready(() => {
metricsService.start()
dockerService.startStatsPolling()
siteMonitor.start()
backupService.startSchedule()
})
app.terminating(() => {
metricsService.stop()
dockerService.stopStatsPolling()
siteMonitor.stop()
backupService.stopSchedule()
})
+68
View File
@@ -0,0 +1,68 @@
/*
|--------------------------------------------------------------------------
| Routes file
|--------------------------------------------------------------------------
|
| The routes file is used for defining the HTTP routes.
|
*/
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'
router
.group(() => {
router.post('auth/login', [controllers.Auth, 'login'])
router
.group(() => {
router.post('auth/logout', [controllers.Auth, 'logout'])
router.get('auth/me', [controllers.Auth, 'me'])
router.get('overview', [controllers.Overview, 'index'])
router.get('metrics/live', [controllers.Metrics, 'live'])
router.get('metrics/history', [controllers.Metrics, 'history'])
router.get('containers', [controllers.Containers, 'index'])
router.get('containers/:id', [controllers.Containers, 'show'])
router.get('containers/:id/logs', [controllers.Containers, 'logs'])
router.post('containers/:id/:action', [controllers.Containers, 'action'])
router.get('sites', [controllers.Sites, 'index'])
router.post('sites/check', [controllers.Sites, 'check'])
router.get('sites/:id', [controllers.Sites, 'show'])
router.get('projects', [controllers.Projects, 'index'])
router.post('projects', [controllers.Projects, 'store'])
router.get('projects/options', [controllers.Projects, 'options'])
router.get('projects/:category/:name', [controllers.Projects, 'show'])
router.post('projects/:category/:name/fetch', [controllers.Projects, 'fetch'])
router.post('projects/:category/:name/actions/:action', [controllers.Projects, 'action'])
router.get('caddy', [controllers.Caddy, 'show'])
router.put('caddy', [controllers.Caddy, 'update'])
router.post('caddy/reload', [controllers.Caddy, 'reload'])
router.get('caddy/versions', [controllers.Caddy, 'versions'])
router.get('caddy/versions/:id', [controllers.Caddy, 'version'])
router.post('caddy/versions/:id/restore', [controllers.Caddy, 'restore'])
router.get('backups', [controllers.Backups, 'index'])
router.post('backups', [controllers.Backups, 'store'])
router.get('backups/:name', [controllers.Backups, 'download'])
router.delete('backups/:name', [controllers.Backups, 'destroy'])
router.get('jobs/:id/stream', [controllers.Jobs, 'stream'])
router.get('audit', [controllers.Audit, 'index'])
router.get('audit/:id', [controllers.Audit, 'show'])
})
.use(middleware.auth())
})
.prefix('/api')
/**
* Every other URL is a page of the Angular application.
*/
router.get('*', [controllers.Spa, 'index'])
+57
View File
@@ -0,0 +1,57 @@
/*
|--------------------------------------------------------------------------
| Validator file
|--------------------------------------------------------------------------
|
| The validator file is used for configuring global transforms for VineJS.
| The transform below converts all VineJS date outputs from JavaScript
| Date objects to Luxon DateTime instances, so that validated dates are
| ready to use with Lucid models and other parts of the app that expect
| Luxon DateTime.
|
*/
import { DateTime } from 'luxon'
import vine, { SimpleMessagesProvider, VineDate } from '@vinejs/vine'
declare module '@vinejs/vine/types' {
interface VineGlobalTransforms {
date: DateTime
}
}
VineDate.transform((value) => DateTime.fromJSDate(value))
/**
* Error messages shown in the dashboard.
*/
vine.messagesProvider = new SimpleMessagesProvider(
{
'required': 'Le champ « {{ field }} » est obligatoire.',
'requiredWhen': 'Le champ « {{ field }} » est obligatoire pour ce type de projet.',
'string': 'Le champ « {{ field }} » doit être un texte.',
'number': 'Le champ « {{ field }} » doit être un nombre.',
'boolean': 'Le champ « {{ field }} » doit être vrai ou faux.',
'enum': 'La valeur de « {{ field }} » n’est pas autorisée.',
'regex': 'Le format de « {{ field }} » est invalide.',
'range': '« {{ field }} » doit être entre {{ min }} et {{ max }}.',
'withoutDecimals': '« {{ field }} » doit être un nombre entier.',
'minLength': '« {{ field }} » doit faire au moins {{ min }} caractères.',
'maxLength': '« {{ field }} » doit faire au plus {{ max }} caractères.',
'code.regex': 'Le code doit contenir 6 chiffres.',
'name.regex':
'Le nom ne peut contenir que des minuscules, des chiffres et des tirets (et commencer par une lettre ou un chiffre).',
'domain.regex': 'Le domaine est invalide.',
'gitUrl.regex': 'L’URL git doit commencer par https://, ssh:// ou git@.',
},
{
username: 'identifiant',
password: 'mot de passe',
code: 'code',
name: 'nom',
gitUrl: 'URL git',
port: 'port',
domain: 'domaine',
content: 'contenu',
}
)