Skip to content

Commit

Permalink
Determine asset content-type from extension (#163)
Browse files Browse the repository at this point in the history
* tweak: disable deprecations dammit

* fix: add Asset::getContentType() so as to stop using octet-stream

* feat: add handy bin/dbsh script alternative to 'php artisan db'

* fix: loosen up slug pattern
  • Loading branch information
chuckadams authored Feb 12, 2025
1 parent 9b3f07b commit 1fa094e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 8 deletions.
18 changes: 18 additions & 0 deletions app/Models/WpOrg/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Enums\AssetType;
use App\Events\AssetCreated;
use App\Utils\Regex;
use Carbon\CarbonImmutable;
use Database\Factories\WpOrg\AssetFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
Expand Down Expand Up @@ -54,4 +55,21 @@ class Asset extends Model
protected $attributes = [
'repository' => 'wp_org', // this is the db default as well since 0.9.3, but it's handy to have it here too
];

public function getContentType(): string
{
$matches = Regex::match('/^.+\.(\w+)$/', $this->local_path);
if (!$matches) {
return 'application/octet-stream';
}
// hardly exhaustive, but enough to cover our assets ;)
return match ($matches[1]) {
'zip' => 'application/zip',
'png' => 'image/png',
'jpg', 'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'svg' => 'image/svg+xml',
default => 'application/octet-stream',
};
}
}
20 changes: 18 additions & 2 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
<?php

// This one file should contain all boot-time actions relevant to the app.
// There should be no need to create more ServiceProviders, unless for an actually separable concern.

namespace App\Providers;

use App\Auth\Role;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
//
// Put only DI container bindings here, if and when they're needed.
}

public function boot(): void
{
$isDev = !$this->app->isProduction();
if (!config('app.report_deprecations')) {
// Both Laravel and Symfony try to force error_reporting(-1) with no way out, and it's super annoying.
error_reporting(E_ALL & ~E_DEPRECATED);
}

// SSL termination means $request->getScheme() is always 'http', so prevent mixed content problems here.
if (str_starts_with(config('app.url'), 'https')) {
URL::forceScheme('https');
}

$isDev = !$this->app->isProduction();
Model::preventLazyLoading($isDev);
Model::preventSilentlyDiscardingAttributes($isDev);

// SuperAdmins bypass all auth checks
Gate::before(fn(User $user) => $user->hasRole(Role::SuperAdmin) ?: null);

// make a vague stab at functional abstraction ;)
$this->bootRoutes();
}

private function bootRoutes(): void
{
Route::pattern('slug', '[-_A-Za-z0-9]+'); // underscore is uncommon, but some assets do use it.
}
}
2 changes: 1 addition & 1 deletion app/Services/Downloads/DownloadService.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function download(AssetType $type, string $slug, string $file, ?string $r
$stream = Storage::disk('s3')->getDriver()->readStream($asset->local_path);
return response()->stream(
fn() => fpassthru($stream),
headers: ['Content-Type' => 'application/octet-stream'],
headers: ['Content-Type' => $asset->getContentType()],
);
}

Expand Down
21 changes: 21 additions & 0 deletions bin/dbsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

cd $(dirname $0)/..

# allow real environment variables to override .env
orig_pgdatabase=${PGDATABASE:-$DB_DATABASE}
orig_pghost=${PGHOST:-$DB_HOST}
orig_pgpassword=${PGPASSWORD:-$DB_PASSWORD}
orig_pgport=${PGPORT:-$DB_PORT}
orig_pguser=${PGUSER:-$DB_USERNAME}

[[ -f .env ]] && source .env

# note we don't respect PG* variables set in .env, since it should only have variables used by Laravel
export PGDATABASE=${orig_pgdatabase:-$DB_DATABASE}
export PGHOST=${orig_pghost:-$DB_HOST}
export PGPASSWORD=${orig_pgpassword:-$DB_PASSWORD}
export PGPORT=${orig_pgport:-$DB_PORT}
export PGUSER=${orig_pguser:-$DB_USERNAME}

exec psql "$@"
10 changes: 5 additions & 5 deletions routes/inc/download.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,26 @@

$router
->get('/download/plugin/{file}', DownloadPluginController::class)
->where('file', '.+\.zip')
->where(['file' => '.+\.zip'])
->name('download.plugin');

$router
->get('/download/theme/{file}', DownloadThemeController::class)
->where('file', '.+\.zip')
->where(['file' => '.+\.zip'])
->name('download.theme');

$router
->get('/download/assets/plugin/{slug}/{revision}/{file}', DownloadPluginAssetController::class)
->where(['slug' => '[a-zA-Z0-9-]+', 'file' => '.+'])
->where(['file' => '.+'])
->name('download.plugin.asset');

$router
->get('/download/gp-icon/plugin/{slug}/{revision}/{file}', DownloadPluginIconController::class)
->where(['slug' => '[a-zA-Z0-9-]+', 'file' => '.+'])
->where(['file' => '.+'])
->name('download.plugin.gp-icon');

$router
->get('/download/assets/theme/{slug}/{revision}/{file}', DownloadThemeScreenshotController::class)
->where(['slug' => '[a-zA-Z0-9-]+', 'file' => '.+'])
->where(['file' => '.+'])
->name('download.theme.asset');
});

0 comments on commit 1fa094e

Please sign in to comment.