1
0
Fork 0

Compare commits

..

No commits in common. "7d03ac49e3c89673472d9bdccaa1ef76834deac5" and "3d5fd48a2202a25308355bc48531a8e0f6e63be2" have entirely different histories.

188 changed files with 3023 additions and 7977 deletions

7
.gitattributes vendored
View File

@ -3,10 +3,3 @@
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
# Collapse diffs for generated files:
public/**/*.js text -diff
public/**/*.json text -diff
public/**/*.css text -diff
public/img/* binary -diff
public/fonts/* binary -diff

View File

@ -1,41 +1,6 @@
# Release Notes
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.12...dev)
### Features
- Curated Onboarding ([8dac2caf](https://github.com/pixelfed/pixelfed/commit/8dac2caf))
### Updates
- Update Inbox, cast live filters to lowercase ([d835e0ad](https://github.com/pixelfed/pixelfed/commit/d835e0ad))
- Update federation config, increase default timeline days falloff to 90 days from 2 days. Fixes #4905 ([011834f4](https://github.com/pixelfed/pixelfed/commit/011834f4))
- Update cache config, use predis as default redis driver client ([ea6b1623](https://github.com/pixelfed/pixelfed/commit/ea6b1623))
- Update .gitattributes to collapse diffs on generated files ([ThisIsMissEm](https://github.com/pixelfed/pixelfed/commit/9978b2b9))
- Update api v1/v2 instance endpoints, bump mastoapi version from 2.7.2 to 3.5.3 ([545f7d5e](https://github.com/pixelfed/pixelfed/commit/545f7d5e))
- Update ApiV1Controller, implement better limit logic to gracefully handle requests with limits that exceed the max ([1f74a95d](https://github.com/pixelfed/pixelfed/commit/1f74a95d))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.12 (2024-02-16)](https://github.com/pixelfed/pixelfed/compare/v0.11.11...v0.11.12)
### Features
- Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a))
- Added Software Update banner to admin home feeds ([b0fb1988](https://github.com/pixelfed/pixelfed/commit/b0fb1988))
### Updates
- Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3))
- Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc))
- Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3))
- Update ActivityPubFetchService, enforce stricter Content-Type validation ([1232cfc8](https://github.com/pixelfed/pixelfed/commit/1232cfc8))
- Update status view, fix unlisted/private scope bug ([0f3ca194](https://github.com/pixelfed/pixelfed/commit/0f3ca194))
## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11)
### Fixes
- Fix api endpoints ([fd7f5dbb](https://github.com/pixelfed/pixelfed/commit/fd7f5dbb))
## [v0.11.10 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.9...v0.11.10)
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.9...dev)
### Added
- Resilient Media Storage ([#4665](https://github.com/pixelfed/pixelfed/pull/4665)) ([fb1deb6](https://github.com/pixelfed/pixelfed/commit/fb1deb6))
@ -55,6 +20,7 @@
### Federation
- Update Privacy Settings, add support for Mastodon `indexable` search flag ([fc24630e](https://github.com/pixelfed/pixelfed/commit/fc24630e))
- Update AP Helpers, consume actor `indexable` attribute ([fbdcdd9d](https://github.com/pixelfed/pixelfed/commit/fbdcdd9d))
- ([](https://github.com/pixelfed/pixelfed/commit/))
### Updates
- Update FollowerService, add forget method to RelationshipService call to reduce load when mass purging ([347e4f59](https://github.com/pixelfed/pixelfed/commit/347e4f59))
@ -127,18 +93,7 @@
- Update ApiV1Controller, properly cast boolean sensitive parameter. Fixes #4888 ([0aff126a](https://github.com/pixelfed/pixelfed/commit/0aff126a))
- Update AccountImport.vue, fix new IG export format ([59aa6a4b](https://github.com/pixelfed/pixelfed/commit/59aa6a4b))
- Update TransformImports command, fix import service condition ([32c59f04](https://github.com/pixelfed/pixelfed/commit/32c59f04))
- Update AP helpers, more efficently update post count ([7caed381](https://github.com/pixelfed/pixelfed/commit/7caed381))
- Update AP helpers, refactor post count decrement logic ([b81ae577](https://github.com/pixelfed/pixelfed/commit/b81ae577))
- Update AP helpers, fix sensitive bug ([00ed330c](https://github.com/pixelfed/pixelfed/commit/00ed330c))
- Update NotificationEpochUpdatePipeline, use more efficient query ([4d401389](https://github.com/pixelfed/pixelfed/commit/4d401389))
- Update notification pipelines, fix non-local saving ([fa97a1f3](https://github.com/pixelfed/pixelfed/commit/fa97a1f3))
- Update NodeinfoService, disable redirects ([240e6bbe](https://github.com/pixelfed/pixelfed/commit/240e6bbe))
- Update Instance model, add entity casts ([289cad47](https://github.com/pixelfed/pixelfed/commit/289cad47))
- Update FetchNodeinfoPipeline, use more efficient dispatch ([ac01f51a](https://github.com/pixelfed/pixelfed/commit/ac01f51a))
- Update horizon.php config ([1e3acade](https://github.com/pixelfed/pixelfed/commit/1e3acade))
- Update PublicApiController, consume InstanceService blocked domains for account and statuses endpoints ([01b33fb3](https://github.com/pixelfed/pixelfed/commit/01b33fb3))
- Update ApiV1Controller, enforce blocked instance domain logic ([5b284cac](https://github.com/pixelfed/pixelfed/commit/5b284cac))
- Update ApiV2Controller, add vapid key to instance object. Thanks thisismissem! ([4d02d6f1](https://github.com/pixelfed/pixelfed/commit/4d02d6f1))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.9 (2023-08-21)](https://github.com/pixelfed/pixelfed/compare/v0.11.8...v0.11.9)

View File

@ -18,7 +18,8 @@ class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerToke
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return [
'created_at' => time(),
'created_at' => time(),
'scope' => 'read write follow push'
];
}
}

View File

@ -1,57 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\AccountService;
use App\Services\Account\AccountStatService;
use App\Status;
use App\Profile;
class AccountPostCountStatUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:account-post-count-stat-update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update post counts from recent activities';
/**
* Execute the console command.
*/
public function handle()
{
$ids = AccountStatService::getAllPostCountIncr();
if(!$ids || !count($ids)) {
return;
}
foreach($ids as $id) {
$acct = AccountService::get($id, true);
if(!$acct) {
AccountStatService::removeFromPostCount($id);
continue;
}
$statusCount = Status::whereProfileId($id)->count();
if($statusCount != $acct['statuses_count']) {
$profile = Profile::find($id);
if(!$profile) {
AccountStatService::removeFromPostCount($id);
continue;
}
$profile->status_count = $statusCount;
$profile->save();
AccountService::del($id);
}
AccountStatService::removeFromPostCount($id);
}
return;
}
}

View File

@ -1,298 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Instance;
use App\Profile;
use App\Services\InstanceService;
use App\Jobs\InstancePipeline\FetchNodeinfoPipeline;
use function Laravel\Prompts\select;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\progress;
use function Laravel\Prompts\search;
use function Laravel\Prompts\table;
class InstanceManager extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:instance-manager';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Manage Instances';
/**
* Execute the console command.
*/
public function handle()
{
$action = select(
'What action do you want to perform?',
[
'Recalculate Stats',
'Ban Instance',
'Unlist Instance',
'Unlisted Instances',
'Banned Instances',
'Unban Instance',
'Relist Instance',
],
);
switch($action) {
case 'Recalculate Stats':
return $this->recalculateStats();
break;
case 'Unlisted Instances':
return $this->viewUnlistedInstances();
break;
case 'Banned Instances':
return $this->viewBannedInstances();
break;
case 'Unlist Instance':
return $this->unlistInstance();
break;
case 'Ban Instance':
return $this->banInstance();
break;
case 'Unban Instance':
return $this->unbanInstance();
break;
case 'Relist Instance':
return $this->relistInstance();
break;
}
}
protected function recalculateStats()
{
$instanceCount = Instance::count();
$confirmed = confirm('Do you want to recalculate stats for all ' . $instanceCount . ' instances?');
if(!$confirmed) {
$this->error('Aborting...');
exit;
}
$users = progress(
label: 'Updating instance stats...',
steps: Instance::all(),
callback: fn ($instance) => $this->updateInstanceStats($instance),
);
}
protected function updateInstanceStats($instance)
{
FetchNodeinfoPipeline::dispatch($instance)->onQueue('intbg');
}
protected function unlistInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereUnlisted(false)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to unlist this instance?');
if(!$confirmed) {
$this->error('Aborting instance unlisting');
exit;
}
$instance->unlisted = true;
$instance->save();
InstanceService::refresh();
$this->info('Successfully unlisted ' . $instance->domain . '!');
exit;
}
protected function relistInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereUnlisted(true)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to re-list this instance?');
if(!$confirmed) {
$this->error('Aborting instance re-listing');
exit;
}
$instance->unlisted = false;
$instance->save();
InstanceService::refresh();
$this->info('Successfully re-listed ' . $instance->domain . '!');
exit;
}
protected function banInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereBanned(false)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to ban this instance?');
if(!$confirmed) {
$this->error('Aborting instance ban');
exit;
}
$instance->banned = true;
$instance->save();
InstanceService::refresh();
$this->info('Successfully banned ' . $instance->domain . '!');
exit;
}
protected function unbanInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereBanned(true)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to unban this instance?');
if(!$confirmed) {
$this->error('Aborting instance unban');
exit;
}
$instance->banned = false;
$instance->save();
InstanceService::refresh();
$this->info('Successfully un-banned ' . $instance->domain . '!');
exit;
}
protected function viewBannedInstances()
{
$data = Instance::whereBanned(true)
->get(['domain', 'user_count', 'status_count'])
->map(function($d) {
return [
'domain' => $d->domain,
'user_count' => number_format($d->user_count),
'status_count' => number_format($d->status_count),
];
})
->toArray();
table(
['Domain', 'User Count', 'Status Count'],
$data
);
}
protected function viewUnlistedInstances()
{
$data = Instance::whereUnlisted(true)
->get(['domain', 'user_count', 'status_count', 'banned'])
->map(function($d) {
return [
'domain' => $d->domain,
'user_count' => number_format($d->user_count),
'status_count' => number_format($d->status_count),
'banned' => $d->banned ? '✅' : null
];
})
->toArray();
table(
['Domain', 'User Count', 'Status Count', 'Banned'],
$data
);
}
}

View File

@ -38,7 +38,7 @@ class Kernel extends ConsoleKernel
}
if(config('import.instagram.enabled')) {
$schedule->command('app:transform-imports')->everyTenMinutes();
$schedule->command('app:transform-imports')->everyFourMinutes();
$schedule->command('app:import-upload-garbage-collection')->hourlyAt(51);
$schedule->command('app:import-remove-deleted-accounts')->hourlyAt(37);
$schedule->command('app:import-upload-clean-storage')->twiceDailyAt(1, 13, 32);
@ -49,7 +49,6 @@ class Kernel extends ConsoleKernel
}
$schedule->command('app:notification-epoch-update')->weeklyOn(1, '2:21');
$schedule->command('app:hashtag-cached-count-update')->hourlyAt(25);
$schedule->command('app:account-post-count-stat-update')->everySixHours(25);
}
/**

View File

@ -17,288 +17,269 @@ use Illuminate\Support\Str;
trait AdminSettingsController
{
public function settings(Request $request)
{
$cloud_storage = ConfigCacheService::get('pixelfed.cloud_storage');
$cloud_disk = config('filesystems.cloud');
$cloud_ready = !empty(config('filesystems.disks.' . $cloud_disk . '.key')) && !empty(config('filesystems.disks.' . $cloud_disk . '.secret'));
$types = explode(',', ConfigCacheService::get('pixelfed.media_types'));
$rules = ConfigCacheService::get('app.rules') ? json_decode(ConfigCacheService::get('app.rules'), true) : null;
$jpeg = in_array('image/jpg', $types) || in_array('image/jpeg', $types);
$png = in_array('image/png', $types);
$gif = in_array('image/gif', $types);
$mp4 = in_array('video/mp4', $types);
$webp = in_array('image/webp', $types);
public function settings(Request $request)
{
$cloud_storage = ConfigCacheService::get('pixelfed.cloud_storage');
$cloud_disk = config('filesystems.cloud');
$cloud_ready = !empty(config('filesystems.disks.' . $cloud_disk . '.key')) && !empty(config('filesystems.disks.' . $cloud_disk . '.secret'));
$types = explode(',', ConfigCacheService::get('pixelfed.media_types'));
$rules = ConfigCacheService::get('app.rules') ? json_decode(ConfigCacheService::get('app.rules'), true) : null;
$jpeg = in_array('image/jpg', $types) || in_array('image/jpeg', $types);
$png = in_array('image/png', $types);
$gif = in_array('image/gif', $types);
$mp4 = in_array('video/mp4', $types);
$webp = in_array('image/webp', $types);
$availableAdmins = User::whereIsAdmin(true)->get();
$currentAdmin = config_cache('instance.admin.pid') ? AccountService::get(config_cache('instance.admin.pid'), true) : null;
$openReg = (bool) config_cache('pixelfed.open_registration');
$curOnboarding = (bool) config_cache('instance.curated_registration.enabled');
$regState = $openReg ? 'open' : ($curOnboarding ? 'filtered' : 'closed');
$availableAdmins = User::whereIsAdmin(true)->get();
$currentAdmin = config_cache('instance.admin.pid') ? AccountService::get(config_cache('instance.admin.pid'), true) : null;
return view('admin.settings.home', compact(
'jpeg',
'png',
'gif',
'mp4',
'webp',
'rules',
'cloud_storage',
'cloud_disk',
'cloud_ready',
'availableAdmins',
'currentAdmin',
'regState'
));
}
// $system = [
// 'permissions' => is_writable(base_path('storage')) && is_writable(base_path('bootstrap')),
// 'max_upload_size' => ini_get('post_max_size'),
// 'image_driver' => config('image.driver'),
// 'image_driver_loaded' => extension_loaded(config('image.driver'))
// ];
public function settingsHomeStore(Request $request)
{
$this->validate($request, [
'name' => 'nullable|string',
'short_description' => 'nullable',
'long_description' => 'nullable',
'max_photo_size' => 'nullable|integer|min:1',
'max_album_length' => 'nullable|integer|min:1|max:100',
'image_quality' => 'nullable|integer|min:1|max:100',
'type_jpeg' => 'nullable',
'type_png' => 'nullable',
'type_gif' => 'nullable',
'type_mp4' => 'nullable',
'type_webp' => 'nullable',
'admin_account_id' => 'nullable',
'regs' => 'required|in:open,filtered,closed'
]);
return view('admin.settings.home', compact(
'jpeg',
'png',
'gif',
'mp4',
'webp',
'rules',
'cloud_storage',
'cloud_disk',
'cloud_ready',
'availableAdmins',
'currentAdmin'
// 'system'
));
}
$orb = false;
$cob = false;
switch($request->input('regs')) {
case 'open':
$orb = true;
$cob = false;
break;
public function settingsHomeStore(Request $request)
{
$this->validate($request, [
'name' => 'nullable|string',
'short_description' => 'nullable',
'long_description' => 'nullable',
'max_photo_size' => 'nullable|integer|min:1',
'max_album_length' => 'nullable|integer|min:1|max:100',
'image_quality' => 'nullable|integer|min:1|max:100',
'type_jpeg' => 'nullable',
'type_png' => 'nullable',
'type_gif' => 'nullable',
'type_mp4' => 'nullable',
'type_webp' => 'nullable',
'admin_account_id' => 'nullable',
]);
case 'filtered':
$orb = false;
$cob = true;
break;
if($request->filled('admin_account_id')) {
ConfigCacheService::put('instance.admin.pid', $request->admin_account_id);
Cache::forget('api:v1:instance-data:contact');
Cache::forget('api:v1:instance-data-response-v1');
}
if($request->filled('rule_delete')) {
$index = (int) $request->input('rule_delete');
$rules = ConfigCacheService::get('app.rules');
$json = json_decode($rules, true);
if(!$rules || empty($json)) {
return;
}
unset($json[$index]);
$json = json_encode(array_values($json));
ConfigCacheService::put('app.rules', $json);
Cache::forget('api:v1:instance-data:rules');
Cache::forget('api:v1:instance-data-response-v1');
return 200;
}
case 'closed':
$orb = false;
$cob = false;
break;
}
$media_types = explode(',', config_cache('pixelfed.media_types'));
$media_types_original = $media_types;
ConfigCacheService::put('pixelfed.open_registration', (bool) $orb);
ConfigCacheService::put('instance.curated_registration.enabled', (bool) $cob);
$mimes = [
'type_jpeg' => 'image/jpeg',
'type_png' => 'image/png',
'type_gif' => 'image/gif',
'type_mp4' => 'video/mp4',
'type_webp' => 'image/webp',
];
if($request->filled('admin_account_id')) {
ConfigCacheService::put('instance.admin.pid', $request->admin_account_id);
Cache::forget('api:v1:instance-data:contact');
Cache::forget('api:v1:instance-data-response-v1');
}
if($request->filled('rule_delete')) {
$index = (int) $request->input('rule_delete');
$rules = ConfigCacheService::get('app.rules');
$json = json_decode($rules, true);
if(!$rules || empty($json)) {
return;
}
unset($json[$index]);
$json = json_encode(array_values($json));
ConfigCacheService::put('app.rules', $json);
Cache::forget('api:v1:instance-data:rules');
Cache::forget('api:v1:instance-data-response-v1');
return 200;
}
foreach ($mimes as $key => $value) {
if($request->input($key) == 'on') {
if(!in_array($value, $media_types)) {
array_push($media_types, $value);
}
} else {
$media_types = array_diff($media_types, [$value]);
}
}
$media_types = explode(',', config_cache('pixelfed.media_types'));
$media_types_original = $media_types;
if($media_types !== $media_types_original) {
ConfigCacheService::put('pixelfed.media_types', implode(',', array_unique($media_types)));
}
$mimes = [
'type_jpeg' => 'image/jpeg',
'type_png' => 'image/png',
'type_gif' => 'image/gif',
'type_mp4' => 'video/mp4',
'type_webp' => 'image/webp',
];
$keys = [
'name' => 'app.name',
'short_description' => 'app.short_description',
'long_description' => 'app.description',
'max_photo_size' => 'pixelfed.max_photo_size',
'max_album_length' => 'pixelfed.max_album_length',
'image_quality' => 'pixelfed.image_quality',
'account_limit' => 'pixelfed.max_account_size',
'custom_css' => 'uikit.custom.css',
'custom_js' => 'uikit.custom.js',
'about_title' => 'about.title'
];
foreach ($mimes as $key => $value) {
if($request->input($key) == 'on') {
if(!in_array($value, $media_types)) {
array_push($media_types, $value);
}
} else {
$media_types = array_diff($media_types, [$value]);
}
}
foreach ($keys as $key => $value) {
$cc = ConfigCache::whereK($value)->first();
$val = $request->input($key);
if($cc && $cc->v != $val) {
ConfigCacheService::put($value, $val);
} else if(!empty($val)) {
ConfigCacheService::put($value, $val);
}
}
if($media_types !== $media_types_original) {
ConfigCacheService::put('pixelfed.media_types', implode(',', array_unique($media_types)));
}
$bools = [
'activitypub' => 'federation.activitypub.enabled',
'open_registration' => 'pixelfed.open_registration',
'mobile_apis' => 'pixelfed.oauth_enabled',
'stories' => 'instance.stories.enabled',
'ig_import' => 'pixelfed.import.instagram.enabled',
'spam_detection' => 'pixelfed.bouncer.enabled',
'require_email_verification' => 'pixelfed.enforce_email_verification',
'enforce_account_limit' => 'pixelfed.enforce_account_limit',
'show_custom_css' => 'uikit.show_custom.css',
'show_custom_js' => 'uikit.show_custom.js',
'cloud_storage' => 'pixelfed.cloud_storage',
'account_autofollow' => 'account.autofollow',
'show_directory' => 'instance.landing.show_directory',
'show_explore_feed' => 'instance.landing.show_explore',
];
$keys = [
'name' => 'app.name',
'short_description' => 'app.short_description',
'long_description' => 'app.description',
'max_photo_size' => 'pixelfed.max_photo_size',
'max_album_length' => 'pixelfed.max_album_length',
'image_quality' => 'pixelfed.image_quality',
'account_limit' => 'pixelfed.max_account_size',
'custom_css' => 'uikit.custom.css',
'custom_js' => 'uikit.custom.js',
'about_title' => 'about.title'
];
foreach ($bools as $key => $value) {
$active = $request->input($key) == 'on';
foreach ($keys as $key => $value) {
$cc = ConfigCache::whereK($value)->first();
$val = $request->input($key);
if($cc && $cc->v != $val) {
ConfigCacheService::put($value, $val);
} else if(!empty($val)) {
ConfigCacheService::put($value, $val);
}
}
if($key == 'activitypub' && $active && !InstanceActor::exists()) {
Artisan::call('instance:actor');
}
$bools = [
'activitypub' => 'federation.activitypub.enabled',
// 'open_registration' => 'pixelfed.open_registration',
'mobile_apis' => 'pixelfed.oauth_enabled',
'stories' => 'instance.stories.enabled',
'ig_import' => 'pixelfed.import.instagram.enabled',
'spam_detection' => 'pixelfed.bouncer.enabled',
'require_email_verification' => 'pixelfed.enforce_email_verification',
'enforce_account_limit' => 'pixelfed.enforce_account_limit',
'show_custom_css' => 'uikit.show_custom.css',
'show_custom_js' => 'uikit.show_custom.js',
'cloud_storage' => 'pixelfed.cloud_storage',
'account_autofollow' => 'account.autofollow',
'show_directory' => 'instance.landing.show_directory',
'show_explore_feed' => 'instance.landing.show_explore',
];
if( $key == 'mobile_apis' &&
$active &&
!file_exists(storage_path('oauth-public.key')) &&
!file_exists(storage_path('oauth-private.key'))
) {
Artisan::call('passport:keys');
Artisan::call('route:cache');
}
foreach ($bools as $key => $value) {
$active = $request->input($key) == 'on';
if(config_cache($value) !== $active) {
ConfigCacheService::put($value, (bool) $active);
}
}
if($key == 'activitypub' && $active && !InstanceActor::exists()) {
Artisan::call('instance:actor');
}
if($request->filled('new_rule')) {
$rules = ConfigCacheService::get('app.rules');
$val = $request->input('new_rule');
if(!$rules) {
ConfigCacheService::put('app.rules', json_encode([$val]));
} else {
$json = json_decode($rules, true);
$json[] = $val;
ConfigCacheService::put('app.rules', json_encode(array_values($json)));
}
Cache::forget('api:v1:instance-data:rules');
Cache::forget('api:v1:instance-data-response-v1');
}
if( $key == 'mobile_apis' &&
$active &&
!file_exists(storage_path('oauth-public.key')) &&
!file_exists(storage_path('oauth-private.key'))
) {
Artisan::call('passport:keys');
Artisan::call('route:cache');
}
if($request->filled('account_autofollow_usernames')) {
$usernames = explode(',', $request->input('account_autofollow_usernames'));
$names = [];
if(config_cache($value) !== $active) {
ConfigCacheService::put($value, (bool) $active);
}
}
foreach($usernames as $n) {
$p = Profile::whereUsername($n)->first();
if(!$p) {
continue;
}
array_push($names, $p->username);
}
if($request->filled('new_rule')) {
$rules = ConfigCacheService::get('app.rules');
$val = $request->input('new_rule');
if(!$rules) {
ConfigCacheService::put('app.rules', json_encode([$val]));
} else {
$json = json_decode($rules, true);
$json[] = $val;
ConfigCacheService::put('app.rules', json_encode(array_values($json)));
}
Cache::forget('api:v1:instance-data:rules');
Cache::forget('api:v1:instance-data-response-v1');
}
ConfigCacheService::put('account.autofollow_usernames', implode(',', $names));
}
if($request->filled('account_autofollow_usernames')) {
$usernames = explode(',', $request->input('account_autofollow_usernames'));
$names = [];
Cache::forget(Config::CACHE_KEY);
foreach($usernames as $n) {
$p = Profile::whereUsername($n)->first();
if(!$p) {
continue;
}
array_push($names, $p->username);
}
return redirect('/i/admin/settings')->with('status', 'Successfully updated settings!');
}
ConfigCacheService::put('account.autofollow_usernames', implode(',', $names));
}
public function settingsBackups(Request $request)
{
$path = storage_path('app/'.config('app.name'));
$files = is_dir($path) ? new \DirectoryIterator($path) : [];
return view('admin.settings.backups', compact('files'));
}
Cache::forget(Config::CACHE_KEY);
public function settingsMaintenance(Request $request)
{
return view('admin.settings.maintenance');
}
return redirect('/i/admin/settings')->with('status', 'Successfully updated settings!');
}
public function settingsStorage(Request $request)
{
$storage = [];
return view('admin.settings.storage', compact('storage'));
}
public function settingsBackups(Request $request)
{
$path = storage_path('app/'.config('app.name'));
$files = is_dir($path) ? new \DirectoryIterator($path) : [];
return view('admin.settings.backups', compact('files'));
}
public function settingsFeatures(Request $request)
{
return view('admin.settings.features');
}
public function settingsMaintenance(Request $request)
{
return view('admin.settings.maintenance');
}
public function settingsPages(Request $request)
{
$pages = Page::orderByDesc('updated_at')->paginate(10);
return view('admin.pages.home', compact('pages'));
}
public function settingsStorage(Request $request)
{
$storage = [];
return view('admin.settings.storage', compact('storage'));
}
public function settingsPageEdit(Request $request)
{
return view('admin.pages.edit');
}
public function settingsFeatures(Request $request)
{
return view('admin.settings.features');
}
public function settingsSystem(Request $request)
{
$sys = [
'pixelfed' => config('pixelfed.version'),
'php' => phpversion(),
'laravel' => app()->version(),
];
switch (config('database.default')) {
case 'pgsql':
$exp = DB::raw('select version();');
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
$sys['database'] = [
'name' => 'Postgres',
'version' => explode(' ', DB::select($expQuery)[0]->version)[1]
];
break;
public function settingsPages(Request $request)
{
$pages = Page::orderByDesc('updated_at')->paginate(10);
return view('admin.pages.home', compact('pages'));
}
case 'mysql':
$exp = DB::raw('select version()');
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
$sys['database'] = [
'name' => 'MySQL',
'version' => DB::select($expQuery)[0]->{'version()'}
];
break;
public function settingsPageEdit(Request $request)
{
return view('admin.pages.edit');
}
public function settingsSystem(Request $request)
{
$sys = [
'pixelfed' => config('pixelfed.version'),
'php' => phpversion(),
'laravel' => app()->version(),
];
switch (config('database.default')) {
case 'pgsql':
$exp = DB::raw('select version();');
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
$sys['database'] = [
'name' => 'Postgres',
'version' => explode(' ', DB::select($expQuery)[0]->version)[1]
];
break;
case 'mysql':
$exp = DB::raw('select version()');
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
$sys['database'] = [
'name' => 'MySQL',
'version' => DB::select($expQuery)[0]->{'version()'}
];
break;
default:
$sys['database'] = [
'name' => 'Unknown',
'version' => '?'
];
break;
}
return view('admin.settings.system', compact('sys'));
}
default:
$sys['database'] = [
'name' => 'Unknown',
'version' => '?'
];
break;
}
return view('admin.settings.system', compact('sys'));
}
}

View File

@ -1,226 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\CuratedRegister;
use App\Models\CuratedRegisterActivity;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use App\Mail\CuratedRegisterRequestDetailsFromUser;
use App\Mail\CuratedRegisterAcceptUser;
use App\Mail\CuratedRegisterRejectUser;
use App\User;
class AdminCuratedRegisterController extends Controller
{
public function __construct()
{
$this->middleware(['auth','admin']);
}
public function index(Request $request)
{
$this->validate($request, [
'filter' => 'sometimes|in:open,all,awaiting,approved,rejected'
]);
$filter = $request->input('filter', 'open');
$records = CuratedRegister::when($filter, function($q, $filter) {
if($filter === 'open') {
return $q->where('is_rejected', false)
->whereNotNull('email_verified_at')
->whereIsClosed(false);
} else if($filter === 'all') {
return $q;
} elseif ($filter === 'awaiting') {
return $q->whereIsClosed(false)
->whereNull('is_rejected')
->whereNull('is_approved');
} elseif ($filter === 'approved') {
return $q->whereIsClosed(true)->whereIsApproved(true);
} elseif ($filter === 'rejected') {
return $q->whereIsClosed(true)->whereIsRejected(true);
}
})
->latest()
->paginate(10);
return view('admin.curated-register.index', compact('records', 'filter'));
}
public function show(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
return view('admin.curated-register.show', compact('record'));
}
public function apiActivityLog(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
$res = collect([
[
'id' => 1,
'action' => 'created',
'title' => 'Onboarding application created',
'message' => null,
'link' => null,
'timestamp' => $record->created_at,
]
]);
if($record->email_verified_at) {
$res->push([
'id' => 3,
'action' => 'email_verified_at',
'title' => 'Applicant successfully verified email address',
'message' => null,
'link' => null,
'timestamp' => $record->email_verified_at,
]);
}
$activities = CuratedRegisterActivity::whereRegisterId($record->id)->get();
$idx = 4;
$userResponses = collect([]);
foreach($activities as $activity) {
$idx++;
if($activity->from_user) {
$userResponses->push($activity);
continue;
}
$res->push([
'id' => $idx,
'aid' => $activity->id,
'action' => $activity->type,
'title' => $activity->from_admin ? 'Admin requested info' : 'User responded',
'message' => $activity->message,
'link' => $activity->adminReviewUrl(),
'timestamp' => $activity->created_at,
]);
}
foreach($userResponses as $ur) {
$res = $res->map(function($r) use($ur) {
if(!isset($r['aid'])) {
return $r;
}
if($ur->reply_to_id === $r['aid']) {
$r['user_response'] = $ur;
return $r;
}
return $r;
});
}
if($record->is_approved) {
$idx++;
$res->push([
'id' => $idx,
'action' => 'approved',
'title' => 'Application Approved',
'message' => null,
'link' => null,
'timestamp' => $record->action_taken_at,
]);
} else if ($record->is_rejected) {
$idx++;
$res->push([
'id' => $idx,
'action' => 'rejected',
'title' => 'Application Rejected',
'message' => null,
'link' => null,
'timestamp' => $record->action_taken_at,
]);
}
return $res->reverse()->values();
}
public function apiMessagePreviewStore(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
return $request->all();
}
public function apiMessageSendStore(Request $request, $id)
{
$this->validate($request, [
'message' => 'required|string|min:5|max:1000'
]);
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$activity = new CuratedRegisterActivity;
$activity->register_id = $record->id;
$activity->admin_id = $request->user()->id;
$activity->secret_code = Str::random(32);
$activity->type = 'request_details';
$activity->from_admin = true;
$activity->message = $request->input('message');
$activity->save();
$record->is_awaiting_more_info = true;
$record->save();
Mail::to($record->email)->send(new CuratedRegisterRequestDetailsFromUser($record, $activity));
return $request->all();
}
public function previewDetailsMessageShow(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$activity = new CuratedRegisterActivity;
$activity->message = $request->input('message');
return new \App\Mail\CuratedRegisterRequestDetailsFromUser($record, $activity);
}
public function previewMessageShow(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$record->message = $request->input('message');
return new \App\Mail\CuratedRegisterSendMessage($record);
}
public function apiHandleReject(Request $request, $id)
{
$this->validate($request, [
'action' => 'required|in:reject-email,reject-silent'
]);
$action = $request->input('action');
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
$record->is_rejected = true;
$record->is_closed = true;
$record->action_taken_at = now();
$record->save();
if($action === 'reject-email') {
Mail::to($record->email)->send(new CuratedRegisterRejectUser($record));
}
return [200];
}
public function apiHandleApprove(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
$record->is_approved = true;
$record->is_closed = true;
$record->action_taken_at = now();
$record->save();
$user = User::create([
'name' => $record->username,
'username' => $record->username,
'email' => $record->email,
'password' => $record->password,
'app_register_ip' => $record->ip_address,
'email_verified_at' => now(),
'register_source' => 'cur_onboarding'
]);
Mail::to($record->email)->send(new CuratedRegisterAcceptUser($record));
return [200];
}
}

View File

@ -40,20 +40,16 @@ class AdminApiController extends Controller
{
public function supported(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
return response()->json(['supported' => true]);
}
public function getStats(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$res = AdminStatsService::summary();
$res['autospam_count'] = AccountInterstitial::whereType('post.autospam')
@ -64,10 +60,8 @@ class AdminApiController extends Controller
public function autospam(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$appeals = AccountInterstitial::whereType('post.autospam')
->whereNull('appeal_handled_at')
@ -101,10 +95,8 @@ class AdminApiController extends Controller
public function autospamHandle(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-post,delete-account',
@ -247,10 +239,8 @@ class AdminApiController extends Controller
public function modReports(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$reports = Report::whereNull('admin_seen')
->orderBy('created_at','desc')
@ -295,10 +285,8 @@ class AdminApiController extends Controller
public function modReportHandle(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'action' => 'required|string',
@ -355,11 +343,8 @@ class AdminApiController extends Controller
public function getConfiguration(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
abort_unless(config('instance.enable_cc'), 400);
return collect([
@ -401,11 +386,8 @@ class AdminApiController extends Controller
public function updateConfiguration(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
abort_unless(config('instance.enable_cc'), 400);
$this->validate($request, [
@ -466,11 +448,8 @@ class AdminApiController extends Controller
public function getUsers(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$this->validate($request, [
'sort' => 'sometimes|in:asc,desc',
]);
@ -487,10 +466,8 @@ class AdminApiController extends Controller
public function getUser(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$id = $request->input('user_id');
$key = 'pf-admin-api:getUser:byId:' . $id;
@ -520,10 +497,8 @@ class AdminApiController extends Controller
public function userAdminAction(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
@ -694,10 +669,8 @@ class AdminApiController extends Controller
public function instances(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'q' => 'sometimes',
@ -734,10 +707,8 @@ class AdminApiController extends Controller
public function getInstance(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$id = $request->input('id');
$res = Instance::findOrFail($id);
@ -747,10 +718,8 @@ class AdminApiController extends Controller
public function moderateInstance(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
@ -773,10 +742,8 @@ class AdminApiController extends Controller
public function refreshInstanceStats(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
@ -793,10 +760,8 @@ class AdminApiController extends Controller
public function getAllStats(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_if(!$request->user(), 404);
abort_unless($request->user()->is_admin === 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
if($request->has('refresh')) {
Cache::forget('admin-api:instance-all-stats-v1');

File diff suppressed because it is too large Load Diff

View File

@ -68,10 +68,9 @@ class ApiV1Dot1Controller extends Controller
public function report(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@ -176,10 +175,9 @@ class ApiV1Dot1Controller extends Controller
*/
public function deleteAvatar(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@ -217,10 +215,9 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountPosts(Request $request, $id)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@ -258,10 +255,8 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountChangePassword(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -285,8 +280,8 @@ class ApiV1Dot1Controller extends Controller
$log->action = 'account.edit.password';
$log->message = 'Password changed';
$log->link = null;
$log->ip_address = "127.0.0.23";
$log->user_agent = "Pixelfed.de";
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
Mail::to($request->user())->send(new PasswordChange($user));
@ -301,16 +296,14 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountLoginActivity(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$agent = new Agent();
$currentIp = "127.0.0.23";
$currentIp = $request->ip();
$activity = AccountLog::whereUserId($user->id)
->whereAction('auth.login')
@ -323,8 +316,8 @@ class ApiV1Dot1Controller extends Controller
return [
'id' => $item->id,
'action' => $item->action,
'ip' => "127.0.0.23",
'ip_current' => "127.0.0.23" === $currentIp,
'ip' => $item->ip_address,
'ip_current' => $item->ip_address === $currentIp,
'is_mobile' => $agent->isMobile(),
'device' => $agent->device(),
'browser' => $agent->browser(),
@ -343,10 +336,8 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountTwoFactor(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@ -367,10 +358,8 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountEmailsFromPixelfed(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -444,10 +433,8 @@ class ApiV1Dot1Controller extends Controller
*/
public function accountApps(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$user = $request->user();
abort_if(!$user, 403);
abort_if($user->status != null, 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@ -487,7 +474,7 @@ class ApiV1Dot1Controller extends Controller
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$rl = RateLimiter::attempt('pf:apiv1.1:iar:'.Str::Random(10), config('pixelfed.app_registration_rate_limit_attempts', 3), function(){}, config('pixelfed.app_registration_rate_limit_decay', 1800));
$rl = RateLimiter::attempt('pf:apiv1.1:iar:'.$request->ip(), config('pixelfed.app_registration_rate_limit_attempts', 3), function(){}, config('pixelfed.app_registration_rate_limit_decay', 1800));
abort_if(!$rl, 400, 'Too many requests');
$this->validate($request, [
@ -559,7 +546,7 @@ class ApiV1Dot1Controller extends Controller
$user->email = $email;
$user->password = Hash::make($password);
$user->register_source = 'app';
$user->app_register_ip = "127.0.0.23";
$user->app_register_ip = $request->ip();
$user->app_register_token = Str::random(40);
$user->save();
@ -616,7 +603,7 @@ class ApiV1Dot1Controller extends Controller
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$rl = RateLimiter::attempt('pf:apiv1.1:iarc:'.Str::Random(10), config('pixelfed.app_registration_confirm_rate_limit_attempts', 20), function(){}, config('pixelfed.app_registration_confirm_rate_limit_decay', 1800));
$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');
$this->validate($request, [
@ -653,8 +640,7 @@ class ApiV1Dot1Controller extends Controller
public function archive(Request $request, $id)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
abort_if(!$request->user(), 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -686,8 +672,7 @@ class ApiV1Dot1Controller extends Controller
public function unarchive(Request $request, $id)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
abort_if(!$request->user(), 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -718,8 +703,7 @@ class ApiV1Dot1Controller extends Controller
public function archivedPosts(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
abort_if(!$request->user(), 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -735,8 +719,7 @@ class ApiV1Dot1Controller extends Controller
public function placesById(Request $request, $id, $slug)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
abort_if(!$request->user(), 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -774,9 +757,8 @@ class ApiV1Dot1Controller extends Controller
public function moderatePost(Request $request, $id)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_if(!$request->user(), 403);
abort_if($request->user()->is_admin != true, 403);
abort_unless($request->user()->tokenCan('admin:write'), 403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
@ -882,9 +864,7 @@ class ApiV1Dot1Controller extends Controller
public function getWebSettings(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
abort_if(!$request->user(), 403);
$uid = $request->user()->id;
$settings = UserSetting::firstOrCreate([
'user_id' => $uid
@ -897,9 +877,7 @@ class ApiV1Dot1Controller extends Controller
public function setWebSettings(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
abort_if(!$request->user(), 403);
$this->validate($request, [
'field' => 'required|in:enable_reblogs,hide_reblog_banner',
'value' => 'required'
@ -923,9 +901,7 @@ class ApiV1Dot1Controller extends Controller
public function getMutualAccounts(Request $request, $id)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('follows'), 403);
abort_if(!$request->user(), 403);
$account = AccountService::get($id, true);
if(!$account || !isset($account['id'])) { return []; }
$res = collect(FollowerService::mutualAccounts($request->user()->profile_id, $id))

View File

@ -71,77 +71,69 @@ class ApiV2Controller extends Controller
->toArray() : [];
});
$res = Cache::remember('api:v2:instance-data-response-v2', 1800, function () use($contact, $rules) {
return [
'domain' => config('pixelfed.domain.app'),
'title' => config_cache('app.name'),
'version' => '3.5.3 (compatible; Pixelfed ' . config('pixelfed.version') .')',
'source_url' => 'https://github.com/pixelfed/pixelfed',
'description' => config_cache('app.short_description'),
'usage' => [
'users' => [
'active_month' => (int) Nodeinfo::activeUsersMonthly()
]
$res = [
'domain' => config('pixelfed.domain.app'),
'title' => config_cache('app.name'),
'version' => config('pixelfed.version'),
'source_url' => 'https://github.com/pixelfed/pixelfed',
'description' => config_cache('app.short_description'),
'usage' => [
'users' => [
'active_month' => (int) Nodeinfo::activeUsersMonthly()
]
],
'thumbnail' => [
'url' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'blurhash' => InstanceService::headerBlurhash(),
'versions' => [
'@1x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'@2x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'))
]
],
'languages' => [config('app.locale')],
'configuration' => [
'urls' => [
'streaming' => 'wss://' . config('pixelfed.domain.app'),
'status' => null
],
'thumbnail' => [
'url' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'blurhash' => InstanceService::headerBlurhash(),
'versions' => [
'@1x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'@2x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'))
]
'accounts' => [
'max_featured_tags' => 0,
],
'languages' => [config('app.locale')],
'configuration' => [
'urls' => [
'streaming' => null,
'status' => null
],
'vapid' => [
'public_key' => config('webpush.vapid.public_key'),
],
'accounts' => [
'max_featured_tags' => 0,
],
'statuses' => [
'max_characters' => (int) config('pixelfed.max_caption_length'),
'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'),
'characters_reserved_per_url' => 23
],
'media_attachments' => [
'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')),
'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'image_matrix_limit' => 3686400,
'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'video_frame_rate_limit' => 240,
'video_matrix_limit' => 3686400
],
'polls' => [
'max_options' => 0,
'max_characters_per_option' => 0,
'min_expiration' => 0,
'max_expiration' => 0,
],
'translation' => [
'enabled' => false,
],
'statuses' => [
'max_characters' => (int) config('pixelfed.max_caption_length'),
'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'),
'characters_reserved_per_url' => 23
],
'registrations' => [
'enabled' => null,
'approval_required' => false,
'message' => null,
'url' => null,
'media_attachments' => [
'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')),
'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'image_matrix_limit' => 3686400,
'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'video_frame_rate_limit' => 240,
'video_matrix_limit' => 3686400
],
'contact' => [
'email' => config('instance.email'),
'account' => $contact
'polls' => [
'max_options' => 4,
'max_characters_per_option' => 50,
'min_expiration' => 300,
'max_expiration' => 2629746,
],
'rules' => $rules
];
});
'translation' => [
'enabled' => false,
],
],
'registrations' => [
'enabled' => (bool) config_cache('pixelfed.open_registration'),
'approval_required' => false,
'message' => null
],
'contact' => [
'email' => config('instance.email'),
'account' => $contact
],
'rules' => $rules
];
$res['registrations']['enabled'] = (bool) config_cache('pixelfed.open_registration');
$res['registrations']['approval_required'] = (bool) config_cache('instance.curated_registration.enabled');
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
@ -153,8 +145,7 @@ class ApiV2Controller extends Controller
*/
public function search(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
abort_if(!$request->user(), 403);
$this->validate($request, [
'q' => 'required|string|min:1|max:100',
@ -205,8 +196,7 @@ class ApiV2Controller extends Controller
*/
public function mediaUploadV2(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
abort_if(!$request->user(), 403);
$this->validate($request, [
'file.*' => [

View File

@ -99,7 +99,6 @@ class BaseApiController extends Controller
public function avatarUpdate(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'upload' => 'required|mimetypes:image/jpeg,image/jpg,image/png|max:'.config('pixelfed.max_avatar_size'),
]);
@ -135,10 +134,9 @@ class BaseApiController extends Controller
public function verifyCredentials(Request $request)
{
abort_if(!$request->user(), 403);
$user = $request->user();
if ($user->status != null) {
abort_if(!$user, 403);
if($user->status != null) {
Auth::logout();
abort(403);
}
@ -149,7 +147,6 @@ class BaseApiController extends Controller
public function accountLikes(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'page' => 'sometimes|int|min:1|max:20',
'limit' => 'sometimes|int|min:1|max:10'

View File

@ -23,8 +23,7 @@ class DomainBlockController extends Controller
public function index(Request $request)
{
abort_if(!$request->user(), 403);
abort_unless($request->user(), 403);
$this->validate($request, [
'limit' => 'sometimes|integer|min:1|max:200'
]);
@ -53,7 +52,7 @@ class DomainBlockController extends Controller
public function store(Request $request)
{
abort_if(!$request->user(), 403);
abort_unless($request->user(), 403);
$this->validate($request, [
'domain' => 'required|active_url|min:1|max:120'
@ -100,7 +99,7 @@ class DomainBlockController extends Controller
public function delete(Request $request)
{
abort_if(!$request->user(), 403);
abort_unless($request->user(), 403);
$this->validate($request, [
'domain' => 'required|min:1|max:120'

View File

@ -32,9 +32,7 @@ class TagsController extends Controller
*/
public function relatedTags(Request $request, $tag)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
abort_unless($request->user(), 403);
$tag = Hashtag::whereSlug($tag)->firstOrFail();
return HashtagRelatedService::get($tag->id);
}

View File

@ -109,8 +109,8 @@ class LoginController extends Controller
$log->action = 'auth.login';
$log->message = 'Account Login';
$log->link = null;
$log->ip_address = "127.0.0.23";
$log->user_agent = "Pixelfed.de";
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
}

View File

@ -70,7 +70,7 @@ class RegisterController extends Controller
$usernameRules = [
'required',
'min:2',
'max:30',
'max:15',
'unique:users',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
@ -174,7 +174,7 @@ class RegisterController extends Controller
*/
public function showRegistrationForm()
{
if((bool) config_cache('pixelfed.open_registration')) {
if(config_cache('pixelfed.open_registration')) {
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp(request()->ip()), 404);
}
@ -191,11 +191,7 @@ class RegisterController extends Controller
return view('auth.register');
}
} else {
if((bool) config_cache('instance.curated_registration.enabled') && config('instance.curated_registration.state.fallback_on_closed_reg')) {
return redirect('/auth/sign_up');
} else {
abort(404);
}
abort(404);
}
}

View File

@ -260,8 +260,6 @@ class ComposeController extends Controller
$q = mb_substr($q, 1);
}
$user = $request->user();
abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
$blocked = UserFilter::whereFilterableType('App\Profile')

View File

@ -1,398 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\User;
use App\Models\CuratedRegister;
use App\Models\CuratedRegisterActivity;
use App\Services\EmailService;
use App\Services\BouncerService;
use App\Util\Lexer\RestrictedNames;
use App\Mail\CuratedRegisterConfirmEmail;
use App\Mail\CuratedRegisterNotifyAdmin;
use Illuminate\Support\Facades\Mail;
use App\Jobs\CuratedOnboarding\CuratedOnboardingNotifyAdminNewApplicationPipeline;
class CuratedRegisterController extends Controller
{
public function __construct()
{
abort_unless((bool) config_cache('instance.curated_registration.enabled'), 404);
if((bool) config_cache('pixelfed.open_registration')) {
abort_if(config('instance.curated_registration.state.only_enabled_on_closed_reg'), 404);
} else {
abort_unless(config('instance.curated_registration.state.fallback_on_closed_reg'), 404);
}
}
public function index(Request $request)
{
abort_if($request->user(), 404);
return view('auth.curated-register.index', ['step' => 1]);
}
public function concierge(Request $request)
{
abort_if($request->user(), 404);
$emailConfirmed = $request->session()->has('cur-reg-con.email-confirmed') &&
$request->has('next') &&
$request->session()->has('cur-reg-con.cr-id');
return view('auth.curated-register.concierge', compact('emailConfirmed'));
}
public function conciergeResponseSent(Request $request)
{
return view('auth.curated-register.user_response_sent');
}
public function conciergeFormShow(Request $request)
{
abort_if($request->user(), 404);
abort_unless(
$request->session()->has('cur-reg-con.email-confirmed') &&
$request->session()->has('cur-reg-con.cr-id') &&
$request->session()->has('cur-reg-con.ac-id'), 404);
$crid = $request->session()->get('cur-reg-con.cr-id');
$arid = $request->session()->get('cur-reg-con.ac-id');
$showCaptcha = config('instance.curated_registration.captcha_enabled');
if($attempts = $request->session()->get('cur-reg-con-attempt')) {
$showCaptcha = $attempts && $attempts >= 2;
} else {
$showCaptcha = false;
}
$activity = CuratedRegisterActivity::whereRegisterId($crid)->whereFromAdmin(true)->findOrFail($arid);
return view('auth.curated-register.concierge_form', compact('activity', 'showCaptcha'));
}
public function conciergeFormStore(Request $request)
{
abort_if($request->user(), 404);
$request->session()->increment('cur-reg-con-attempt');
abort_unless(
$request->session()->has('cur-reg-con.email-confirmed') &&
$request->session()->has('cur-reg-con.cr-id') &&
$request->session()->has('cur-reg-con.ac-id'), 404);
$attempts = $request->session()->get('cur-reg-con-attempt');
$messages = [];
$rules = [
'response' => 'required|string|min:5|max:1000',
'crid' => 'required|integer|min:1',
'acid' => 'required|integer|min:1'
];
if(config('instance.curated_registration.captcha_enabled') && $attempts >= 3) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$crid = $request->session()->get('cur-reg-con.cr-id');
$acid = $request->session()->get('cur-reg-con.ac-id');
abort_if((string) $crid !== $request->input('crid'), 404);
abort_if((string) $acid !== $request->input('acid'), 404);
if(CuratedRegisterActivity::whereRegisterId($crid)->whereReplyToId($acid)->exists()) {
return redirect()->back()->withErrors(['code' => 'You already replied to this request.']);
}
$act = CuratedRegisterActivity::create([
'register_id' => $crid,
'reply_to_id' => $acid,
'type' => 'user_response',
'message' => $request->input('response'),
'from_user' => true,
'action_required' => true,
]);
$request->session()->pull('cur-reg-con');
$request->session()->pull('cur-reg-con-attempt');
return view('auth.curated-register.user_response_sent');
}
public function conciergeStore(Request $request)
{
abort_if($request->user(), 404);
$rules = [
'sid' => 'required_if:action,email|integer|min:1|max:20000000',
'id' => 'required_if:action,email|integer|min:1|max:20000000',
'code' => 'required_if:action,email',
'action' => 'required|string|in:email,message',
'email' => 'required_if:action,email|email',
'response' => 'required_if:action,message|string|min:20|max:1000',
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$action = $request->input('action');
$sid = $request->input('sid');
$id = $request->input('id');
$code = $request->input('code');
$email = $request->input('email');
$cr = CuratedRegister::whereIsClosed(false)->findOrFail($sid);
$ac = CuratedRegisterActivity::whereRegisterId($cr->id)->whereFromAdmin(true)->findOrFail($id);
if(!hash_equals($ac->secret_code, $code)) {
return redirect()->back()->withErrors(['code' => 'Invalid code']);
}
if(!hash_equals($cr->email, $email)) {
return redirect()->back()->withErrors(['email' => 'Invalid email']);
}
$request->session()->put('cur-reg-con.email-confirmed', true);
$request->session()->put('cur-reg-con.cr-id', $cr->id);
$request->session()->put('cur-reg-con.ac-id', $ac->id);
$emailConfirmed = true;
return redirect('/auth/sign_up/concierge/form');
}
public function confirmEmail(Request $request)
{
if($request->user()) {
return redirect(route('help.email-confirmation-issues'));
}
return view('auth.curated-register.confirm_email');
}
public function emailConfirmed(Request $request)
{
if($request->user()) {
return redirect(route('help.email-confirmation-issues'));
}
return view('auth.curated-register.email_confirmed');
}
public function resendConfirmation(Request $request)
{
return view('auth.curated-register.resend-confirmation');
}
public function resendConfirmationProcess(Request $request)
{
$rules = [
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'exists:curated_registers',
]
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$cur = CuratedRegister::whereEmail($request->input('email'))->whereIsClosed(false)->first();
if(!$cur) {
return redirect()->back()->withErrors(['email' => 'The selected email is invalid.']);
}
$totalCount = CuratedRegisterActivity::whereRegisterId($cur->id)
->whereType('user_resend_email_confirmation')
->count();
if($totalCount && $totalCount >= config('instance.curated_registration.resend_confirmation_limit')) {
return redirect()->back()->withErrors(['email' => 'You have re-attempted too many times. To proceed with your application, please <a href="/site/contact" class="text-white" style="text-decoration: underline;">contact the admin team</a>.']);
}
$count = CuratedRegisterActivity::whereRegisterId($cur->id)
->whereType('user_resend_email_confirmation')
->where('created_at', '>', now()->subHours(12))
->count();
if($count) {
return redirect()->back()->withErrors(['email' => 'You can only re-send the confirmation email once per 12 hours. Try again later.']);
}
CuratedRegisterActivity::create([
'register_id' => $cur->id,
'type' => 'user_resend_email_confirmation',
'admin_only_view' => true,
'from_admin' => false,
'from_user' => false,
'action_required' => false,
]);
Mail::to($cur->email)->send(new CuratedRegisterConfirmEmail($cur));
return view('auth.curated-register.resent-confirmation');
return $request->all();
}
public function confirmEmailHandle(Request $request)
{
$rules = [
'sid' => 'required',
'code' => 'required'
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$cr = CuratedRegister::whereNull('email_verified_at')
->where('created_at', '>', now()->subHours(24))
->find($request->input('sid'));
if(!$cr) {
return redirect(route('help.email-confirmation-issues'));
}
if(!hash_equals($cr->verify_code, $request->input('code'))) {
return redirect(route('help.email-confirmation-issues'));
}
$cr->email_verified_at = now();
$cr->save();
if(config('instance.curated_registration.notify.admin.on_verify_email.enabled')) {
CuratedOnboardingNotifyAdminNewApplicationPipeline::dispatch($cr);
}
return view('auth.curated-register.email_confirmed');
}
public function proceed(Request $request)
{
$this->validate($request, [
'step' => 'required|integer|in:1,2,3,4'
]);
$step = $request->input('step');
switch($step) {
case 1:
$step = 2;
$request->session()->put('cur-step', 1);
return view('auth.curated-register.index', compact('step'));
break;
case 2:
$this->stepTwo($request);
$step = 3;
$request->session()->put('cur-step', 2);
return view('auth.curated-register.index', compact('step'));
break;
case 3:
$this->stepThree($request);
$step = 3;
$request->session()->put('cur-step', 3);
$verifiedEmail = true;
$request->session()->pull('cur-reg');
return view('auth.curated-register.index', compact('step', 'verifiedEmail'));
break;
}
}
protected function stepTwo($request)
{
if($request->filled('reason')) {
$request->session()->put('cur-reg.form-reason', $request->input('reason'));
}
if($request->filled('username')) {
$request->session()->put('cur-reg.form-username', $request->input('username'));
}
if($request->filled('email')) {
$request->session()->put('cur-reg.form-email', $request->input('email'));
}
$this->validate($request, [
'username' => [
'required',
'min:2',
'max:15',
'unique:curated_registers',
'unique:users',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
$underscore = substr_count($value, '_');
$period = substr_count($value, '.');
if(ends_with($value, ['.php', '.js', '.css'])) {
return $fail('Username is invalid.');
}
if(($dash + $underscore + $period) > 1) {
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
}
if (!ctype_alnum($value[0])) {
return $fail('Username is invalid. Must start with a letter or number.');
}
if (!ctype_alnum($value[strlen($value) - 1])) {
return $fail('Username is invalid. Must end with a letter or number.');
}
$val = str_replace(['_', '.', '-'], '', $value);
if(!ctype_alnum($val)) {
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
}
$restricted = RestrictedNames::get();
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
return $fail('Username cannot be used.');
}
},
],
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'max:255',
'unique:users',
'unique:curated_registers',
function ($attribute, $value, $fail) {
$banned = EmailService::isBanned($value);
if($banned) {
return $fail('Email is invalid.');
}
},
],
'password' => 'required|min:8',
'password_confirmation' => 'required|same:password',
'reason' => 'required|min:20|max:1000',
'agree' => 'required|accepted'
]);
$request->session()->put('cur-reg.form-email', $request->input('email'));
$request->session()->put('cur-reg.form-password', $request->input('password'));
}
protected function stepThree($request)
{
$this->validate($request, [
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'max:255',
'unique:users',
'unique:curated_registers',
function ($attribute, $value, $fail) {
$banned = EmailService::isBanned($value);
if($banned) {
return $fail('Email is invalid.');
}
},
]
]);
$cr = new CuratedRegister;
$cr->email = $request->email;
$cr->username = $request->session()->get('cur-reg.form-username');
$cr->password = bcrypt($request->session()->get('cur-reg.form-password'));
$cr->ip_address = $request->ip();
$cr->reason_to_join = $request->session()->get('cur-reg.form-reason');
$cr->verify_code = Str::random(40);
$cr->save();
Mail::to($cr->email)->send(new CuratedRegisterConfirmEmail($cr));
}
}

View File

@ -279,16 +279,16 @@ class DiscoverController extends Controller
}
return [
'hashtags' => [
'enabled' => true,
'enabled' => false,
],
'memories' => [
'enabled' => true,
'enabled' => false,
],
'insights' => [
'enabled' => true,
'enabled' => false,
],
'friends' => [
'enabled' => true,
'enabled' => false,
],
'server' => [
'enabled' => false,

View File

@ -253,7 +253,7 @@ class FederationController extends Controller
'type' => 'OrderedCollection',
'totalItems' => $account['following_count'] ?? 0,
];
return response()->json($obj)->header('Content-Type', 'application/activity+json');
return response()->json($obj);
}
public function userFollowers(Request $request, $username)
@ -269,6 +269,6 @@ class FederationController extends Controller
'type' => 'OrderedCollection',
'totalItems' => $account['followers_count'] ?? 0,
];
return response()->json($obj)->header('Content-Type', 'application/activity+json');
return response()->json($obj);
}
}

View File

@ -42,7 +42,6 @@ use App\Services\{
use App\Jobs\StatusPipeline\NewStatusPipeline;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\InstanceService;
class PublicApiController extends Controller
{
@ -662,10 +661,6 @@ class PublicApiController extends Controller
public function account(Request $request, $id)
{
$res = AccountService::get($id);
if($res && isset($res['local'], $res['url']) && !$res['local']) {
$domain = parse_url($res['url'], PHP_URL_HOST);
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
}
return response()->json($res);
}
@ -685,11 +680,6 @@ class PublicApiController extends Controller
$profile = AccountService::get($id);
abort_if(!$profile, 404);
if($profile && isset($profile['local'], $profile['url']) && !$profile['local']) {
$domain = parse_url($profile['url'], PHP_URL_HOST);
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
}
$limit = $request->limit ?? 9;
$max_id = $request->max_id;
$min_id = $request->min_id;

View File

@ -320,7 +320,7 @@ class RemoteAuthController extends Controller
'webfinger' => $res['_webfinger'],
], [
'software' => 'mastodon',
'ip_address' => "127.0.0.23",
'ip_address' => $request->ip(),
'bearer_token' => $token,
'verify_credentials' => $res,
'last_verify_credentials_at' => now(),
@ -702,7 +702,7 @@ class RemoteAuthController extends Controller
'email' => $data['email'],
'password' => Hash::make($data['password']),
'email_verified_at' => config('remote-auth.mastodon.contraints.skip_email_verification') ? now() : null,
'app_register_ip' => "127.0.0.23",
'app_register_ip' => request()->ip(),
'register_source' => 'mastodon'
])));

View File

@ -230,8 +230,8 @@ class SeasonalController extends Controller
'action' => 'seasonal.my2020.view'
],
[
'ip_address' => "127.0.0.23",
'user_agent' => "Pixelfed.de"
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent()
]
]);
return response()->json(200);

View File

@ -139,8 +139,8 @@ trait HomeSettings
$log->action = 'account.edit.password';
$log->message = 'Password changed';
$log->link = null;
$log->ip_address = "127.0.0.23";
$log->user_agent = "Pixelfed.de";
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
Mail::to($request->user())->send(new PasswordChange($user));
@ -186,8 +186,8 @@ trait HomeSettings
$log->action = 'account.edit.email';
$log->message = 'Email changed';
$log->link = null;
$log->ip_address = "127.0.0.23";
$log->user_agent = "Pixelfed.de";
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\Internal\SoftwareUpdateService;
class SoftwareUpdateController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('admin');
}
public function getSoftwareUpdateCheck(Request $request)
{
$res = SoftwareUpdateService::get();
return $res;
}
}

View File

@ -80,8 +80,8 @@ class UserEmailForgotController extends Controller
{
UserEmailForgot::create([
'user_id' => $user->id,
'ip_address' => "127.0.0.23",
'user_agent' => "Pixelfed.de",
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'email_sent_at' => now()
]);

View File

@ -6,77 +6,63 @@ use Illuminate\Database\Eloquent\Model;
class Instance extends Model
{
protected $casts = [
'last_crawled_at' => 'datetime',
'actors_last_synced_at' => 'datetime',
'notes' => 'array',
'nodeinfo_last_fetched' => 'datetime',
'delivery_next_after' => 'datetime',
];
protected $fillable = ['domain', 'banned', 'auto_cw', 'unlisted', 'notes'];
protected $fillable = [
'domain',
'banned',
'auto_cw',
'unlisted',
'notes'
];
public function profiles()
{
return $this->hasMany(Profile::class, 'domain', 'domain');
}
public function profiles()
{
return $this->hasMany(Profile::class, 'domain', 'domain');
}
public function statuses()
{
return $this->hasManyThrough(
Status::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function statuses()
{
return $this->hasManyThrough(
Status::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function reported()
{
return $this->hasManyThrough(
Report::class,
Profile::class,
'domain',
'reported_profile_id',
'domain',
'id'
);
}
public function reported()
{
return $this->hasManyThrough(
Report::class,
Profile::class,
'domain',
'reported_profile_id',
'domain',
'id'
);
}
public function reports()
{
return $this->hasManyThrough(
Report::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function reports()
{
return $this->hasManyThrough(
Report::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function media()
{
return $this->hasManyThrough(
Media::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function media()
{
return $this->hasManyThrough(
Media::class,
Profile::class,
'domain',
'profile_id',
'domain',
'id'
);
}
public function getUrl()
{
return url("/i/admin/instances/show/{$this->id}");
}
public function getUrl()
{
return url("/i/admin/instances/show/{$this->id}");
}
}

View File

@ -91,21 +91,19 @@ class CommentPipeline implements ShouldQueue
return;
}
if($target->user_id && $target->domain === null) {
DB::transaction(function() use($target, $actor, $comment) {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->item_id = $comment->id;
$notification->item_type = "App\Status";
$notification->save();
DB::transaction(function() use($target, $actor, $comment) {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->item_id = $comment->id;
$notification->item_type = "App\Status";
$notification->save();
NotificationService::setNotification($notification);
NotificationService::set($notification->profile_id, $notification->id);
StatusService::del($comment->id);
});
}
NotificationService::setNotification($notification);
NotificationService::set($notification->profile_id, $notification->id);
StatusService::del($comment->id);
});
if($exists = Cache::get('status:replies:all:' . $status->id)) {
if($exists && $exists->count() == 3) {

View File

@ -1,65 +0,0 @@
<?php
namespace App\Jobs\CuratedOnboarding;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\CuratedRegister;
use App\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use App\Mail\CuratedRegisterNotifyAdmin;
class CuratedOnboardingNotifyAdminNewApplicationPipeline implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $cr;
/**
* Create a new job instance.
*/
public function __construct(CuratedRegister $cr)
{
$this->cr = $cr;
}
/**
* Execute the job.
*/
public function handle(): void
{
if(!config('instance.curated_registration.notify.admin.on_verify_email.enabled')) {
return;
}
config('instance.curated_registration.notify.admin.on_verify_email.bundle') ?
$this->handleBundled() :
$this->handleUnbundled();
}
protected function handleBundled()
{
$cr = $this->cr;
Storage::append('conanap.json', json_encode([
'id' => $cr->id,
'email' => $cr->email,
'created_at' => $cr->created_at,
'updated_at' => $cr->updated_at,
]));
}
protected function handleUnbundled()
{
$cr = $this->cr;
if($aid = config_cache('instance.admin.pid')) {
$admin = User::whereProfileId($aid)->first();
if($admin && $admin->email) {
Mail::to($admin->email)->send(new CuratedRegisterNotifyAdmin($cr));
}
}
}
}

View File

@ -22,9 +22,9 @@ use App\Notification;
use App\Services\AccountService;
use App\Services\NetworkTimelineService;
use App\Services\StatusService;
use App\Jobs\ProfilePipeline\DecrementPostCount;
use App\Jobs\MediaPipeline\MediaDeletePipeline;
use Cache;
use App\Services\Account\AccountStatService;
class DeleteRemoteStatusPipeline implements ShouldQueue
{
@ -56,7 +56,10 @@ class DeleteRemoteStatusPipeline implements ShouldQueue
{
$status = $this->status;
AccountStatService::decrementPostCount($status->profile_id);
if(AccountService::get($status->profile_id, true)) {
DecrementPostCount::dispatch($status->profile_id)->onQueue('low');
}
NetworkTimelineService::del($status->id);
StatusService::del($status->id, true);
Bookmark::whereStatusId($status->id)->delete();

View File

@ -72,18 +72,16 @@ class FollowPipeline implements ShouldQueue
$target->save();
AccountService::del($target->id);
if($target->user_id && $target->domain === null) {
try {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'follow';
$notification->item_id = $target->id;
$notification->item_type = "App\Profile";
$notification->save();
} catch (Exception $e) {
Log::error($e);
}
try {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'follow';
$notification->item_id = $target->id;
$notification->item_type = "App\Profile";
$notification->save();
} catch (Exception $e) {
Log::error($e);
}
}
}

View File

@ -4,7 +4,6 @@ namespace App\Jobs\InstancePipeline;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
@ -13,71 +12,45 @@ use Illuminate\Support\Facades\Http;
use App\Instance;
use App\Profile;
use App\Services\NodeinfoService;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Support\Facades\Cache;
class FetchNodeinfoPipeline implements ShouldQueue, ShouldBeUniqueUntilProcessing
class FetchNodeinfoPipeline implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $instance;
protected $instance;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Instance $instance)
{
$this->instance = $instance;
}
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Instance $instance)
{
$this->instance = $instance;
}
/**
* The number of seconds after which the job's unique lock will be released.
*
* @var int
*/
public $uniqueFor = 14400;
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$instance = $this->instance;
/**
* Get the unique ID for the job.
*/
public function uniqueId(): string
{
return $this->instance->id;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$instance = $this->instance;
if( $instance->nodeinfo_last_fetched &&
$instance->nodeinfo_last_fetched->gt(now()->subHours(12)) ||
$instance->delivery_timeout &&
$instance->delivery_next_after->gt(now())
) {
return;
}
$ni = NodeinfoService::get($instance->domain);
$instance->last_crawled_at = now();
if($ni) {
if(isset($ni['software']) && is_array($ni['software']) && isset($ni['software']['name'])) {
$software = $ni['software']['name'];
$instance->software = strtolower(strip_tags($software));
$instance->user_count = Profile::whereDomain($instance->domain)->count();
$instance->nodeinfo_last_fetched = now();
$instance->save();
}
} else {
$instance->delivery_timeout = 1;
$instance->delivery_next_after = now()->addHours(14);
$instance->save();
}
}
$ni = NodeinfoService::get($instance->domain);
if($ni) {
if(isset($ni['software']) && is_array($ni['software']) && isset($ni['software']['name'])) {
$software = $ni['software']['name'];
$instance->software = strtolower(strip_tags($software));
$instance->last_crawled_at = now();
$instance->user_count = Profile::whereDomain($instance->domain)->count();
$instance->save();
}
} else {
$instance->user_count = Profile::whereDomain($instance->domain)->count();
$instance->last_crawled_at = now();
$instance->save();
}
}
}

View File

@ -61,12 +61,7 @@ class NotificationEpochUpdatePipeline implements ShouldQueue, ShouldBeUniqueUnti
*/
public function handle(): void
{
$pid = Cache::get(NotificationService::EPOCH_CACHE_KEY . '6');
if($pid && $pid > 1) {
$rec = Notification::where('id', '>', $pid)->whereDate('created_at', now()->subMonths(6)->format('Y-m-d'))->first();
} else {
$rec = Notification::whereDate('created_at', now()->subMonths(6)->format('Y-m-d'))->first();
}
$rec = Notification::where('created_at', '>', now()->subMonths(6))->first();
$id = 1;
if($rec) {
$id = $rec->id;

View File

@ -79,18 +79,16 @@ class LikePipeline implements ShouldQueue
return true;
}
if($status->uri === null && $status->object_url === null && $status->url === null) {
try {
$notification = new Notification();
$notification->profile_id = $status->profile_id;
$notification->actor_id = $actor->id;
$notification->action = 'like';
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();
try {
$notification = new Notification();
$notification->profile_id = $status->profile_id;
$notification->actor_id = $actor->id;
$notification->action = 'like';
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();
} catch (Exception $e) {
}
} catch (Exception $e) {
}
}

View File

@ -35,7 +35,18 @@ class DecrementPostCount implements ShouldQueue
*/
public function handle()
{
// deprecated
return;
$id = $this->id;
$profile = Profile::find($id);
if(!$profile) {
return 1;
}
$profile->status_count = $profile->status_count ? $profile->status_count - 1 : 0;
$profile->save();
AccountService::del($id);
return 1;
}
}

View File

@ -14,12 +14,42 @@ use App\Profile;
use App\Status;
use App\Services\AccountService;
class IncrementPostCount implements ShouldQueue
class IncrementPostCount implements ShouldQueue, ShouldBeUniqueUntilProcessing
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $id;
public $timeout = 900;
public $tries = 3;
public $maxExceptions = 1;
public $failOnTimeout = true;
/**
* The number of seconds after which the job's unique lock will be released.
*
* @var int
*/
public $uniqueFor = 3600;
/**
* Get the unique ID for the job.
*/
public function uniqueId(): string
{
return 'propipe:ipc:' . $this->id;
}
/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
return [(new WithoutOverlapping("propipe:ipc:{$this->id}"))->shared()->dontRelease()];
}
/**
* Create a new job instance.
*
@ -37,7 +67,20 @@ class IncrementPostCount implements ShouldQueue
*/
public function handle()
{
// deprecated
return;
$id = $this->id;
$profile = Profile::find($id);
if(!$profile) {
return 1;
}
$profile->status_count = $profile->status_count + 1;
$profile->last_status_at = now();
$profile->save();
AccountService::del($id);
AccountService::get($id);
return 1;
}
}

View File

@ -39,8 +39,8 @@ use App\Services\AccountService;
use App\Services\CollectionService;
use App\Services\StatusService;
use App\Jobs\MediaPipeline\MediaDeletePipeline;
use App\Jobs\ProfilePipeline\DecrementPostCount;
use App\Services\NotificationService;
use App\Services\Account\AccountStatService;
class RemoteStatusDelete implements ShouldQueue, ShouldBeUniqueUntilProcessing
{
@ -109,7 +109,9 @@ class RemoteStatusDelete implements ShouldQueue, ShouldBeUniqueUntilProcessing
}
StatusService::del($status->id, true);
AccountStatService::decrementPostCount($status->profile_id);
DecrementPostCount::dispatch($status->profile_id)->onQueue('inbox');
return $this->unlinkRemoveMedia($status);
}

View File

@ -87,20 +87,18 @@ class StatusReplyPipeline implements ShouldQueue
Cache::forget('status:replies:all:' . $reply->id);
Cache::forget('status:replies:all:' . $status->id);
if($target->user_id && $target->domain === null) {
DB::transaction(function() use($target, $actor, $status) {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();
DB::transaction(function() use($target, $actor, $status) {
$notification = new Notification();
$notification->profile_id = $target->id;
$notification->actor_id = $actor->id;
$notification->action = 'comment';
$notification->item_id = $status->id;
$notification->item_type = "App\Status";
$notification->save();
NotificationService::setNotification($notification);
NotificationService::set($notification->profile_id, $notification->id);
});
}
NotificationService::setNotification($notification);
NotificationService::set($notification->profile_id, $notification->id);
});
if($exists = Cache::get('status:replies:all:' . $reply->id)) {
if($exists && $exists->count() == 3) {

View File

@ -122,8 +122,8 @@ class AuthLogin
$device = DB::transaction(function() use($user) {
return UserDevice::firstOrCreate([
'user_id' => $user->id,
'ip' => "127.0.0.23",
'user_agent' => "Pixelfed.de",
'ip' => request()->ip(),
'user_agent' => str_limit(request()->userAgent(), 180),
]);
});
}

View File

@ -41,8 +41,8 @@ class LogFailedLogin
$log->action = 'auth.failed';
$log->message = 'Failed login attempt';
$log->link = null;
$log->ip_address = "127.0.0.23";
$log->user_agent = "Pixelfed.de";
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CuratedRegisterAcceptUser extends Mailable
{
use Queueable, SerializesModels;
public $verify;
/**
* Create a new message instance.
*/
public function __construct($verify)
{
$this->verify = $verify;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.request-accepted',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\CuratedRegister;
class CuratedRegisterConfirmEmail extends Mailable
{
use Queueable, SerializesModels;
public $verify;
/**
* Create a new message instance.
*/
public function __construct(CuratedRegister $verify)
{
$this->verify = $verify;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Welcome to Pixelfed! Please Confirm Your Email',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.confirm_email',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\CuratedRegister;
class CuratedRegisterNotifyAdmin extends Mailable
{
use Queueable, SerializesModels;
public $verify;
/**
* Create a new message instance.
*/
public function __construct(CuratedRegister $verify)
{
$this->verify = $verify;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: '[Requires Action]: New Curated Onboarding Application',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.admin_notify',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CuratedRegisterNotifyAdminUserResponse extends Mailable
{
use Queueable, SerializesModels;
public $activity;
/**
* Create a new message instance.
*/
public function __construct($activity)
{
$this->activity = $activity;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Curated Register Notify Admin User Response',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.admin_notify_user_response',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CuratedRegisterRejectUser extends Mailable
{
use Queueable, SerializesModels;
public $verify;
/**
* Create a new message instance.
*/
public function __construct($verify)
{
$this->verify = $verify;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.request-rejected',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use App\Models\CuratedRegister;
use App\Models\CuratedRegisterActivity;
class CuratedRegisterRequestDetailsFromUser extends Mailable
{
use Queueable, SerializesModels;
public $verify;
public $activity;
/**
* Create a new message instance.
*/
public function __construct(CuratedRegister $verify, CuratedRegisterActivity $activity)
{
$this->verify = $verify;
$this->activity = $activity;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: '[Action Needed]: Additional information requested',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.request-details-from-user',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,55 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CuratedRegisterSendMessage extends Mailable
{
use Queueable, SerializesModels;
public $verify;
/**
* Create a new message instance.
*/
public function __construct($verify)
{
$this->verify = $verify;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'emails.curated-register.message-from-admin',
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CuratedRegister extends Model
{
use HasFactory;
protected $casts = [
'autofollow_account_ids' => 'array',
'admin_notes' => 'array',
'email_verified_at' => 'datetime',
'admin_notified_at' => 'datetime',
'action_taken_at' => 'datetime',
];
public function adminStatusLabel()
{
if(!$this->email_verified_at) {
return '<span class="border border-danger px-3 py-1 rounded text-white font-weight-bold">Unverified email</span>';
}
if($this->is_accepted) { return 'Approved'; }
if($this->is_rejected) { return 'Rejected'; }
if($this->is_awaiting_more_info ) {
return '<span class="border border-info px-3 py-1 rounded text-white font-weight-bold">Awaiting Details</span>';
}
if($this->is_closed ) { return 'Closed'; }
return '<span class="border border-success px-3 py-1 rounded text-white font-weight-bold">Open</span>';
}
public function emailConfirmUrl()
{
return url('/auth/sign_up/confirm?sid=' . $this->id . '&code=' . $this->verify_code);
}
public function emailReplyUrl()
{
return url('/auth/sign_up/concierge?sid=' . $this->id . '&code=' . $this->verify_code . '&sc=' . str_random(8));
}
public function adminReviewUrl()
{
return url('/i/admin/curated-onboarding/show/' . $this->id);
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CuratedRegisterActivity extends Model
{
use HasFactory;
protected $guarded = [];
protected $casts = [
'metadata' => 'array',
'admin_notified_at' => 'datetime',
'action_taken_at' => 'datetime',
];
public function application()
{
return $this->belongsTo(CuratedRegister::class, 'register_id');
}
public function emailReplyUrl()
{
return url('/auth/sign_up/concierge?sid='.$this->register_id . '&id=' . $this->id . '&code=' . $this->secret_code);
}
public function adminReviewUrl()
{
$url = '/i/admin/curated-onboarding/show/' . $this->register_id . '/?ah=' . $this->id;
if($this->reply_to_id) {
$url .= '&rtid=' . $this->reply_to_id;
}
return url($url);
}
}

View File

@ -31,20 +31,17 @@ class AuthServiceProvider extends ServiceProvider
if(config('instance.oauth.pat.enabled')) {
Passport::personalAccessClientId(config('instance.oauth.pat.id'));
}
Passport::setDefaultScope([
'read',
'write',
'follow',
]);
Passport::tokensCan([
'read' => 'Full read access to your account',
'write' => 'Full write access to your account',
'follow' => 'Ability to follow other profiles',
'admin:read' => 'Read all data on the server',
'admin:write' => 'Modify all data on the server',
'push' => 'Receive your push notifications'
]);
Passport::setDefaultScope([
'read',
'write',
'follow',
'push' => ''
]);
}

View File

@ -23,6 +23,8 @@ class RouteServiceProvider extends ServiceProvider
*/
public function boot()
{
//
parent::boot();
}
@ -34,7 +36,10 @@ class RouteServiceProvider extends ServiceProvider
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
/**
@ -46,18 +51,6 @@ class RouteServiceProvider extends ServiceProvider
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web-admin.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web-portfolio.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web-api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));

View File

@ -1,31 +0,0 @@
<?php
namespace App\Services\Account;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;
class AccountStatService
{
const REFRESH_CACHE_KEY = 'pf:services:accountstats:refresh:daily';
public static function incrementPostCount($pid)
{
return Redis::zadd(self::REFRESH_CACHE_KEY, $pid, $pid);
}
public static function decrementPostCount($pid)
{
return Redis::zadd(self::REFRESH_CACHE_KEY, $pid, $pid);
}
public static function removeFromPostCount($pid)
{
return Redis::zrem(self::REFRESH_CACHE_KEY, $pid);
}
public static function getAllPostCountIncr($limit = -1)
{
return Redis::zrange(self::REFRESH_CACHE_KEY, 0, $limit);
}
}

View File

@ -120,7 +120,6 @@ class RemoteAuthService
}
return Cache::remember(self::CACHE_KEY . 'domain-compatible:' . $domain, 14400, function() use($domain) {
return true;
try {
$res = Http::timeout(20)->retry(3, 750)->get('https://beagle.pixelfed.net/api/v1/raa/domain?domain=' . $domain);
if(!$res->ok()) {
@ -145,7 +144,6 @@ class RemoteAuthService
public static function lookupWebfingerUses($wf)
{
return 0;
try {
$res = Http::timeout(20)->retry(3, 750)->get('https://beagle.pixelfed.net/api/v1/raa/lookup?webfinger=' . $wf);
if(!$res->ok()) {
@ -168,7 +166,6 @@ class RemoteAuthService
public static function submitToBeagle($ow, $ou, $dw, $du)
{
return;
try {
$url = 'https://beagle.pixelfed.net/api/v1/raa/submit';
$res = Http::throw()->timeout(10)->get($url, [

View File

@ -11,61 +11,38 @@ use Illuminate\Http\Client\RequestException;
class ActivityPubFetchService
{
public static function get($url, $validateUrl = true)
{
public static function get($url, $validateUrl = true)
{
if($validateUrl === true) {
if(!Helpers::validateUrl($url)) {
return 0;
}
if(!Helpers::validateUrl($url)) {
return 0;
}
}
$baseHeaders = [
'Accept' => 'application/activity+json, application/ld+json',
];
$baseHeaders = [
'Accept' => 'application/activity+json, application/ld+json',
];
$headers = HttpSignature::instanceActorSign($url, false, $baseHeaders, 'get');
$headers['Accept'] = 'application/activity+json, application/ld+json';
$headers['User-Agent'] = 'PixelFedBot/1.0.0 (Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')';
$headers = HttpSignature::instanceActorSign($url, false, $baseHeaders, 'get');
$headers['Accept'] = 'application/activity+json, application/ld+json';
$headers['User-Agent'] = 'PixelFedBot/1.0.0 (Pixelfed/'.config('pixelfed.version').'; +'.config('app.url').')';
try {
$res = Http::withOptions(['allow_redirects' => false])
->withHeaders($headers)
->timeout(30)
->connectTimeout(5)
->retry(3, 500)
->get($url);
} catch (RequestException $e) {
return;
} catch (ConnectionException $e) {
return;
} catch (Exception $e) {
return;
}
if(!$res->ok()) {
return;
}
if(!$res->hasHeader('Content-Type')) {
return;
}
$acceptedTypes = [
'application/activity+json; charset=utf-8',
'application/activity+json',
'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
];
$contentType = $res->getHeader('Content-Type')[0];
if(!$contentType) {
return;
}
if(!in_array($contentType, $acceptedTypes)) {
return;
}
return $res->body();
}
try {
$res = Http::withOptions(['allow_redirects' => false])->withHeaders($headers)
->timeout(30)
->connectTimeout(5)
->retry(3, 500)
->get($url);
} catch (RequestException $e) {
return;
} catch (ConnectionException $e) {
return;
} catch (Exception $e) {
return;
}
if(!$res->ok()) {
return;
}
return $res->body();
}
}

View File

@ -72,8 +72,6 @@ class ConfigCacheService
'instance.banner.blurhash',
'autospam.nlp.enabled',
'instance.curated_registration.enabled',
// 'system.user_mode'
];

View File

@ -1,68 +0,0 @@
<?php
namespace App\Services\Internal;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
class SoftwareUpdateService
{
const CACHE_KEY = 'pf:services:software-update:';
public static function get()
{
$curVersion = config('pixelfed.version');
$versions = Cache::remember(self::CACHE_KEY . 'latest:v1.0.0', 1800, function() {
return self::fetchLatest();
});
if(!$versions || !isset($versions['latest'], $versions['latest']['version'])) {
$hideWarning = (bool) config('instance.software-update.disable_failed_warning');
return [
'current' => $curVersion,
'latest' => [
'version' => null,
'published_at' => null,
'url' => null,
],
'running_latest' => $hideWarning ? true : null
];
}
return [
'current' => $curVersion,
'latest' => [
'version' => $versions['latest']['version'],
'published_at' => $versions['latest']['published_at'],
'url' => $versions['latest']['url'],
],
'running_latest' => strval($versions['latest']['version']) === strval($curVersion)
];
}
public static function fetchLatest()
{
try {
$res = Http::withOptions(['allow_redirects' => false])
->timeout(5)
->connectTimeout(5)
->retry(2, 500)
->get('https://versions.pixelfed.org/versions.json');
} catch (RequestException $e) {
return;
} catch (ConnectionException $e) {
return;
} catch (Exception $e) {
return;
}
if(!$res->ok()) {
return;
}
return $res->json();
}
}

View File

@ -48,16 +48,13 @@ class LandingService
->toArray() : [];
});
$openReg = (bool) config_cache('pixelfed.open_registration');
$res = [
'name' => config_cache('app.name'),
'url' => config_cache('app.url'),
'domain' => config('pixelfed.domain.app'),
'show_directory' => config_cache('instance.landing.show_directory'),
'show_explore_feed' => config_cache('instance.landing.show_explore'),
'open_registration' => (bool) $openReg,
'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'),
'open_registration' => config_cache('pixelfed.open_registration') == 1,
'version' => config('pixelfed.version'),
'about' => [
'banner_image' => config_cache('app.banner_image') ?? url('/storage/headers/default.jpg'),

View File

@ -22,10 +22,7 @@ class NodeinfoService
$wk = $url . '/.well-known/nodeinfo';
try {
$res = Http::withOptions([
'allow_redirects' => false,
])
->withHeaders($headers)
$res = Http::withHeaders($headers)
->timeout(5)
->get($wk);
} catch (RequestException $e) {
@ -64,10 +61,7 @@ class NodeinfoService
}
try {
$res = Http::withOptions([
'allow_redirects' => false,
])
->withHeaders($headers)
$res = Http::withHeaders($headers)
->timeout(5)
->get($href);
} catch (RequestException $e) {

View File

@ -39,9 +39,10 @@ use App\Jobs\HomeFeedPipeline\FeedInsertRemotePipeline;
use App\Util\Media\License;
use App\Models\Poll;
use Illuminate\Contracts\Cache\LockTimeoutException;
use App\Jobs\ProfilePipeline\IncrementPostCount;
use App\Jobs\ProfilePipeline\DecrementPostCount;
use App\Services\DomainService;
use App\Services\UserFilterService;
use App\Services\Account\AccountStatService;
class Helpers {
@ -315,23 +316,6 @@ class Helpers {
return;
}
if(config('autospam.live_filters.enabled')) {
$filters = config('autospam.live_filters.filters');
if(!empty($filters) && isset($res['content']) && !empty($res['content']) && strlen($filters) > 3) {
$filters = array_map('trim', explode(',', $filters));
$content = $res['content'];
foreach($filters as $filter) {
$filter = trim(strtolower($filter));
if(!$filter || !strlen($filter)) {
continue;
}
if(str_contains(strtolower($content), $filter)) {
return;
}
}
}
}
if(isset($res['object'])) {
$activity = $res;
} else {
@ -389,10 +373,6 @@ class Helpers {
$idDomain = parse_url($id, PHP_URL_HOST);
$urlDomain = parse_url($url, PHP_URL_HOST);
if($idDomain && $urlDomain && strtolower($idDomain) !== strtolower($urlDomain)) {
return;
}
if(!self::validateUrl($id)) {
return;
}
@ -476,21 +456,14 @@ class Helpers {
public static function storeStatus($url, $profile, $activity)
{
$originalUrl = $url;
$id = isset($activity['id']) ? self::pluckval($activity['id']) : self::pluckval($activity['url']);
$url = isset($activity['url']) && is_string($activity['url']) ? self::pluckval($activity['url']) : self::pluckval($id);
$idDomain = parse_url($id, PHP_URL_HOST);
$urlDomain = parse_url($url, PHP_URL_HOST);
$originalUrlDomain = parse_url($originalUrl, PHP_URL_HOST);
if(!self::validateUrl($id) || !self::validateUrl($url)) {
return;
}
if( strtolower($originalUrlDomain) !== strtolower($idDomain) ||
strtolower($originalUrlDomain) !== strtolower($urlDomain) ) {
return;
}
$reply_to = self::getReplyTo($activity);
$ts = self::pluckval($activity['published']);
@ -563,7 +536,7 @@ class Helpers {
}
}
AccountStatService::incrementPostCount($pid);
IncrementPostCount::dispatch($pid)->onQueue('low');
if( $status->in_reply_to_id === null &&
in_array($status->type, ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
@ -576,11 +549,10 @@ class Helpers {
public static function getSensitive($activity, $url)
{
if(!$url || !strlen($url)) {
return true;
}
$id = isset($activity['id']) ? self::pluckval($activity['id']) : self::pluckval($url);
$url = isset($activity['url']) ? self::pluckval($activity['url']) : $id;
$urlDomain = parse_url($url, PHP_URL_HOST);
$cw = isset($activity['sensitive']) ? (bool) $activity['sensitive'] : false;
if(in_array($urlDomain, InstanceService::getNsfwDomains())) {
@ -791,11 +763,7 @@ class Helpers {
if(!$res || isset($res['id']) == false) {
return;
}
$urlDomain = parse_url($url, PHP_URL_HOST);
$domain = parse_url($res['id'], PHP_URL_HOST);
if(strtolower($urlDomain) !== strtolower($domain)) {
return;
}
if(!isset($res['preferredUsername']) && !isset($res['nickname'])) {
return;
}
@ -863,9 +831,6 @@ class Helpers {
public static function sendSignedObject($profile, $url, $body)
{
if(app()->environment() !== 'production') {
return;
}
ActivityPubDeliveryService::queue()
->from($profile)
->to($url)

View File

@ -48,6 +48,8 @@ use App\Services\UserFilterService;
use App\Services\NetworkTimelineService;
use App\Models\Conversation;
use App\Models\RemoteReport;
use App\Jobs\ProfilePipeline\IncrementPostCount;
use App\Jobs\ProfilePipeline\DecrementPostCount;
use App\Jobs\HomeFeedPipeline\FeedRemoveRemotePipeline;
class Inbox
@ -197,22 +199,6 @@ class Inbox
public function handleCreateActivity()
{
$activity = $this->payload['object'];
if(config('autospam.live_filters.enabled')) {
$filters = config('autospam.live_filters.filters');
if(!empty($filters) && isset($activity['content']) && !empty($activity['content']) && strlen($filters) > 3) {
$filters = array_map('trim', explode(',', $filters));
$content = $activity['content'];
foreach($filters as $filter) {
$filter = trim(strtolower($filter));
if(!$filter || !strlen($filter)) {
continue;
}
if(str_contains(strtolower($content), $filter)) {
return;
}
}
}
}
$actor = $this->actorFirstOrCreate($this->payload['actor']);
if(!$actor || $actor->domain == null) {
return;

View File

@ -5,7 +5,7 @@
"license": "AGPL-3.0-only",
"type": "project",
"require": {
"php": "^8.1|^8.2|^8.3",
"php": "^8.1|^8.2",
"ext-bcmath": "*",
"ext-ctype": "*",
"ext-curl": "*",

1078
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,5 @@ return [
'nlp' => [
'enabled' => false,
'spam_sample_limit' => env('PF_AUTOSPAM_NLP_SPAM_SAMPLE_LIMIT', 200),
],
'live_filters' => [
'enabled' => env('PF_AUTOSPAM_LIVE_FILTERS_ENABLED', false),
'filters' => env('PF_AUTOSPAM_LIVE_FILTERS_CSV', ''),
]
];

View File

@ -74,7 +74,7 @@ return [
'redis' => [
'driver' => 'redis',
'lock_connection' => 'default',
'client' => env('REDIS_CLIENT', 'predis'),
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'scheme' => env('REDIS_SCHEME', 'tcp'),

View File

@ -49,7 +49,7 @@ return [
],
'network_timeline' => env('PF_NETWORK_TIMELINE', true),
'network_timeline_days_falloff' => env('PF_NETWORK_TIMELINE_DAYS_FALLOFF', 90),
'network_timeline_days_falloff' => env('PF_NETWORK_TIMELINE_DAYS_FALLOFF', 2),
'custom_emoji' => [
'enabled' => env('CUSTOM_EMOJI', false),

View File

@ -2,202 +2,201 @@
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This is the subdomain where Horizon will be accessible from. If this
| setting is null, Horizon will reside under the same domain as the
| application. Otherwise, this value will serve as the subdomain.
|
*/
'domain' => null,
'domain' => null,
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the URI will not
| affect the paths of its internal API that aren't exposed to users.
|
*/
'path' => 'horizon',
'path' => 'horizon',
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| meta information required for it to function. It includes the list
| of supervisors, failed jobs, job metrics, and other information.
|
*/
'use' => 'default',
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix when you are running multiple installations
| of Horizon on the same server so that they don't have problems.
|
*/
'prefix' => env('HORIZON_PREFIX', 'horizon-'),
'prefix' => env('HORIZON_PREFIX', 'horizon-'),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web'],
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the LongWaitDetected event
| will be fired. Every connection / queue combination may have its
| own, unique threshold (in seconds) before this event is fired.
|
*/
'waits' => [
'redis:feed' => 30,
'redis:follow' => 30,
'redis:shared' => 30,
'redis:default' => 30,
'redis:inbox' => 30,
'redis:low' => 30,
'redis:high' => 30,
'redis:delete' => 30,
'redis:story' => 30,
'redis:mmo' => 30,
'redis:intbg' => 30,
],
'waits' => [
'redis:feed' => 30,
'redis:follow' => 30,
'redis:shared' => 30,
'redis:default' => 30,
'redis:inbox' => 30,
'redis:low' => 30,
'redis:high' => 30,
'redis:delete' => 30,
'redis:story' => 30,
'redis:mmo' => 30,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| Here you can configure for how long (in minutes) you desire Horizon to
| persist the recent and failed jobs. Typically, recent jobs are kept
| for one hour while all failed jobs are stored for an entire week.
|
*/
'trim' => [
'recent' => 60,
'pending' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
'trim' => [
'recent' => 60,
'pending' => 60,
'completed' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
/*
|--------------------------------------------------------------------------
| Metrics
|--------------------------------------------------------------------------
|
| Here you can configure how many snapshots should be kept to display in
| the metrics graph. This will get used in combination with Horizon's
| `horizon:snapshot` schedule to define how long to retain metrics.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait on all of the workers to terminate unless the --wait option
| is provided. Fast termination can shorten deployment delay by
| allowing a new instance of Horizon to start while the last
| instance will continue to terminate each of its workers.
|
*/
'fast_termination' => false,
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon worker
| may consume before it is terminated and restarted. You should set
| this value according to the resources available to your server.
|
*/
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon worker
| may consume before it is terminated and restarted. You should set
| this value according to the resources available to your server.
|
*/
'memory_limit' => env('HORIZON_MEMORY_LIMIT', 64),
'memory_limit' => env('HORIZON_MEMORY_LIMIT', 64),
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These supervisors and settings handle all your
| queued jobs and will be provisioned by Horizon during deployment.
|
*/
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg'],
'balance' => env('HORIZON_BALANCE_STRATEGY', 'auto'),
'minProcesses' => env('HORIZON_MIN_PROCESSES', 1),
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 20),
'memory' => env('HORIZON_SUPERVISOR_MEMORY', 64),
'tries' => env('HORIZON_SUPERVISOR_TRIES', 3),
'nice' => env('HORIZON_SUPERVISOR_NICE', 0),
'timeout' => env('HORIZON_SUPERVISOR_TIMEOUT', 300),
],
],
'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo'],
'balance' => env('HORIZON_BALANCE_STRATEGY', 'auto'),
'minProcesses' => env('HORIZON_MIN_PROCESSES', 1),
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 20),
'memory' => env('HORIZON_SUPERVISOR_MEMORY', 64),
'tries' => env('HORIZON_SUPERVISOR_TRIES', 3),
'nice' => env('HORIZON_SUPERVISOR_NICE', 0),
'timeout' => env('HORIZON_SUPERVISOR_TIMEOUT', 300),
],
],
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo', 'intbg'],
'balance' => 'auto',
'minProcesses' => 1,
'maxProcesses' => 20,
'memory' => 128,
'tries' => 3,
'nice' => 0,
'timeout' => 300
],
],
],
'local' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['high', 'default', 'follow', 'shared', 'inbox', 'feed', 'low', 'story', 'delete', 'mmo'],
'balance' => 'auto',
'minProcesses' => 1,
'maxProcesses' => 20,
'memory' => 128,
'tries' => 3,
'nice' => 0,
'timeout' => 300
],
],
],
'darkmode' => env('HORIZON_DARKMODE', false),
'darkmode' => env('HORIZON_DARKMODE', false),
];

View File

@ -29,12 +29,11 @@ return [
],
'local' => [
'cached' => env('INSTANCE_PUBLIC_TIMELINE_CACHED', false),
'is_public' => env('INSTANCE_PUBLIC_LOCAL_TIMELINE', false)
],
'network' => [
'cached' => env('PF_NETWORK_TIMELINE') ? env('INSTANCE_NETWORK_TIMELINE_CACHED', false) : false,
'cached' => env('PF_NETWORK_TIMELINE') ? env('INSTANCE_NETWORK_TIMELINE_CACHED', true) : false,
'cache_dropoff' => env('INSTANCE_NETWORK_TIMELINE_CACHE_DROPOFF', 100),
'max_hours_old' => env('INSTANCE_NETWORK_TIMELINE_CACHE_MAX_HOUR_INGEST', 6)
]
@ -140,40 +139,5 @@ return [
'max_children' => env('INSTANCE_PARENTAL_CONTROLS_MAX_CHILDREN', 1),
'auto_verify_email' => true,
],
],
'software-update' => [
'disable_failed_warning' => env('INSTANCE_SOFTWARE_UPDATE_DISABLE_FAILED_WARNING', false)
],
'notifications' => [
'gc' => [
'enabled' => env('INSTANCE_NOTIFY_AUTO_GC', false),
'delete_after_days' => env('INSTANCE_NOTIFY_AUTO_GC_DEL_AFTER_DAYS', 365)
]
],
'curated_registration' => [
'enabled' => env('INSTANCE_CUR_REG', false),
'resend_confirmation_limit' => env('INSTANCE_CUR_REG_RESEND_LIMIT', 5),
'captcha_enabled' => env('INSTANCE_CUR_REG_CAPTCHA', env('CAPTCHA_ENABLED', false)),
'state' => [
'fallback_on_closed_reg' => true,
'only_enabled_on_closed_reg' => env('INSTANCE_CUR_REG_STATE_ONLY_ON_CLOSED', true),
],
'notify' => [
'admin' => [
'on_verify_email' => [
'enabled' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY', false),
'bundle' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_BUNDLE', false),
'max_per_day' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_MPD', 10),
],
'on_user_response' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_USER_RESPONSE', false),
]
],
],
]
];

View File

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

View File

@ -1,36 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('instances', function (Blueprint $table) {
$table->boolean('active_deliver')->nullable()->index()->after('domain');
$table->boolean('valid_nodeinfo')->nullable();
$table->timestamp('nodeinfo_last_fetched')->nullable();
$table->boolean('delivery_timeout')->default(false);
$table->timestamp('delivery_next_after')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('instances', function (Blueprint $table) {
$table->dropColumn('active_deliver');
$table->dropColumn('valid_nodeinfo');
$table->dropColumn('nodeinfo_last_fetched');
$table->dropColumn('delivery_timeout');
$table->dropColumn('delivery_next_after');
});
}
};

View File

@ -1,44 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('curated_registers', function (Blueprint $table) {
$table->id();
$table->string('email')->unique()->nullable()->index();
$table->string('username')->unique()->nullable()->index();
$table->string('password')->nullable();
$table->string('ip_address')->nullable();
$table->string('verify_code')->nullable();
$table->text('reason_to_join')->nullable();
$table->unsignedBigInteger('invited_by')->nullable()->index();
$table->boolean('is_approved')->default(0)->index();
$table->boolean('is_rejected')->default(0)->index();
$table->boolean('is_awaiting_more_info')->default(0)->index();
$table->boolean('is_closed')->default(0)->index();
$table->json('autofollow_account_ids')->nullable();
$table->json('admin_notes')->nullable();
$table->unsignedInteger('approved_by_admin_id')->nullable();
$table->timestamp('email_verified_at')->nullable();
$table->timestamp('admin_notified_at')->nullable();
$table->timestamp('action_taken_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('curated_registers');
}
};

View File

@ -1,42 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('curated_register_activities', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('register_id')->nullable()->index();
$table->unsignedInteger('admin_id')->nullable();
$table->unsignedInteger('reply_to_id')->nullable()->index();
$table->string('secret_code')->nullable();
$table->string('type')->nullable()->index();
$table->string('title')->nullable();
$table->string('link')->nullable();
$table->text('message')->nullable();
$table->json('metadata')->nullable();
$table->boolean('from_admin')->default(false)->index();
$table->boolean('from_user')->default(false)->index();
$table->boolean('admin_only_view')->default(true);
$table->boolean('action_required')->default(false);
$table->timestamp('admin_notified_at')->nullable();
$table->timestamp('action_taken_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('curated_register_activities');
}
};

1543
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
},
"devDependencies": {
"acorn": "^8.7.1",
"axios": ">=1.6.0",
"axios": "^0.21.1",
"bootstrap": "^4.5.2",
"cross-env": "^5.2.1",
"jquery": "^3.6.0",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/admin.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
"use strict";(self.webpackChunkpixelfed=self.webpackChunkpixelfed||[]).push([[4774],{92182:(e,t,o)=>{o.r(t);var a=o(62893),l=o(63288),n=o(32252),r=o.n(n),s=o(65201),d=o.n(s),c=o(24786),i=o(57742),u=o.n(i),f=o(89829),h=o.n(f),m=(o(34352),o(80158),o(74692));function p(e){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p(e)}window.Vue=a.default,a.default.use(h()),a.default.use(u()),a.default.use(l.default),a.default.use(r()),a.default.use(d()),a.default.use(c.default,{name:"Timeago",locale:"en"}),pixelfed.readmore=function(){m(".read-more").each((function(e,t){var o=m(this),a=o.attr("data-readmore");"undefined"!==p(a)&&!1!==a||o.readmore({collapsedHeight:45,heightMargin:48,moreLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show more</a>',lessLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show less</a>'})}))};try{document.createEvent("TouchEvent"),m("body").addClass("touch")}catch(e){}window.filesize=o(91139),m('[data-toggle="tooltip"]').tooltip();console.log("%cStop!","color:red; font-size:60px; font-weight: bold; -webkit-text-stroke: 1px black;"),console.log('%cThis is a browser feature intended for developers. If someone told you to copy and paste something here to enable a Pixelfed feature or "hack" someone\'s account, it is a scam and will give them access to your Pixelfed account.',"font-size: 18px;")}},e=>{e.O(0,[3660],(()=>{return t=92182,e(e.s=t);var t}));e.O()}]);
"use strict";(self.webpackChunkpixelfed=self.webpackChunkpixelfed||[]).push([[9008],{23331:(e,t,o)=>{o.r(t);var a=o(70538),l=o(25518),n=o(30306),r=o.n(n),s=o(16654),d=o.n(s),c=o(92987),i=o(37409),u=o.n(i),f=o(74870),h=o.n(f),m=(o(86368),o(46737),o(19755));function p(e){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p(e)}window.Vue=a.default,a.default.use(h()),a.default.use(u()),a.default.use(l.default),a.default.use(r()),a.default.use(d()),a.default.use(c.default,{name:"Timeago",locale:"en"}),pixelfed.readmore=function(){m(".read-more").each((function(e,t){var o=m(this),a=o.attr("data-readmore");"undefined"!==p(a)&&!1!==a||o.readmore({collapsedHeight:45,heightMargin:48,moreLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show more</a>',lessLink:'<a href="#" class="d-block small font-weight-bold text-dark text-center">Show less</a>'})}))};try{document.createEvent("TouchEvent"),m("body").addClass("touch")}catch(e){}window.filesize=o(42317),m('[data-toggle="tooltip"]').tooltip();console.log("%cStop!","color:red; font-size:60px; font-weight: bold; -webkit-text-stroke: 1px black;"),console.log('%cThis is a browser feature intended for developers. If someone told you to copy and paste something here to enable a Pixelfed feature or "hack" someone\'s account, it is a scam and will give them access to your Pixelfed account.',"font-size: 18px;")}},e=>{e.O(0,[8898],(()=>{return t=23331,e(e.s=t);var t}));e.O()}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/direct.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More