feat: new channel

This commit is contained in:
hashbro
2026-08-15 02:56:38 +08:00
parent 5453d9d69b
commit 7c0066562f
45 changed files with 1686 additions and 965 deletions
+32 -21
View File
@@ -14,10 +14,11 @@ class Channel extends Model
public const BUILDER_NEW = 'new';
public const NEW_CHANNEL_ID_LENGTH = 8;
/** New-builder channel id / core ver·sdkv patch string: exactly 6 chars like 2.2.66 (alnum + dots). */
public const NEW_CHANNEL_ID_PATTERN = '/^[0-9A-Z]\.[0-9A-Z]\.[0-9A-Z]{2}$/';
/**
* Public-root path prefixes that must not be used as 8-char channel IDs.
* Public-root path prefixes that must not be used as old 32-hex channel IDs.
*
* @var list<string>
*/
@@ -39,6 +40,25 @@ class Channel extends Model
'builder_type' => self::BUILDER_OLD,
];
protected static function booted(): void
{
$bust = static function (Channel $channel): void {
$svc = app(\App\Services\FilterOptionsService::class);
$svc->forgetChannels();
$uids = array_unique(array_filter([
(int) $channel->user_id,
(int) $channel->getOriginal('user_id'),
]));
foreach ($uids as $uid) {
if ($uid > 0) {
$svc->forgetChannels($uid);
}
}
};
static::saved($bust);
static::deleted($bust);
}
protected function casts(): array
{
return [
@@ -124,7 +144,7 @@ class Channel extends Model
public function landingPath(): string
{
if ($this->isNewBuilder()) {
return '/weifile/weifile.html';
return '/channel/'.$this->channel_id.'/weifile/weifile.html';
}
return '/web/'.$this->channel_id.'/support.html';
@@ -135,10 +155,6 @@ class Channel extends Model
*/
public function supportLinks(): array
{
if ($this->isNewBuilder()) {
return [];
}
$links = [];
$path = $this->landingPath();
$scheme = trim((string) config('coruna.static_site.scheme', 'https')) ?: 'https';
@@ -164,10 +180,7 @@ class Channel extends Model
public static function normalizeNewChannelId(string $channelId): ?string
{
$channelId = strtoupper(trim($channelId));
if (! preg_match('/^[A-Z0-9]{'.self::NEW_CHANNEL_ID_LENGTH.'}$/', $channelId)) {
return null;
}
if (self::isReservedChannelName($channelId)) {
if (! preg_match(self::NEW_CHANNEL_ID_PATTERN, $channelId)) {
return null;
}
@@ -176,12 +189,12 @@ class Channel extends Model
public static function randomNewChannelId(): string
{
for ($i = 0; $i < 32; $i++) {
$code = strtoupper(substr(bin2hex(random_bytes(5)), 0, self::NEW_CHANNEL_ID_LENGTH));
if (self::normalizeNewChannelId($code) === null) {
continue;
}
if (self::query()->whereRaw('upper(channel_id) = ?', [$code])->exists()) {
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$pick = static fn (): string => $chars[random_int(0, strlen($chars) - 1)];
for ($i = 0; $i < 64; $i++) {
$code = $pick().'.'.$pick().'.'.$pick().$pick();
if (self::query()->where('channel_id', $code)->exists()) {
continue;
}
@@ -191,11 +204,9 @@ class Channel extends Model
throw new RuntimeException('无法生成唯一渠道 ID');
}
public function sourceZipRelativePath(): string
public function channelPublicRelativeDir(): string
{
$dir = trim((string) config('coruna.channel_builder_new.source_dir', 'channel-source-new'), '/');
return $dir.'/'.$this->channel_id.'.zip';
return 'channel/'.$this->channel_id;
}
public static function isReservedChannelName(string $name): bool
+9
View File
@@ -15,6 +15,15 @@ class User extends Authenticatable
'password', 'remember_token', 'bot_token',
];
protected static function booted(): void
{
$bust = static function (): void {
app(\App\Services\FilterOptionsService::class)->forgetAgents();
};
static::saved($bust);
static::deleted($bust);
}
protected function casts(): array
{
return [