From 27079892f2bf543ad40094690986807951186abb Mon Sep 17 00:00:00 2001 From: Ghostie Date: Sun, 29 Dec 2024 14:10:32 -0500 Subject: [PATCH] now profile edits are federalized --- app/Actions/ActionsUser.php | 33 ++++++++++++++++ app/Http/Controllers/AP/APActorController.php | 3 +- .../Controllers/AP/APOutboxController.php | 34 +++++++++++++++++ app/Http/Controllers/ProfileController.php | 6 +++ app/Models/Actor.php | 8 ++++ app/Models/Instance.php | 12 ++++++ app/Types/TypeActivity.php | 37 ++++++++++++++++-- app/Types/TypeActor.php | 9 +++++ ...24_12_29_012241_create_instances_table.php | 30 +++++++++++++++ resources/views/users/profile.blade.php | 38 +++++++++++-------- 10 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 app/Actions/ActionsUser.php create mode 100644 app/Models/Instance.php create mode 100644 database/migrations/2024_12_29_012241_create_instances_table.php diff --git a/app/Actions/ActionsUser.php b/app/Actions/ActionsUser.php new file mode 100644 index 0000000..d14bbb0 --- /dev/null +++ b/app/Actions/ActionsUser.php @@ -0,0 +1,33 @@ +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"]; + } +} diff --git a/app/Http/Controllers/AP/APActorController.php b/app/Http/Controllers/AP/APActorController.php index c4a0db5..f6a39a5 100644 --- a/app/Http/Controllers/AP/APActorController.php +++ b/app/Http/Controllers/AP/APActorController.php @@ -39,8 +39,7 @@ class APActorController extends Controller public function following (User $user) { - $actor_id = '"' . str_replace ("/", "\/", $user->actor->actor_id) . '"'; - $following = Activity::where ("type", "Follow")->where ("actor", $actor_id); + $following = Activity::where ("type", "Follow")->where ("actor", $user->actor->actor_id); $ordered_collection = new TypeOrderedCollection (); $ordered_collection->collection = $following->get ()->pluck ("object")->toArray (); $ordered_collection->url = route ("ap.following", $user->name); diff --git a/app/Http/Controllers/AP/APOutboxController.php b/app/Http/Controllers/AP/APOutboxController.php index 4396e06..42d91b9 100644 --- a/app/Http/Controllers/AP/APOutboxController.php +++ b/app/Http/Controllers/AP/APOutboxController.php @@ -5,8 +5,10 @@ namespace App\Http\Controllers\AP; use App\Models\User; use App\Models\Actor; use App\Models\Activity; +use App\Models\Instance; use App\Types\TypeActivity; +use App\Types\TypeActor; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; @@ -16,8 +18,13 @@ class APOutboxController extends Controller { public function outbox (User $user, Request $request) { + // TODO: check we are logged in and we are the logged in user switch ($request->get ("type")) { + case "UpdateProfile": + return $this->handle_update_profile ($user); + break; + case "Follow": return $this->handle_follow ($user, $request->get ("object")); 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) { $object_actor = Actor::where ("actor_id", $object)->first (); if (!$object_actor) 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); $response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 193bc02..ab6d9de 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -11,6 +11,8 @@ use Intervention\Image\Drivers\Gd\Driver; use App\Models\User; use App\Models\Actor; +use App\Actions\ActionsUser; + class ProfileController extends Controller { public function show ($user_name) @@ -96,6 +98,10 @@ class ProfileController extends Controller 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!"); } } diff --git a/app/Models/Actor.php b/app/Models/Actor.php index d19840b..0d77077 100644 --- a/app/Models/Actor.php +++ b/app/Models/Actor.php @@ -39,6 +39,14 @@ class Actor extends Model "private_key" ]; + protected $hidden = [ + "id", + "user_id", + "created_at", + "updated_at", + "private_key" + ]; + public function user () { return $this->belongsTo (User::class); diff --git a/app/Models/Instance.php b/app/Models/Instance.php new file mode 100644 index 0000000..4c19797 --- /dev/null +++ b/app/Models/Instance.php @@ -0,0 +1,12 @@ +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) { @@ -87,7 +99,13 @@ class TypeActivity { $hash = hash ("sha256", $activity, true); $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; 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); $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); try { + $target_inbox = null; + + if ($target instanceof Actor) + { + $target_inbox = $target->inbox; + } + else + { + $target_inbox = $target; + } + $client = new Client (); - $response = $client->post ($target->inbox, [ + $response = $client->post ($target_inbox, [ "headers" => $headers, "body" => $activity_json, "debug" => true diff --git a/app/Types/TypeActor.php b/app/Types/TypeActor.php index 8726466..80f42f1 100644 --- a/app/Types/TypeActor.php +++ b/app/Types/TypeActor.php @@ -4,6 +4,7 @@ namespace App\Types; use App\Models\User; use App\Models\Actor; +use App\Models\Instance; use GuzzleHttp\Client; use Illuminate\Support\Facades\Log; @@ -137,6 +138,14 @@ class TypeActor { $actor->save (); + $instances = Instance::where ("inbox", $actor->sharedInbox); + if (!$instances->first ()) + { + $instance = new Instance (); + $instance->inbox = $actor->sharedInbox; + $instance->save (); + } + return $actor; } diff --git a/database/migrations/2024_12_29_012241_create_instances_table.php b/database/migrations/2024_12_29_012241_create_instances_table.php new file mode 100644 index 0000000..b0654f3 --- /dev/null +++ b/database/migrations/2024_12_29_012241_create_instances_table.php @@ -0,0 +1,30 @@ +id(); + + $table->string ("inbox")->unique (); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('instances'); + } +}; diff --git a/resources/views/users/profile.blade.php b/resources/views/users/profile.blade.php index d65b6bc..4a48260 100644 --- a/resources/views/users/profile.blade.php +++ b/resources/views/users/profile.blade.php @@ -49,21 +49,29 @@ @auth
-
- @if (auth ()->user ()->actor->friends_with ($actor)) -
- @csrf - - Remove Friend -
- @else -
- @csrf - - Add to Friends -
- @endif -
+ @if (!auth ()->user ()->is ($user)) +
+ @if (auth ()->user ()->actor->friends_with ($actor)) +
+ @csrf + + Remove Friend +
+ @else +
+ @csrf + + Add to Friends +
+ @endif +
+ @else + + @endif