feat: new user route

This commit is contained in:
Jason Rasmussen
2025-12-19 13:44:22 -05:00
parent 5b80323326
commit 48d5e4118b
9 changed files with 46 additions and 19 deletions

View File

@@ -1,3 +1,5 @@
export const UUID_REGEX = /^[\dA-Fa-f]{8}(?:\b-[\dA-Fa-f]{4}){3}\b-[\dA-Fa-f]{12}$/;
export enum AssetAction {
ARCHIVE = 'archive',
UNARCHIVE = 'unarchive',
@@ -20,6 +22,7 @@ export enum AssetAction {
export enum AppRoute {
ADMIN_USERS = '/admin/users',
ADMIN_USERS_NEW = '/admin/users/new',
ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management',
ADMIN_SETTINGS = '/admin/system-settings',
ADMIN_STATS = '/admin/server-status',

View File

@@ -1,8 +1,8 @@
import { goto } from '$app/navigation';
import { AppRoute } from '$lib/constants';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
import UserEditModal from '$lib/modals/UserEditModal.svelte';
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
@@ -39,7 +39,7 @@ export const getUserAdminsActions = ($t: MessageFormatter) => {
title: $t('create_user'),
type: $t('command'),
icon: mdiPlusBoxOutline,
onAction: () => modalManager.show(UserCreateModal, {}),
onAction: () => goto(AppRoute.ADMIN_USERS_NEW),
shortcuts: { shift: true, key: 'n' },
};
@@ -103,7 +103,7 @@ export const handleCreateUserAdmin = async (dto: UserAdminCreateDto) => {
const response = await createUserAdmin({ userAdminCreateDto: dto });
eventManager.emit('UserAdminCreate', response);
toastManager.success();
return true;
return response;
} catch (error) {
handleError(error, $t('errors.unable_to_create_user'));
}

View File

@@ -1,6 +1,7 @@
import { UUID_REGEX } from '$lib/constants';
import type { ParamMatcher } from '@sveltejs/kit';
/* Returns true if the given param matches UUID format */
export const match: ParamMatcher = (param: string) => {
return /^[\dA-Fa-f]{8}(?:\b-[\dA-Fa-f]{4}){3}\b-[\dA-Fa-f]{12}$/.test(param);
return UUID_REGEX.test(param);
};

View File

@@ -7,14 +7,16 @@
import { searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
import { Button, CommandPaletteContext, Icon } from '@immich/ui';
import { mdiInfinity } from '@mdi/js';
import type { Snippet } from 'svelte';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
import type { LayoutData } from './$types';
type Props = {
data: PageData;
children?: Snippet;
data: LayoutData;
};
let { data }: Props = $props();
let { children, data }: Props = $props();
let allUsers: UserAdminResponseDto[] = $state(data.allUsers);
@@ -91,3 +93,5 @@
</section>
</section>
</AdminPageLayout>
{@render children?.()}

View File

@@ -1,7 +1,7 @@
import { authenticate, requestServerInfo } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import { searchUsersAdmin } from '@immich/sdk';
import type { PageLoad } from './$types';
import type { LayoutLoad } from './$types';
export const load = (async ({ url }) => {
await authenticate(url, { admin: true });
@@ -15,4 +15,4 @@ export const load = (async ({ url }) => {
title: $t('admin.user_management'),
},
};
}) satisfies PageLoad;
}) satisfies LayoutLoad;

View File

@@ -1,4 +1,6 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { AppRoute } from '$lib/constants';
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
import { userInteraction } from '$lib/stores/user.svelte';
@@ -18,12 +20,6 @@
} from '@immich/ui';
import { t } from 'svelte-i18n';
type Props = {
onClose: () => void;
};
let { onClose }: Props = $props();
let success = $state(false);
let email = $state('');
@@ -46,6 +42,10 @@
const passwordMismatchMessage = $derived(passwordMismatch ? $t('password_does_not_match') : '');
const valid = $derived(!passwordMismatch && !isCreatingUser);
const onClose = async () => {
await goto(AppRoute.ADMIN_USERS);
};
const onSubmit = async (event: Event) => {
event.preventDefault();
@@ -55,7 +55,7 @@
isCreatingUser = true;
const success = await handleCreateUserAdmin({
const response = await handleCreateUserAdmin({
email,
password,
shouldChangePassword,
@@ -65,8 +65,8 @@
isAdmin,
});
if (success) {
onClose();
if (response) {
await goto(`${AppRoute.ADMIN_USERS}/${response.id}`);
}
isCreatingUser = false;

View File

@@ -0,0 +1,14 @@
import { authenticate } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import type { PageLoad } from './$types';
export const load = (async ({ url }) => {
await authenticate(url, { admin: true });
const $t = await getFormatter();
return {
meta: {
title: $t('admin.user_management'),
},
};
}) satisfies PageLoad;

View File

@@ -1,4 +1,4 @@
import { AppRoute } from '$lib/constants';
import { AppRoute, UUID_REGEX } from '$lib/constants';
import { authenticate, requestServerInfo } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk';
@@ -8,6 +8,11 @@ import type { PageLoad } from './$types';
export const load = (async ({ params, url }) => {
await authenticate(url, { admin: true });
await requestServerInfo();
if (!UUID_REGEX.test(params.id)) {
redirect(302, AppRoute.ADMIN_USERS);
}
const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []);
if (!user) {
redirect(302, AppRoute.ADMIN_USERS);