mirror of
https://github.com/pixelfed/pixelfed.git
synced 2025-01-25 05:50:45 +00:00
Merge branch 'staging' into dev
This commit is contained in:
commit
3dd515006c
200 changed files with 8540 additions and 4877 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -2,6 +2,12 @@
|
|||
|
||||
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.12.3...dev)
|
||||
|
||||
### Features
|
||||
- WebGL photo filters ([#5374](https://github.com/pixelfed/pixelfed/pull/5374))
|
||||
|
||||
### OAuth
|
||||
- Fix oauth oob (urn:ietf:wg:oauth:2.0:oob) support. ([8afbdb03](https://github.com/pixelfed/pixelfed/commit/8afbdb03))
|
||||
|
||||
### Updates
|
||||
- Update AP helpers, reject statuses with invalid dates ([960f3849](https://github.com/pixelfed/pixelfed/commit/960f3849))
|
||||
- Update DirectMessage API, fix broken threading ([044d410c](https://github.com/pixelfed/pixelfed/commit/044d410c))
|
||||
|
@ -12,6 +18,19 @@
|
|||
- Update DirectMessageController, remove 72h limit for admins ([639df410](https://github.com/pixelfed/pixelfed/commit/639df410))
|
||||
- Update StatusService, fix newlines ([56c07b7a](https://github.com/pixelfed/pixelfed/commit/56c07b7a))
|
||||
- Update confirm email template, add plaintext link. Fixes #5375 ([45986707](https://github.com/pixelfed/pixelfed/commit/45986707))
|
||||
- Update UserVerifyEmail command ([77da9ad8](https://github.com/pixelfed/pixelfed/commit/77da9ad8))
|
||||
- Update StatusStatelessTransformer, refactor the caption field to be compliant with the MastoAPI. Fixes #5364 ([79039ba5](https://github.com/pixelfed/pixelfed/commit/79039ba5))
|
||||
- Update mailgun config, add endpoint and scheme ([271d5114](https://github.com/pixelfed/pixelfed/commit/271d5114))
|
||||
- Update search and status logic to fix postgres bugs ([8c39ef4](https://github.com/pixelfed/pixelfed/commit/8c39ef4))
|
||||
- Update db, fix sqlite migrations ([#5379](https://github.com/pixelfed/pixelfed/pull/5379))
|
||||
- Update CatchUnoptimizedMedia command, make 1hr limit opt-in ([99b15b73](https://github.com/pixelfed/pixelfed/commit/99b15b73))
|
||||
- Update IG, fix Instagram import. Closes #5411 ([fd434aec](https://github.com/pixelfed/pixelfed/commit/fd434aec))
|
||||
- Update StatusTagsPipeline, fix hashtag bug and formatting ([d516b799](https://github.com/pixelfed/pixelfed/commit/d516b799))
|
||||
- Update CollectionController, fix showCollection signature ([4e1dd599](https://github.com/pixelfed/pixelfed/commit/4e1dd599))
|
||||
- Update ApiV1Dot1Controller, fix in-app registration ([56f17b99](https://github.com/pixelfed/pixelfed/commit/56f17b99))
|
||||
- Update VerifyCsrfToken middleware, add oauth token. Fixes #5426 ([79ebbc2d](https://github.com/pixelfed/pixelfed/commit/79ebbc2d))
|
||||
- Update AdminSettingsController, increase max photo size limit from 50MB to 1GB ([aa448354](https://github.com/pixelfed/pixelfed/commit/aa448354))
|
||||
- Update BearerTokenResponse, return scopes in /oauth/token endpoint. Fixes #5286 ([d8f5c302](https://github.com/pixelfed/pixelfed/commit/d8f5c302))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.12.4 (2024-11-08)](https://github.com/pixelfed/pixelfed/compare/v0.12.4...dev)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<a href="https://circleci.com/gh/pixelfed/pixelfed"><img src="https://circleci.com/gh/pixelfed/pixelfed.svg?style=svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/pixelfed/pixelfed"><img src="https://poser.pugx.org/pixelfed/pixelfed/license.svg" alt="License"></a>
|
||||
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/pixelfed"><img src="https://badges.crowdin.net/pixelfed/localized.svg"></a>
|
||||
</p>
|
||||
|
||||
## Introduction
|
||||
|
|
|
@ -11,13 +11,13 @@ class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerToke
|
|||
* AuthorizationServer::getResponseType() to pull in your version of
|
||||
* this class rather than the default.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
return [
|
||||
'scope' => array_map(fn ($scope) => $scope->getIdentifier(), $accessToken->getScopes()),
|
||||
'created_at' => time(),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -40,10 +40,11 @@ class CatchUnoptimizedMedia extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
$hasLimit = (bool) config('media.image_optimize.catch_unoptimized_media_hour_limit');
|
||||
Media::whereNull('processed_at')
|
||||
->where('created_at', '>', now()->subHours(1))
|
||||
->where('skip_optimize', '!=', true)
|
||||
->whereNull('remote_url')
|
||||
->when($hasLimit, function($q, $hasLimit) {
|
||||
$q->where('created_at', '>', now()->subHours(1));
|
||||
})->whereNull('remote_url')
|
||||
->whereNotNull('status_id')
|
||||
->whereNotNull('media_path')
|
||||
->whereIn('mime', [
|
||||
|
@ -52,6 +53,7 @@ class CatchUnoptimizedMedia extends Command
|
|||
])
|
||||
->chunk(50, function($medias) {
|
||||
foreach ($medias as $media) {
|
||||
if ($media->skip_optimize) continue;
|
||||
ImageOptimize::dispatch($media);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -600,7 +600,7 @@ trait AdminSettingsController
|
|||
$this->validate($request, [
|
||||
'image_quality' => 'required|integer|min:1|max:100',
|
||||
'max_album_length' => 'required|integer|min:1|max:20',
|
||||
'max_photo_size' => 'required|integer|min:100|max:50000',
|
||||
'max_photo_size' => 'required|integer|min:100|max:1000000',
|
||||
'media_types' => 'required',
|
||||
'optimize_image' => 'required',
|
||||
'optimize_video' => 'required',
|
||||
|
|
|
@ -137,7 +137,10 @@ class ApiV1Controller extends Controller
|
|||
'redirect_uris' => 'required',
|
||||
]);
|
||||
|
||||
$uris = implode(',', explode('\n', $request->redirect_uris));
|
||||
$uris = collect(explode("\n", $request->redirect_uris))
|
||||
->map('urldecode')
|
||||
->filter()
|
||||
->join(',');
|
||||
|
||||
$client = Passport::client()->forceFill([
|
||||
'user_id' => null,
|
||||
|
@ -3509,6 +3512,7 @@ class ApiV1Controller extends Controller
|
|||
|
||||
$status = new Status;
|
||||
$status->caption = $content;
|
||||
$status->rendered = $defaultCaption;
|
||||
$status->scope = $visibility;
|
||||
$status->visibility = $visibility;
|
||||
$status->profile_id = $user->profile_id;
|
||||
|
@ -3533,6 +3537,7 @@ class ApiV1Controller extends Controller
|
|||
if (! $in_reply_to_id) {
|
||||
$status = new Status;
|
||||
$status->caption = $content;
|
||||
$status->rendered = $defaultCaption;
|
||||
$status->profile_id = $user->profile_id;
|
||||
$status->is_nsfw = $cw;
|
||||
$status->cw_summary = $spoilerText;
|
||||
|
@ -3685,7 +3690,10 @@ class ApiV1Controller extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$defaultCaption = config_cache('database.default') === 'mysql' ? null : '';
|
||||
$share = Status::firstOrCreate([
|
||||
'caption' => $defaultCaption,
|
||||
'rendered' => $defaultCaption,
|
||||
'profile_id' => $user->profile_id,
|
||||
'reblog_of_id' => $status->id,
|
||||
'type' => 'share',
|
||||
|
|
|
@ -629,9 +629,6 @@ class ApiV1Dot1Controller extends Controller
|
|||
abort_if(BouncerService::checkIp($request->ip()), 404);
|
||||
}
|
||||
|
||||
$rl = RateLimiter::attempt('pf:apiv1.1:iarc:'.$request->ip(), config('pixelfed.app_registration_confirm_rate_limit_attempts', 20), function () {}, config('pixelfed.app_registration_confirm_rate_limit_decay', 1800));
|
||||
abort_if(! $rl, 429, 'Too many requests');
|
||||
|
||||
$request->validate([
|
||||
'user_token' => 'required',
|
||||
'random_token' => 'required',
|
||||
|
@ -658,7 +655,7 @@ class ApiV1Dot1Controller extends Controller
|
|||
$user->last_active_at = now();
|
||||
$user->save();
|
||||
|
||||
$token = $user->createToken('Pixelfed', ['read', 'write', 'follow', 'admin:read', 'admin:write', 'push']);
|
||||
$token = $user->createToken('Pixelfed', ['read', 'write', 'follow', 'push']);
|
||||
|
||||
return response()->json([
|
||||
'access_token' => $token->accessToken,
|
||||
|
@ -1292,13 +1289,14 @@ class ApiV1Dot1Controller extends Controller
|
|||
if ($user->last_active_at == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = $request->filled('status') ? strip_tags(Purify::clean($request->input('status'))) : null;
|
||||
$defaultCaption = '';
|
||||
$content = $request->filled('status') ? strip_tags(Purify::clean($request->input('status'))) : $defaultCaption;
|
||||
$cw = $user->profile->cw == true ? true : $request->boolean('sensitive', false);
|
||||
$spoilerText = $cw && $request->filled('spoiler_text') ? $request->input('spoiler_text') : null;
|
||||
|
||||
$status = new Status;
|
||||
$status->caption = $content;
|
||||
$status->rendered = $defaultCaption;
|
||||
$status->profile_id = $user->profile_id;
|
||||
$status->is_nsfw = $cw;
|
||||
$status->cw_summary = $spoilerText;
|
||||
|
|
|
@ -29,7 +29,7 @@ class CollectionController extends Controller
|
|||
return view('collection.create', compact('collection'));
|
||||
}
|
||||
|
||||
public function show(Request $request, int $id)
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
$user = $request->user();
|
||||
$collection = CollectionService::getCollection($id);
|
||||
|
|
|
@ -60,6 +60,7 @@ class CommentController extends Controller
|
|||
$reply->profile_id = $profile->id;
|
||||
$reply->is_nsfw = $nsfw;
|
||||
$reply->caption = Purify::clean($comment);
|
||||
$reply->rendered = "";
|
||||
$reply->in_reply_to_id = $status->id;
|
||||
$reply->in_reply_to_profile_id = $status->profile_id;
|
||||
$reply->scope = $scope;
|
||||
|
|
|
@ -30,6 +30,7 @@ use App\Util\Media\License;
|
|||
use Auth;
|
||||
use Cache;
|
||||
use DB;
|
||||
use Purify;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Fractal;
|
||||
|
@ -569,7 +570,9 @@ class ComposeController extends Controller
|
|||
$status->cw_summary = $request->input('spoiler_text');
|
||||
}
|
||||
|
||||
$status->caption = strip_tags($request->caption);
|
||||
$defaultCaption = "";
|
||||
$status->caption = strip_tags($request->input('caption')) ?? $defaultCaption;
|
||||
$status->rendered = $defaultCaption;
|
||||
$status->scope = 'draft';
|
||||
$status->visibility = 'draft';
|
||||
$status->profile_id = $profile->id;
|
||||
|
@ -673,6 +676,7 @@ class ComposeController extends Controller
|
|||
$place = $request->input('place');
|
||||
$cw = $request->input('cw');
|
||||
$tagged = $request->input('tagged');
|
||||
$defaultCaption = config_cache('database.default') === 'mysql' ? null : "";
|
||||
|
||||
if ($place && is_array($place)) {
|
||||
$status->place_id = $place['id'];
|
||||
|
@ -682,7 +686,8 @@ class ComposeController extends Controller
|
|||
$status->comments_disabled = (bool) $request->input('comments_disabled');
|
||||
}
|
||||
|
||||
$status->caption = strip_tags($request->caption);
|
||||
$status->caption = $request->filled('caption') ? strip_tags($request->caption) : $defaultCaption;
|
||||
$status->rendered = $defaultCaption;
|
||||
$status->profile_id = $profile->id;
|
||||
$entities = [];
|
||||
$visibility = $profile->unlisted == true && $visibility == 'public' ? 'unlisted' : $visibility;
|
||||
|
|
99
app/Http/Controllers/OAuth/OobAuthorizationController.php
Normal file
99
app/Http/Controllers/OAuth/OobAuthorizationController.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\OAuth;
|
||||
|
||||
use Laravel\Passport\Http\Controllers\ApproveAuthorizationController;
|
||||
use Illuminate\Http\Request;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use Nyholm\Psr7\Response as Psr7Response;
|
||||
|
||||
class OobAuthorizationController extends ApproveAuthorizationController
|
||||
{
|
||||
/**
|
||||
* Approve the authorization request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function approve(Request $request)
|
||||
{
|
||||
$this->assertValidAuthToken($request);
|
||||
|
||||
$authRequest = $this->getAuthRequestFromSession($request);
|
||||
$authRequest->setAuthorizationApproved(true);
|
||||
|
||||
return $this->withErrorHandling(function () use ($authRequest) {
|
||||
$response = $this->server->completeAuthorizationRequest($authRequest, new Psr7Response);
|
||||
|
||||
if ($this->isOutOfBandRequest($authRequest)) {
|
||||
$code = $this->extractAuthorizationCode($response);
|
||||
return response()->json([
|
||||
'code' => $code,
|
||||
'state' => $authRequest->getState()
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->convertResponse($response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request is an out-of-band OAuth request.
|
||||
*
|
||||
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
|
||||
* @return bool
|
||||
*/
|
||||
protected function isOutOfBandRequest($authRequest)
|
||||
{
|
||||
return $authRequest->getRedirectUri() === 'urn:ietf:wg:oauth:2.0:oob';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the authorization code from the PSR-7 response.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @return string
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
*/
|
||||
protected function extractAuthorizationCode($response)
|
||||
{
|
||||
$location = $response->getHeader('Location')[0] ?? '';
|
||||
|
||||
if (empty($location)) {
|
||||
throw OAuthServerException::serverError('Missing authorization code in response');
|
||||
}
|
||||
|
||||
parse_str(parse_url($location, PHP_URL_QUERY), $params);
|
||||
|
||||
if (!isset($params['code'])) {
|
||||
throw OAuthServerException::serverError('Invalid authorization code format');
|
||||
}
|
||||
|
||||
return $params['code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle OAuth errors for both redirect and OOB flows.
|
||||
*
|
||||
* @param \Closure $callback
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
protected function withErrorHandling($callback)
|
||||
{
|
||||
try {
|
||||
return $callback();
|
||||
} catch (OAuthServerException $e) {
|
||||
if ($this->isOutOfBandRequest($this->getAuthRequestFromSession(request()))) {
|
||||
return response()->json([
|
||||
'error' => $e->getErrorType(),
|
||||
'message' => $e->getMessage(),
|
||||
'hint' => $e->getHint()
|
||||
], $e->getHttpStatusCode());
|
||||
}
|
||||
|
||||
return $this->convertResponse(
|
||||
$e->generateHttpResponse(new Psr7Response)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -309,7 +309,7 @@ class StatusController extends Controller
|
|||
abort_if(! $statusAccount || isset($statusAccount['moved'], $statusAccount['moved']['id']), 422, 'Account moved');
|
||||
|
||||
$count = $status->reblogs_count;
|
||||
|
||||
$defaultCaption = config_cache('database.default') === 'mysql' ? null : "";
|
||||
$exists = Status::whereProfileId(Auth::user()->profile->id)
|
||||
->whereReblogOfId($status->id)
|
||||
->exists();
|
||||
|
@ -324,6 +324,8 @@ class StatusController extends Controller
|
|||
}
|
||||
} else {
|
||||
$share = new Status;
|
||||
$share->caption = $defaultCaption;
|
||||
$share->rendered = $defaultCaption;
|
||||
$share->profile_id = $profile->id;
|
||||
$share->reblog_of_id = $status->id;
|
||||
$share->in_reply_to_profile_id = $status->profile_id;
|
||||
|
|
|
@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
|
|||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'/api/v1/*'
|
||||
'/api/v1/*',
|
||||
'oauth/token'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,27 +2,28 @@
|
|||
|
||||
namespace App\Jobs\StatusPipeline;
|
||||
|
||||
use App\Hashtag;
|
||||
use App\Jobs\MentionPipeline\MentionPipeline;
|
||||
use App\Mention;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\CustomEmojiService;
|
||||
use App\Services\StatusService;
|
||||
use App\Services\TrendingHashtagService;
|
||||
use App\StatusHashtag;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\CustomEmojiService;
|
||||
use App\Services\StatusService;
|
||||
use App\Jobs\MentionPipeline\MentionPipeline;
|
||||
use App\Mention;
|
||||
use App\Hashtag;
|
||||
use App\StatusHashtag;
|
||||
use App\Services\TrendingHashtagService;
|
||||
use App\Util\ActivityPub\Helpers;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StatusTagsPipeline implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $activity;
|
||||
|
||||
protected $status;
|
||||
|
||||
/**
|
||||
|
@ -46,92 +47,126 @@ class StatusTagsPipeline implements ShouldQueue
|
|||
$res = $this->activity;
|
||||
$status = $this->status;
|
||||
|
||||
if(isset($res['tag']['type'], $res['tag']['name'])) {
|
||||
if (isset($res['tag']['type'], $res['tag']['name'])) {
|
||||
$res['tag'] = [$res['tag']];
|
||||
}
|
||||
|
||||
$tags = collect($res['tag']);
|
||||
|
||||
// Emoji
|
||||
$tags->filter(function($tag) {
|
||||
$tags->filter(function ($tag) {
|
||||
return $tag && isset($tag['id'], $tag['icon'], $tag['name'], $tag['type']) && $tag['type'] == 'Emoji';
|
||||
})
|
||||
->map(function($tag) {
|
||||
CustomEmojiService::import($tag['id'], $this->status->id);
|
||||
});
|
||||
->map(function ($tag) {
|
||||
CustomEmojiService::import($tag['id'], $this->status->id);
|
||||
});
|
||||
|
||||
// Hashtags
|
||||
$tags->filter(function($tag) {
|
||||
$tags->filter(function ($tag) {
|
||||
return $tag && $tag['type'] == 'Hashtag' && isset($tag['href'], $tag['name']);
|
||||
})
|
||||
->map(function($tag) use($status) {
|
||||
$name = substr($tag['name'], 0, 1) == '#' ?
|
||||
substr($tag['name'], 1) : $tag['name'];
|
||||
->map(function ($tag) use ($status) {
|
||||
$name = substr($tag['name'], 0, 1) == '#' ?
|
||||
substr($tag['name'], 1) : $tag['name'];
|
||||
|
||||
$banned = TrendingHashtagService::getBannedHashtagNames();
|
||||
$banned = TrendingHashtagService::getBannedHashtagNames();
|
||||
|
||||
if(count($banned)) {
|
||||
if(in_array(strtolower($name), array_map('strtolower', $banned))) {
|
||||
return;
|
||||
if (count($banned)) {
|
||||
if (in_array(strtolower($name), array_map('strtolower', $banned))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(config('database.default') === 'pgsql') {
|
||||
$hashtag = Hashtag::where('name', 'ilike', $name)
|
||||
->orWhere('slug', 'ilike', str_slug($name, '-', false))
|
||||
->first();
|
||||
if (config('database.default') === 'pgsql') {
|
||||
$hashtag = DB::transaction(function () use ($name) {
|
||||
$baseSlug = str_slug($name, '-', false);
|
||||
$slug = $baseSlug;
|
||||
$counter = 1;
|
||||
|
||||
if(!$hashtag) {
|
||||
$hashtag = Hashtag::updateOrCreate([
|
||||
'slug' => str_slug($name, '-', false),
|
||||
'name' => $name
|
||||
]);
|
||||
$existing = Hashtag::where('name', $name)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
if ($existing->slug !== $slug) {
|
||||
while (Hashtag::where('slug', $slug)
|
||||
->where('name', '!=', $name)
|
||||
->exists()) {
|
||||
$slug = $baseSlug.'-'.$counter++;
|
||||
}
|
||||
$existing->slug = $slug;
|
||||
$existing->save();
|
||||
}
|
||||
|
||||
return $existing;
|
||||
}
|
||||
|
||||
while (Hashtag::where('slug', $slug)->exists()) {
|
||||
$slug = $baseSlug.'-'.$counter++;
|
||||
}
|
||||
|
||||
return Hashtag::create([
|
||||
'name' => $name,
|
||||
'slug' => $slug,
|
||||
]);
|
||||
});
|
||||
} else {
|
||||
$hashtag = DB::transaction(function () use ($name) {
|
||||
$baseSlug = str_slug($name, '-', false);
|
||||
$slug = $baseSlug;
|
||||
$counter = 1;
|
||||
|
||||
while (Hashtag::where('slug', $slug)
|
||||
->where('name', '!=', $name)
|
||||
->exists()) {
|
||||
$slug = $baseSlug.'-'.$counter++;
|
||||
}
|
||||
|
||||
return Hashtag::updateOrCreate(
|
||||
['name' => $name],
|
||||
['slug' => $slug]
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$hashtag = Hashtag::updateOrCreate([
|
||||
'slug' => str_slug($name, '-', false),
|
||||
'name' => $name
|
||||
|
||||
StatusHashtag::firstOrCreate([
|
||||
'status_id' => $status->id,
|
||||
'hashtag_id' => $hashtag->id,
|
||||
'profile_id' => $status->profile_id,
|
||||
'status_visibility' => $status->scope,
|
||||
]);
|
||||
}
|
||||
|
||||
StatusHashtag::firstOrCreate([
|
||||
'status_id' => $status->id,
|
||||
'hashtag_id' => $hashtag->id,
|
||||
'profile_id' => $status->profile_id,
|
||||
'status_visibility' => $status->scope
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// Mentions
|
||||
$tags->filter(function($tag) {
|
||||
$tags->filter(function ($tag) {
|
||||
return $tag &&
|
||||
$tag['type'] == 'Mention' &&
|
||||
isset($tag['href']) &&
|
||||
substr($tag['href'], 0, 8) === 'https://';
|
||||
})
|
||||
->map(function($tag) use($status) {
|
||||
if(Helpers::validateLocalUrl($tag['href'])) {
|
||||
$parts = explode('/', $tag['href']);
|
||||
if(!$parts) {
|
||||
return;
|
||||
->map(function ($tag) use ($status) {
|
||||
if (Helpers::validateLocalUrl($tag['href'])) {
|
||||
$parts = explode('/', $tag['href']);
|
||||
if (! $parts) {
|
||||
return;
|
||||
}
|
||||
$pid = AccountService::usernameToId(end($parts));
|
||||
if (! $pid) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$acct = Helpers::profileFetch($tag['href']);
|
||||
if (! $acct) {
|
||||
return;
|
||||
}
|
||||
$pid = $acct->id;
|
||||
}
|
||||
$pid = AccountService::usernameToId(end($parts));
|
||||
if(!$pid) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$acct = Helpers::profileFetch($tag['href']);
|
||||
if(!$acct) {
|
||||
return;
|
||||
}
|
||||
$pid = $acct->id;
|
||||
}
|
||||
$mention = new Mention;
|
||||
$mention->status_id = $status->id;
|
||||
$mention->profile_id = $pid;
|
||||
$mention->save();
|
||||
MentionPipeline::dispatch($status, $mention);
|
||||
});
|
||||
$mention = new Mention;
|
||||
$mention->status_id = $status->id;
|
||||
$mention->profile_id = $pid;
|
||||
$mention->save();
|
||||
MentionPipeline::dispatch($status, $mention);
|
||||
});
|
||||
|
||||
StatusService::refresh($status->id);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class Media extends Model
|
|||
protected $casts = [
|
||||
'srcset' => 'array',
|
||||
'deleted_at' => 'datetime',
|
||||
'skip_optimize' => 'boolean'
|
||||
];
|
||||
|
||||
public function status()
|
||||
|
|
|
@ -2,81 +2,99 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Observers\{
|
||||
AvatarObserver,
|
||||
FollowerObserver,
|
||||
HashtagFollowObserver,
|
||||
LikeObserver,
|
||||
NotificationObserver,
|
||||
ModLogObserver,
|
||||
ProfileObserver,
|
||||
StatusHashtagObserver,
|
||||
StatusObserver,
|
||||
UserObserver,
|
||||
UserFilterObserver,
|
||||
};
|
||||
use App\{
|
||||
Avatar,
|
||||
Follower,
|
||||
HashtagFollow,
|
||||
Like,
|
||||
Notification,
|
||||
ModLog,
|
||||
Profile,
|
||||
StatusHashtag,
|
||||
Status,
|
||||
User,
|
||||
UserFilter
|
||||
};
|
||||
use Auth, Horizon, URL;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Avatar;
|
||||
use App\Follower;
|
||||
use App\HashtagFollow;
|
||||
use App\Like;
|
||||
use App\ModLog;
|
||||
use App\Notification;
|
||||
use App\Observers\AvatarObserver;
|
||||
use App\Observers\FollowerObserver;
|
||||
use App\Observers\HashtagFollowObserver;
|
||||
use App\Observers\LikeObserver;
|
||||
use App\Observers\ModLogObserver;
|
||||
use App\Observers\NotificationObserver;
|
||||
use App\Observers\ProfileObserver;
|
||||
use App\Observers\StatusHashtagObserver;
|
||||
use App\Observers\StatusObserver;
|
||||
use App\Observers\UserFilterObserver;
|
||||
use App\Observers\UserObserver;
|
||||
use App\Profile;
|
||||
use App\Services\AccountService;
|
||||
use App\Status;
|
||||
use App\StatusHashtag;
|
||||
use App\User;
|
||||
use App\UserFilter;
|
||||
use Auth;
|
||||
use Horizon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Pulse\Facades\Pulse;
|
||||
use URL;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if(config('instance.force_https_urls', true)) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if (config('instance.force_https_urls', true)) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
Schema::defaultStringLength(191);
|
||||
Paginator::useBootstrap();
|
||||
Avatar::observe(AvatarObserver::class);
|
||||
Follower::observe(FollowerObserver::class);
|
||||
HashtagFollow::observe(HashtagFollowObserver::class);
|
||||
Like::observe(LikeObserver::class);
|
||||
Notification::observe(NotificationObserver::class);
|
||||
ModLog::observe(ModLogObserver::class);
|
||||
Profile::observe(ProfileObserver::class);
|
||||
StatusHashtag::observe(StatusHashtagObserver::class);
|
||||
User::observe(UserObserver::class);
|
||||
Schema::defaultStringLength(191);
|
||||
Paginator::useBootstrap();
|
||||
Avatar::observe(AvatarObserver::class);
|
||||
Follower::observe(FollowerObserver::class);
|
||||
HashtagFollow::observe(HashtagFollowObserver::class);
|
||||
Like::observe(LikeObserver::class);
|
||||
Notification::observe(NotificationObserver::class);
|
||||
ModLog::observe(ModLogObserver::class);
|
||||
Profile::observe(ProfileObserver::class);
|
||||
StatusHashtag::observe(StatusHashtagObserver::class);
|
||||
User::observe(UserObserver::class);
|
||||
Status::observe(StatusObserver::class);
|
||||
UserFilter::observe(UserFilterObserver::class);
|
||||
Horizon::auth(function ($request) {
|
||||
return Auth::check() && $request->user()->is_admin;
|
||||
});
|
||||
Validator::includeUnvalidatedArrayKeys();
|
||||
UserFilter::observe(UserFilterObserver::class);
|
||||
Horizon::auth(function ($request) {
|
||||
return Auth::check() && $request->user()->is_admin;
|
||||
});
|
||||
Validator::includeUnvalidatedArrayKeys();
|
||||
|
||||
// Model::preventLazyLoading(true);
|
||||
}
|
||||
Gate::define('viewPulse', function (User $user) {
|
||||
return $user->is_admin === 1;
|
||||
});
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
Pulse::user(function ($user) {
|
||||
$acct = AccountService::get($user->profile_id, true);
|
||||
|
||||
return $acct ? [
|
||||
'name' => $acct['username'],
|
||||
'extra' => $user->email,
|
||||
'avatar' => $acct['avatar'],
|
||||
] : [
|
||||
'name' => $user->username,
|
||||
'extra' => 'DELETED',
|
||||
'avatar' => '/storage/avatars/default.jpg',
|
||||
];
|
||||
});
|
||||
|
||||
// Model::preventLazyLoading(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||
public function boot()
|
||||
{
|
||||
if(config('pixelfed.oauth_enabled') == true) {
|
||||
Passport::ignoreRoutes();
|
||||
Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356)));
|
||||
Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400)));
|
||||
Passport::enableImplicitGrant();
|
||||
|
|
|
@ -14,7 +14,7 @@ class ImportService
|
|||
if($userId > 999999) {
|
||||
return;
|
||||
}
|
||||
if($year < 9 || $year > 23) {
|
||||
if($year < 9 || $year > (int) now()->addYear()->format('y')) {
|
||||
return;
|
||||
}
|
||||
if($month < 1 || $month > 12) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -71,9 +71,14 @@ class HttpSignature
|
|||
public static function instanceActorSign($url, $body = false, $addlHeaders = [], $method = 'post')
|
||||
{
|
||||
$keyId = config('app.url').'/i/actor#main-key';
|
||||
$privateKey = Cache::rememberForever(InstanceActor::PKI_PRIVATE, function () {
|
||||
return InstanceActor::first()->private_key;
|
||||
});
|
||||
if(config_cache('database.default') === 'mysql') {
|
||||
$privateKey = Cache::rememberForever(InstanceActor::PKI_PRIVATE, function () {
|
||||
return InstanceActor::first()->private_key;
|
||||
});
|
||||
} else {
|
||||
$privateKey = InstanceActor::first()?->private_key;
|
||||
}
|
||||
abort_if(!$privateKey || empty($privateKey), 400, 'Missing instance actor key, please run php artisan instance:actor');
|
||||
if ($body) {
|
||||
$digest = self::_digest($body);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"laravel/helpers": "^1.1",
|
||||
"laravel/horizon": "^5.0",
|
||||
"laravel/passport": "^12.0",
|
||||
"laravel/pulse": "^1.3",
|
||||
"laravel/tinker": "^2.9",
|
||||
"laravel/ui": "^4.2",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
|
|
1224
composer.lock
generated
1224
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -85,11 +85,29 @@ return [
|
|||
'database' => env('REDIS_DATABASE', 0),
|
||||
],
|
||||
|
||||
'session' => [
|
||||
'scheme' => env('REDIS_SCHEME', 'tcp'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATABASE_SESSION', 1),
|
||||
],
|
||||
|
||||
'pulse' => [
|
||||
'scheme' => env('REDIS_SCHEME', 'tcp'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATABASE_PULSE', 2),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'redis:session' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
'connection' => 'session',
|
||||
'prefix' => 'pf_session',
|
||||
],
|
||||
|
||||
|
|
|
@ -143,6 +143,24 @@ return [
|
|||
'database' => env('REDIS_DATABASE', 0),
|
||||
],
|
||||
|
||||
'session' => [
|
||||
'scheme' => env('REDIS_SCHEME', 'tcp'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATABASE_SESSION', 1),
|
||||
],
|
||||
|
||||
'pulse' => [
|
||||
'scheme' => env('REDIS_SCHEME', 'tcp'),
|
||||
'path' => env('REDIS_PATH'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATABASE_PULSE', 2),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'dbal' => [
|
||||
|
|
|
@ -24,6 +24,10 @@ return [
|
|||
],
|
||||
],
|
||||
|
||||
'image_optimize' => [
|
||||
'catch_unoptimized_media_hour_limit' => env('PF_CATCHUNOPTIMIZEDMEDIA', false),
|
||||
],
|
||||
|
||||
'hls' => [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
236
config/pulse.php
Normal file
236
config/pulse.php
Normal file
|
@ -0,0 +1,236 @@
|
|||
<?php
|
||||
|
||||
use Laravel\Pulse\Http\Middleware\Authorize;
|
||||
use Laravel\Pulse\Pulse;
|
||||
use Laravel\Pulse\Recorders;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the subdomain which the Pulse dashboard will be accessible from.
|
||||
| When set to null, the dashboard will reside under the same domain as
|
||||
| the application. Remember to configure your DNS entries correctly.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('PULSE_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the path which the Pulse dashboard will be accessible from. Feel
|
||||
| free to change this path to anything you'd like. Note that this won't
|
||||
| affect the path of the internal API that is never exposed to users.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('PULSE_PATH', 'pulse'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Master Switch
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option may be used to completely disable all Pulse
|
||||
| data recorders regardless of their individual configurations. This
|
||||
| provides a single option to quickly disable all Pulse recording.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('PULSE_ENABLED', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Storage Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines which storage driver will be used
|
||||
| while storing entries from Pulse's recorders. In addition, you also
|
||||
| may provide any options to configure the selected storage driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'storage' => [
|
||||
'driver' => env('PULSE_STORAGE_DRIVER', 'database'),
|
||||
|
||||
'trim' => [
|
||||
'keep' => env('PULSE_STORAGE_KEEP', '7 days'),
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'connection' => env('PULSE_DB_CONNECTION'),
|
||||
'chunk' => 1000,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Ingest Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration options determines the ingest driver that will be used
|
||||
| to capture entries from Pulse's recorders. Ingest drivers are great to
|
||||
| free up your request workers quickly by offloading the data storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'ingest' => [
|
||||
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
|
||||
|
||||
'buffer' => env('PULSE_INGEST_BUFFER', 5_000),
|
||||
|
||||
'trim' => [
|
||||
'lottery' => [1, 1_000],
|
||||
'keep' => env('PULSE_INGEST_KEEP', '7 days'),
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'connection' => env('PULSE_REDIS_CONNECTION'),
|
||||
'chunk' => 1000,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Cache Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines the cache driver that will be used
|
||||
| for various tasks, including caching dashboard results, establishing
|
||||
| locks for events that should only occur on one server and signals.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => env('PULSE_CACHE_DRIVER'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Route Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These middleware will be assigned to every Pulse route, giving you the
|
||||
| chance to add your own middleware to this list or change any of the
|
||||
| existing middleware. Of course, reasonable defaults are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'web',
|
||||
Authorize::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pulse Recorders
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following array lists the "recorders" that will be registered with
|
||||
| Pulse, along with their configuration. Recorders gather application
|
||||
| event data from requests and tasks to pass to your ingest driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'recorders' => [
|
||||
Recorders\CacheInteractions::class => [
|
||||
'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
...Pulse::defaultVendorCacheKeys(),
|
||||
],
|
||||
'groups' => [
|
||||
'/^job-exceptions:.*/' => 'job-exceptions:*',
|
||||
// '/:\d+/' => ':*',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Exceptions::class => [
|
||||
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
|
||||
'location' => env('PULSE_EXCEPTIONS_LOCATION', true),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Exceptions\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Queues::class => [
|
||||
'enabled' => env('PULSE_QUEUES_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\Servers::class => [
|
||||
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
|
||||
'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')),
|
||||
],
|
||||
|
||||
Recorders\SlowJobs::class => [
|
||||
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowOutgoingRequests::class => [
|
||||
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
// '#^http://127\.0\.0\.1:13714#', // Inertia SSR...
|
||||
],
|
||||
'groups' => [
|
||||
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
|
||||
// '#^https?://([^/]*).*$#' => '\1',
|
||||
// '#/\d+#' => '/*',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowQueries::class => [
|
||||
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
|
||||
'location' => env('PULSE_SLOW_QUERIES_LOCATION', true),
|
||||
'max_query_length' => env('PULSE_SLOW_QUERIES_MAX_QUERY_LENGTH'),
|
||||
'ignore' => [
|
||||
'/(["`])pulse_[\w]+?\1/', // Pulse tables...
|
||||
'/(["`])telescope_[\w]+?\1/', // Telescope tables...
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\SlowRequests::class => [
|
||||
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
|
||||
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
|
||||
'ignore' => [
|
||||
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||
'#^/telescope#', // Telescope dashboard...
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\UserJobs::class => [
|
||||
'enabled' => env('PULSE_USER_JOBS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
// '/^Package\\\\Jobs\\\\/',
|
||||
],
|
||||
],
|
||||
|
||||
Recorders\UserRequests::class => [
|
||||
'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true),
|
||||
'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1),
|
||||
'ignore' => [
|
||||
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||
'#^/telescope#', // Telescope dashboard...
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
|
@ -17,6 +17,8 @@ return [
|
|||
'mailgun' => [
|
||||
'domain' => env('MAILGUN_DOMAIN'),
|
||||
'secret' => env('MAILGUN_SECRET'),
|
||||
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
|
||||
'scheme' => 'https',
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
|
|
|
@ -19,7 +19,7 @@ class CreateDirectMessagesTable extends Migration
|
|||
$table->bigInteger('from_id')->unsigned()->index();
|
||||
$table->string('from_profile_ids')->nullable();
|
||||
$table->boolean('group_message')->default(false);
|
||||
$table->bigInteger('status_id')->unsigned()->integer();
|
||||
$table->bigInteger('status_id')->unsigned();
|
||||
$table->unique(['to_id', 'from_id', 'status_id']);
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Laravel\Pulse\Support\PulseMigration;
|
||||
|
||||
return new class extends PulseMigration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! $this->shouldRun()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('pulse_values', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('timestamp');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->mediumText('value');
|
||||
|
||||
$table->index('timestamp'); // For trimming...
|
||||
$table->index('type'); // For fast lookups and purging...
|
||||
$table->unique(['type', 'key_hash']); // For data integrity and upserts...
|
||||
});
|
||||
|
||||
Schema::create('pulse_entries', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('timestamp');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->bigInteger('value')->nullable();
|
||||
|
||||
$table->index('timestamp'); // For trimming...
|
||||
$table->index('type'); // For purging...
|
||||
$table->index('key_hash'); // For mapping...
|
||||
$table->index(['timestamp', 'type', 'key_hash', 'value']); // For aggregate queries...
|
||||
});
|
||||
|
||||
Schema::create('pulse_aggregates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('bucket');
|
||||
$table->unsignedMediumInteger('period');
|
||||
$table->string('type');
|
||||
$table->mediumText('key');
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
};
|
||||
$table->string('aggregate');
|
||||
$table->decimal('value', 20, 2);
|
||||
$table->unsignedInteger('count')->nullable();
|
||||
|
||||
$table->unique(['bucket', 'period', 'type', 'aggregate', 'key_hash']); // Force "on duplicate update"...
|
||||
$table->index(['period', 'bucket']); // For trimming...
|
||||
$table->index('type'); // For purging...
|
||||
$table->index(['period', 'type', 'aggregate', 'bucket']); // For aggregate queries...
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('pulse_values');
|
||||
Schema::dropIfExists('pulse_entries');
|
||||
Schema::dropIfExists('pulse_aggregates');
|
||||
}
|
||||
};
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
|
@ -12,6 +13,9 @@ return new class extends Migration
|
|||
public function up(): void
|
||||
{
|
||||
Schema::table('group_posts', function (Blueprint $table) {
|
||||
if (DB::getDriverName() === 'sqlite') {
|
||||
$table->dropUnique(['status_id']);
|
||||
}
|
||||
$table->dropColumn('status_id');
|
||||
$table->dropColumn('reply_child_id');
|
||||
$table->dropColumn('in_reply_to_id');
|
||||
|
|
|
@ -17,7 +17,7 @@ services:
|
|||
#
|
||||
# See: https://github.com/nginx-proxy/nginx-proxy/tree/main/docs
|
||||
proxy:
|
||||
image: nginxproxy/nginx-proxy:1.4
|
||||
image: nginxproxy/nginx-proxy:1.6.2
|
||||
container_name: "${DOCKER_ALL_CONTAINER_NAME_PREFIX}-proxy"
|
||||
restart: unless-stopped
|
||||
profiles:
|
||||
|
|
842
package-lock.json
generated
842
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -71,6 +71,7 @@
|
|||
"vue-loading-overlay": "^3.3.3",
|
||||
"vue-timeago": "^5.1.2",
|
||||
"vue-tribute": "^1.0.7",
|
||||
"webgl-media-editor": "^0.0.1",
|
||||
"zuck.js": "^1.6.0"
|
||||
},
|
||||
"collective": {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/js/compose.chunk.b06beb250e24db17.js
vendored
Normal file
BIN
public/js/compose.chunk.b06beb250e24db17.js
vendored
Normal file
Binary file not shown.
BIN
public/js/compose.chunk.e1f297b242137d23.js
vendored
BIN
public/js/compose.chunk.e1f297b242137d23.js
vendored
Binary file not shown.
BIN
public/js/compose.js
vendored
BIN
public/js/compose.js
vendored
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue