Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3067307
Improve not found error handling
Sabr1n4W Mar 13, 2026
0bdae6d
Fix tests
Sabr1n4W Mar 13, 2026
8294a2b
Start improving not found error handling inside room
Sabr1n4W Mar 13, 2026
5e14f88
Adjust room tests
Sabr1n4W Mar 17, 2026
c97057a
Improve error messages
Sabr1n4W Mar 18, 2026
935d372
Start adjusting backend tests
Sabr1n4W Mar 22, 2026
7a96b9c
Continue improving not found error handling and frontend tests
Sabr1n4W Mar 31, 2026
6ebe7aa
Improve frontend tests
Sabr1n4W Apr 1, 2026
e79ebf2
Improve 404 error handling
Sabr1n4W Apr 7, 2026
5360808
Improve backend tests
Sabr1n4W Apr 7, 2026
e6c6348
Use scope bindings instead of custom handling
Sabr1n4W Apr 8, 2026
032cf4e
Merge remote-tracking branch 'origin/develop' into 86-improve-notfoun…
Sabr1n4W Apr 9, 2026
f31f2c4
Add missing model translations
Sabr1n4W Apr 10, 2026
fa24bdc
Remove custom 404 handling for server connection check
Sabr1n4W Apr 10, 2026
fb9b10a
Cleanup backend tests
Sabr1n4W Apr 10, 2026
8c048e4
Code cleanup
Sabr1n4W Apr 10, 2026
256bf30
Code cleanup
Sabr1n4W Apr 13, 2026
d9c6002
Cleanup model translations
Sabr1n4W Apr 17, 2026
44f9387
Update changelog
Sabr1n4W Apr 17, 2026
3712ea3
Merge remote-tracking branch 'origin/develop' into 86-improve-notfoun…
Sabr1n4W Apr 17, 2026
ddcfd5c
Merge branch 'develop' into 86-improve-notfoundexceptions
samuelwei Apr 23, 2026
506331f
Update locales
Sabr1n4W Apr 28, 2026
9472cd2
Merge remote-tracking branch 'origin/develop' into 86-improve-notfoun…
Sabr1n4W May 6, 2026
8aa4fa4
Remove additional 404 handling inside RoomsView
Sabr1n4W May 6, 2026
76ecb4f
Improve id handling for error message
Sabr1n4W May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Improved error handling when a room requires an access code but none was provided ([#3035])
- Login URL and external login redirect URLs now support authenticated users and honor the redirect query parameter ([#3078], [#3079])
- Improved 404 error messages for better readability and clarity ([#86], [#3036])
- Standardized 404 error handling on admin pages ([#1676], [#3036])
- Improved and standardized room not found error handling in the room view ([#3036])

### Fixed

Expand Down Expand Up @@ -549,6 +552,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#31]: https://github.com/THM-Health/PILOS/issues/31
[#75]: https://github.com/THM-Health/PILOS/issues/75
[#77]: https://github.com/THM-Health/PILOS/issues/77
[#86]: https://github.com/THM-Health/PILOS/issues/86
[#300]: https://github.com/THM-Health/PILOS/issues/300
[#315]: https://github.com/THM-Health/PILOS/issues/315
[#372]: https://github.com/THM-Health/PILOS/issues/372
Expand Down Expand Up @@ -645,6 +649,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#1636]: https://github.com/THM-Health/PILOS/issues/1636
[#1651]: https://github.com/THM-Health/PILOS/issues/1651
[#1675]: https://github.com/THM-Health/PILOS/issues/1675
[#1676]: https://github.com/THM-Health/PILOS/issues/1676
[#1677]: https://github.com/THM-Health/PILOS/issues/1677
[#1678]: https://github.com/THM-Health/PILOS/pull/1678
[#1679]: https://github.com/THM-Health/PILOS/issues/1679
Expand Down Expand Up @@ -780,6 +785,7 @@ You can find the changelog for older versions there [here](https://github.com/TH
[#3028]: https://github.com/THM-Health/PILOS/issues/3028
[#3029]: https://github.com/THM-Health/PILOS/pull/3029
[#3035]: https://github.com/THM-Health/PILOS/pull/3035
[#3036]: https://github.com/THM-Health/PILOS/pull/3036
[#3039]: https://github.com/THM-Health/PILOS/issues/3039
[#3040]: https://github.com/THM-Health/PILOS/pull/3040
[#3078]: https://github.com/THM-Health/PILOS/issues/3078
Expand Down
19 changes: 19 additions & 0 deletions app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Exceptions;

use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Foundation\ViteException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Psr\Log\LogLevel;
use Spatie\LaravelIgnition\Exceptions\ViewException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable;

class Handler extends ExceptionHandler
Expand Down Expand Up @@ -77,5 +80,21 @@ public function register(): void

return null;
});

$this->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->expectsJson()) {
$modelNotFoundException = $e->getPrevious() instanceof ModelNotFoundException ? $e->getPrevious() : null;

if ($modelNotFoundException) {
$json = [
'message' => 'model_not_found',
'model' => Str::snake(class_basename($modelNotFoundException->getModel())),
'ids' => $modelNotFoundException->getIds(),
Comment thread
samuelwei marked this conversation as resolved.
];

return response()->json($json, 404);
}
}
});
}
}
18 changes: 6 additions & 12 deletions app/Http/Controllers/api/v1/RoomMemberController.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,11 @@ public function bulkImport(Room $room, BulkImportRequest $request)
*
* @return Response
*/
public function update(Room $room, User $user, UpdateRoomMemberRequest $request)
public function update(Room $room, User $member, UpdateRoomMemberRequest $request)
{
if (! $room->members->contains($user)) {
abort(410, __('app.errors.not_member_of_room'));
}
$room->members()->updateExistingPivot($user, ['role' => $request->role]);
$room->members()->updateExistingPivot($member, ['role' => $request->role]);

Log::info('Changed role for member {member} to {role} in room {room}', ['room' => $room->getLogLabel(), 'role' => RoomUserRole::from($request->role)->label(), 'member' => $user->getLogLabel()]);
Log::info('Changed role for member {member} to {role} in room {room}', ['room' => $room->getLogLabel(), 'role' => RoomUserRole::from($request->role)->label(), 'member' => $member->getLogLabel()]);

return response()->noContent();
}
Expand All @@ -155,14 +152,11 @@ public function bulkUpdate(Room $room, BulkUpdateRequest $request)
*
* @return Response
*/
public function destroy(Room $room, User $user)
public function destroy(Room $room, User $member)
{
if (! $room->members->contains($user)) {
abort(410, __('app.errors.not_member_of_room'));
}
$room->members()->detach($user);
$room->members()->detach($member);

Log::info('Removed member {member} from room {room}', ['room' => $room->getLogLabel(), 'member' => $user->getLogLabel()]);
Log::info('Removed member {member} from room {room}', ['room' => $room->getLogLabel(), 'member' => $member->getLogLabel()]);

return response()->noContent();
}
Expand Down
28 changes: 10 additions & 18 deletions app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,16 @@ public function store(Room $room, RoomPersonalizedLinkRequest $request)
*
* @return RoomPersonalizedLinkResource
*/
public function update(Room $room, RoomPersonalizedLink $link, RoomPersonalizedLinkRequest $request)
public function update(Room $room, RoomPersonalizedLink $personalizedLink, RoomPersonalizedLinkRequest $request)
{
if (! $link->room->is($room)) {
abort(404, __('app.errors.personalized_link_not_found'));
}

$link->firstname = $request->firstname;
$link->lastname = $request->lastname;
$link->role = $request->role;
$link->save();
$personalizedLink->firstname = $request->firstname;
$personalizedLink->lastname = $request->lastname;
$personalizedLink->role = $request->role;
$personalizedLink->save();

Log::info('Updated personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $link->role->label(), 'name' => $link->fullname]);
Log::info('Updated personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $personalizedLink->role->label(), 'name' => $personalizedLink->fullname]);

return new RoomPersonalizedLinkResource($link);
return new RoomPersonalizedLinkResource($personalizedLink);
}

/**
Expand All @@ -126,15 +122,11 @@ public function update(Room $room, RoomPersonalizedLink $link, RoomPersonalizedL
*
* @throws \Exception
*/
public function destroy(Room $room, RoomPersonalizedLink $link)
public function destroy(Room $room, RoomPersonalizedLink $personalizedLink)
{
if (! $link->room->is($room)) {
abort(404, __('app.errors.personalized_link_not_found'));
}

$link->delete();
$personalizedLink->delete();

Log::info('Removed personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $link->role->label(), 'name' => $link->fullname]);
Log::info('Removed personalized room link for guest {name} with the role {role} for room {room}', ['room' => $room->getLogLabel(), 'role' => $personalizedLink->role->label(), 'name' => $personalizedLink->fullname]);

return response()->noContent();
}
Expand Down
17 changes: 16 additions & 1 deletion lang/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
'membership_disabled' => 'Membership failed! Membership for this room is currently not available.',
'no_room_access' => 'You does not have the necessary permissions, to edit this room.',
'no_server_available' => 'Currently there are no servers available.',
'not_member_of_room' => 'The person is not a member of this room (anymore).',
'not_member_of_room' => 'The user is not a member of this room.',
'not_running' => 'Joining the room has failed as it is currently closed.',
'personalized_link_not_found' => 'The personalized room link could not be found.',
Comment thread
samuelwei marked this conversation as resolved.
'record_agreement_missing' => 'Consent to the recording is required.',
Expand All @@ -66,6 +66,10 @@
'flash' => [
'client_error' => 'An unknown error occurred in the application!',
'guests_only' => 'The request can only be made by guests!',
'model_not_found' => [
'title' => 'The :model was not found!',
'details' => 'ID: :ids',
],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
'server_error' => [
'empty_message' => 'An error occurred on the server during request!',
'error_code' => 'Error code: :statusCode',
Expand Down Expand Up @@ -97,10 +101,21 @@
'fr' => 'French',
],
'model' => [
'meeting' => 'Meeting',
'recording' => 'Recording',
'recording_format' => 'Recording format',
'role' => 'Role',
'roles' => 'role',
'room' => 'Room',
'room_file' => 'Room file',
'room_personalized_link' => 'Room personalized link',
'room_type' => 'Room type',
'room_types' => 'room type',
'server' => 'Server',
'server_pool' => 'Server pool',
'server_pools' => 'server pool',
'servers' => 'server',
'user' => 'User',
'users' => 'user',
],
'model_name' => 'Name',
Expand Down
2 changes: 2 additions & 0 deletions resources/js/components/RoomCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<RoomFavoriteButton
:room="props.room"
class="room-card-button h-8 w-8 p-0 text-sm"
:redirect-on-room-model-not-found="false"
@favorites-changed="$emit('favoritesChanged')"
/>
</div>
Expand Down Expand Up @@ -72,6 +73,7 @@
<div class="room-card-buttons shrink-0">
<RoomFavoriteButton
:room="props.room"
:redirect-on-room-model-not-found="false"
@favorites-changed="$emit('favoritesChanged')"
/>
</div>
Expand Down
5 changes: 5 additions & 0 deletions resources/js/components/RoomFavoriteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
redirectOnRoomModelNotFound: {
type: Boolean,
default: true,
},
});

const isLoading = ref(false);
Expand All @@ -57,6 +61,7 @@ function toggleFavorite() {
.catch((error) => {
api.error(error, {
redirectOnUnauthenticated: props.redirectOnUnauthenticated,
redirectOnRoomModelNotFound: props.redirectOnRoomModelNotFound,
});
})
.finally(() => {
Expand Down
6 changes: 5 additions & 1 deletion resources/js/components/RoomTabFilesDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_FILE } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -101,7 +102,10 @@ function deleteFile() {
// deleting failed
if (error.response) {
// file not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_FILE
) {
toast.error(t("rooms.flash.file_gone"));
emit("notFound");
modalVisible.value = false;
Expand Down
6 changes: 5 additions & 1 deletion resources/js/components/RoomTabFilesEditButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import { ref, watch } from "vue";
import { useFormErrors } from "../composables/useFormErrors.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_FILE } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -195,7 +196,10 @@ function save() {
// editing failed
if (error.response) {
// file not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_FILE
) {
toast.error(t("rooms.flash.file_gone"));
emit("notFound");
modalVisible.value = false;
Expand Down
12 changes: 11 additions & 1 deletion resources/js/components/RoomTabMembersDeleteButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
import env from "../env";
import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { USER } from "../constants/modelNames.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -83,6 +86,8 @@ const props = defineProps({
const emit = defineEmits(["deleted", "gone"]);

const api = useApi();
const toast = useToast();
const { t } = useI18n();

const modalVisible = ref(false);
const isLoadingAction = ref(false);
Expand All @@ -106,9 +111,14 @@ function deleteMember() {
// editing failed
if (error.response) {
// user not found
if (error.response.status === env.HTTP_GONE) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === USER
) {
toast.error(t("app.errors.not_member_of_room"));
emit("gone");
modalVisible.value = false;
return;
}
}
api.error(error, { redirectOnUnauthenticated: false });
Expand Down
12 changes: 11 additions & 1 deletion resources/js/components/RoomTabMembersEditButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ import env from "../env";
import { useApi } from "../composables/useApi.js";
import { useFormErrors } from "../composables/useFormErrors.js";
import { ref } from "vue";
import { USER } from "../constants/modelNames.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -131,6 +134,8 @@ const emit = defineEmits(["edited", "gone"]);

const api = useApi();
const formErrors = useFormErrors();
const toast = useToast();
const { t } = useI18n();

const modalVisible = ref(false);
const newRole = ref(null);
Expand Down Expand Up @@ -168,9 +173,14 @@ function save() {
// editing failed
if (error.response) {
// user not found
if (error.response.status === env.HTTP_GONE) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === USER
) {
toast.error(t("app.errors.not_member_of_room"));
emit("gone");
modalVisible.value = false;
return;
}
// failed due to form validation errors
if (error.response.status === env.HTTP_UNPROCESSABLE_ENTITY) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { useApi } from "../composables/useApi.js";
import { ref } from "vue";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_PERSONALIZED_LINK } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -120,7 +121,10 @@ function deleteLink() {
// deleting failed
if (error.response) {
// personalized link not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_PERSONALIZED_LINK
) {
toast.error(t("rooms.flash.personalized_link_gone"));
modalVisible.value = false;
emit("notFound");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import { ref } from "vue";
import env from "../env.js";
import { useToast } from "../composables/useToast.js";
import { useI18n } from "vue-i18n";
import { ROOM_PERSONALIZED_LINK } from "../constants/modelNames.js";

const props = defineProps({
roomId: {
Expand Down Expand Up @@ -189,7 +190,10 @@ function save() {
// editing failed
if (error.response) {
// token not found
if (error.response.status === env.HTTP_NOT_FOUND) {
if (
error.response.status === env.HTTP_NOT_FOUND &&
error.response.data?.model === ROOM_PERSONALIZED_LINK
) {
toast.error(t("rooms.flash.personalized_link_gone"));
modalVisible.value = false;
emit("notFound");
Expand Down
Loading
Loading