now profile edits are federalized
This commit is contained in:
parent
0d14d08246
commit
27079892f2
33
app/Actions/ActionsUser.php
Normal file
33
app/Actions/ActionsUser.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
use App\Types\TypeActor;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class ActionsUser
|
||||||
|
{
|
||||||
|
public static function update_profile ()
|
||||||
|
{
|
||||||
|
if (!auth ()->check ())
|
||||||
|
return ["error" => "You must be logged in to update your profile."];
|
||||||
|
|
||||||
|
$user = auth ()->user ();
|
||||||
|
try {
|
||||||
|
$client = new Client ();
|
||||||
|
$response = $client->post ($user->actor->outbox, [
|
||||||
|
"json" => [
|
||||||
|
"type" => "UpdateProfile"
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e)
|
||||||
|
{
|
||||||
|
Log::error ("Error updating profile: " . $e->getMessage ());
|
||||||
|
return ["error" => "Error updating profile"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["success" => "Profile updated"];
|
||||||
|
}
|
||||||
|
}
|
@ -39,8 +39,7 @@ class APActorController extends Controller
|
|||||||
|
|
||||||
public function following (User $user)
|
public function following (User $user)
|
||||||
{
|
{
|
||||||
$actor_id = '"' . str_replace ("/", "\/", $user->actor->actor_id) . '"';
|
$following = Activity::where ("type", "Follow")->where ("actor", $user->actor->actor_id);
|
||||||
$following = Activity::where ("type", "Follow")->where ("actor", $actor_id);
|
|
||||||
$ordered_collection = new TypeOrderedCollection ();
|
$ordered_collection = new TypeOrderedCollection ();
|
||||||
$ordered_collection->collection = $following->get ()->pluck ("object")->toArray ();
|
$ordered_collection->collection = $following->get ()->pluck ("object")->toArray ();
|
||||||
$ordered_collection->url = route ("ap.following", $user->name);
|
$ordered_collection->url = route ("ap.following", $user->name);
|
||||||
|
@ -5,8 +5,10 @@ namespace App\Http\Controllers\AP;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Actor;
|
use App\Models\Actor;
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
|
use App\Models\Instance;
|
||||||
|
|
||||||
use App\Types\TypeActivity;
|
use App\Types\TypeActivity;
|
||||||
|
use App\Types\TypeActor;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -16,8 +18,13 @@ class APOutboxController extends Controller
|
|||||||
{
|
{
|
||||||
public function outbox (User $user, Request $request)
|
public function outbox (User $user, Request $request)
|
||||||
{
|
{
|
||||||
|
// TODO: check we are logged in and we are the logged in user
|
||||||
switch ($request->get ("type"))
|
switch ($request->get ("type"))
|
||||||
{
|
{
|
||||||
|
case "UpdateProfile":
|
||||||
|
return $this->handle_update_profile ($user);
|
||||||
|
break;
|
||||||
|
|
||||||
case "Follow":
|
case "Follow":
|
||||||
return $this->handle_follow ($user, $request->get ("object"));
|
return $this->handle_follow ($user, $request->get ("object"));
|
||||||
break;
|
break;
|
||||||
@ -33,12 +40,39 @@ class APOutboxController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function handle_update_profile (User $user)
|
||||||
|
{
|
||||||
|
$actor = $user->actor ()->first ();
|
||||||
|
$actor_response = TypeActor::build_response ($actor);
|
||||||
|
|
||||||
|
$update_activity = TypeActivity::craft_update ($actor, $actor_response);
|
||||||
|
$instances = Instance::all ();
|
||||||
|
foreach ($instances as $instance)
|
||||||
|
{
|
||||||
|
$response = TypeActivity::post_activity ($update_activity, $actor, $instance->inbox);
|
||||||
|
if ($response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return response ()->json ("success", 200);
|
||||||
|
}
|
||||||
|
|
||||||
public function handle_follow (User $user, string $object)
|
public function handle_follow (User $user, string $object)
|
||||||
{
|
{
|
||||||
$object_actor = Actor::where ("actor_id", $object)->first ();
|
$object_actor = Actor::where ("actor_id", $object)->first ();
|
||||||
if (!$object_actor)
|
if (!$object_actor)
|
||||||
return response ()->json ([ "error" => "object not found" ], 404);
|
return response ()->json ([ "error" => "object not found" ], 404);
|
||||||
|
|
||||||
|
if ($user->actor ()->first ()->actor_id == $object_actor->actor_id)
|
||||||
|
return response ()->json ([ "error" => "cannot follow self" ], 400);
|
||||||
|
|
||||||
|
// check we are not following already
|
||||||
|
$following_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id)
|
||||||
|
->where ("object", '"' . str_replace ("/", "\/", $object_actor->actor_id) . '"')
|
||||||
|
->where ("type", "Follow")
|
||||||
|
->first ();
|
||||||
|
if ($following_activity)
|
||||||
|
return response ()->json ([ "error" => "already following" ], 400);
|
||||||
|
|
||||||
$follow_activity = TypeActivity::craft_follow ($user->actor ()->first (), $object_actor);
|
$follow_activity = TypeActivity::craft_follow ($user->actor ()->first (), $object_actor);
|
||||||
$response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor);
|
$response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor);
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ use Intervention\Image\Drivers\Gd\Driver;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Actor;
|
use App\Models\Actor;
|
||||||
|
|
||||||
|
use App\Actions\ActionsUser;
|
||||||
|
|
||||||
class ProfileController extends Controller
|
class ProfileController extends Controller
|
||||||
{
|
{
|
||||||
public function show ($user_name)
|
public function show ($user_name)
|
||||||
@ -96,6 +98,10 @@ class ProfileController extends Controller
|
|||||||
Storage::disk ("public")->delete (str_replace ("/storage/", "", $old_avatar));
|
Storage::disk ("public")->delete (str_replace ("/storage/", "", $old_avatar));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$response = ActionsUser::update_profile ();
|
||||||
|
if (isset ($response["error"]))
|
||||||
|
return back ()->with ("error", "Error updating profile: " . $response["error"]);
|
||||||
|
|
||||||
return back ()->with ("success", "Profile updated successfully!");
|
return back ()->with ("success", "Profile updated successfully!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,14 @@ class Actor extends Model
|
|||||||
"private_key"
|
"private_key"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $hidden = [
|
||||||
|
"id",
|
||||||
|
"user_id",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"private_key"
|
||||||
|
];
|
||||||
|
|
||||||
public function user ()
|
public function user ()
|
||||||
{
|
{
|
||||||
return $this->belongsTo (User::class);
|
return $this->belongsTo (User::class);
|
||||||
|
12
app/Models/Instance.php
Normal file
12
app/Models/Instance.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Instance extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
"inbox"
|
||||||
|
];
|
||||||
|
}
|
@ -69,7 +69,19 @@ class TypeActivity {
|
|||||||
return $follow_activity;
|
return $follow_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function craft_signed_headers ($activity, Actor $source, Actor $target)
|
public static function craft_update (Actor $actor, $fields)
|
||||||
|
{
|
||||||
|
$update_activity = new Activity ();
|
||||||
|
$update_activity->activity_id = env ("APP_URL") . "/activity/" . uniqid ();
|
||||||
|
$update_activity->type = "Update";
|
||||||
|
$update_activity->actor = $actor->actor_id;
|
||||||
|
$update_activity->object = $fields;
|
||||||
|
$update_activity->save ();
|
||||||
|
|
||||||
|
return $update_activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function craft_signed_headers ($activity, Actor $source, $target)
|
||||||
{
|
{
|
||||||
if (!$source->user)
|
if (!$source->user)
|
||||||
{
|
{
|
||||||
@ -87,7 +99,13 @@ class TypeActivity {
|
|||||||
$hash = hash ("sha256", $activity, true);
|
$hash = hash ("sha256", $activity, true);
|
||||||
$digest = base64_encode ($hash);
|
$digest = base64_encode ($hash);
|
||||||
|
|
||||||
$url = parse_url ($target->inbox);
|
$url = null;
|
||||||
|
|
||||||
|
if ($target instanceof Actor)
|
||||||
|
$url = parse_url ($target->inbox);
|
||||||
|
else
|
||||||
|
$url = parse_url ($target);
|
||||||
|
|
||||||
$string_to_sign = "(request-target): post ". $url["path"] . "\nhost: " . $url["host"] . "\ndate: " . $date . "\ndigest: SHA-256=" . $digest;
|
$string_to_sign = "(request-target): post ". $url["path"] . "\nhost: " . $url["host"] . "\ndate: " . $date . "\ndigest: SHA-256=" . $digest;
|
||||||
|
|
||||||
openssl_sign ($string_to_sign, $signature, $signer, OPENSSL_ALGO_SHA256);
|
openssl_sign ($string_to_sign, $signature, $signer, OPENSSL_ALGO_SHA256);
|
||||||
@ -105,7 +123,7 @@ class TypeActivity {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function post_activity (Activity $activity, Actor $source, Actor $target)
|
public static function post_activity (Activity $activity, Actor $source, $target)
|
||||||
{
|
{
|
||||||
$crafted_activity = TypeActivity::craft_response ($activity);
|
$crafted_activity = TypeActivity::craft_response ($activity);
|
||||||
$activity_json = json_encode ($crafted_activity, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
$activity_json = json_encode ($crafted_activity, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION);
|
||||||
@ -114,8 +132,19 @@ class TypeActivity {
|
|||||||
$headers = TypeActivity::craft_signed_headers ($activity_json, $source, $target);
|
$headers = TypeActivity::craft_signed_headers ($activity_json, $source, $target);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$target_inbox = null;
|
||||||
|
|
||||||
|
if ($target instanceof Actor)
|
||||||
|
{
|
||||||
|
$target_inbox = $target->inbox;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$target_inbox = $target;
|
||||||
|
}
|
||||||
|
|
||||||
$client = new Client ();
|
$client = new Client ();
|
||||||
$response = $client->post ($target->inbox, [
|
$response = $client->post ($target_inbox, [
|
||||||
"headers" => $headers,
|
"headers" => $headers,
|
||||||
"body" => $activity_json,
|
"body" => $activity_json,
|
||||||
"debug" => true
|
"debug" => true
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Types;
|
|||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Actor;
|
use App\Models\Actor;
|
||||||
|
use App\Models\Instance;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -137,6 +138,14 @@ class TypeActor {
|
|||||||
|
|
||||||
$actor->save ();
|
$actor->save ();
|
||||||
|
|
||||||
|
$instances = Instance::where ("inbox", $actor->sharedInbox);
|
||||||
|
if (!$instances->first ())
|
||||||
|
{
|
||||||
|
$instance = new Instance ();
|
||||||
|
$instance->inbox = $actor->sharedInbox;
|
||||||
|
$instance->save ();
|
||||||
|
}
|
||||||
|
|
||||||
return $actor;
|
return $actor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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('instances', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->string ("inbox")->unique ();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('instances');
|
||||||
|
}
|
||||||
|
};
|
@ -49,21 +49,29 @@
|
|||||||
@auth
|
@auth
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="f-row">
|
<div class="f-row">
|
||||||
<div class="f-col">
|
@if (!auth ()->user ()->is ($user))
|
||||||
@if (auth ()->user ()->actor->friends_with ($actor))
|
<div class="f-col">
|
||||||
<form action="{{ route ('user.unfriend') }}" onclick="this.submit ()" method="post">
|
@if (auth ()->user ()->actor->friends_with ($actor))
|
||||||
@csrf
|
<form action="{{ route ('user.unfriend') }}" onclick="this.submit ()" method="post" style="cursor: pointer">
|
||||||
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
@csrf
|
||||||
<img loading="lazy" src="/resources/icons/delete.png" alt=""> Remove Friend
|
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
||||||
</form>
|
<img loading="lazy" src="/resources/icons/delete.png" alt=""> Remove Friend
|
||||||
@else
|
</form>
|
||||||
<form action="{{ route ('user.friend') }}" onclick="this.submit ()" method="post">
|
@else
|
||||||
@csrf
|
<form action="{{ route ('user.friend') }}" onclick="this.submit ()" method="post" style="cursor: pointer">
|
||||||
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
@csrf
|
||||||
<img loading="lazy" src="/resources/icons/add.png" alt=""> Add to Friends
|
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
||||||
</form>
|
<img loading="lazy" src="/resources/icons/add.png" alt=""> Add to Friends
|
||||||
@endif
|
</form>
|
||||||
</div>
|
@endif
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="f-col">
|
||||||
|
<a href="{{ route ('users.edit') }}">
|
||||||
|
<img loading="lazy" src="/resources/icons/asterisk_yellow.png" alt=""> Edit Profile
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="f-col">
|
<div class="f-col">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user