Files
coruna-lab/app/Models/Channel.php
T
2026-08-13 07:08:02 +08:00

193 lines
5.0 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use RuntimeException;
class Channel extends Model
{
public const OFFICIAL_USER_ID = 0;
public const BUILDER_OLD = 'old';
public const BUILDER_NEW = 'new';
public const CHANNEL_NAME_LENGTH = 8;
/**
* Public-root path prefixes that must not be used as channel_name.
*
* @var list<string>
*/
public const RESERVED_CHANNEL_NAMES = [
'admin', 'user', 'api', 'web', 'sync', 'details', 'weifile', 'hooks',
'link', 'statistic', 'vhx', 'event', 'log', 'storage', 'build', 'hot',
'vendor', 'css', 'js', 'up', 'index', 'assets', 'static', 'source', 'channel',
'out', 't', 'a', 'u', 'uj', 'us', 'ub', 'ba', 'result', 'favicon',
'robots', 'sitemap', 'public', 'app', 'bootstrap', 'config',
'database', 'resources', 'routes', 'tests', 'artisan', 'livewire',
'sanctum', 'telescope', 'horizon', 'pulse',
];
protected $fillable = [
'channel_id', 'builder_type', 'channel_name', 'user_id', 'domains', 'status', 'remark',
];
protected $attributes = [
'builder_type' => self::BUILDER_OLD,
];
protected function casts(): array
{
return [
'domains' => 'array',
'status' => 'integer',
'user_id' => 'integer',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function isEnabled(): bool
{
return (int) $this->status === 1;
}
public function isOfficial(): bool
{
return (int) $this->user_id === self::OFFICIAL_USER_ID;
}
public function isNewBuilder(): bool
{
return $this->builderType() === self::BUILDER_NEW;
}
public function builderType(): string
{
$type = strtolower(trim((string) ($this->builder_type ?? '')));
return $type === self::BUILDER_NEW ? self::BUILDER_NEW : self::BUILDER_OLD;
}
public function agentLabel(): string
{
if ($this->isOfficial()) {
return '官方';
}
return $this->user?->username ?: ('#'.$this->user_id);
}
/**
* Domains for support-link display.
* Channel row wins; otherwise CORUNA_LAB_CHANNEL_DOMAINS (system settings).
*
* @return list<string>
*/
public function resolvedDomains(): array
{
$stored = $this->normalizeDomainList($this->domains);
if ($stored !== []) {
return $stored;
}
return $this->normalizeDomainList(config('coruna.channel_domains', []));
}
/**
* @param mixed $domains
* @return list<string>
*/
public static function normalizeDomainList(mixed $domains): array
{
if (! is_array($domains)) {
return [];
}
return array_values(array_unique(array_filter(array_map(static function ($d) {
if (! is_string($d)) {
return '';
}
$d = trim($d);
$d = preg_replace('#^https?://#i', '', $d) ?? $d;
return rtrim($d, '/');
}, $domains))));
}
public function landingPath(): string
{
if ($this->isNewBuilder()) {
return '/weifile/weifile.html';
}
return '/web/'.$this->channel_id.'/support.html';
}
/**
* @return list<string>
*/
public function supportLinks(): array
{
$links = [];
$path = $this->landingPath();
$scheme = trim((string) config('coruna.static_site.scheme', 'https')) ?: 'https';
foreach ($this->resolvedDomains() as $domain) {
$origin = rtrim($domain, '/');
if ($origin === '') {
continue;
}
if (! preg_match('#^https?://#i', $origin)) {
$origin = $scheme.'://'.$origin;
}
$links[] = $origin.$path;
}
return array_values(array_unique($links));
}
public static function randomChannelId(): string
{
return bin2hex(random_bytes(16));
}
public static function isReservedChannelName(string $name): bool
{
return in_array(strtolower($name), self::RESERVED_CHANNEL_NAMES, true);
}
public static function randomChannelName(): string
{
for ($i = 0; $i < 32; $i++) {
$name = substr(bin2hex(random_bytes(5)), 0, self::CHANNEL_NAME_LENGTH);
if (self::isReservedChannelName($name)) {
continue;
}
if (self::query()->where('channel_name', $name)->exists()) {
continue;
}
$public = public_path('source/'.$name);
if (is_dir($public) || is_file($public)) {
continue;
}
return $name;
}
throw new RuntimeException('无法生成唯一 channel_name');
}
public static function maxPerAgent(): int
{
$n = (int) config('coruna.channels.max_per_agent', 5);
return max(1, $n);
}
}