ajout images
This commit is contained in:
@@ -8,3 +8,4 @@ docker/certs/*
|
|||||||
deploy.env
|
deploy.env
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
docker/nginx/admin-allow.inc
|
docker/nginx/admin-allow.inc
|
||||||
|
/.env
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Admin\Concerns\ScopedToTenant;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\AuditLog;
|
||||||
|
use App\Models\Campaign;
|
||||||
|
use App\Models\Template;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Images des signatures (logos, bannières), servies publiquement par Nginx sous /img.
|
||||||
|
* Formats acceptés : PNG, JPEG, GIF. Pas de SVG (mal supporté par Outlook et vecteur de script),
|
||||||
|
* pas de WebP (non affiché par plusieurs clients de messagerie).
|
||||||
|
*/
|
||||||
|
class ImageController extends Controller
|
||||||
|
{
|
||||||
|
use ScopedToTenant;
|
||||||
|
|
||||||
|
private const EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif'];
|
||||||
|
|
||||||
|
public function index(): View
|
||||||
|
{
|
||||||
|
$dir = $this->dir();
|
||||||
|
$images = collect(File::files($dir))
|
||||||
|
->filter(fn ($f) => in_array(strtolower($f->getExtension()), self::EXTENSIONS, true))
|
||||||
|
->map(function ($f) {
|
||||||
|
$size = @getimagesize($f->getPathname()) ?: [null, null];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $f->getFilename(),
|
||||||
|
'url' => config('signature.public_url').'/img/'.rawurlencode($f->getFilename()),
|
||||||
|
'kb' => (int) ceil($f->getSize() / 1024),
|
||||||
|
'width' => $size[0],
|
||||||
|
'height' => $size[1],
|
||||||
|
'modified' => $f->getMTime(),
|
||||||
|
'used_in' => $this->usages($f->getFilename()),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->sortByDesc('modified')->values();
|
||||||
|
|
||||||
|
return view('admin.images.index', ['images' => $images, 'maxKb' => config('signature.image_max_kb')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'image' => ['required', 'file', 'mimes:png,jpg,jpeg,gif', 'mimetypes:image/png,image/jpeg,image/gif', 'max:'.config('signature.image_max_kb')],
|
||||||
|
'name' => ['nullable', 'string', 'max:80'],
|
||||||
|
], [
|
||||||
|
'image.mimes' => 'Formats acceptés : PNG, JPEG ou GIF.',
|
||||||
|
'image.mimetypes' => 'Le fichier n\'est pas une image PNG, JPEG ou GIF valide.',
|
||||||
|
'image.max' => 'Image trop lourde (2 Mo maximum ; visez moins de 100 Ko pour une signature).',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$file = $request->file('image');
|
||||||
|
abort_unless(@getimagesize($file->getRealPath()), 422, 'Image illisible.');
|
||||||
|
|
||||||
|
$ext = strtolower($file->getClientOriginalExtension()) === 'jpeg' ? 'jpg' : strtolower($file->getClientOriginalExtension());
|
||||||
|
$base = Str::slug(pathinfo($request->input('name') ?: $file->getClientOriginalName(), PATHINFO_FILENAME)) ?: 'image';
|
||||||
|
$name = $this->uniqueName($base, $ext);
|
||||||
|
|
||||||
|
$file->move($this->dir(), $name);
|
||||||
|
@chmod($this->dir().DIRECTORY_SEPARATOR.$name, 0644);
|
||||||
|
AuditLog::record('admin', $this->actor(), 'image.upload', null, ['file' => $name], $this->tenantId());
|
||||||
|
|
||||||
|
return back()->with('status', "Image « {$name} » en ligne. Dans un template : {{site_url}}/img/{$name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(string $name): RedirectResponse
|
||||||
|
{
|
||||||
|
$name = basename($name);
|
||||||
|
$path = $this->dir().DIRECTORY_SEPARATOR.$name;
|
||||||
|
abort_unless(File::isFile($path) && in_array(strtolower(pathinfo($name, PATHINFO_EXTENSION)), self::EXTENSIONS, true), 404);
|
||||||
|
|
||||||
|
if ($used = $this->usages($name)) {
|
||||||
|
return back()->withErrors(['image' => "« {$name} » est utilisée par : ".implode(', ', $used).'. Retirez-la d\'abord.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
File::delete($path);
|
||||||
|
AuditLog::record('admin', $this->actor(), 'image.delete', null, ['file' => $name], $this->tenantId());
|
||||||
|
|
||||||
|
return back()->with('status', "Image « {$name} » supprimée.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dir(): string
|
||||||
|
{
|
||||||
|
$dir = config('signature.image_path');
|
||||||
|
File::ensureDirectoryExists($dir);
|
||||||
|
|
||||||
|
return $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function uniqueName(string $base, string $ext): string
|
||||||
|
{
|
||||||
|
$name = "{$base}.{$ext}";
|
||||||
|
for ($i = 2; File::exists($this->dir().DIRECTORY_SEPARATOR.$name); $i++) {
|
||||||
|
$name = "{$base}-{$i}.{$ext}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Templates (publiés ou brouillons) et campagnes qui référencent l'image. */
|
||||||
|
private function usages(string $name): array
|
||||||
|
{
|
||||||
|
$like = '%/img/'.$name.'%';
|
||||||
|
$templates = Template::where('tenant_id', $this->tenantId())
|
||||||
|
->where(fn ($q) => $q->where('html_full', 'like', $like)->orWhere('html_short', 'like', $like)
|
||||||
|
->orWhere('draft_html_full', 'like', $like)->orWhere('draft_html_short', 'like', $like))
|
||||||
|
->pluck('name')->map(fn ($n) => "template {$n}");
|
||||||
|
$campaigns = Campaign::where('tenant_id', $this->tenantId())->where('image_path', $name)
|
||||||
|
->pluck('name')->map(fn ($n) => "campagne {$n}");
|
||||||
|
|
||||||
|
return $templates->merge($campaigns)->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,6 +48,10 @@ return [
|
|||||||
// Variante utilisée en réponse / transfert quand l'agent n'a rien choisi : short | full | none
|
// Variante utilisée en réponse / transfert quand l'agent n'a rien choisi : short | full | none
|
||||||
'default_reply_variant' => env('SIGNATURE_DEFAULT_REPLY_VARIANT', 'short'),
|
'default_reply_variant' => env('SIGNATURE_DEFAULT_REPLY_VARIANT', 'short'),
|
||||||
|
|
||||||
|
// Dossier des images de signature (servi par Nginx sous /img). Surchargé dans les tests.
|
||||||
|
'image_path' => env('SIGNATURE_IMAGE_PATH', public_path('img')),
|
||||||
|
'image_max_kb' => 2048,
|
||||||
|
|
||||||
// URL publique (images, liens).
|
// URL publique (images, liens).
|
||||||
'public_url' => rtrim((string) env('APP_URL', 'https://signature.beziers-mediterranee.fr'), '/'),
|
'public_url' => rtrim((string) env('APP_URL', 'https://signature.beziers-mediterranee.fr'), '/'),
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
@section('title', 'Images — Signatures CABM')
|
||||||
|
@section('content')
|
||||||
|
@php
|
||||||
|
$tag = fn (string $inner) => '{'.'{'.$inner.'}'.'}';
|
||||||
|
$isAdmin = auth()->user()->hasRole('admin');
|
||||||
|
@endphp
|
||||||
|
<h1>Images</h1>
|
||||||
|
<p class="muted" style="margin-top:-8px">
|
||||||
|
Logos et bannières utilisés dans les signatures, publiés sur <code>{{ config('signature.public_url') }}/img/</code>.
|
||||||
|
Les destinataires les chargent depuis ce serveur : visez moins de 100 Ko et la largeur réelle d'affichage (ex. 120 px pour un logo, 600 px pour une bannière).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Ajouter une image</h2>
|
||||||
|
<form method="post" action="{{ route('admin.images.store') }}" enctype="multipart/form-data" class="row" style="align-items:flex-end">
|
||||||
|
@csrf
|
||||||
|
<div style="flex:2;min-width:240px">
|
||||||
|
<label for="image">Fichier (PNG, JPEG, GIF — {{ (int) ($maxKb / 1024) }} Mo max.)</label>
|
||||||
|
<input type="file" id="image" name="image" accept="image/png,image/jpeg,image/gif" required>
|
||||||
|
</div>
|
||||||
|
<div style="flex:1;min-width:200px">
|
||||||
|
<label for="name">Nom (facultatif)</label>
|
||||||
|
<input type="text" id="name" name="name" maxlength="80" placeholder="ex. logo-cabm">
|
||||||
|
</div>
|
||||||
|
<button class="btn" type="submit">Envoyer</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Images en ligne ({{ $images->count() }})</h2>
|
||||||
|
<table class="list">
|
||||||
|
<thead><tr><th style="width:150px">Aperçu</th><th>Fichier</th><th>À coller dans un template</th><th>Utilisée par</th><th></th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse ($images as $img)
|
||||||
|
@php $snippet = '<img src="'.$tag('site_url').'/img/'.$img['name'].'" alt="" width="'.($img['width'] ?? '').'" style="display:block;border:0">'; @endphp
|
||||||
|
<tr>
|
||||||
|
<td><img src="{{ asset('img/'.$img['name']) }}" alt="" style="max-width:140px;max-height:70px;border:1px solid var(--line);background:#fff"></td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ $img['url'] }}" target="_blank" rel="noopener">{{ $img['name'] }}</a>
|
||||||
|
<div class="muted">{{ $img['width'] }}×{{ $img['height'] }} px · {{ $img['kb'] }} Ko
|
||||||
|
@if ($img['kb'] > 150) <span class="badge warn">lourde</span> @endif</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<code style="font-size:12px;word-break:break-all">{{ $snippet }}</code>
|
||||||
|
<button type="button" class="btn secondary small" data-copy="{{ $snippet }}">Copier</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="muted">{{ $img['used_in'] ? implode(', ', $img['used_in']) : '—' }}</td>
|
||||||
|
<td style="text-align:right">
|
||||||
|
@if ($isAdmin)
|
||||||
|
<form class="inline" method="post" action="{{ route('admin.images.destroy', $img['name']) }}"
|
||||||
|
onsubmit="return confirm('Supprimer {{ $img['name'] }} ? Les signatures déjà envoyées qui l\'affichent ne la montreront plus.')">
|
||||||
|
@csrf @method('DELETE')
|
||||||
|
<button class="btn danger small" type="submit" @disabled($img['used_in'])>Supprimer</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr><td colspan="5" class="muted">Aucune image. Commencez par le logo (ex. logo-cabm.png, utilisé par le template par défaut).</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('click', async (e) => {
|
||||||
|
const text = e.target.dataset && e.target.dataset.copy;
|
||||||
|
if (!text) return;
|
||||||
|
try { await navigator.clipboard.writeText(text); e.target.textContent = 'Copié'; }
|
||||||
|
catch { e.target.textContent = 'Copie impossible'; }
|
||||||
|
setTimeout(() => { e.target.textContent = 'Copier'; }, 1500);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
@@ -55,6 +55,7 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<a href="{{ route('admin.dashboard') }}" @class(['active' => request()->routeIs('admin.dashboard')])>Tableau de bord</a>
|
<a href="{{ route('admin.dashboard') }}" @class(['active' => request()->routeIs('admin.dashboard')])>Tableau de bord</a>
|
||||||
<a href="{{ route('admin.templates.index') }}" @class(['active' => request()->routeIs('admin.templates.*')])>Templates</a>
|
<a href="{{ route('admin.templates.index') }}" @class(['active' => request()->routeIs('admin.templates.*')])>Templates</a>
|
||||||
|
<a href="{{ route('admin.images.index') }}" @class(['active' => request()->routeIs('admin.images.*')])>Images</a>
|
||||||
@if (auth()->user()->hasRole('admin'))
|
@if (auth()->user()->hasRole('admin'))
|
||||||
<a href="{{ route('admin.assignments.index') }}" @class(['active' => request()->routeIs('admin.assignments.*')])>Affectations</a>
|
<a href="{{ route('admin.assignments.index') }}" @class(['active' => request()->routeIs('admin.assignments.*')])>Affectations</a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use App\Http\Controllers\Admin\AgentController;
|
use App\Http\Controllers\Admin\AgentController;
|
||||||
use App\Http\Controllers\Admin\AssignmentController;
|
use App\Http\Controllers\Admin\AssignmentController;
|
||||||
use App\Http\Controllers\Admin\DashboardController;
|
use App\Http\Controllers\Admin\DashboardController;
|
||||||
|
use App\Http\Controllers\Admin\ImageController;
|
||||||
use App\Http\Controllers\Admin\SyncController;
|
use App\Http\Controllers\Admin\SyncController;
|
||||||
use App\Http\Controllers\Admin\TemplateController;
|
use App\Http\Controllers\Admin\TemplateController;
|
||||||
use App\Http\Controllers\Auth\EntraLoginController;
|
use App\Http\Controllers\Auth\EntraLoginController;
|
||||||
@@ -34,6 +35,10 @@ Route::prefix('admin')->middleware(['admin.network', 'auth', 'role:editor'])->na
|
|||||||
Route::put('/templates/{template}', [TemplateController::class, 'update'])->name('templates.update');
|
Route::put('/templates/{template}', [TemplateController::class, 'update'])->name('templates.update');
|
||||||
Route::post('/templates/{template}/versions/{version}/restore', [TemplateController::class, 'restore'])->name('templates.restore');
|
Route::post('/templates/{template}/versions/{version}/restore', [TemplateController::class, 'restore'])->name('templates.restore');
|
||||||
|
|
||||||
|
// Images (logos, bannières) — éditeur : dépôt ; administrateur : suppression
|
||||||
|
Route::get('/images', [ImageController::class, 'index'])->name('images.index');
|
||||||
|
Route::post('/images', [ImageController::class, 'store'])->name('images.store');
|
||||||
|
|
||||||
Route::middleware('role:admin')->group(function () {
|
Route::middleware('role:admin')->group(function () {
|
||||||
Route::post('/templates/{template}/publish', [TemplateController::class, 'publish'])->name('templates.publish');
|
Route::post('/templates/{template}/publish', [TemplateController::class, 'publish'])->name('templates.publish');
|
||||||
Route::post('/templates/{template}/default', [TemplateController::class, 'makeDefault'])->name('templates.default');
|
Route::post('/templates/{template}/default', [TemplateController::class, 'makeDefault'])->name('templates.default');
|
||||||
@@ -45,6 +50,7 @@ Route::prefix('admin')->middleware(['admin.network', 'auth', 'role:editor'])->na
|
|||||||
Route::delete('/affectations/{assignment}', [AssignmentController::class, 'destroy'])->name('assignments.destroy');
|
Route::delete('/affectations/{assignment}', [AssignmentController::class, 'destroy'])->name('assignments.destroy');
|
||||||
|
|
||||||
Route::post('/synchro', SyncController::class)->name('sync');
|
Route::post('/synchro', SyncController::class)->name('sync');
|
||||||
|
Route::delete('/images/{name}', [ImageController::class, 'destroy'])->name('images.destroy')->where('name', '[A-Za-z0-9._-]+');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Agents : simulation « qui reçoit quoi » et recherche pour l'aperçu
|
// Agents : simulation « qui reçoit quoi » et recherche pour l'aperçu
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Template;
|
||||||
|
use App\Models\Tenant;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AdminImagesTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
private Tenant $tenant;
|
||||||
|
private string $dir;
|
||||||
|
|
||||||
|
// PNG 1×1 valide
|
||||||
|
private const PNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->dir = storage_path('framework/testing/img-'.uniqid());
|
||||||
|
config(['signature.admin_allowed_cidrs' => [], 'signature.image_path' => $this->dir]);
|
||||||
|
File::ensureDirectoryExists($this->dir);
|
||||||
|
$this->tenant = Tenant::create(['name' => 'CABM', 'entra_tenant_id' => '11111111-1111-1111-1111-111111111111']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
File::deleteDirectory($this->dir);
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function user(string $role): User
|
||||||
|
{
|
||||||
|
return User::create(['name' => $role, 'email' => "{$role}@x.fr", 'tenant_id' => $this->tenant->id, 'entra_oid' => '22222222-2222-2222-2222-222222222222', 'role' => $role]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function png(string $client = 'Logo CABM.png'): UploadedFile
|
||||||
|
{
|
||||||
|
$tmp = tempnam(sys_get_temp_dir(), 'png');
|
||||||
|
file_put_contents($tmp, base64_decode(self::PNG));
|
||||||
|
|
||||||
|
return new UploadedFile($tmp, $client, 'image/png', null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_editeur_depose_une_image_au_nom_nettoye_sans_ecraser(): void
|
||||||
|
{
|
||||||
|
$editor = $this->user('editor');
|
||||||
|
$this->actingAs($editor)->post('/admin/images', ['image' => $this->png()])->assertRedirect()->assertSessionHasNoErrors();
|
||||||
|
$this->actingAs($editor)->post('/admin/images', ['image' => $this->png()])->assertRedirect();
|
||||||
|
|
||||||
|
$this->assertFileExists($this->dir.'/logo-cabm.png');
|
||||||
|
$this->assertFileExists($this->dir.'/logo-cabm-2.png');
|
||||||
|
$this->actingAs($editor)->get('/admin/images')->assertOk()->assertSee('logo-cabm-2.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_refuse_un_faux_png_et_le_svg(): void
|
||||||
|
{
|
||||||
|
$tmp = tempnam(sys_get_temp_dir(), 'x');
|
||||||
|
file_put_contents($tmp, '<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>');
|
||||||
|
$this->actingAs($this->user('editor'))
|
||||||
|
->post('/admin/images', ['image' => new UploadedFile($tmp, 'logo.png', 'image/png', null, true)])
|
||||||
|
->assertSessionHasErrors('image');
|
||||||
|
$this->assertSame([], File::files($this->dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_suppression_reservee_admin_et_bloquee_si_utilisee(): void
|
||||||
|
{
|
||||||
|
File::ensureDirectoryExists($this->dir);
|
||||||
|
file_put_contents($this->dir.'/logo.png', base64_decode(self::PNG));
|
||||||
|
|
||||||
|
$this->actingAs($this->user('editor'))->delete('/admin/images/logo.png')->assertForbidden();
|
||||||
|
|
||||||
|
$admin = User::create(['name' => 'a', 'email' => 'a@x.fr', 'tenant_id' => $this->tenant->id, 'entra_oid' => '33333333-3333-3333-3333-333333333333', 'role' => 'admin']);
|
||||||
|
$t = Template::create(['tenant_id' => $this->tenant->id, 'name' => 'Défaut', 'draft_html_full' => '<img src="{{site_url}}/img/logo.png">']);
|
||||||
|
$this->actingAs($admin)->delete('/admin/images/logo.png')->assertSessionHasErrors('image');
|
||||||
|
$this->assertFileExists($this->dir.'/logo.png');
|
||||||
|
|
||||||
|
$t->update(['draft_html_full' => 'texte']);
|
||||||
|
$this->actingAs($admin)->delete('/admin/images/logo.png')->assertSessionHasNoErrors();
|
||||||
|
$this->assertFileDoesNotExist($this->dir.'/logo.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,6 +147,8 @@ if (( FIRST_INSTALL )); then
|
|||||||
echo "DB_PASSWORD=$DB_PASSWORD"
|
echo "DB_PASSWORD=$DB_PASSWORD"
|
||||||
} > "$DEPLOY_ENV"
|
} > "$DEPLOY_ENV"
|
||||||
umask 022
|
umask 022
|
||||||
|
# .env à la racine = fichier lu automatiquement par « docker compose » : les commandes manuelles fonctionnent sans --env-file
|
||||||
|
ln -sfn deploy.env "$ROOT/.env"
|
||||||
c_ok "deploy.env et docker-compose.override.yml créés (cible NPM : http://$FORWARD_HOST:$FORWARD_PORT)."
|
c_ok "deploy.env et docker-compose.override.yml créés (cible NPM : http://$FORWARD_HOST:$FORWARD_PORT)."
|
||||||
|
|
||||||
# --- Identifiants Entra -----------------------------------------------------
|
# --- Identifiants Entra -----------------------------------------------------
|
||||||
@@ -212,6 +214,7 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
[[ -e "$ROOT/.env" ]] || ln -s deploy.env "$ROOT/.env"
|
||||||
[[ -f "$ADMIN_ALLOW" ]] || printf 'allow 10.0.0.0/8;\nallow 172.16.0.0/12;\nallow 192.168.0.0/16;\n' > "$ADMIN_ALLOW"
|
[[ -f "$ADMIN_ALLOW" ]] || printf 'allow 10.0.0.0/8;\nallow 172.16.0.0/12;\nallow 192.168.0.0/16;\n' > "$ADMIN_ALLOW"
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user