added the blogs feature

This commit is contained in:
Ghostie 2025-01-12 21:35:10 -05:00
parent 25f00b1dc7
commit 81f0d8818f
39 changed files with 995 additions and 92 deletions

View File

@ -195,11 +195,12 @@ Restart nginx:
sudo systemctl restart nginx sudo systemctl restart nginx
``` ```
Now link the storage to the public folder and install the dependencies for reverb: Now link the storage to the public folder, install the dependencies for reverb and seed the database:
```bash ```bash
php artisan storage:link php artisan storage:link
php artisan install:broadcasting php artisan install:broadcasting
php artisan db:seed
``` ```
Now, we need to create three services to handle the jobs that OurSpace needs to run, another to handle the notifications' queue and another one to run Laravel Reverb. So run something `emacs /lib/systemd/system/ourspace-queue.service`, `emacs /lib/systemd/system/ourspace-notifications.service`, `emacs /lib/systemd/system/ourspace-ws.service` and add the following content: Now, we need to create three services to handle the jobs that OurSpace needs to run, another to handle the notifications' queue and another one to run Laravel Reverb. So run something `emacs /lib/systemd/system/ourspace-queue.service`, `emacs /lib/systemd/system/ourspace-notifications.service`, `emacs /lib/systemd/system/ourspace-ws.service` and add the following content:

View File

@ -115,7 +115,11 @@ class ActionsPost
$processed = ActionsPost::process_content_and_attachments ($request); $processed = ActionsPost::process_content_and_attachments ($request);
$actor = auth ()->user ()->actor ()->first (); $actor = null;
if ($request ["blog_id"])
$actor = Actor::where ("blog_id", $request ["blog_id"])->first ();
else
$actor = auth ()->user ()->actor ()->first ();
try { try {
$client = new Client (); $client = new Client ();

View File

@ -0,0 +1,31 @@
<?php
namespace App\Events;
use App\Models\Blog;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class BlogCreatedEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public Blog $blog;
public User $user;
/**
* Create a new event instance.
*/
public function __construct(Blog $blog, User $user)
{
$this->blog = $blog;
$this->user = $user;
}
}

View File

@ -17,25 +17,36 @@ use App\Http\Controllers\Controller;
class APActorController extends Controller class APActorController extends Controller
{ {
public function user (User $user) public function user ($name)
{ {
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
if (!$actor)
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
if (str_contains (request ()->header ("Accept"), "text/html")) { if (str_contains (request ()->header ("Accept"), "text/html")) {
return redirect (route ("users.show", ["user_name" => $user->name])); if ($actor->blog_id) {
return redirect (route ("blogs.show", ["blog" => $actor->preferredUsername]));
}
return redirect (route ("users.show", ["user_name" => $actor->preferredUsername]));
} }
$actor = $user->actor ()->get (); $response = Actor::build_response ($actor);
$response = Actor::build_response ($actor->first ());
return response ()->json ($response)->header ("Content-Type", "application/activity+json"); return response ()->json ($response)->header ("Content-Type", "application/activity+json");
} }
public function followers (User $user) public function followers ($name)
{ {
$follower_ids = Follow::where ("object", $user->actor->id)->get (); $actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
if (!$actor)
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
$follower_ids = Follow::where ("object", $actor->id)->get ();
$followers = Actor::whereIn ("id", $follower_ids->pluck ("actor")->toArray ()); $followers = Actor::whereIn ("id", $follower_ids->pluck ("actor")->toArray ());
$ordered_collection = new TypeOrderedCollection (); $ordered_collection = new TypeOrderedCollection ();
$ordered_collection->collection = $followers->get ()->pluck ("actor_id")->toArray (); $ordered_collection->collection = $followers->get ()->pluck ("actor_id")->toArray ();
$ordered_collection->url = route ("ap.followers", $user->name); $ordered_collection->url = route ("ap.followers", $actor->name);
$ordered_collection->page_size = 10; $ordered_collection->page_size = 10;
if (request ()->has ("page")) { if (request ()->has ("page")) {
@ -46,14 +57,18 @@ class APActorController extends Controller
return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json"); return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json");
} }
public function following (User $user) public function following ($name)
{ {
$following_ids = Follow::where ("actor", $user->actor->id)->get (); $actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
if (!$actor)
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
$following_ids = Follow::where ("actor", $actor->id)->get ();
$following = Actor::whereIn ("id", $following_ids->pluck ("object")->toArray ()); $following = Actor::whereIn ("id", $following_ids->pluck ("object")->toArray ());
$ordered_collection = new TypeOrderedCollection (); $ordered_collection = new TypeOrderedCollection ();
$ordered_collection->collection = $following->get ()->pluck ("actor_id")->toArray (); $ordered_collection->collection = $following->get ()->pluck ("actor_id")->toArray ();
$ordered_collection->url = route ("ap.following", $user->name); $ordered_collection->url = route ("ap.following", $actor->name);
$ordered_collection->page_size = 10; $ordered_collection->page_size = 10;
if (request ()->has ("page")) { if (request ()->has ("page")) {
@ -64,9 +79,13 @@ class APActorController extends Controller
return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json"); return response ()->json ($ordered_collection->build_response_main ())->header ("Content-Type", "application/activity+json");
} }
public function featured (User $user) public function featured ($name)
{ {
$featured_ids = ProfilePin::where ("actor_id", $user->actor->id)->pluck ("note_id")->toArray (); $actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
if (!$actor)
return response ()->json (["error" => "Actor not found"], 404)->header ("Content-Type", "application/activity+json");
$featured_ids = ProfilePin::where ("actor_id", $actor->id)->pluck ("note_id")->toArray ();
$notes = Note::whereIn ("id", $featured_ids)->get (); $notes = Note::whereIn ("id", $featured_ids)->get ();
$collection = []; $collection = [];
@ -77,7 +96,7 @@ class APActorController extends Controller
$ordered_collection = new TypeOrderedCollection (); $ordered_collection = new TypeOrderedCollection ();
$ordered_collection->collection = $collection; $ordered_collection->collection = $collection;
$ordered_collection->url = route ("ap.featured", $user->name); $ordered_collection->url = route ("ap.featured", $actor->preferredUsername);
$ordered_collection->page_size = 10; $ordered_collection->page_size = 10;
if (request ()->has ("page")) { if (request ()->has ("page")) {

View File

@ -25,8 +25,12 @@ use App\Http\Controllers\Controller;
class APInboxController extends Controller class APInboxController extends Controller
{ {
public function inbox (User $user) public function inbox ($name)
{ {
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
if (!$actor)
return response ()->json ([ "error" => "Actor not found" ], 404);
$request = request (); $request = request ();
$type = $request->get ("type"); $type = $request->get ("type");
@ -35,15 +39,15 @@ class APInboxController extends Controller
switch ($type) { switch ($type) {
case "Follow": case "Follow":
$this->handle_follow ($user, $request->all ()); $this->handle_follow ($actor, $request->all ());
break; break;
case "Undo": case "Undo":
$this->handle_undo ($user, $request->all ()); $this->handle_undo ($actor, $request->all ());
break; break;
case "Like": case "Like":
$this->handle_like ($user, $request->all ()); $this->handle_like ($actor, $request->all ());
break; break;
default: default:
@ -53,17 +57,17 @@ class APInboxController extends Controller
} }
} }
private function handle_follow (User $user, $activity) private function handle_follow (Actor $actor, $activity)
{ {
ActivityFollowEvent::dispatch ($activity); ActivityFollowEvent::dispatch ($activity);
} }
public function handle_undo (User $user, $activity) public function handle_undo (Actor $actor, $activity)
{ {
ActivityUndoEvent::dispatch ($activity); ActivityUndoEvent::dispatch ($activity);
} }
public function handle_like (User $user, $activity) public function handle_like (Actor $actor, $activity)
{ {
ActivityLikeEvent::dispatch ($activity); ActivityLikeEvent::dispatch ($activity);
} }

View File

@ -29,45 +29,47 @@ use Illuminate\Support\Facades\Storage;
class APOutboxController extends Controller class APOutboxController extends Controller
{ {
public function outbox (User $user, Request $request) public function outbox ($name, Request $request)
{ {
$actor = Actor::where ("preferredUsername", $name)->where ("user_id", "!=", null)->first ();
// TODO: check we are logged in and we are the logged in user // TODO: check we are logged in and we are the logged in user
switch ($request->get ("type")) switch ($request->get ("type"))
{ {
case "UpdateProfile": case "UpdateProfile":
return $this->handle_update_profile ($user); return $this->handle_update_profile ($actor);
break; break;
case "UpdateNote": case "UpdateNote":
return $this->handle_update_note ($user, $request); return $this->handle_update_note ($actor, $request);
break; break;
case "DeleteNote": case "DeleteNote":
return $this->handle_delete_note ($user, $request); return $this->handle_delete_note ($actor, $request);
break; break;
case "Follow": case "Follow":
return $this->handle_follow ($user, $request->get ("object")); return $this->handle_follow ($actor, $request->get ("object"));
break; break;
case "Unfollow": case "Unfollow":
return $this->handle_unfollow ($user, $request->get ("object")); return $this->handle_unfollow ($actor, $request->get ("object"));
break; break;
case "Like": case "Like":
return $this->handle_like ($user, $request->get ("object")); return $this->handle_like ($actor, $request->get ("object"));
break; break;
case "Boost": case "Boost":
return $this->handle_boost ($user, $request->get ("object")); return $this->handle_boost ($actor, $request->get ("object"));
break; break;
case "Pin": case "Pin":
return $this->handle_pin ($user, $request->get ("object")); return $this->handle_pin ($actor, $request->get ("object"));
break; break;
case "Post": case "Post":
return $this->handle_post ($user, $request); return $this->handle_post ($actor, $request);
break; break;
default: default:
@ -77,9 +79,8 @@ class APOutboxController extends Controller
} }
} }
public function handle_update_profile (User $user) public function handle_update_profile (Actor $actor)
{ {
$actor = $user->actor ()->first ();
$actor_response = TypeActor::build_response ($actor); $actor_response = TypeActor::build_response ($actor);
$update_activity = TypeActivity::craft_update ($actor, $actor_response); $update_activity = TypeActivity::craft_update ($actor, $actor_response);
@ -87,10 +88,8 @@ class APOutboxController extends Controller
return response ()->json ("success", 200); return response ()->json ("success", 200);
} }
public function handle_update_note (User $user, $request) public function handle_update_note (Actor $actor, $request)
{ {
$actor = $user->actor ()->first ();
// first check if there are new attachments // first check if there are new attachments
if ($request ["attachments"]) if ($request ["attachments"])
{ {
@ -135,9 +134,8 @@ class APOutboxController extends Controller
return response ()->json ("success", 200); return response ()->json ("success", 200);
} }
public function handle_delete_note (User $user, $request) public function handle_delete_note (Actor $actor, $request)
{ {
$actor = $user->actor ()->first ();
$note = Note::where ("id", $request ["note"])->first (); $note = Note::where ("id", $request ["note"])->first ();
if (!$note) if (!$note)
return response ()->json ([ "error" => "note not found" ], 404); return response ()->json ([ "error" => "note not found" ], 404);
@ -154,29 +152,29 @@ class APOutboxController extends Controller
return response ()->json ("success", 200); return response ()->json ("success", 200);
} }
public function handle_follow (User $user, string $object) public function handle_follow (Actor $actor, 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) if ($actor->actor_id == $object_actor->actor_id)
return response ()->json ([ "error" => "cannot follow self" ], 400); return response ()->json ([ "error" => "cannot follow self" ], 400);
// check we are not following already // check we are not following already
$following_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id) $following_activity = Activity::where ("actor", $actor->actor_id)
->where ("object", '"' . str_replace ("/", "\/", $object_actor->actor_id) . '"') ->where ("object", '"' . str_replace ("/", "\/", $object_actor->actor_id) . '"')
->where ("type", "Follow") ->where ("type", "Follow")
->first (); ->first ();
if ($following_activity) if ($following_activity)
return response ()->json ([ "error" => "already following" ], 400); return response ()->json ([ "error" => "already following" ], 400);
$follow_activity = TypeActivity::craft_follow ($user->actor ()->first (), $object_actor); $follow_activity = TypeActivity::craft_follow ($actor, $object_actor);
$response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor); $response = TypeActivity::post_activity ($follow_activity, $actor, $object_actor);
$follow = Follow::create ([ $follow = Follow::create ([
"activity_id" => $follow_activity->id, "activity_id" => $follow_activity->id,
"actor" => $user->actor ()->first ()->id, "actor" => $actor->id,
"object" => $object_actor->id, "object" => $object_actor->id,
]); ]);
@ -189,21 +187,21 @@ class APOutboxController extends Controller
]; ];
} }
public function handle_unfollow (User $user, string $object) public function handle_unfollow (Actor $actor, 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);
$follow_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id) $follow_activity = Activity::where ("actor", $actor->actor_id)
->where ("object", json_encode ($object_actor->actor_id, JSON_UNESCAPED_SLASHES)) ->where ("object", json_encode ($object_actor->actor_id, JSON_UNESCAPED_SLASHES))
->where ("type", "Follow") ->where ("type", "Follow")
->first (); ->first ();
if (!$follow_activity) if (!$follow_activity)
return response ()->json ([ "error" => "no follow activity found. " . $user->actor ()->first ()->actor_id . " unfollowing " . $object_actor->actor_id ], 404); return response ()->json ([ "error" => "no follow activity found. " . $actor->actor_id . " unfollowing " . $object_actor->actor_id ], 404);
$unfollow_activity = TypeActivity::craft_undo ($follow_activity, $user->actor ()->first ()); $unfollow_activity = TypeActivity::craft_undo ($follow_activity, $actor);
$response = TypeActivity::post_activity ($unfollow_activity, $user->actor ()->first (), $object_actor); $response = TypeActivity::post_activity ($unfollow_activity, $actor, $object_actor);
// TODO: Check if it was successfully sent // TODO: Check if it was successfully sent
/* if (!$response || $response->getStatusCode () < 200 || $response->getStatusCode () >= 300) /* if (!$response || $response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
@ -216,13 +214,12 @@ class APOutboxController extends Controller
]; ];
} }
public function handle_like (User $user, $request) public function handle_like (Actor $actor, $request)
{ {
$object = Note::where ("note_id", $request)->first (); $object = Note::where ("note_id", $request)->first ();
if (!$object) if (!$object)
return response ()->json ([ "error" => "object not found" ], 404); return response ()->json ([ "error" => "object not found" ], 404);
$actor = $user->actor ()->first ();
$already_liked = $actor->liked_note ($object); $already_liked = $actor->liked_note ($object);
if ($already_liked) if ($already_liked)
{ {
@ -262,13 +259,12 @@ class APOutboxController extends Controller
]; ];
} }
public function handle_boost (User $user, $object) public function handle_boost (Actor $actor, $object)
{ {
$object = Note::where ("note_id", $object)->first (); $object = Note::where ("note_id", $object)->first ();
if (!$object) if (!$object)
return response ()->json ([ "error" => "object not found" ], 404); return response ()->json ([ "error" => "object not found" ], 404);
$actor = $user->actor ()->first ();
$already_boosted = $actor->boosted_note ($object); $already_boosted = $actor->boosted_note ($object);
if ($already_boosted) if ($already_boosted)
{ {
@ -302,13 +298,12 @@ class APOutboxController extends Controller
]; ];
} }
public function handle_pin (User $user, $object) public function handle_pin (Actor $actor, $object)
{ {
$object = Note::where ("note_id", $object)->first (); $object = Note::where ("note_id", $object)->first ();
if (!$object) if (!$object)
return response ()->json ([ "error" => "object not found" ], 404); return response ()->json ([ "error" => "object not found" ], 404);
$actor = $user->actor ()->first ();
$already_pinned = $object->is_pinned ($actor); $already_pinned = $object->is_pinned ($actor);
if ($already_pinned) if ($already_pinned)
{ {
@ -342,9 +337,8 @@ class APOutboxController extends Controller
]; ];
} }
public function handle_post (User $user, $request) public function handle_post (Actor $actor, $request)
{ {
$actor = $user->actor ()->first ();
$note = TypeNote::craft_from_outbox ($actor, $request); $note = TypeNote::craft_from_outbox ($actor, $request);
if (isset ($request ["attachments"])) if (isset ($request ["attachments"]))
@ -417,6 +411,18 @@ class APOutboxController extends Controller
} }
$note->visibility = $request ["visibility"]; $note->visibility = $request ["visibility"];
// if the parent note is not public, responses shouldn't be either
if ($request ["inReplyTo"])
{
$parent_note = TypeNote::note_exists($request ["inReplyTo"]);
if ($parent_note)
{
$note->to = $parent_note->to;
$note->cc = $parent_note->cc;
$note->visibility = $parent_note->visibility;
}
}
$note->save (); $note->save ();
$create_activity = TypeActivity::craft_create ($actor, $note); $create_activity = TypeActivity::craft_create ($actor, $note);

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers\AP; namespace App\Http\Controllers\AP;
use App\Models\User; use App\Models\User;
use App\Models\Blog;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -32,7 +33,9 @@ class APWebfingerController extends Controller
$user = $user[0]; $user = $user[0];
$actual_user = User::where ("name", $user)->first (); $actual_user = User::where ("name", $user)->first ();
if (!isset ($actual_user)) { if (!isset ($actual_user)) {
return response ()->json ([ "error" => "user not found" ], 404); $actual_user = Blog::where ("slug", $user)->first ();
if (!$actual_user)
return response ()->json ([ "error" => "user not found" ], 404);
} }
$webfinger = [ $webfinger = [

View File

@ -0,0 +1,94 @@
<?php
namespace App\Http\Controllers;
use App\Models\Blog;
use App\Events\BlogCreatedEvent;
use App\Models\BlogCategory;
use App\Helpers\PaginationHelper;
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class BlogController extends Controller
{
public function index ()
{
$categories = BlogCategory::all ();
$user = null;
if (auth ()->check ())
$user = auth ()->user ();
$blogs = Blog::orderBy ("created_at", "desc")->paginate (10);
return view ("blogs", compact ("user", "blogs", "categories"));
}
public function create ()
{
$categories = BlogCategory::all ();
return view ("blogs.create", compact ("categories"));
}
public function store (Request $request)
{
if (!auth ()->check ())
return redirect ()->route ("login")->with ("error", "You must be logged in to create a blog.");
$request->validate ([
"name" => "required|unique:users|unique:blogs",
"description" => "required",
"icon" => "required|image|max:4096",
"category" => "required"
]);
$user = auth ()->user ();
$category = BlogCategory::find ($request->category);
if (!$category)
return redirect ()->route ("blogs.create")->with ("error", "Invalid category selected.");
$icon = null;
$fname = $user->id . "-" . uniqid();
if ($request->icon)
{
$manager = new ImageManager (new Driver ());
$image = $manager->read ($request->file ("icon"));
$image_data = $image->cover (256, 256)->toJpeg ();
Storage::disk ("public")->put ("blog_icons/" . $fname . ".jpg", $image_data);
}
$blog = Blog::create ([
"name" => $request ["name"],
"slug" => Str::slug ($request ["name"]),
"description" => Str::markdown($request ["description"]),
"icon" => $fname . ".jpg",
"user_id" => $user->id,
"blog_category_id" => $category->id
]);
BlogCreatedEvent::dispatch ($blog, $user);
return redirect ()->route ("blogs.show", [ 'blog' => $blog->slug ])->with ("success", "Blog created successfully!");
}
public function show (Blog $blog)
{
$notes = PaginationHelper::paginate ($blog->notes ()->orderBy ("created_at", "desc")->get (), 10);
return view ("blogs.show", compact ("blog", "notes"));
}
public function new_entry (Blog $blog)
{
return view ("blogs.new_entry", compact ("blog"));
}
}

View File

@ -9,6 +9,7 @@ use App\Models\User;
use App\Models\Actor; use App\Models\Actor;
use App\Models\Note; use App\Models\Note;
use App\Models\Hashtag; use App\Models\Hashtag;
use App\Models\BlogCategory;
use App\Helpers\PaginationHelper; use App\Helpers\PaginationHelper;

View File

@ -106,8 +106,7 @@ class PostController extends Controller
if (!auth ()->check ()) if (!auth ()->check ())
return back ()->with ("error", "You need to be logged in to pin a post."); return back ()->with ("error", "You need to be logged in to pin a post.");
$user = auth ()->user (); $actor = $note->get_actor ()->first ();
$actor = $user->actor ()->first ();
$response = ActionsPost::pin_post ($actor, $note); $response = ActionsPost::pin_post ($actor, $note);

View File

@ -11,6 +11,7 @@ use Intervention\Image\Drivers\Gd\Driver;
use App\Models\User; use App\Models\User;
use App\Models\Actor; use App\Models\Actor;
use App\Models\Note; use App\Models\Note;
use App\Models\Blog;
use App\Actions\ActionsUser; use App\Actions\ActionsUser;
use App\Helpers\PaginationHelper; use App\Helpers\PaginationHelper;
@ -168,8 +169,9 @@ class ProfileController extends Controller
$ids = $user->mutual_friends (); $ids = $user->mutual_friends ();
if (request ()->get ("query")) if (request ()->get ("query"))
{ {
$query = request ()->get ("query");
$friends = Actor::whereIn ("actor_id", $ids) $friends = Actor::whereIn ("actor_id", $ids)
->where ("preferredUsername", "like", "%" . request ()->get ("query") . "%") ->where ("preferredUsername", "like", "%" . $query . "%")
->get (); ->get ();
} }
else else
@ -231,4 +233,20 @@ class ProfileController extends Controller
return view ("users.notifications", compact ("user", "notifications", "processed_notifications", "unread_notifications")); return view ("users.notifications", compact ("user", "notifications", "processed_notifications", "unread_notifications"));
} }
public function blogs ($user_name)
{
if (str_starts_with ($user_name, "@"))
{
return redirect ()->route ("users.show", [ "user_name" => $user_name ]);
}
$user = User::where ("name", $user_name)->first ();
if (!$user)
return redirect ()->route ("home");
$blogs = Blog::where ("user_id", $user->id)->orderBy ("created_at", "desc")->get ();
return view ("users.blogs", compact ("user", "blogs"));
}
} }

View File

@ -26,7 +26,7 @@ class UserController extends Controller
public function do_signup (Request $request) public function do_signup (Request $request)
{ {
$incoming_fields = $request->validate ([ $incoming_fields = $request->validate ([
"name" => "required|alpha_dash", "name" => "required|alpha_dash|unique:users|unique:blogs",
"email" => "required|email|unique:users", "email" => "required|email|unique:users",
"password" => "required|confirmed" "password" => "required|confirmed"
]); ]);

View File

@ -0,0 +1,36 @@
<?php
namespace App\Listeners;
use App\Models\Actor;
use App\Types\TypeActor;
use App\Events\BlogCreatedEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class BlogCreatedListener
{
/**
* Create the event listener.
*/
public function __construct()
{
//
}
/**
* Handle the event.
*/
public function handle(BlogCreatedEvent $event): void
{
$actor = new Actor ();
$actor = $actor->create_from_blog ($event->blog);
$actor->blog_id = $event->blog->id;
$actor->user_id = $event->user->id;
$actor->save ();
$event->blog->actor_id = $actor->id;
$event->blog->save ();
}
}

View File

@ -5,6 +5,7 @@ namespace App\Models;
use App\Models\User; use App\Models\User;
use App\Models\Announcement; use App\Models\Announcement;
use App\Models\Note; use App\Models\Note;
use App\Models\Blog;
use App\Helpers\PaginationHelper; use App\Helpers\PaginationHelper;
@ -19,6 +20,7 @@ class Actor extends Model
"type", "type",
"actor_id", "actor_id",
"blog_id",
"local_actor_id", "local_actor_id",
"following", "following",
@ -57,6 +59,11 @@ class Actor extends Model
return $this->belongsTo (User::class); return $this->belongsTo (User::class);
} }
public function blog ()
{
return $this->belongsTo (Blog::class);
}
public function profile_attachment () public function profile_attachment ()
{ {
return $this->hasMany (ProfileAttachment::class); return $this->hasMany (ProfileAttachment::class);
@ -84,6 +91,12 @@ class Actor extends Model
return $this->create ($data); return $this->create ($data);
} }
public function create_from_blog (Blog $blog)
{
$data = TypeActor::create_from_blog ($blog);
return $this->create ($data);
}
public static function build_response (Actor $actor) public static function build_response (Actor $actor)
{ {
return TypeActor::build_response ($actor); return TypeActor::build_response ($actor);

38
app/Models/Blog.php Normal file
View File

@ -0,0 +1,38 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
protected $fillable = [
"name",
"slug",
"description",
"icon",
"user_id",
"actor_id",
"blog_category_id"
];
public function user ()
{
return $this->belongsTo (User::class);
}
public function actor ()
{
return $this->belongsTo (Actor::class);
}
public function notes ()
{
return $this->hasMany (Note::class, "actor_id", "actor_id");
}
public function pinned_notes ()
{
return $this->hasMany (ProfilePin::class, "actor_id", "actor_id");
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BlogCategory extends Model
{
protected $fillable = [
"name",
"slug"
];
}

View File

@ -11,4 +11,19 @@ class ProfilePin extends Model
"note_id", "note_id",
"actor_id" "actor_id"
]; ];
public function activity ()
{
return $this->belongsTo (Activity::class);
}
public function note ()
{
return $this->belongsTo (Note::class);
}
public function actor ()
{
return $this->belongsTo (Actor::class);
}
} }

View File

@ -99,6 +99,11 @@ class User extends Authenticatable
return Cache::has ("user-online-" . $this->id); return Cache::has ("user-online-" . $this->id);
} }
public function blogs ()
{
return $this->hasMany (Blog::class);
}
public function mutual_friends () public function mutual_friends ()
{ {
$followers = Follow::where ("actor", $this->actor->id)->pluck ("object")->toArray (); $followers = Follow::where ("actor", $this->actor->id)->pluck ("object")->toArray ();

View File

@ -3,6 +3,7 @@
namespace App\Types; namespace App\Types;
use App\Models\User; use App\Models\User;
use App\Models\Blog;
use App\Models\Actor; use App\Models\Actor;
use App\Models\ProfileAttachment; use App\Models\ProfileAttachment;
use App\Models\Instance; use App\Models\Instance;
@ -62,6 +63,40 @@ class TypeActor {
]; ];
} }
public static function create_from_blog (Blog $blog)
{
$keys = TypeActor::gen_keys ();
$app_url = env ("APP_URL");
return [
"blog_id" => $blog->id,
"type" => "Person",
"actor_id" => $app_url . "/ap/v1/user/" . $blog->slug,
"inbox" => $app_url . "/ap/v1/user/" . $blog->slug . "/inbox",
"outbox" => $app_url . "/ap/v1/user/" . $blog->slug . "/outbox",
"following" => $app_url . "/ap/v1/user/" . $blog->slug . "/following",
"followers" => $app_url . "/ap/v1/user/" . $blog->slug . "/followers",
"liked" => $app_url . "/ap/v1/user/" . $blog->slug . "/liked",
"featured" => $app_url . "/ap/v1/user/" . $blog->slug . "/collections/featured",
"featured_tags" => $app_url . "/ap/v1/user/" . $blog->slug . "/collections/featured/tags",
"sharedInbox" => $app_url . "/ap/v1/inbox",
"preferredUsername" => $blog->slug,
"name" => $blog->name,
"summary" => $blog->description,
"icon" => $app_url . "/storage/blog_icons/" . $blog->icon,
"public_key" => $keys["public_key"]["key"],
"private_key" => $keys["private_key"]
];
}
public static function build_response (Actor $actor) public static function build_response (Actor $actor)
{ {
$response = [ $response = [
@ -122,7 +157,7 @@ class TypeActor {
] ]
]; ];
if ($actor->user) if ($actor->user && !$actor->blog_id)
{ {
// appent to @context // appent to @context
$response ["@context"][] = [ $response ["@context"][] = [

View File

@ -0,0 +1,39 @@
<?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('blogs', function (Blueprint $table) {
$table->id();
$table->string ("name")->unique ();
$table->string ("slug")->unique ();
$table->text ("description")->nullable ();
$table->string ("icon")->nullable ();
$table->foreignId ("user_id")->nullable ()->constrained ()->onDelete ("cascade");
$table->foreignId ("actor_id")->nullable ()->constrained ()->onDelete ("cascade");
$table->foreignId ("blog_category_id")->nullable ()->constrained ();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('blogs');
}
};

View File

@ -0,0 +1,31 @@
<?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('blog_categories', function (Blueprint $table) {
$table->id();
$table->string ("name")->unique ();
$table->string ("slug")->unique ();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('blog_categories');
}
};

View File

@ -0,0 +1,29 @@
<?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('actors', function (Blueprint $table) {
$table->foreignId ("blog_id")->nullable ()->after ("user_id")->constrained ()->onDelete ("cascade");
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('actors', function (Blueprint $table) {
$table->dropForeign (["blog_id"]);
$table->dropColumn ("blog_id");
});
}
};

View File

@ -0,0 +1,95 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class BlogCategorySeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$categories = [
[
"name" => "Art",
"slug" => "art"
],
[
"name" => "Automotive",
"slug" => "automotive"
],
[
"name" => "Fashion",
"slug" => "fashion"
],
[
"name" => "Financial",
"slug" => "financial"
],
[
"name" => "Food",
"slug" => "food"
],
[
"name" => "Games",
"slug" => "games"
],
[
"name" => "Life",
"slug" => "life"
],
[
"name" => "Literature",
"slug" => "literature"
],
[
"name" => "Math & Science",
"slug" => "math-science"
],
[
"name" => "Movies & TV",
"slug" => "movies-tv"
],
[
"name" => "Music",
"slug" => "music"
],
[
"name" => "Paranormal",
"slug" => "paranormal"
],
[
"name" => "Politics",
"slug" => "politics"
],
[
"name" => "Humanity",
"slug" => "humanity"
],
[
"name" => "Romance",
"slug" => "romance"
],
[
"name" => "Sports",
"slug" => "sports"
],
[
"name" => "Technology",
"slug" => "technology"
],
[
"name" => "Travel",
"slug" => "travel"
]
];
foreach ($categories as $category) {
DB::table("blog_categories")->insert($category);
}
}
}

View File

@ -15,9 +15,8 @@ class DatabaseSeeder extends Seeder
{ {
// User::factory(10)->create(); // User::factory(10)->create();
User::factory()->create([ $this->call ([
'name' => 'Test User', BlogCategorySeeder::class
'email' => 'test@example.com',
]); ]);
} }
} }

View File

@ -0,0 +1,85 @@
@extends ("partials.layout")
@section ("title", "Blogs")
@section ("content")
<div class="row blog-category">
<div class="col w-20 left">
<div class="category-list">
<b>View:</b>
<ul>
<li>
<a href="{{ route ('blogs') }}">
<img loading="lazy" src="/resources/icons/clock.png" class="icon">
<b>Recent Blogs</b>
</a>
</li>
{{-- TODO: Top entries and blogs I'm following --}}
</ul>
<b>Categories:</b>
<ul>
@foreach ($categories as $category)
<li>
<a href="#">{{ $category->name }}</a>
</li>
@endforeach
</ul>
</div>
</div>
<div class="col right">
<h1>Blogs</h1>
@auth
<div class="blog-preview">
<h3>
[
<a href="{{ route ('blogs.create') }}">Create a blog</a>
]
</h3>
<h3>
[
<a href="#">View your blogs</a>
]
</h3>
</div>
@endauth
<hr>
<h3>Latest Blogs</h3>
<div class="blog-entries">
@foreach ($blogs as $blog)
<div class="entry">
<p class="publish-date">
<time class="ago">{{ $blog->created_at->diffForHumans () }}</time>
&mdash; by <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">{{ $blog->user->name }}</a>
&mdash; <a>{{ count ($blog->notes) }} Posts</a>
</p>
<div class="inner">
<h3 class="title">
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
{{ $blog->name }}
</a>
</h3>
<p>
{!! $blog->description !!}
</p>
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
&raquo; Read more
</a>
</div>
</div>
@endforeach
{{ $blogs->links ("pagination::default") }}
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,52 @@
@extends ("partials.layout")
@section ("title", "Create a new blog")
@section ("content")
<div class="row edit-blog-entry">
<div class="col w-20 left">
<div class="edit-info">
<p>You can use markdown in the description of your blog!</p>
</div>
</div>
<div class="col right">
<h1>Create Blog</h1>
<br>
<form method="POST" class="ctrl-enter-submit" enctype="multipart/form-data">
@csrf
<label for="name">Name:</label>
<input type="text" name="name" id="name" value="{{ old ('name') }}" required>
@error("name")
<p class="error">{{ $message }}</p>
@enderror
<label for="description">Description:</label>
<textarea name="description" id="description">{{ old ("description") }}</textarea>
@error("description")
<p class="error">{{ $message }}</p>
@enderror
<label for="icon">Logo:</label>
<input type="file" name="icon" id="icon" accept="image/*" required>
@error("icon")
<p class="error">{{ $message }}</p>
@enderror
<br>
<label for="category">Category:</label>
<select name="category" id="category">
@foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
@endforeach
</select>
<div class="publish">
<button type="submit" name="submit">Create Blog!</button>
</div>
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,56 @@
@extends ("partials.layout")
@section ("title", "Create New Entry")
@section ("content")
<div class="row edit-blog-entry">
<div class="col w-20 left">
<div class="edit-info">
<p>You can use Markdown in the content of your entry!</p>
</div>
</div>
<div class="col right">
<h1>New Entry</h1>
<br>
<form method="POST" class="ctrl-enter-submit" enctype="multipart/form-data" action="{{ route ('user.post.new') }}">
@csrf
<input type="hidden" name="blog_id" value="{{ $blog->id }}">
<label for="summary">Title:</label>
<input type="text" name="summary" id="summary" value="{{ old ('summary') }}" placeholder="A really cool post!" required>
@error("summary")
<p class="error">{{ $message }}</p>
@enderror
<br>
<textarea name="content" placeholder="What's on your mind?">{{ old ('content') }}</textarea>
<small>Markdown is supported</small>
@error("content")
<p class="error">{{ $message }}</p>
@enderror
<br>
<label for="files">Attachments:</label><br>
<input type="file" name="files[]" accept="image/*" id="files" multiple><br>
@error("files.*")
<p class="error">{{ $message }}</p>
@enderror
<br>
<label for="visibility">Visibility</label>
<select name="visibility">
<option value="public">Public</option>
<option value="followers">Friends only</option>
<option value="private">Private</option>
</select>
<br><br>
<button type="submit">Post!</button>
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,88 @@
@extends ("partials.layout")
@section ("title", $blog->name)
@section ("content")
<div class="row profile">
<div class="col w-30 left">
<h1>
{{ $blog->name }}
</h1>
<div class="general-about">
<div class="profile-pic">
<img loading="lazy" src="{{ $blog->actor->icon }}" alt="{{ $blog->name }}" class="pfp-fallback">
</div>
<div class="details below">
@if ($blog->user->is_online ())
<p class="online">
<img loading="lazy" src="/resources/img/green_person.png"> ONLINE!
</p>
@endif
</div>
</div>
<div class="mood">
<p><b>Mood:</b> {{ $blog->user->mood }}</p>
<br>
<p>
<b>View my: <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">Profile</a></b>
</p>
</div>
<div class="url-info">
<p>
<b>
Federation Handle:
</b>
</p>
<p>@php echo "@" . $blog->slug . "@" . explode ("/", env ("APP_URL"))[2] @endphp</p>
</div>
<div class="url-info view-full-profile">
<p>
<a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">
<b>View Full Profile</b>
</a>
</p>
</div>
</div>
<div class="col right">
<div class="blog-preview">
<h1>
{{ $blog->name }}'s Blog Entries
</h1>
<div class="blog-preview">
<h3>
[
<a href="{{ route ('blogs.new_entry', [ 'blog' => $blog->slug ]) }}">
New Entry
</a>
]
</h3>
</div>
<br>
<div class="blog-entries">
<h3>Pinned</h3>
@foreach ($blog->pinned_notes as $note)
<x-blog_entry_block :note="$note->note" />
@endforeach
<hr>
@foreach ($notes as $note)
<x-blog_entry_block :note="$note" />
@endforeach
{{ $notes->links () }}
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,20 @@
<div class="entry">
<p class="publish-date">
<time class="ago">
{{ $note->created_at->diffForHumans () }}
</time>
</p>
<div class="inner">
<h3 class="title">
<a href="{{ route ('posts.show', [ 'note' => $note ]) }}">
{{ $note->summary }}
</a>
</h3>
<p>
<a href="{{ route ('posts.show', [ 'note' => $note ]) }}">
&raquo; View Blog Entry
</a>
</p>
</div>
</div>

View File

@ -17,7 +17,9 @@ if (!$actor)
return; return;
} }
if ($actor->user_id) if ($actor->blog_id)
$actor_url = route ('blogs.show', [ 'blog' => $actor->blog->slug ]);
else if ($actor->user_id)
$actor_url = route ('users.show', [ 'user_name' => $actor->user->name ]); $actor_url = route ('users.show', [ 'user_name' => $actor->user->name ]);
else else
$actor_url = route ('users.show', [ 'user_name' => $actor->local_actor_id ]); $actor_url = route ('users.show', [ 'user_name' => $actor->local_actor_id ]);
@ -35,11 +37,7 @@ if (!$display_post->can_view ())
</a> </a>
<a href="{{ $actor_url }}"> <a href="{{ $actor_url }}">
<p> <p>
@if ($actor->user) <img loading="lazy" src="{{ $actor->icon }}" class="pfp-fallback" width="50">
<img loading="lazy" src="{{ $actor->user->avatar }}" class="pfp-fallback" width="50">
@else
<img loading="lazy" src="{{ $actor->icon }}" class="pfp-fallback" width="50">
@endif
</p> </p>
</a> </a>
</td> </td>

View File

@ -13,14 +13,14 @@
<small>Markdown is supported</small> <small>Markdown is supported</small>
<br> <br>
<input type="file" name="files[]" accept="image/*" multiple><br> <input type="file" name="files[]" accept="image/*" multiple><br>
<p> <div>
<b>Visibility:</b> <b>Visibility:</b>
<select name="visibility"> <select name="visibility">
<option value="public">Public</option> <option value="public">Public</option>
<option value="followers">Friends only</option> <option value="followers">Friends only</option>
<option value="private">Mentioned Only</option> <option value="private">Mentioned Only</option>
</select> </select>
</p> </div>
<button type="submit">Post</button> <button type="submit">Post</button>
@error ("content") @error ("content")

View File

@ -25,7 +25,7 @@
View My View My
<a href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}">Profile</a> <a href="{{ route('users.show', ['user_name' => auth()->user()->name]) }}">Profile</a>
| |
<a href="#">Blog</a> <a href="{{ route ('users.blogs', [ 'user_name' => auth ()->user ()->name ]) }}">Blog</a>
| |
<a href="#">Bulletins</a> <a href="#">Bulletins</a>
| |

View File

@ -47,7 +47,11 @@
</li> </li>
<li> <li>
<a href="#">&nbsp;Blog </a> @auth
<a href="{{ route ('blogs') }}">&nbsp;Blog </a>
@else
<a href="{{ route ('login') }}">&nbsp; Blog</a>
@endauth
</li> </li>
<li> <li>

View File

@ -1,3 +1,20 @@
@php
$user_url = null;
if ($actor->blog_id)
{
$user_url = route ('blogs.show', [ 'blog' => $actor->blog->slug ]);
}
else if ($actor->user_id)
{
$user_url = route ('users.show', [ 'user_name' => $actor->user->name ]);
}
else
{
$user_url = route ('users.show', [ 'user_name' => $actor->local_actor_id ]);
}
@endphp
@extends("partials.layout") @extends("partials.layout")
@section ("title", "View Post") @section ("title", "View Post")
@ -8,14 +25,14 @@
<div class="col w-20 left"> <div class="col w-20 left">
<div class="edit-info"> <div class="edit-info">
<div class="profile-pic"> <div class="profile-pic">
<img loading="lazy" src="{{ $actor->user ? $actor->user->avatar : $actor->icon }}" class="pfp-fallback"> <img loading="lazy" src="{{ $actor->icon }}" class="pfp-fallback">
</div> </div>
<div class="author-details"> <div class="author-details">
<h4> <h4>
Published by Published by
<span> <span>
<a href="{{ route ('users.show', [ 'user_name' => $actor->user ? $actor->user->name : $actor->local_actor_id ]) }}"> <a href="{{ $user_url }}">
{{ $actor->name }} {{ $actor->name }}
</a> </a>
</span> </span>
@ -29,9 +46,9 @@
</p> </p>
<p class="links"> <p class="links">
<a href="{{ route ('users.show', [ 'user_name' => $actor->user ? $actor->user->name : $actor->local_actor_id ]) }}"> <a href="{{ $user_url }}">
<img loading="lazy" src="/resources/icons/user.png" class="icon"> <img loading="lazy" src="/resources/icons/user.png" class="icon">
<span class="m-hide">View</span> Profile <span class="m-hide">View</span> {{ $actor->blog_id ? "Blog" : "Profile" }}
</a> </a>
</p> </p>
</div> </div>

View File

@ -0,0 +1,42 @@
@extends ("partials.layout")
@section ("title", $user->name . "'s Blogs")
@section ("content")
<div class="simple-container">
<h1>{{ $user->name }}'s Blogs</h1>
<p>
<a href="{{ route ('users.show', [ 'user_name' => $user->name ]) }}">&laquo; Back to profile</a>
</p>
<br>
<div class="blog-entries">
@foreach ($blogs as $blog)
<div class="entry">
<p class="publish-date">
<time class="ago">{{ $blog->created_at->diffForHumans () }}</time>
&mdash; by <a href="{{ route ('users.show', [ 'user_name' => $blog->user->name ]) }}">{{ $blog->user->name }}</a>
&mdash; <a>{{ count ($blog->notes) }} Posts</a>
</p>
<div class="inner">
<h3 class="title">
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
{{ $blog->name }}
</a>
</h3>
<p>
{!! $blog->description !!}
</p>
<a href="{{ route ('blogs.show', [ 'blog' => $blog ]) }}">
&raquo; Read more
</a>
</div>
</div>
@endforeach
</div>
</div>
@endsection

View File

@ -39,7 +39,11 @@
@elseif ($notification ['type'] == 'Follow') @elseif ($notification ['type'] == 'Follow')
<p>Followed you</p> <p>Followed you</p>
@elseif ($notification ['type'] == 'Unfollow') @elseif ($notification ['type'] == 'Unfollow')
<p>Unfollowed you</p> @if ($notification ["object"]->id == auth ()->user ()->id)
<p>You unfollowed you</p>
@else
<p>Unfollowed <b>{{ $notification ['object']->name }}</b></p>
@endif
@elseif ($notification ['type'] == 'Boost') @elseif ($notification ['type'] == 'Boost')
<p>Boosted this <b><a href="{{ route ('posts.show', ['note' => $notification['object']->id]) }}">post</a></b></p> <p>Boosted this <b><a href="{{ route ('posts.show', ['note' => $notification['object']->id]) }}">post</a></b></p>
@elseif ($notification ['type'] == 'Like') @elseif ($notification ['type'] == 'Like')

View File

@ -50,7 +50,7 @@
<div class="mood"> <div class="mood">
@if ($user != null) @if ($user != null)
<p><b>Mood:</b> {{ $user->mood }}</p> <p><b>Mood:</b> {{ $user->mood }}</p>
<p><b>View my: <a href="#">Blog</a> | <a href="#">Bulletins</a></b></p> <p><b>View my: <a href="{{ route ('users.blogs', [ 'user_name' => $user->name ]) }}">Blog</a> | <a href="#">Bulletins</a></b></p>
@endif @endif
</div> </div>
@ -280,10 +280,9 @@
@if ($user != null) @if ($user != null)
<div class="blog-preview"> <div class="blog-preview">
<h4> <h4>
{{ $user->name }}'s Latest Blog Entries [<a href="#">View Blog</a>] {{ $user->name }}'s Latest Blog Entries [<a href="{{ route ('users.blogs', [ 'user_name' => $user->name ]) }}">View Blog</a>]
</h4> </h4>
<p> <p>
<i>There are no Blog Entries yet.</i>
</p> </p>
</div> </div>
@endif @endif

View File

@ -19,15 +19,16 @@ Route::get ("/.well-known/nodeinfo/2.1", [ APNodeInfoController::class, "nodeinf
Route::prefix ("/ap/v1")->group (function () { Route::prefix ("/ap/v1")->group (function () {
// users // users
Route::post ("/user/{user:name}/inbox", [ APInboxController::class, "inbox" ])->name ("ap.inbox"); Route::post ("/user/{name}/inbox", [ APInboxController::class, "inbox" ])->name ("ap.inbox");
Route::post ("/user/{user:name}/outbox", [ APOutboxController::class, "outbox" ])->name ("ap.outbox"); Route::post ("/user/{name}/outbox", [ APOutboxController::class, "outbox" ])->name ("ap.outbox");
Route::get ("/user/{user:name}/followers", [ APActorController::class, "followers" ])->name ("ap.followers"); Route::get ("/user/{name}/followers", [ APActorController::class, "followers" ])->name ("ap.followers");
Route::get ("/user/{user:name}/following", [ APActorController::class, "following" ])->name ("ap.following"); Route::get ("/user/{name}/following", [ APActorController::class, "following" ])->name ("ap.following");
Route::get ("/user/{user:name}/collections/featured", [ APActorController::class, "featured" ])->name ("ap.featured"); Route::get ("/user/{name}/collections/featured", [ APActorController::class, "featured" ])->name ("ap.featured");
Route::get ("/user/{user:name}", [ APActorController::class, "user" ])->name ("ap.user"); Route::get ("/user/{name}", [ APActorController::class, "user" ])->name ("ap.user");
// notes // notes
Route::get ("/note/{note:private_id}", [ APGeneralController::class, "note" ])->name ("ap.note"); Route::get ("/note/{note:private_id}", [ APGeneralController::class, "note" ])->name ("ap.note");
// instance
Route::post ("/inbox", [ APInstanceInboxController::class, "inbox" ])->name ("ap.inbox"); Route::post ("/inbox", [ APInstanceInboxController::class, "inbox" ])->name ("ap.inbox");
}); });

View File

@ -7,6 +7,7 @@ use App\Http\Controllers\PostController;
use App\Http\Controllers\UserController; use App\Http\Controllers\UserController;
use App\Http\Controllers\ProfileController; use App\Http\Controllers\ProfileController;
use App\Http\Controllers\UserActionController; use App\Http\Controllers\UserActionController;
use App\Http\Controllers\BlogController;
// auth related // auth related
Route::get ("/auth/login", [ UserController::class, "login" ])->name ("login")->middleware ("guest"); Route::get ("/auth/login", [ UserController::class, "login" ])->name ("login")->middleware ("guest");
@ -28,6 +29,7 @@ Route::middleware ("update_online")->group (function () {
Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth"); Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth");
Route::get ("/user/notifications", [ ProfileController::class, "notifications" ])->name ("users.notifications")->middleware ("auth"); Route::get ("/user/notifications", [ ProfileController::class, "notifications" ])->name ("users.notifications")->middleware ("auth");
Route::get ("/user/{user_name}/friends", [ ProfileController::class, "friends" ])->name ("users.friends"); Route::get ("/user/{user_name}/friends", [ ProfileController::class, "friends" ])->name ("users.friends");
Route::get ("/user/{user_name}/blogs", [ ProfileController::class, "blogs" ])->name ("users.blogs");
Route::get ("/user/{user_name}", [ ProfileController::class, "show" ])->name ("users.show"); Route::get ("/user/{user_name}", [ ProfileController::class, "show" ])->name ("users.show");
// posts routes // posts routes
@ -42,10 +44,17 @@ Route::middleware ("update_online")->group (function () {
// other routes // other routes
Route::get ("/browse", [ HomeController::class, "browse" ])->name ("browse"); Route::get ("/browse", [ HomeController::class, "browse" ])->name ("browse");
Route::get ("/search", [ HomeController::class, "search" ])->name ("search"); Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
Route::get ("/tags/{tag}", [ HomeController::class, "tag" ])->name ("tags"); // TODO: This Route::get ("/tags/{tag}", [ HomeController::class, "tag" ])->name ("tags");
Route::get ("/search", [ HomeController::class, "search" ])->name ("search"); Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth"); Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth");
Route::post ("/requests", [ HomeController::class, "requests_accept" ])->middleware ("auth"); Route::post ("/requests", [ HomeController::class, "requests_accept" ])->middleware ("auth");
// blog routes
Route::get ("/blogs/create", [ BlogController::class, "create" ])->name ("blogs.create")->middleware ("auth");
Route::post ("/blogs/create", [ BlogController::class, "store" ])->middleware ("auth");
Route::get ("/blogs/{blog:slug}/entry/new", [ BlogController::class, "new_entry" ])->name ("blogs.new_entry")->middleware("auth");
Route::get ("/blogs/{blog:slug}", [ BlogController::class, "show" ])->name ("blogs.show");
Route::get ("/blogs", [ BlogController::class, "index" ])->name ("blogs");
}); });
require __DIR__ . "/api.php"; require __DIR__ . "/api.php";