Files
3dealer/app/Services/Stoic.php
T
2026-09-28 18:17:33 +03:00

191 lines
5.5 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Spatie\YamlFrontMatter\YamlFrontMatter;
use Symfony\Component\Yaml\Yaml;
class Stoic
{
public static array $config = [];
public static array $presets = [];
public static string $thumbs_path;
private static function loadConfig(): void
{
if (!empty(static::$config)) {
return;
}
$config = Yaml::parseFile(resource_path("stoic/stoic_config.yml"));
static::$config = $config;
static::$thumbs_path = $config["thumbs_path"];
static::$presets = $config["presets"];
}
public static function presets(): array
{
static::loadConfig();
return static::$presets;
}
// Stoic's default locale (first in i18n.locales), or null when i18n is off.
// Its content folder holds every field; other locales only translatable ones.
public static function defaultLocale(): ?string
{
static::loadConfig();
return static::$config["i18n"]["locales"][0]["code"] ?? null;
}
// The current locale's translated fields for a content file (path relative
// to a locale folder, e.g. "pages/home.md"), with the markdown body as
// `content`. Empty when the current locale is the default one (or i18n is
// off), the file doesn't exist, or a field is left blank — so layering this
// over the default-locale values falls back to them field by field.
public static function localeOverrides(string $file): array
{
$default = static::defaultLocale();
$locale = app()->getLocale();
if ($default === null || $locale === $default) {
return [];
}
$path = resource_path("stoic/content/{$locale}/{$file}");
if (!is_file($path)) {
return [];
}
$document = YamlFrontMatter::parse(file_get_contents($path));
return array_filter(
[...$document->matter(), "content" => trim($document->body())],
fn($value) => filled($value),
);
}
// Returns the public URL for a stoic image at a given preset.
public static function image(string $filename, string $preset): string
{
static::loadConfig();
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($ext === "svg") {
$rel = static::thumbsRelPath() . "images/" . $filename;
return Storage::disk("public")->url($rel);
}
$webp = substr($filename, 0, -strlen($ext)) . "webp";
$rel = static::thumbsRelPath() . "presets/" . $preset . "/" . $webp;
return Storage::disk("public")->url($rel);
}
// Generates the full srcset string for all configured presets.
public static function srcset(string $filename): string
{
static::loadConfig();
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($ext === "svg") {
return "";
}
return collect(static::$presets)
->map(
fn($p) => static::image($filename, $p["code"]) .
" " .
$p["width"] .
"w",
)
->implode(", ");
}
// Returns [width, height] for a given preset from the sidecar JSON.
public static function dimensions(string $filename, string $preset): array
{
static::loadConfig();
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($ext === "svg") {
return [null, null];
}
$rel = static::thumbsRelPath() . "images/" . $filename . ".json";
$path = Storage::disk("public")->path($rel);
$meta = json_decode(@file_get_contents($path), true);
$p = $meta["presets"][$preset] ?? null;
if (!$p) {
return [null, null];
}
return [(int) $p["w"], (int) $p["h"]];
}
// Path of the thumbs directory relative to the public storage disk root.
// Uses a fixed marker so it works regardless of the app's base path (local vs Docker).
private static function thumbsRelPath(): string
{
$marker = "/storage/app/public/";
$pos = strpos(static::$thumbs_path, $marker);
if ($pos !== false) {
return substr(static::$thumbs_path, $pos + strlen($marker));
}
return ltrim(static::$thumbs_path, "/");
}
public static function token(string $email): string
{
$ttl = 60;
$payload = base64_encode(
json_encode([
"email" => $email,
"exp" => time() + $ttl,
]),
);
$sig = hash_hmac(
"sha256",
$payload,
config("services.stoic.sso_secret"),
);
$token = "t=" . urlencode($payload) . "&s=" . $sig;
return $token;
}
public static function hasMany(
string $relation,
array $slugs,
string $prefix,
) {
$slugs = array_map(
fn($i) => str_replace($prefix . "/", "", $i),
$slugs,
);
$entries = $relation::whereIn("slug", $slugs)->get();
return collect(array_map(
fn($i) => $entries->firstWhere("slug", $i),
$slugs
));
}
public static function hasOne(
string $relation,
string $slug,
string $prefix,
) {
$slug = str_replace($prefix . "/", "", $slug);
return $relation::firstWhere("slug", $slug);
}
}