mirror of https://github.com/pixelfed/pixelfed
commit
c33e5b0777
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
class ApiController extends Controller {
|
||||||
|
public function json($res, $headers = [], $code = 200) {
|
||||||
|
return response()->json($res, $code, $this->filterHeaders($headers), JSON_UNESCAPED_SLASHES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function linksForCollection($paginator) {
|
||||||
|
$link = null;
|
||||||
|
|
||||||
|
if ($paginator->onFirstPage()) {
|
||||||
|
if ($paginator->hasMorePages()) {
|
||||||
|
$link = '<'.$paginator->nextPageUrl().'>; rel="prev"';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($paginator->previousPageUrl()) {
|
||||||
|
$link = '<'.$paginator->previousPageUrl().'>; rel="next"';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($paginator->hasMorePages()) {
|
||||||
|
$link .= ($link ? ', ' : '').'<'.$paginator->nextPageUrl().'>; rel="prev"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterHeaders($headers) {
|
||||||
|
return array_filter($headers, function($v, $k) {
|
||||||
|
return $v != null;
|
||||||
|
}, ARRAY_FILTER_USE_BOTH);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1\Admin;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use App\Http\Controllers\Api\ApiController;
|
||||||
|
use App\Instance;
|
||||||
|
use App\Services\InstanceService;
|
||||||
|
use App\Http\Resources\MastoApi\Admin\DomainBlockResource;
|
||||||
|
|
||||||
|
class DomainBlocksController extends ApiController {
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->middleware(['auth:api', 'api.admin', 'scope:admin:read,admin:read:domain_blocks'])->only(['index', 'show']);
|
||||||
|
$this->middleware(['auth:api', 'api.admin', 'scope:admin:write,admin:write:domain_blocks'])->only(['create', 'update', 'delete']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Request $request) {
|
||||||
|
$this->validate($request, [
|
||||||
|
'limit' => 'sometimes|integer|max:100|min:1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$limit = $request->input('limit', 100);
|
||||||
|
|
||||||
|
$res = Instance::moderated()
|
||||||
|
->orderBy('id')
|
||||||
|
->cursorPaginate($limit)
|
||||||
|
->withQueryString();
|
||||||
|
|
||||||
|
return $this->json(DomainBlockResource::collection($res), [
|
||||||
|
'Link' => $this->linksForCollection($res)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Request $request, $id) {
|
||||||
|
$domain_block = Instance::moderated()->find($id);
|
||||||
|
|
||||||
|
if (!$domain_block) {
|
||||||
|
return $this->json([ 'error' => 'Record not found'], [], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json(new DomainBlockResource($domain_block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(Request $request) {
|
||||||
|
$this->validate($request, [
|
||||||
|
'domain' => 'required|string|min:1|max:120',
|
||||||
|
'severity' => [
|
||||||
|
'sometimes',
|
||||||
|
Rule::in(['noop', 'silence', 'suspend'])
|
||||||
|
],
|
||||||
|
'reject_media' => 'sometimes|required|boolean',
|
||||||
|
'reject_reports' => 'sometimes|required|boolean',
|
||||||
|
'private_comment' => 'sometimes|string|min:1|max:1000',
|
||||||
|
'public_comment' => 'sometimes|string|min:1|max:1000',
|
||||||
|
'obfuscate' => 'sometimes|required|boolean'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$domain = $request->input('domain');
|
||||||
|
$severity = $request->input('severity', 'silence');
|
||||||
|
$private_comment = $request->input('private_comment');
|
||||||
|
|
||||||
|
abort_if(!strpos($domain, '.'), 400, 'Invalid domain');
|
||||||
|
abort_if(!filter_var($domain, FILTER_VALIDATE_DOMAIN), 400, 'Invalid domain');
|
||||||
|
|
||||||
|
// This is because Pixelfed can't currently support wildcard domain blocks
|
||||||
|
// We have to find something that could plausibly be an instance
|
||||||
|
$parts = explode('.', $domain);
|
||||||
|
if ($parts[0] == '*') {
|
||||||
|
// If we only have two parts, e.g., "*", "example", then we want to fail:
|
||||||
|
abort_if(count($parts) <= 2, 400, 'Invalid domain: This API does not support wildcard domain blocks yet');
|
||||||
|
|
||||||
|
// Otherwise we convert the *.foo.example to foo.example
|
||||||
|
$domain = implode('.', array_slice($parts, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double check we definitely haven't let anything through:
|
||||||
|
abort_if(str_contains($domain, '*'), 400, 'Invalid domain');
|
||||||
|
|
||||||
|
$existing_domain_block = Instance::moderated()->whereDomain($domain)->first();
|
||||||
|
|
||||||
|
if ($existing_domain_block) {
|
||||||
|
return $this->json([
|
||||||
|
'error' => 'A domain block already exists for this domain',
|
||||||
|
'existing_domain_block' => new DomainBlockResource($existing_domain_block)
|
||||||
|
], [], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain_block = Instance::updateOrCreate(
|
||||||
|
[ 'domain' => $domain ],
|
||||||
|
[ 'banned' => $severity === 'suspend', 'unlisted' => $severity === 'silence', 'notes' => [$private_comment]]
|
||||||
|
);
|
||||||
|
|
||||||
|
InstanceService::refresh();
|
||||||
|
|
||||||
|
return $this->json(new DomainBlockResource($domain_block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, $id) {
|
||||||
|
$this->validate($request, [
|
||||||
|
'severity' => [
|
||||||
|
'sometimes',
|
||||||
|
Rule::in(['noop', 'silence', 'suspend'])
|
||||||
|
],
|
||||||
|
'reject_media' => 'sometimes|required|boolean',
|
||||||
|
'reject_reports' => 'sometimes|required|boolean',
|
||||||
|
'private_comment' => 'sometimes|string|min:1|max:1000',
|
||||||
|
'public_comment' => 'sometimes|string|min:1|max:1000',
|
||||||
|
'obfuscate' => 'sometimes|required|boolean'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$severity = $request->input('severity', 'silence');
|
||||||
|
$private_comment = $request->input('private_comment');
|
||||||
|
|
||||||
|
$domain_block = Instance::moderated()->find($id);
|
||||||
|
|
||||||
|
if (!$domain_block) {
|
||||||
|
return $this->json([ 'error' => 'Record not found'], [], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain_block->banned = $severity === 'suspend';
|
||||||
|
$domain_block->unlisted = $severity === 'silence';
|
||||||
|
$domain_block->notes = [$private_comment];
|
||||||
|
$domain_block->save();
|
||||||
|
|
||||||
|
InstanceService::refresh();
|
||||||
|
|
||||||
|
return $this->json(new DomainBlockResource($domain_block));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(Request $request, $id) {
|
||||||
|
$domain_block = Instance::moderated()->find($id);
|
||||||
|
|
||||||
|
if (!$domain_block) {
|
||||||
|
return $this->json([ 'error' => 'Record not found'], [], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain_block->banned = false;
|
||||||
|
$domain_block->unlisted = false;
|
||||||
|
$domain_block->save();
|
||||||
|
|
||||||
|
InstanceService::refresh();
|
||||||
|
|
||||||
|
return $this->json(null, [], 200);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware\Api;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class Admin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
if (Auth::check() == false || Auth::user()->is_admin == false) {
|
||||||
|
return abort(403, "You must be an administrator to do that");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\MastoApi\Admin;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class DomainBlockResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
$severity = 'noop';
|
||||||
|
if ($this->banned) {
|
||||||
|
$severity = 'suspend';
|
||||||
|
} else if ($this->unlisted) {
|
||||||
|
$severity = 'silence';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'domain' => $this->domain,
|
||||||
|
// This property is coming in Mastodon 4.3, although it'll only be
|
||||||
|
// useful if Pixelfed supports obfuscating domains:
|
||||||
|
'digest' => hash('sha256', $this->domain),
|
||||||
|
'severity' => $severity,
|
||||||
|
// Using the updated_at value as this is going to be the closest to
|
||||||
|
// when the domain was banned
|
||||||
|
'created_at' => $this->updated_at,
|
||||||
|
// We don't have data for these fields
|
||||||
|
'reject_media' => false,
|
||||||
|
'reject_reports' => false,
|
||||||
|
'private_comment' => $this->notes ? join('; ', $this->notes) : null,
|
||||||
|
'public_comment' => $this->limit_reason,
|
||||||
|
'obfuscate' => false
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue