mirror of
https://github.com/immich-app/immich.git
synced 2025-12-23 17:25:11 +03:00
feat: new user route
This commit is contained in:
@@ -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 {
|
export enum AssetAction {
|
||||||
ARCHIVE = 'archive',
|
ARCHIVE = 'archive',
|
||||||
UNARCHIVE = 'unarchive',
|
UNARCHIVE = 'unarchive',
|
||||||
@@ -20,6 +22,7 @@ export enum AssetAction {
|
|||||||
|
|
||||||
export enum AppRoute {
|
export enum AppRoute {
|
||||||
ADMIN_USERS = '/admin/users',
|
ADMIN_USERS = '/admin/users',
|
||||||
|
ADMIN_USERS_NEW = '/admin/users/new',
|
||||||
ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management',
|
ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management',
|
||||||
ADMIN_SETTINGS = '/admin/system-settings',
|
ADMIN_SETTINGS = '/admin/system-settings',
|
||||||
ADMIN_STATS = '/admin/server-status',
|
ADMIN_STATS = '/admin/server-status',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||||
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
import PasswordResetSuccessModal from '$lib/modals/PasswordResetSuccessModal.svelte';
|
||||||
import UserCreateModal from '$lib/modals/UserCreateModal.svelte';
|
|
||||||
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte';
|
||||||
import UserEditModal from '$lib/modals/UserEditModal.svelte';
|
import UserEditModal from '$lib/modals/UserEditModal.svelte';
|
||||||
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte';
|
||||||
@@ -39,7 +39,7 @@ export const getUserAdminsActions = ($t: MessageFormatter) => {
|
|||||||
title: $t('create_user'),
|
title: $t('create_user'),
|
||||||
type: $t('command'),
|
type: $t('command'),
|
||||||
icon: mdiPlusBoxOutline,
|
icon: mdiPlusBoxOutline,
|
||||||
onAction: () => modalManager.show(UserCreateModal, {}),
|
onAction: () => goto(AppRoute.ADMIN_USERS_NEW),
|
||||||
shortcuts: { shift: true, key: 'n' },
|
shortcuts: { shift: true, key: 'n' },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ export const handleCreateUserAdmin = async (dto: UserAdminCreateDto) => {
|
|||||||
const response = await createUserAdmin({ userAdminCreateDto: dto });
|
const response = await createUserAdmin({ userAdminCreateDto: dto });
|
||||||
eventManager.emit('UserAdminCreate', response);
|
eventManager.emit('UserAdminCreate', response);
|
||||||
toastManager.success();
|
toastManager.success();
|
||||||
return true;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_create_user'));
|
handleError(error, $t('errors.unable_to_create_user'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import { UUID_REGEX } from '$lib/constants';
|
||||||
import type { ParamMatcher } from '@sveltejs/kit';
|
import type { ParamMatcher } from '@sveltejs/kit';
|
||||||
|
|
||||||
/* Returns true if the given param matches UUID format */
|
/* Returns true if the given param matches UUID format */
|
||||||
export const match: ParamMatcher = (param: string) => {
|
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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,14 +7,16 @@
|
|||||||
import { searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
|
import { searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
|
||||||
import { Button, CommandPaletteContext, Icon } from '@immich/ui';
|
import { Button, CommandPaletteContext, Icon } from '@immich/ui';
|
||||||
import { mdiInfinity } from '@mdi/js';
|
import { mdiInfinity } from '@mdi/js';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { LayoutData } from './$types';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: PageData;
|
children?: Snippet;
|
||||||
|
data: LayoutData;
|
||||||
};
|
};
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { children, data }: Props = $props();
|
||||||
|
|
||||||
let allUsers: UserAdminResponseDto[] = $state(data.allUsers);
|
let allUsers: UserAdminResponseDto[] = $state(data.allUsers);
|
||||||
|
|
||||||
@@ -91,3 +93,5 @@
|
|||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</AdminPageLayout>
|
</AdminPageLayout>
|
||||||
|
|
||||||
|
{@render children?.()}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import { searchUsersAdmin } from '@immich/sdk';
|
import { searchUsersAdmin } from '@immich/sdk';
|
||||||
import type { PageLoad } from './$types';
|
import type { LayoutLoad } from './$types';
|
||||||
|
|
||||||
export const load = (async ({ url }) => {
|
export const load = (async ({ url }) => {
|
||||||
await authenticate(url, { admin: true });
|
await authenticate(url, { admin: true });
|
||||||
@@ -15,4 +15,4 @@ export const load = (async ({ url }) => {
|
|||||||
title: $t('admin.user_management'),
|
title: $t('admin.user_management'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}) satisfies PageLoad;
|
}) satisfies LayoutLoad;
|
||||||
0
web/src/routes/admin/users/(list)/+page.svelte
Normal file
0
web/src/routes/admin/users/(list)/+page.svelte
Normal file
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { AppRoute } from '$lib/constants';
|
||||||
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
|
||||||
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
import { handleCreateUserAdmin } from '$lib/services/user-admin.service';
|
||||||
import { userInteraction } from '$lib/stores/user.svelte';
|
import { userInteraction } from '$lib/stores/user.svelte';
|
||||||
@@ -18,12 +20,6 @@
|
|||||||
} from '@immich/ui';
|
} from '@immich/ui';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
type Props = {
|
|
||||||
onClose: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
let { onClose }: Props = $props();
|
|
||||||
|
|
||||||
let success = $state(false);
|
let success = $state(false);
|
||||||
|
|
||||||
let email = $state('');
|
let email = $state('');
|
||||||
@@ -46,6 +42,10 @@
|
|||||||
const passwordMismatchMessage = $derived(passwordMismatch ? $t('password_does_not_match') : '');
|
const passwordMismatchMessage = $derived(passwordMismatch ? $t('password_does_not_match') : '');
|
||||||
const valid = $derived(!passwordMismatch && !isCreatingUser);
|
const valid = $derived(!passwordMismatch && !isCreatingUser);
|
||||||
|
|
||||||
|
const onClose = async () => {
|
||||||
|
await goto(AppRoute.ADMIN_USERS);
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = async (event: Event) => {
|
const onSubmit = async (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
isCreatingUser = true;
|
isCreatingUser = true;
|
||||||
|
|
||||||
const success = await handleCreateUserAdmin({
|
const response = await handleCreateUserAdmin({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
shouldChangePassword,
|
shouldChangePassword,
|
||||||
@@ -65,8 +65,8 @@
|
|||||||
isAdmin,
|
isAdmin,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (success) {
|
if (response) {
|
||||||
onClose();
|
await goto(`${AppRoute.ADMIN_USERS}/${response.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
isCreatingUser = false;
|
isCreatingUser = false;
|
||||||
14
web/src/routes/admin/users/(list)/new/+page.ts
Normal file
14
web/src/routes/admin/users/(list)/new/+page.ts
Normal 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;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute, UUID_REGEX } from '$lib/constants';
|
||||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
import { authenticate, requestServerInfo } from '$lib/utils/auth';
|
||||||
import { getFormatter } from '$lib/utils/i18n';
|
import { getFormatter } from '$lib/utils/i18n';
|
||||||
import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk';
|
import { getUserPreferencesAdmin, getUserSessionsAdmin, getUserStatisticsAdmin, searchUsersAdmin } from '@immich/sdk';
|
||||||
@@ -8,6 +8,11 @@ import type { PageLoad } from './$types';
|
|||||||
export const load = (async ({ params, url }) => {
|
export const load = (async ({ params, url }) => {
|
||||||
await authenticate(url, { admin: true });
|
await authenticate(url, { admin: true });
|
||||||
await requestServerInfo();
|
await requestServerInfo();
|
||||||
|
|
||||||
|
if (!UUID_REGEX.test(params.id)) {
|
||||||
|
redirect(302, AppRoute.ADMIN_USERS);
|
||||||
|
}
|
||||||
|
|
||||||
const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []);
|
const [user] = await searchUsersAdmin({ id: params.id, withDeleted: true }).catch(() => []);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
redirect(302, AppRoute.ADMIN_USERS);
|
redirect(302, AppRoute.ADMIN_USERS);
|
||||||
|
|||||||
Reference in New Issue
Block a user