Merge pull request #6469 from pixelfed/staging

Staging
pull/6474/head v0.12.7
dansup 5 months ago committed by GitHub
commit e33026a9e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,12 +1,20 @@
# Release Notes
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.12.5...dev)
- ([](https://github.com/pixelfed/pixelfed/commit/))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.12.7 (2026-02-17)](https://github.com/pixelfed/pixelfed/compare/v0.12.7...dev)
- Update Status storage, add SanitizerService to fix spacing in html stripped content ([3686c9212](https://github.com/pixelfed/pixelfed/commit/3686c9212))
- Update app config, add description and rule env variables ([0980519a9](https://github.com/pixelfed/pixelfed/commit/0980519a9))
- Update InstanceService, fix total post count when config_cache is disabled ([f0bc9d66e](https://github.com/pixelfed/pixelfed/commit/f0bc9d66e))
- Update media storage pipeline, improve support for non-local filesystems ([2e719bd00](https://github.com/pixelfed/pixelfed/commit/2e719bd00))
- ([](https://github.com/pixelfed/pixelfed/commit/))
- Update partial status updates nullify omitted caption/CW causing data loss) ([416c02e](https://github.com/pixelfed/pixelfed/commit/416c02e))
- Update compose, improve validation ([f1af72e](https://github.com/pixelfed/pixelfed/commit/f1af72e))
- Update ImportMediaToCloudPipeline to handle multiple files being uploaded ([bff9aae](https://github.com/pixelfed/pixelfed/commit/bff9aae))
- InboxWorker and InboxValidator moved lock after the signature validation ([4b923ed](https://github.com/pixelfed/pixelfed/commit/4b923ed))
- Update sendmail security ([5bc768b](https://github.com/pixelfed/pixelfed/commit/5bc768b))
## [v0.12.6 (2025-09-03)](https://github.com/pixelfed/pixelfed/compare/v0.12.6...dev)

@ -165,13 +165,11 @@ class ComposeController extends Controller
{
$this->validate($request, [
'id' => 'required',
'file' => function () {
return [
'required',
'mimetypes:'.config_cache('pixelfed.media_types'),
'max:'.config_cache('pixelfed.max_photo_size'),
];
},
'file' => [
'required',
'mimetypes:'.config_cache('pixelfed.media_types'),
'max:'.config_cache('pixelfed.max_photo_size'),
],
]);
$user = Auth::user();

@ -434,13 +434,11 @@ class DirectMessageController extends Controller
public function mediaUpload(Request $request)
{
$this->validate($request, [
'file' => function () {
return [
'required',
'mimetypes:'.config_cache('pixelfed.media_types'),
'max:'.config_cache('pixelfed.max_photo_size'),
];
},
'file' => [
'required',
'mimetypes:'.config_cache('pixelfed.media_types'),
'max:'.config_cache('pixelfed.max_photo_size'),
],
'to_id' => 'required',
]);

@ -264,13 +264,11 @@ class StoryApiV1Controller extends Controller
abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404);
$this->validate($request, [
'file' => function () {
return [
'required',
'mimetypes:image/jpeg,image/jpg,image/png,video/mp4',
'max:'.config_cache('pixelfed.max_photo_size'),
];
},
'file' => [
'required',
'mimetypes:image/jpeg,image/jpg,image/png,video/mp4',
'max:'.config_cache('pixelfed.max_photo_size'),
],
'duration' => 'sometimes|integer|min:0|max:30',
]);

@ -41,13 +41,11 @@ class StoryComposeController extends Controller
abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404);
$this->validate($request, [
'file' => function () {
return [
'required',
'mimetypes:image/jpeg,image/png,video/mp4,image/jpg',
'max:'.config_cache('pixelfed.max_photo_size'),
];
},
'file' => [
'required',
'mimetypes:image/jpeg,image/png,video/mp4,image/jpg',
'max:'.config_cache('pixelfed.max_photo_size'),
],
]);
$user = $request->user();

@ -134,7 +134,7 @@ class DeleteRemoteProfilePipeline implements ShouldQueue
});
// Delete reports
Report::whereProfileId($profile->id)->orWhere('reported_profile_id')->forceDelete();
Report::whereProfileId($pid)->orWhere('reported_profile_id', $pid)->forceDelete();
// Delete profile
Profile::findOrFail($profile->id)->delete();

@ -118,8 +118,7 @@ class FeedInsertPipeline implements ShouldBeUniqueUntilProcessing, ShouldQueue
$filters = UserFilter::whereFilterableType('App\Profile')
->whereFilterableId($status['account']['id'])
->whereIn('filter_type', ['mute', 'block'])
->join('profiles', 'user_filters.user_id', '=', 'profiles.user_id')
->pluck('profiles.id')
->pluck('user_id')
->toArray();
if ($filters && count($filters)) {

@ -116,8 +116,7 @@ class FeedInsertRemotePipeline implements ShouldBeUniqueUntilProcessing, ShouldQ
$filters = UserFilter::whereFilterableType('App\Profile')
->whereFilterableId($status['account']['id'])
->whereIn('filter_type', ['mute', 'block'])
->join('profiles', 'user_filters.user_id', '=', 'profiles.user_id')
->pluck('profiles.id')
->pluck('user_id')
->toArray();
if ($filters && count($filters)) {

@ -116,8 +116,7 @@ class HashtagInsertFanoutPipeline implements ShouldBeUniqueUntilProcessing, Shou
$filters = UserFilter::whereFilterableType('App\Profile')
->whereFilterableId($status['account']['id'])
->whereIn('filter_type', ['mute', 'block'])
->join('profiles', 'user_filters.user_id', '=', 'profiles.user_id')
->pluck('profiles.id')
->pluck('user_id')
->toArray();
if ($filters && count($filters)) {

@ -69,6 +69,8 @@ class ImageThumbnail implements ShouldQueue
if (config('app.dev_log')) {
Log::error('Thumbnail generation failed: '.$e->getMessage());
}
return;
}
$media->processed_at = Carbon::now();

@ -93,32 +93,46 @@ class ImportMediaToCloudPipeline implements ShouldBeUniqueUntilProcessing, Shoul
return;
}
$allSucceeded = true;
foreach ($media as $mediaPart) {
$this->handleMedia($mediaPart);
if (! $this->handleMedia($mediaPart)) {
$allSucceeded = false;
}
}
if ($allSucceeded) {
$importPost = ImportPost::find($ip->id);
if ($importPost) {
$importPost->uploaded_to_s3 = true;
$importPost->save();
}
}
}
protected function handleMedia($media)
{
// Skip media already uploaded to cloud storage
if ($media->cdn_url) {
return true;
}
$ip = $this->importPost;
$importPost = ImportPost::find($ip->id);
if (! $importPost) {
return;
return false;
}
$res = MediaStorageService::move($media);
$importPost->uploaded_to_s3 = true;
$importPost->save();
if (! $res) {
return;
return false;
}
if ($res === 'invalid file') {
return;
return false;
}
if ($res === 'success') {
@ -127,6 +141,10 @@ class ImportMediaToCloudPipeline implements ShouldBeUniqueUntilProcessing, Shoul
} else {
Storage::disk('local')->delete($media->media_path);
}
return true;
}
return false;
}
}

@ -59,15 +59,6 @@ class InboxValidator implements ShouldQueue
$payload = json_decode($this->payload, true, 8);
if (isset($payload['id'])) {
$lockKey = 'pf:ap:user-inbox:activity:'.hash('sha256', $payload['id']);
if (Cache::get($lockKey) !== null) {
// Job processed already
return 1;
}
Cache::put($lockKey, 1, 3600);
}
$profile = Profile::whereNull('domain')->whereUsername($username)->first();
if (empty($profile) || empty($headers) || empty($payload)) {
@ -79,6 +70,14 @@ class InboxValidator implements ShouldQueue
}
if ($this->verifySignature($headers, $profile, $payload) == true) {
if (isset($payload['id'])) {
$lockKey = 'pf:ap:user-inbox:activity:'.hash('sha256', $payload['id']);
if (! Cache::add($lockKey, 1, 3600)) {
// Already processed after valid signature check
return 1;
}
}
if (isset($payload['type']) && in_array($payload['type'], ['Follow', 'Accept'])) {
ActivityHandler::dispatch($headers, $profile, $payload)->onQueue('follow');
} else {

@ -55,16 +55,15 @@ class InboxWorker implements ShouldQueue
$payload = json_decode($this->payload, true, 8);
if (isset($payload['id'])) {
$lockKey = 'pf:ap:user-inbox:activity:'.hash('sha256', $payload['id']);
if (Cache::get($lockKey) !== null) {
// Job processed already
return 1;
if ($this->verifySignature($headers, $payload) == true) {
if (isset($payload['id'])) {
$lockKey = 'pf:ap:user-inbox:activity:'.hash('sha256', $payload['id']);
if (! Cache::add($lockKey, 1, 3600)) {
// Already processed after valid signature check
return 1;
}
}
Cache::put($lockKey, 1, 3600);
}
if ($this->verifySignature($headers, $payload) == true) {
ActivityHandler::dispatch($headers, $profile, $payload)->onQueue('shared');
return;

@ -9,6 +9,7 @@ use App\Services\StatusService;
use App\Util\Media\Blurhash;
use Cache;
use FFMpeg;
use Log;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -107,7 +108,11 @@ class VideoThumbnail implements ShouldBeUniqueUntilProcessing, ShouldQueue
VideoHlsPipeline::dispatch($media)->onQueue('mmo');
}
} catch (\Exception $e) {
if (config('app.dev_log')) {
Log::error('Video thumbnail generation failed: '.$e->getMessage());
}
throw $e;
}
if ($media->status_id) {

@ -62,7 +62,7 @@ class HashtagFollowService
public static function isWarm($hid)
{
return Redis::zcount(self::CACHE_KEY.$hid, 0, -1) ?? Redis::zscore(self::CACHE_WARMED, $hid) != null;
return Redis::zcard(self::CACHE_KEY.$hid) > 0 || Redis::zscore(self::CACHE_WARMED, $hid) !== null;
}
public static function setWarm($hid)

@ -65,8 +65,6 @@ class UpdateStatusService
if (isset($attributes['status'])) {
$cleaned = Purify::clean($attributes['status']);
$status->caption = $cleaned;
} else {
$status->caption = null;
}
if (isset($attributes['sensitive'])) {
if ($status->is_nsfw != (bool) $attributes['sensitive'] &&
@ -84,8 +82,6 @@ class UpdateStatusService
}
if (isset($attributes['spoiler_text'])) {
$status->cw_summary = Purify::clean($attributes['spoiler_text']);
} else {
$status->cw_summary = null;
}
if (isset($attributes['location'])) {
if (isset($attributes['location']['id'])) {
@ -119,8 +115,8 @@ class UpdateStatusService
public static function createEdit(Status $status, $attributes)
{
$cleaned = isset($attributes['status']) ? Purify::clean($attributes['status']) : null;
$spoiler_text = isset($attributes['spoiler_text']) ? Purify::clean($attributes['spoiler_text']) : null;
$cleaned = isset($attributes['status']) ? Purify::clean($attributes['status']) : $status->caption;
$spoiler_text = isset($attributes['spoiler_text']) ? Purify::clean($attributes['spoiler_text']) : $status->cw_summary;
$sensitive = isset($attributes['sensitive']) ? $attributes['sensitive'] : null;
$mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null;
StatusEdit::create([

@ -292,8 +292,6 @@ class Image
}
} catch (\Exception $e) {
$media->processed_at = now();
$media->save();
if (config('app.dev_log')) {
Log::info('MediaResizeException: '.$e->getMessage().' | Could not process media id: '.$media->id);
}

@ -72,7 +72,7 @@ return [
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i --'),
],
'log' => [

@ -23,7 +23,7 @@ return [
| This value is the version of your Pixelfed instance.
|
*/
'version' => '0.12.6',
'version' => '0.12.7',
/*
|--------------------------------------------------------------------------

@ -12,7 +12,7 @@ return [
'share' => 'Teilen',
'shared' => 'Geteilt',
'shares' => 'Geteilt',
'unshare' => 'Teilen rückgängig machen',
'unshare' => 'Nicht mehr teilen',
'bookmark' => 'Lesezeichen',
'cancel' => 'Abbrechen',
@ -22,12 +22,12 @@ return [
'errorMsg' => 'Etwas ist schief gelaufen. Bitter versuch es später nochmal.',
'oops' => 'Hoppla!',
'other' => 'Anderes',
'readMore' => 'Weiterlesen',
'readMore' => 'Mehr lesen',
'success' => 'Erfolg',
'proceed' => 'Fortfahren',
'next' => 'Weiter',
'close' => 'Schließen',
'clickHere' => 'hier klicken',
'clickHere' => 'Hier klicken',
'sensitive' => 'Sensibel',
'sensitiveContent' => 'Sensibler Inhalt',
@ -60,10 +60,10 @@ return [
'drive' => 'Festplatte',
'settings' => 'Einstellungen',
'compose' => 'Neu erstellen:',
'logout' => 'Ausloggen',
'logout' => 'Abmelden',
// Nav footer
'about' => 'Über uns',
'about' => 'Über',
'help' => 'Hilfe',
'language' => 'Sprache',
'privacy' => 'Privatsphäre',
@ -93,7 +93,7 @@ return [
'mentioned' => 'erwähnte',
'you' => 'dir',
'yourApplication' => 'Deine Bewerbung um beizutreten',
'yourApplication' => 'Deine Beitrittsanfrage',
'applicationApproved' => 'wurde genehmigt!',
'applicationRejected' => 'wurde abgelehnt. Du kannst dich in 6 Monaten erneut für den Beitritt bewerben.',
@ -109,13 +109,13 @@ return [
'shareToFollowers' => 'Mit Folgenden teilen',
'shareToOther' => 'Mit anderen teilen',
'noLikes' => 'Gefällt bisher noch niemandem',
'uploading' => 'Lädt hoch',
'uploading' => 'Wird hochgeladen',
],
'profile' => [
'posts' => 'Beiträge',
'followers' => 'Folgende',
'following' => 'Folgend',
'following' => 'Folgt',
'admin' => 'Admin',
'collections' => 'Sammlungen',
'follow' => 'Folgen',

@ -12,7 +12,31 @@ return [
|
*/
'failed' => 'Ces informations d\'identification ne correspondent à aucune se trouvant dans notre base de données.',
'failed' => 'Ces informations didentification ne correspondent à aucune se trouvant dans notre base de données.',
'throttle' => 'Trop de tentatives de connexion ont été effectuées. Veuillez réessayer dans :seconds secondes.',
'verifyYourEmailAddress' => ' - Vérifiez votre adresse de courrier électronique',
'loginTitle' => 'Connexion à mon compte',
'password' => 'Mot de passe' ,
'remember' => 'Se souvenir de moi',
'forgot' => 'Mot de passe oublié',
'login' => 'Se connecter',
'register' => 'Sinscrire',
'reset' => 'Réinitialiser le mot de passe',
'name' => 'Nom public',
'username' => 'Identifiant',
'confirm-password' => 'Confirmer le mot de passe',
'age' => 'Jai 16 ans ou plus',
'terms' => "En vous inscrivant, vous acceptez nos <a href=\"".route('site.terms')."\" class=\"font-weight-bold text-dark\">conditions dutilisation</a> et <a href=\"".route('site.privacy')."\" class=\"font-weight-bold text-dark\">politique de confidentialité</a>.",
'emailAddress' => "Adresse de courriel",
'registerTitle' => 'Créer un nouveau profil',
'sendReset' => 'Envoyer le lien de réinitialisation de mot de passe',
'backLogin' => 'Retour à lidentification',
'signInMastodon' => 'Sinscrire avec Mastodon',
];

Loading…
Cancel
Save