added a help system, replaced the words "tenant" and "Pwa" with better alternatives. corrected and implemented cron jobs. prepared going live on a coolify-powered system.

This commit is contained in:
Codex Agent
2025-11-10 16:23:09 +01:00
parent ba9e64dfcb
commit 447a90a742
123 changed files with 6398 additions and 153 deletions

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers\Api;
use App\Support\Help\HelpRepository;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken;
use RuntimeException;
class HelpController extends Controller
{
public function __construct(private readonly HelpRepository $repository) {}
public function index(Request $request): JsonResponse
{
[$audience, $locale] = $this->resolveContext($request);
$articles = $this->getArticles($audience, $locale)
->map(fn ($article) => Arr::only($article, config('help.list_fields')))
->values();
return response()->json([
'data' => $articles,
]);
}
public function show(Request $request, string $slug): JsonResponse
{
[$audience, $locale] = $this->resolveContext($request);
$article = $this->getArticle($audience, $locale, $slug);
abort_if(! $article, 404, 'Help article not found.');
return response()->json([
'data' => $article,
]);
}
/**
* @return array{string, string}
*/
private function resolveContext(Request $request): array
{
$this->attemptTokenAuthentication($request);
$audience = Str::of($request->string('audience', 'guest'))->lower()->value();
$locale = Str::of($request->string('locale', config('help.default_locale')))->lower()->value();
if ($audience === 'admin' && ! $request->user()) {
abort(401, 'Authentication required for admin help content.');
}
if (! in_array($audience, config('help.audiences', []), true)) {
abort(400, 'Invalid audience supplied.');
}
return [$audience, $locale];
}
private function attemptTokenAuthentication(Request $request): void
{
if ($request->user()) {
return;
}
$bearer = $request->bearerToken();
if (! $bearer) {
return;
}
$token = PersonalAccessToken::findToken($bearer);
if (! $token) {
return;
}
$user = $token->tokenable;
if (! $user) {
return;
}
if (method_exists($user, 'withAccessToken')) {
$user->withAccessToken($token);
}
Auth::setUser($user);
$request->setUserResolver(fn () => $user);
}
private function getArticles(string $audience, string $locale)
{
try {
return $this->repository->list($audience, $locale);
} catch (RuntimeException $e) {
$fallback = config('help.fallback_locale');
if ($locale === $fallback) {
throw $e;
}
return $this->repository->list($audience, $fallback);
}
}
private function getArticle(string $audience, string $locale, string $slug): ?array
{
try {
$article = $this->repository->find($audience, $locale, $slug);
} catch (RuntimeException $e) {
$fallback = config('help.fallback_locale');
if ($locale !== $fallback) {
return $this->repository->find($audience, $fallback, $slug);
}
throw $e;
}
return $article;
}
}