mirror of https://github.com/pixelfed/pixelfed
commit
1b16e5d696
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\{Profile, Status, User};
|
||||||
|
use Cache;
|
||||||
|
|
||||||
|
class InstanceApiController extends Controller {
|
||||||
|
|
||||||
|
protected function getData()
|
||||||
|
{
|
||||||
|
$contact = Cache::remember('api:v1:instance:contact', 1440, function() {
|
||||||
|
$admin = User::whereIsAdmin(true)->first()->profile;
|
||||||
|
return [
|
||||||
|
'id' => $admin->id,
|
||||||
|
'username' => $admin->username,
|
||||||
|
'acct' => $admin->username,
|
||||||
|
'display_name' => e($admin->name),
|
||||||
|
'locked' => (bool) $admin->is_private,
|
||||||
|
'bot' => false,
|
||||||
|
'created_at' => $admin->created_at->format('c'),
|
||||||
|
'note' => e($admin->bio),
|
||||||
|
'url' => $admin->url(),
|
||||||
|
'avatar' => $admin->avatarUrl(),
|
||||||
|
'avatar_static' => $admin->avatarUrl(),
|
||||||
|
'header' => null,
|
||||||
|
'header_static' => null,
|
||||||
|
'moved' => null,
|
||||||
|
'fields' => null,
|
||||||
|
'bot' => null,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$res = [
|
||||||
|
'uri' => config('pixelfed.domain.app'),
|
||||||
|
'title' => config('app.name'),
|
||||||
|
'description' => '',
|
||||||
|
'version' => config('pixelfed.version'),
|
||||||
|
'urls' => [],
|
||||||
|
'stats' => [
|
||||||
|
'user_count' => User::count(),
|
||||||
|
'status_count' => Status::whereNull('uri')->count(),
|
||||||
|
'domain_count' => Profile::whereNotNull('domain')
|
||||||
|
->groupBy('domain')
|
||||||
|
->pluck('domain')
|
||||||
|
->count()
|
||||||
|
],
|
||||||
|
'thumbnail' => '',
|
||||||
|
'languages' => [],
|
||||||
|
'contact_account' => $contact
|
||||||
|
];
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function instance()
|
||||||
|
{
|
||||||
|
$res = Cache::remember('api:v1:instance', 60, function() {
|
||||||
|
return json_encode($this->getData());
|
||||||
|
});
|
||||||
|
|
||||||
|
return response($res)->header('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs\FollowPipeline;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
use Cache, Log, Redis;
|
||||||
|
use League\Fractal;
|
||||||
|
use League\Fractal\Serializer\ArraySerializer;
|
||||||
|
use App\FollowRequest;
|
||||||
|
use App\Util\ActivityPub\Helpers;
|
||||||
|
use App\Transformer\ActivityPub\Verb\Follow;
|
||||||
|
|
||||||
|
class FollowActivityPubDeliver implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
protected $followRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(FollowRequest $followRequest)
|
||||||
|
{
|
||||||
|
$this->followRequest = $followRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$follow = $this->followRequest;
|
||||||
|
$actor = $follow->actor;
|
||||||
|
$target = $follow->target;
|
||||||
|
|
||||||
|
if($target->domain == null || $target->inbox_url == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fractal = new Fractal\Manager();
|
||||||
|
$fractal->setSerializer(new ArraySerializer());
|
||||||
|
$resource = new Fractal\Resource\Item($follow, new Follow());
|
||||||
|
$activity = $fractal->createData($resource)->toArray();
|
||||||
|
$url = $target->sharedInbox ?? $target->inbox_url;
|
||||||
|
|
||||||
|
Helpers::sendSignedObject($actor, $url, $activity);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Transformer\Api;
|
||||||
|
|
||||||
|
use League\Fractal;
|
||||||
|
|
||||||
|
class AttachmentTransformer extends Fractal\TransformerAbstract
|
||||||
|
{
|
||||||
|
public function transform(Media $media)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $media->id,
|
||||||
|
'type' => $media->activityVerb(),
|
||||||
|
'url' => $media->url(),
|
||||||
|
'remote_url' => null,
|
||||||
|
'preview_url' => $media->thumbnailUrl(),
|
||||||
|
'text_url' => null,
|
||||||
|
'meta' => null,
|
||||||
|
'description' => $media->caption,
|
||||||
|
'license' => $media->license,
|
||||||
|
'is_nsfw' => $media->is_nsfw,
|
||||||
|
'orientation' => $media->orientation,
|
||||||
|
'filter_name' => $media->filter_name,
|
||||||
|
'filter_class' => $media->filter_class,
|
||||||
|
'mime' => $media->mime,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Transformer\Api;
|
||||||
|
|
||||||
|
use League\Fractal;
|
||||||
|
|
||||||
|
class ContextTransformer extends Fractal\TransformerAbstract
|
||||||
|
{
|
||||||
|
public function transform()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'ancestors' => [],
|
||||||
|
'descendants' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Transformer\Api;
|
||||||
|
|
||||||
|
use League\Fractal;
|
||||||
|
|
||||||
|
class FilterTransformer extends Fractal\TransformerAbstract
|
||||||
|
{
|
||||||
|
public function transform()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => (string) '',
|
||||||
|
'phrase' => (string) '',
|
||||||
|
'context' => [],
|
||||||
|
'expires_at' => null,
|
||||||
|
'irreversible' => (bool) false,
|
||||||
|
'whole_word' => (bool) false
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Transformer\Api;
|
||||||
|
|
||||||
|
use League\Fractal;
|
||||||
|
|
||||||
|
class ResultsTransformer extends Fractal\TransformerAbstract
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $defaultIncludes = [
|
||||||
|
'account',
|
||||||
|
'mentions',
|
||||||
|
'media_attachments',
|
||||||
|
'tags',
|
||||||
|
];
|
||||||
|
public function transform()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'accounts' => [],
|
||||||
|
'statuses' => [],
|
||||||
|
'hashtags' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Util\ActivityPub\Validator;
|
||||||
|
|
||||||
|
use Validator;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class Announce {
|
||||||
|
|
||||||
|
public static function validate($payload)
|
||||||
|
{
|
||||||
|
$valid = Validator::make($payload, [
|
||||||
|
'@context' => 'required',
|
||||||
|
'id' => 'required|string',
|
||||||
|
'type' => [
|
||||||
|
'required',
|
||||||
|
Rule::in(['Announce'])
|
||||||
|
],
|
||||||
|
'actor' => 'required|url|active_url',
|
||||||
|
'published' => 'required|date',
|
||||||
|
'to' => 'required',
|
||||||
|
'cc' => 'required',
|
||||||
|
'object' => 'required|url|active_url'
|
||||||
|
])->passes();
|
||||||
|
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class UpdateProfilesTableUseTextForBio extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('profiles', function (Blueprint $table) {
|
||||||
|
$table->text('bio')->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('profiles', function (Blueprint $table) {
|
||||||
|
$table->string('bio')->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\ActivityPub\Verb;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use App\Util\ActivityPub\Validator\Announce;
|
||||||
|
|
||||||
|
class AnnounceTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->validAnnounce = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->invalidAnnounce = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce2",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->invalidDate = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59ZEZE",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->contextMissing = [
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->audienceMissing = [
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->audienceMissing2 = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "https://example.org/users/alice",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => null,
|
||||||
|
"cc" => null,
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->invalidActor = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"actor" => "10000",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->invalidActor2 = [
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "https://example.org/users/alice/statuses/100000000000001/activity",
|
||||||
|
"type" => "Announce",
|
||||||
|
"published" => "2018-12-31T23:59:59Z",
|
||||||
|
"to" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc" => [
|
||||||
|
"https://example.org/users/bob",
|
||||||
|
"https://example.org/users/alice/followers"
|
||||||
|
],
|
||||||
|
"object" => "https://example.org/p/bob/100000000000000",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function basic_accept()
|
||||||
|
{
|
||||||
|
$this->assertTrue(Announce::validate($this->validAnnounce));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalid_accept()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Announce::validate($this->invalidAnnounce));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalid_date()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Announce::validate($this->invalidDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function context_missing()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Announce::validate($this->contextMissing));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function audience_missing()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Announce::validate($this->audienceMissing));
|
||||||
|
$this->assertFalse(Announce::validate($this->audienceMissing2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function invalid_actor()
|
||||||
|
{
|
||||||
|
$this->assertFalse(Announce::validate($this->invalidActor));
|
||||||
|
$this->assertFalse(Announce::validate($this->invalidActor2));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue