Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
11a1581
Update Node.js package dependencies (#2329)
notAreYouScared May 11, 2026
92bfccb
Fix multi file upload (#2331)
rxtted May 19, 2026
8f7dbe5
move some hardcoded ratelimits for the api to env vars (#2332)
O2theC May 19, 2026
dcba4a9
Listen for `install completed` event (#2345)
notAreYouScared May 20, 2026
81bb09f
Add option to disable password login (#2318)
karaolidis May 20, 2026
110fdd2
Laravel 13.11.2 Shift (#2348)
lancepioch May 20, 2026
8f01095
Update readme.md (#2351)
lancepioch May 21, 2026
a99f18b
chore(i18n): Automate Crowdin source sync and translation pull workfl…
apple050620312 May 22, 2026
447a8c1
ci: Fix Crowdin synchronization paths and optimize configs (#2354)
apple050620312 May 26, 2026
13bb47e
Make sure plugin name doesn't contain path when importing (#2356)
Boy132 May 26, 2026
cb21f13
composer upgrade (#2359)
notAreYouScared May 27, 2026
f676b6c
Add error handling when trying to get extension from url (#2361)
Boy132 May 28, 2026
02e4527
Replace hardcoded `pelican` with config value (#2362)
Boy132 May 28, 2026
2a328a2
Improve Global Searching in admin panel (#2327)
notAreYouScared May 28, 2026
be6abce
Add `acceptedFileTypes` to font upload and add can check to save sett…
Boy132 Jun 2, 2026
28452e2
Return empty array instead of null when plugin update checking fails …
Boy132 Jun 2, 2026
1362242
Fix dark mode (#2373)
hUwUtao Jun 3, 2026
4d11457
Laravel 13.14.0 Shift (#2374)
lancepioch Jun 8, 2026
b3fe96f
docker: Update php to 8.5 and node to 22 (#2380)
QuintenQVD0 Jun 8, 2026
f791a3a
new api settings ui stuff (#2350)
O2theC Jun 9, 2026
a46443e
restore daemon error surface for wings 4xx (#2325)
rxtted Jun 9, 2026
ac3db5a
Add back `bg-white` to small stat (#2381)
Boy132 Jun 9, 2026
90fbc87
Fix sed command syntax in docker-publish.yml (#2372)
hUwUtao Jun 9, 2026
1bd4492
Laravel 13.15.0 Shift (#2382)
lancepioch Jun 9, 2026
c639234
merge upstream pelican sync through laravel 13.15
rxtted Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN composer install --no-dev --no-interaction --no-autoloader --no-scripts
# ================================
# Stage 1-2: Yarn Install
# ================================
FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
FROM --platform=$TARGETOS/$TARGETARCH node:22-alpine AS yarn

WORKDIR /build

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.base
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ================================
# Stage 0: Build PHP Base Image
# ================================
FROM --platform=$TARGETOS/$TARGETARCH php:8.4-fpm-alpine
FROM --platform=$TARGETOS/$TARGETARCH php:8.5-fpm-alpine

ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker.io/docker/dockerfile:1.13-labs
# Pelican Development Dockerfile

FROM --platform=$TARGETOS/$TARGETARCH php:8.4-fpm-alpine AS base
FROM --platform=$TARGETOS/$TARGETARCH php:8.5-fpm-alpine AS base

ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

Expand All @@ -26,7 +26,7 @@ RUN composer install --no-dev --no-interaction --no-autoloader --no-scripts
# ================================
# Stage 1-2: Yarn Install
# ================================
FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
FROM --platform=$TARGETOS/$TARGETARCH node:22-alpine AS yarn

WORKDIR /build

Expand Down
9 changes: 7 additions & 2 deletions app/Console/Commands/Egg/CheckEggUpdatesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ private function check(Egg $egg, EggExporterService $exporterService): void
return;
}

$ext = strtolower(pathinfo(parse_url($egg->update_url, PHP_URL_PATH), PATHINFO_EXTENSION));
$isYaml = in_array($ext, ['yaml', 'yml']);
$extension = strtolower(pathinfo(parse_url($egg->update_url, PHP_URL_PATH), PATHINFO_EXTENSION));

if (empty($extension)) {
throw new Exception('Unsupported file format.');
}

$isYaml = in_array($extension, ['yaml', 'yml']);

$local = $isYaml
? Yaml::parse($exporterService->handle($egg->id, EggFormat::YAML))
Expand Down
12 changes: 6 additions & 6 deletions app/Enums/ResourceLimit.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ public function middleware(): string
public function limit(): Limit
{
return match ($this) {
self::Websocket => Limit::perMinute(5),
self::BackupRestore => Limit::perMinutes(15, 3),
self::DatabaseCreate => Limit::perMinute(2),
self::SubuserCreate => Limit::perMinutes(15, 10),
self::FilePull => Limit::perMinutes(10, 5),
default => Limit::perMinute(2),
self::Websocket => Limit::perMinutes(config('http.rate_limit.websocket_period'), config('http.rate_limit.websocket')),
self::BackupRestore => Limit::perMinutes(config('http.rate_limit.backup_restore_period'), config('http.rate_limit.backup_restore')),
self::DatabaseCreate => Limit::perMinutes(config('http.rate_limit.database_create_period'), config('http.rate_limit.database_create')),
self::SubuserCreate => Limit::perMinutes(config('http.rate_limit.subuser_create_period'), config('http.rate_limit.subuser_create')),
self::FilePull => Limit::perMinutes(config('http.rate_limit.file_pull_period'), config('http.rate_limit.file_pull')),
default => Limit::perMinutes(config('http.rate_limit.default_period'), config('http.rate_limit.default')),
};
}

Expand Down
2 changes: 1 addition & 1 deletion app/Filament/Admin/Pages/Dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function getColumns(): int|array

public function getHeading(): string
{
return trans('admin/dashboard.heading');
return trans('admin/dashboard.heading', ['app' => config('app.name')]);
}

public function getSubheading(): string
Expand Down
188 changes: 170 additions & 18 deletions app/Filament/Admin/Pages/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
use Filament\Pages\Page;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\Component;
use Filament\Schemas\Components\Fieldset;
use Filament\Schemas\Components\Group;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\StateCasts\BooleanStateCast;
use Filament\Schemas\Components\Tabs;
use Filament\Schemas\Components\Tabs\Tab;
use Filament\Schemas\Components\Text;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Contracts\HasSchemas;
Expand Down Expand Up @@ -794,24 +796,172 @@ private function miscSettings(): array
]),
Section::make(trans('admin/setting.misc.api.title'))
->description(trans('admin/setting.misc.api.helper'))
->columns()
->columns(1)
->collapsible()
->collapsed()
->schema([
TextInput::make('APP_API_CLIENT_RATELIMIT')
->label(trans('admin/setting.misc.api.client_rate'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.rpm'))
->default(env('APP_API_CLIENT_RATELIMIT', config('http.rate_limit.client'))),
TextInput::make('APP_API_APPLICATION_RATELIMIT')
->label(trans('admin/setting.misc.api.app_rate'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.rpm'))
->default(env('APP_API_APPLICATION_RATELIMIT', config('http.rate_limit.application'))),
Fieldset::make()
->label(trans('admin/setting.misc.api.client_title'))
->schema([
TextInput::make('APP_API_CLIENT_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_CLIENT_RATELIMIT', config('http.rate_limit.client'))),

TextInput::make('APP_API_CLIENT_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_CLIENT_RATELIMIT_PERIOD', config('http.rate_limit.client_period'))),
Text::make(trans('admin/setting.misc.api.client_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.application_title'))
->schema([
TextInput::make('APP_API_APPLICATION_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_APPLICATION_RATELIMIT', config('http.rate_limit.application'))),

TextInput::make('APP_API_APPLICATION_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_APPLICATION_RATELIMIT_PERIOD', config('http.rate_limit.application_period'))),
Text::make(trans('admin/setting.misc.api.application_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.password_reset_title'))
->schema([
TextInput::make('APP_API_PASSWORD_RESET_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_PASSWORD_RESET_RATELIMIT', config('http.rate_limit.password_reset'))),

TextInput::make('APP_API_PASSWORD_RESET_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_PASSWORD_RESET_RATELIMIT_PERIOD', config('http.rate_limit.password_reset_period'))),
Text::make(trans('admin/setting.misc.api.password_reset_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.websocket_title'))
->schema([
TextInput::make('APP_API_WEBSOCKET_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_WEBSOCKET_RATELIMIT', config('http.rate_limit.websocket'))),

TextInput::make('APP_API_WEBSOCKET_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_WEBSOCKET_RATELIMIT_PERIOD', config('http.rate_limit.websocket_period'))),
Text::make(trans('admin/setting.misc.api.websocket_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.backup_restore_title'))
->schema([
TextInput::make('APP_API_BACKUP_RESTORE_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_BACKUP_RESTORE_RATELIMIT', config('http.rate_limit.backup_restore'))),

TextInput::make('APP_API_BACKUP_RESTORE_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_BACKUP_RESTORE_RATELIMIT_PERIOD', config('http.rate_limit.backup_restore_period'))),
Text::make(trans('admin/setting.misc.api.backup_restore_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.database_create_title'))
->schema([
TextInput::make('APP_API_DATABASE_CREATE_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_DATABASE_CREATE_RATELIMIT', config('http.rate_limit.database_create'))),

TextInput::make('APP_API_DATABASE_CREATE_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_DATABASE_CREATE_RATELIMIT_PERIOD', config('http.rate_limit.database_create_period'))),
Text::make(trans('admin/setting.misc.api.database_create_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.subuser_create_title'))
->schema([
TextInput::make('APP_API_SUBUSER_CREATE_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_SUBUSER_CREATE_RATELIMIT', config('http.rate_limit.subuser_create'))),

TextInput::make('APP_API_SUBUSER_CREATE_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_SUBUSER_CREATE_RATELIMIT_PERIOD', config('http.rate_limit.subuser_create_period'))),
Text::make(trans('admin/setting.misc.api.subuser_create_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.file_pull_title'))
->schema([
TextInput::make('APP_API_FILE_PULL_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_FILE_PULL_RATELIMIT', config('http.rate_limit.file_pull'))),

TextInput::make('APP_API_FILE_PULL_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_FILE_PULL_RATELIMIT_PERIOD', config('http.rate_limit.file_pull_period'))),
Text::make(trans('admin/setting.misc.api.file_pull_helper')),
]),
Fieldset::make()
->label(trans('admin/setting.misc.api.default_title'))
->schema([
TextInput::make('APP_API_DEFAULT_RATELIMIT')
->label(trans('admin/setting.misc.api.requests_per_period'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.requests'))->default(env('APP_API_DEFAULT_RATELIMIT', config('http.rate_limit.default'))),

TextInput::make('APP_API_DEFAULT_RATELIMIT_PERIOD')
->label(trans('admin/setting.misc.api.period_length'))
->required()
->numeric()
->minValue(1)
->suffix(trans('admin/setting.misc.api.minutes'))->default(env('APP_API_DEFAULT_RATELIMIT_PERIOD', config('http.rate_limit.default_period'))),
Text::make(trans('admin/setting.misc.api.default_helper')),
]),
]),
Section::make(trans('admin/setting.misc.server.title'))
->description(trans('admin/setting.misc.server.helper'))
Expand All @@ -830,13 +980,14 @@ private function miscSettings(): array
->formatStateUsing(fn ($state): bool => (bool) $state)
->afterStateUpdated(fn ($state, Set $set) => $set('PANEL_EDITABLE_SERVER_DESCRIPTIONS', (bool) $state))
->default(env('PANEL_EDITABLE_SERVER_DESCRIPTIONS', config('panel.editable_server_descriptions'))),
FileUpload::make('ConsoleFonts')
FileUpload::make('console_font')
->hint(trans('admin/setting.misc.server.console_font_hint'))
->label(trans('admin/setting.misc.server.console_font_upload'))
->directory('fonts')
->disk('public')
->columnSpan(1)
->maxFiles(1)
->acceptedFileTypes(['font/*'])
->preserveFilenames(),
]),
Section::make(trans('admin/setting.misc.webhook.title'))
Expand Down Expand Up @@ -864,9 +1015,11 @@ protected function getFormStatePath(): ?string

public function save(): void
{
abort_unless(user()?->can('update settings'), 403);

try {
$data = $this->form->getState();
unset($data['ConsoleFonts']);
unset($data['console_font']);

$data = array_map(function ($value) {
// Convert bools to a string, so they are correctly written to the .env file
Expand Down Expand Up @@ -913,6 +1066,5 @@ protected function getDefaultHeaderActions(): array
->authorize(fn () => user()?->can('update settings'))
->keyBindings(['mod+s']),
];

}
}
4 changes: 3 additions & 1 deletion app/Filament/Admin/Resources/Eggs/Pages/ListEggs.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ class ListEggs extends ListRecords
*/
public function table(Table $table): Table
{
$defaultEggIcon = 'data:image/svg+xml;base64,' . base64_encode(file_get_contents(public_path('pelican.svg')));
$defaultEggIcon = config('app.logo');
$defaultEggIcon = empty($defaultEggIcon) || !is_file(public_path($defaultEggIcon)) ? 'pelican.svg' : $defaultEggIcon;
$defaultEggIcon = 'data:image/svg+xml;base64,' . base64_encode(file_get_contents(public_path($defaultEggIcon)));

return $table
->searchable(true)
Expand Down
2 changes: 1 addition & 1 deletion app/Filament/Admin/Resources/Plugins/PluginResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public static function table(Table $table): Table
/** @var UploadedFile $file */
$file = $data['file'];

$pluginName = str($file->getClientOriginalName())->before('.zip')->toString();
$pluginName = str($file->getClientOriginalName())->basename()->before('.zip')->toString();

if (Plugin::where('id', $pluginName)->exists()) {
throw new Exception(trans('admin/plugin.notifications.import_exists'));
Expand Down
4 changes: 4 additions & 0 deletions app/Filament/Components/Actions/UploadIcon.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ protected function setUp(): void

$extension = strtolower(pathinfo(parse_url($data['icon_url'], PHP_URL_PATH), PATHINFO_EXTENSION));

if (empty($extension)) {
throw new Exception(trans('admin/egg.import.invalid_url'));
}

$record->writeIcon($extension, $content);

Notification::make()
Expand Down
Loading
Loading