mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	Migrate SvelteKit to the latest version 431 (#526)
				
					
				
			This commit is contained in:
		| @@ -6,6 +6,7 @@ services: | |||||||
|     build: |     build: | ||||||
|       context: ../server |       context: ../server | ||||||
|       dockerfile: Dockerfile |       dockerfile: Dockerfile | ||||||
|  |       target: builder | ||||||
|     command: npm run start:dev immich |     command: npm run start:dev immich | ||||||
|     volumes: |     volumes: | ||||||
|       - ../server:/usr/src/app |       - ../server:/usr/src/app | ||||||
| @@ -24,6 +25,7 @@ services: | |||||||
|     build: |     build: | ||||||
|       context: ../machine-learning |       context: ../machine-learning | ||||||
|       dockerfile: Dockerfile |       dockerfile: Dockerfile | ||||||
|  |       target: builder | ||||||
|     command: npm run start:dev |     command: npm run start:dev | ||||||
|     volumes: |     volumes: | ||||||
|       - ../machine-learning:/usr/src/app |       - ../machine-learning:/usr/src/app | ||||||
| @@ -41,6 +43,7 @@ services: | |||||||
|     build: |     build: | ||||||
|       context: ../server |       context: ../server | ||||||
|       dockerfile: Dockerfile |       dockerfile: Dockerfile | ||||||
|  |       target: builder | ||||||
|     command: npm run start:dev microservices |     command: npm run start:dev microservices | ||||||
|     volumes: |     volumes: | ||||||
|       - ../server:/usr/src/app |       - ../server:/usr/src/app | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ WORKDIR /usr/src/app | |||||||
|  |  | ||||||
| COPY package.json package-lock.json ./ | COPY package.json package-lock.json ./ | ||||||
|  |  | ||||||
| RUN apk add --update-cache build-base python3 libheif vips-dev | RUN apk add --update-cache build-base python3 libheif vips-dev ffmpeg | ||||||
| RUN npm ci | RUN npm ci | ||||||
|  |  | ||||||
| COPY . . | COPY . . | ||||||
| @@ -22,7 +22,7 @@ COPY package.json package-lock.json ./ | |||||||
| COPY start-server.sh start-microservices.sh ./ | COPY start-server.sh start-microservices.sh ./ | ||||||
|  |  | ||||||
| RUN mkdir -p /usr/src/app/dist \ | RUN mkdir -p /usr/src/app/dist \ | ||||||
|     && apk add --no-cache libheif vips ffmpeg |   && apk add --no-cache libheif vips ffmpeg | ||||||
|  |  | ||||||
| COPY --from=builder /usr/src/app/node_modules ./node_modules | COPY --from=builder /usr/src/app/node_modules ./node_modules | ||||||
| COPY --from=builder /usr/src/app/dist ./dist | COPY --from=builder /usr/src/app/dist ./dist | ||||||
|   | |||||||
							
								
								
									
										1550
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1550
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,7 +6,6 @@ | |||||||
| 		"build": "vite build", | 		"build": "vite build", | ||||||
| 		"package": "svelte-kit package", | 		"package": "svelte-kit package", | ||||||
| 		"preview": "vite preview", | 		"preview": "vite preview", | ||||||
| 		"prepare": "svelte-kit sync", |  | ||||||
| 		"check": "svelte-check --tsconfig ./tsconfig.json", | 		"check": "svelte-check --tsconfig ./tsconfig.json", | ||||||
| 		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", | 		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", | ||||||
| 		"lint": "prettier --check --plugin-search-dir=. . && eslint .", | 		"lint": "prettier --check --plugin-search-dir=. . && eslint .", | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								web/src/app.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								web/src/app.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -8,10 +8,4 @@ declare namespace App { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// interface Platform {} | 	// interface Platform {} | ||||||
|  |  | ||||||
| 	interface Session { |  | ||||||
| 		user?: import('@api').UserResponseDto; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// interface Stuff {} |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,31 +1,5 @@ | |||||||
| import type { ExternalFetch, GetSession, Handle } from '@sveltejs/kit'; | import type { Handle } from '@sveltejs/kit'; | ||||||
| import * as cookie from 'cookie'; |  | ||||||
| import { serverApi } from '@api'; |  | ||||||
|  |  | ||||||
| export const handle: Handle = async ({ event, resolve }) => { | export const handle: Handle = async ({ event, resolve }) => { | ||||||
| 	const cookies = cookie.parse(event.request.headers.get('cookie') || ''); | 	return await resolve(event); | ||||||
|  |  | ||||||
| 	if (!cookies['immich_is_authenticated']) { |  | ||||||
| 		return await resolve(event); |  | ||||||
| 	} |  | ||||||
| 	const accessToken = cookies['immich_access_token']; |  | ||||||
|  |  | ||||||
| 	try { |  | ||||||
| 		serverApi.setAccessToken(accessToken); |  | ||||||
| 		const { data } = await serverApi.userApi.getMyUserInfo(); |  | ||||||
| 		event.locals.user = data; |  | ||||||
|  |  | ||||||
| 		return await resolve(event); |  | ||||||
| 	} catch (error) { |  | ||||||
| 		event.locals.user = undefined; |  | ||||||
| 		return await resolve(event); |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| export const getSession: GetSession = async ({ locals }) => { |  | ||||||
| 	if (!locals.user) return {}; |  | ||||||
|  |  | ||||||
| 	return { |  | ||||||
| 		user: locals.user |  | ||||||
| 	}; |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| 	import { AlbumResponseDto, api, ThumbnailFormat } from '@api'; | 	import { AlbumResponseDto, api, ThumbnailFormat } from '@api'; | ||||||
| 	import { createEventDispatcher, onMount } from 'svelte'; | 	import { createEventDispatcher, onMount } from 'svelte'; | ||||||
| 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; | 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte'; | ||||||
| 	import { fade } from 'svelte/transition'; | 	import { fly } from 'svelte/transition'; | ||||||
| 	import CircleIconButton from '../shared-components/circle-icon-button.svelte'; | 	import CircleIconButton from '../shared-components/circle-icon-button.svelte'; | ||||||
|  |  | ||||||
| 	export let album: AlbumResponseDto; | 	export let album: AlbumResponseDto; | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ | |||||||
| 	import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte'; | 	import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte'; | ||||||
| 	import type { UploadAsset } from '$lib/models/upload-asset'; | 	import type { UploadAsset } from '$lib/models/upload-asset'; | ||||||
| 	import { getAssetsInfo } from '$lib/stores/assets'; | 	import { getAssetsInfo } from '$lib/stores/assets'; | ||||||
| 	import { session } from '$app/stores'; |  | ||||||
| 	let showDetail = true; | 	let showDetail = true; | ||||||
|  |  | ||||||
| 	let uploadLength = 0; | 	let uploadLength = 0; | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								web/src/routes/+error.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web/src/routes/+error.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | <script> | ||||||
|  | 	import { page } from '$app/stores'; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <div class="h-screen w-screen  flex place-items-center place-content-center flex-col"> | ||||||
|  | 	<div class="min-w-[500px]  bg-gray-300 rounded-2xl my-4 p-4"> | ||||||
|  | 		<code class="text-xs text-red-500">Error code {$page.status}</code> | ||||||
|  | 		<br /> | ||||||
|  | 		<code class="text-sm"> | ||||||
|  | 			{$page.error.message} | ||||||
|  | 		</code> | ||||||
|  | 		<br /> | ||||||
|  | 		<div class="mt-5"> | ||||||
|  | 			<p class="text-sm font-medium">Verbose</p> | ||||||
|  | 			<pre class="text-xs">{Object.values($page.error)}</pre> | ||||||
|  | 		</div> | ||||||
|  |  | ||||||
|  | 		<a | ||||||
|  | 			href="https://github.com/immich-app/immich/issues/new/choose" | ||||||
|  | 			target="_blank" | ||||||
|  | 			rel="noopener noreferrer" | ||||||
|  | 		> | ||||||
|  | 			<button | ||||||
|  | 				class="px-5 py-2 rounded-lg text-sm mt-6 bg-immich-primary text-white hover:bg-immich-primary/75" | ||||||
|  | 				>Get help</button | ||||||
|  | 			> | ||||||
|  | 		</a> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
							
								
								
									
										23
									
								
								web/src/routes/+layout.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/routes/+layout.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | import { browser } from '$app/env'; | ||||||
|  | import { api, serverApi } from '@api'; | ||||||
|  | import * as cookieParser from 'cookie'; | ||||||
|  |  | ||||||
|  | import type { LayoutServerLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: LayoutServerLoad = async ({ request }) => { | ||||||
|  | 	const cookies = cookieParser.parse(request.headers.get('cookie') || ''); | ||||||
|  | 	const accessToken = cookies['immich_access_token']; | ||||||
|  |  | ||||||
|  | 	if (!accessToken) { | ||||||
|  | 		return { | ||||||
|  | 			user: undefined | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	serverApi.setAccessToken(accessToken); | ||||||
|  | 	const { data: userInfo } = await serverApi.userApi.getMyUserInfo(); | ||||||
|  |  | ||||||
|  | 	return { | ||||||
|  | 		user: userInfo | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
| @@ -1,14 +1,3 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
| 	import { checkAppVersion } from '$lib/utils/check-app-version'; |  | ||||||
| 
 |  | ||||||
| 	export const load: Load = async ({ url }) => { |  | ||||||
| 		return { |  | ||||||
| 			props: { url } |  | ||||||
| 		}; |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import '../app.css'; | 	import '../app.css'; | ||||||
| 
 | 
 | ||||||
| @@ -17,8 +6,9 @@ | |||||||
| 	import AnnouncementBox from '$lib/components/shared-components/announcement-box.svelte'; | 	import AnnouncementBox from '$lib/components/shared-components/announcement-box.svelte'; | ||||||
| 	import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; | 	import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; | ||||||
| 	import { onMount } from 'svelte'; | 	import { onMount } from 'svelte'; | ||||||
|  | 	import { checkAppVersion } from '$lib/utils/check-app-version'; | ||||||
|  | 	import { page } from '$app/stores'; | ||||||
| 
 | 
 | ||||||
| 	export let url: string; |  | ||||||
| 	let shouldShowAnnouncement: boolean; | 	let shouldShowAnnouncement: boolean; | ||||||
| 	let localVersion: string; | 	let localVersion: string; | ||||||
| 	let remoteVersion: string; | 	let remoteVersion: string; | ||||||
| @@ -33,7 +23,7 @@ | |||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <main> | <main> | ||||||
| 	{#key url} | 	{#key $page.url} | ||||||
| 		<div in:fade={{ duration: 100 }}> | 		<div in:fade={{ duration: 100 }}> | ||||||
| 			<slot /> | 			<slot /> | ||||||
| 			<DownloadPanel /> | 			<DownloadPanel /> | ||||||
							
								
								
									
										29
									
								
								web/src/routes/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web/src/routes/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import { goto } from '$app/navigation'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  |  | ||||||
|  | 	export let data: PageData; | ||||||
|  |  | ||||||
|  | 	async function onGettingStartedClicked() { | ||||||
|  | 		data.isAdminUserExist ? await goto('/auth/login') : await goto('/auth/register'); | ||||||
|  | 	} | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <svelte:head> | ||||||
|  | 	<title>Welcome 🎉 - Immich</title> | ||||||
|  | 	<meta name="description" content="Immich Web Interface" /> | ||||||
|  | </svelte:head> | ||||||
|  |  | ||||||
|  | <section class="h-screen w-screen flex place-items-center place-content-center"> | ||||||
|  | 	<div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]"> | ||||||
|  | 		<div class="flex place-items-center place-content-center "> | ||||||
|  | 			<img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo" /> | ||||||
|  | 		</div> | ||||||
|  | 		<h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1> | ||||||
|  | 		<button | ||||||
|  | 			class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]" | ||||||
|  | 			on:click={onGettingStartedClicked} | ||||||
|  | 			>Getting Started | ||||||
|  | 		</button> | ||||||
|  | 	</div> | ||||||
|  | </section> | ||||||
							
								
								
									
										21
									
								
								web/src/routes/+page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/routes/+page.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | export const prerender = false; | ||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | import { api } from '@api'; | ||||||
|  | import { browser } from '$app/env'; | ||||||
|  | import type { PageLoad } from './$types'; | ||||||
|  | import { goto } from '$app/navigation'; | ||||||
|  |  | ||||||
|  | export const load: PageLoad = async ({ parent }) => { | ||||||
|  | 	const { user } = await parent(); | ||||||
|  | 	if (user) { | ||||||
|  | 		throw redirect(302, '/photos'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (browser) { | ||||||
|  | 		const { data } = await api.userApi.getUserCount(); | ||||||
|  |  | ||||||
|  | 		return { | ||||||
|  | 			isAdminUserExist: data.userCount != 0 | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										19
									
								
								web/src/routes/admin/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/routes/admin/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | import { serverApi, UserResponseDto } from '@api'; | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent }) => { | ||||||
|  | 	const { user } = await parent(); | ||||||
|  |  | ||||||
|  | 	if (!user) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} else if (!user.isAdmin) { | ||||||
|  | 		throw redirect(302, '/photos'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const { data: allUsers } = await serverApi.userApi.getAllUsers(false); | ||||||
|  | 	return { | ||||||
|  | 		user: user, | ||||||
|  | 		allUsers: allUsers | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
| @@ -1,47 +1,3 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
| 	import { api, UserResponseDto } from '@api'; |  | ||||||
| 	import { browser } from '$app/env'; |  | ||||||
| 
 |  | ||||||
| 	export const load: Load = async ({ fetch, session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		try { |  | ||||||
| 			const user: UserResponseDto = await fetch('/data/user/get-my-user-info').then((r) => |  | ||||||
| 				r.json() |  | ||||||
| 			); |  | ||||||
| 			const allUsers: UserResponseDto[] = await fetch('/data/user/get-all-users?isAll=false').then( |  | ||||||
| 				(r) => r.json() |  | ||||||
| 			); |  | ||||||
| 
 |  | ||||||
| 			if (!user.isAdmin) { |  | ||||||
| 				return { |  | ||||||
| 					status: 302, |  | ||||||
| 					redirect: '/photos' |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return { |  | ||||||
| 				status: 200, |  | ||||||
| 				props: { |  | ||||||
| 					user: user, |  | ||||||
| 					allUsers: allUsers |  | ||||||
| 				} |  | ||||||
| 			}; |  | ||||||
| 		} catch (e) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import { onMount } from 'svelte'; | 	import { onMount } from 'svelte'; | ||||||
| 
 | 
 | ||||||
| @@ -54,11 +10,12 @@ | |||||||
| 	import CreateUserForm from '$lib/components/forms/create-user-form.svelte'; | 	import CreateUserForm from '$lib/components/forms/create-user-form.svelte'; | ||||||
| 	import EditUserForm from '$lib/components/forms/edit-user-form.svelte'; | 	import EditUserForm from '$lib/components/forms/edit-user-form.svelte'; | ||||||
| 	import StatusBox from '$lib/components/shared-components/status-box.svelte'; | 	import StatusBox from '$lib/components/shared-components/status-box.svelte'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  | 	import { api, UserResponseDto } from '@api'; | ||||||
| 
 | 
 | ||||||
| 	let selectedAction: AdminSideBarSelection = AdminSideBarSelection.USER_MANAGEMENT; | 	let selectedAction: AdminSideBarSelection = AdminSideBarSelection.USER_MANAGEMENT; | ||||||
| 
 | 
 | ||||||
| 	export let user: UserResponseDto; | 	export let data: PageData; | ||||||
| 	export let allUsers: UserResponseDto[]; |  | ||||||
| 
 | 
 | ||||||
| 	let editUser: UserResponseDto; | 	let editUser: UserResponseDto; | ||||||
| 
 | 
 | ||||||
| @@ -75,8 +32,8 @@ | |||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const onUserCreated = async () => { | 	const onUserCreated = async () => { | ||||||
| 		const { data } = await api.userApi.getAllUsers(false); | 		const getAllUsersRes = await api.userApi.getAllUsers(false); | ||||||
| 		allUsers = data; | 		data.allUsers = getAllUsersRes.data; | ||||||
| 		shouldShowCreateUserForm = false; | 		shouldShowCreateUserForm = false; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| @@ -87,14 +44,14 @@ | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const onEditUserSuccess = async () => { | 	const onEditUserSuccess = async () => { | ||||||
| 		const { data } = await api.userApi.getAllUsers(false); | 		const getAllUsersRes = await api.userApi.getAllUsers(false); | ||||||
| 		allUsers = data; | 		data.allUsers = getAllUsersRes.data; | ||||||
| 		shouldShowEditUserForm = false; | 		shouldShowEditUserForm = false; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	const onEditPasswordSuccess = async () => { | 	const onEditPasswordSuccess = async () => { | ||||||
| 		const { data } = await api.userApi.getAllUsers(false); | 		const getAllUsersRes = await api.userApi.getAllUsers(false); | ||||||
| 		allUsers = data; | 		data.allUsers = getAllUsersRes.data; | ||||||
| 		shouldShowEditUserForm = false; | 		shouldShowEditUserForm = false; | ||||||
| 		shouldShowInfoPanel = true; | 		shouldShowInfoPanel = true; | ||||||
| 	}; | 	}; | ||||||
| @@ -104,7 +61,7 @@ | |||||||
| 	<title>Administration - Immich</title> | 	<title>Administration - Immich</title> | ||||||
| </svelte:head> | </svelte:head> | ||||||
| 
 | 
 | ||||||
| <NavigationBar {user} /> | <NavigationBar user={data.user} /> | ||||||
| 
 | 
 | ||||||
| {#if shouldShowCreateUserForm} | {#if shouldShowCreateUserForm} | ||||||
| 	<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}> | 	<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}> | ||||||
| @@ -125,7 +82,7 @@ | |||||||
| {#if shouldShowInfoPanel} | {#if shouldShowInfoPanel} | ||||||
| 	<FullScreenModal on:clickOutside={() => (shouldShowInfoPanel = false)}> | 	<FullScreenModal on:clickOutside={() => (shouldShowInfoPanel = false)}> | ||||||
| 		<div class="border bg-white shadow-sm w-[500px] rounded-3xl p-8 text-sm"> | 		<div class="border bg-white shadow-sm w-[500px] rounded-3xl p-8 text-sm"> | ||||||
| 			<h1 class="font-bold text-immich-primary text-lg mb-4">Password reset success</h1> | 			<h1 class="font-medium text-immich-primary text-lg mb-4">Password reset success</h1> | ||||||
| 
 | 
 | ||||||
| 			<p> | 			<p> | ||||||
| 				The user's password has been reset to the default <code | 				The user's password has been reset to the default <code | ||||||
| @@ -170,7 +127,7 @@ | |||||||
| 			<section class="w-[800px] pt-4"> | 			<section class="w-[800px] pt-4"> | ||||||
| 				{#if selectedAction === AdminSideBarSelection.USER_MANAGEMENT} | 				{#if selectedAction === AdminSideBarSelection.USER_MANAGEMENT} | ||||||
| 					<UserManagement | 					<UserManagement | ||||||
| 						{allUsers} | 						allUsers={data.allUsers} | ||||||
| 						on:create-user={() => (shouldShowCreateUserForm = true)} | 						on:create-user={() => (shouldShowCreateUserForm = true)} | ||||||
| 						on:edit-user={editUserHandler} | 						on:edit-user={editUserHandler} | ||||||
| 					/> | 					/> | ||||||
							
								
								
									
										22
									
								
								web/src/routes/albums/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								web/src/routes/albums/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  | import { AlbumResponseDto, serverApi } from '@api'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent }) => { | ||||||
|  | 	try { | ||||||
|  | 		const { user } = await parent(); | ||||||
|  |  | ||||||
|  | 		if (!user) { | ||||||
|  | 			throw Error('User is not logged in'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const { data: albums } = await serverApi.albumApi.getAllAlbums(); | ||||||
|  |  | ||||||
|  | 		return { | ||||||
|  | 			user: user, | ||||||
|  | 			albums: albums | ||||||
|  | 		}; | ||||||
|  | 	} catch (e) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
| @@ -1,44 +1,3 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
| 
 |  | ||||||
| 	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte'; |  | ||||||
| 
 |  | ||||||
| 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; |  | ||||||
| 	import { ImmichUser } from '$lib/models/immich-user'; |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
| 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; |  | ||||||
| 	import { AlbumResponseDto, api } from '@api'; |  | ||||||
| 
 |  | ||||||
| 	export const load: Load = async ({ fetch, session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		try { |  | ||||||
| 			const [user, albums] = await Promise.all([ |  | ||||||
| 				fetch('/data/user/get-my-user-info').then((r) => r.json()), |  | ||||||
| 				fetch('/data/album/get-all-albums').then((r) => r.json()) |  | ||||||
| 			]); |  | ||||||
| 
 |  | ||||||
| 			return { |  | ||||||
| 				status: 200, |  | ||||||
| 				props: { |  | ||||||
| 					user: user, |  | ||||||
| 					albums: albums |  | ||||||
| 				} |  | ||||||
| 			}; |  | ||||||
| 		} catch (e) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import AlbumCard from '$lib/components/album-page/album-card.svelte'; | 	import AlbumCard from '$lib/components/album-page/album-card.svelte'; | ||||||
| 	import { goto } from '$app/navigation'; | 	import { goto } from '$app/navigation'; | ||||||
| @@ -46,27 +5,29 @@ | |||||||
| 	import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte'; | 	import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte'; | ||||||
| 	import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; | 	import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; | ||||||
| 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte'; | 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte'; | ||||||
| 	import { browser } from '$app/env'; | 	import type { PageData } from './$types'; | ||||||
|  | 	import { AlbumResponseDto, api } from '@api'; | ||||||
|  | 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; | ||||||
|  | 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | ||||||
|  | 	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let user: ImmichUser; | 	export let data: PageData; | ||||||
| 	export let albums: AlbumResponseDto[]; |  | ||||||
| 
 | 
 | ||||||
| 	let isShowContextMenu = false; | 	let isShowContextMenu = false; | ||||||
| 	let contextMenuPosition = { x: 0, y: 0 }; | 	let contextMenuPosition = { x: 0, y: 0 }; | ||||||
| 	let targetAlbum: AlbumResponseDto; | 	let targetAlbum: AlbumResponseDto; | ||||||
| 
 | 
 | ||||||
| 	onMount(async () => { | 	onMount(async () => { | ||||||
| 		const { data } = await api.albumApi.getAllAlbums(); | 		const getAllAlbumsRes = await api.albumApi.getAllAlbums(); | ||||||
| 		albums = data; | 		data.albums = getAllAlbumsRes.data; | ||||||
| 
 | 
 | ||||||
| 		// Delete album that has no photos and is named 'Untitled' | 		// Delete album that has no photos and is named 'Untitled' | ||||||
| 		for (const album of albums) { | 		for (const album of data.albums) { | ||||||
| 			if (album.albumName === 'Untitled' && album.assetCount === 0) { | 			if (album.albumName === 'Untitled' && album.assetCount === 0) { | ||||||
| 				const isDeleted = await autoDeleteAlbum(album); | 				setTimeout(async () => { | ||||||
| 
 | 					await autoDeleteAlbum(album); | ||||||
| 				if (isDeleted) { | 					data.albums = data.albums.filter((a) => a.id !== album.id); | ||||||
| 					albums = albums.filter((a) => a.id !== album.id); | 				}, 500); | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| @@ -101,7 +62,7 @@ | |||||||
| 		) { | 		) { | ||||||
| 			try { | 			try { | ||||||
| 				await api.albumApi.deleteAlbum(targetAlbum.id); | 				await api.albumApi.deleteAlbum(targetAlbum.id); | ||||||
| 				albums = albums.filter((a) => a.id !== targetAlbum.id); | 				data.albums = data.albums.filter((a) => a.id !== targetAlbum.id); | ||||||
| 			} catch (e) { | 			} catch (e) { | ||||||
| 				console.log('Error [userDeleteMenu] ', e); | 				console.log('Error [userDeleteMenu] ', e); | ||||||
| 			} | 			} | ||||||
| @@ -127,7 +88,7 @@ | |||||||
| </svelte:head> | </svelte:head> | ||||||
| 
 | 
 | ||||||
| <section> | <section> | ||||||
| 	<NavigationBar {user} on:uploadClicked={() => {}} /> | 	<NavigationBar user={data.user} on:uploadClicked={() => {}} /> | ||||||
| </section> | </section> | ||||||
| 
 | 
 | ||||||
| <section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg "> | <section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg "> | ||||||
| @@ -158,7 +119,7 @@ | |||||||
| 
 | 
 | ||||||
| 			<!-- Album Card --> | 			<!-- Album Card --> | ||||||
| 			<div class="flex flex-wrap gap-8"> | 			<div class="flex flex-wrap gap-8"> | ||||||
| 				{#each albums as album} | 				{#each data.albums as album} | ||||||
| 					{#key album.id} | 					{#key album.id} | ||||||
| 						<a sveltekit:prefetch href={`albums/${album.id}`}> | 						<a sveltekit:prefetch href={`albums/${album.id}`}> | ||||||
| 							<AlbumCard {album} on:showalbumcontextmenu={(e) => showAlbumContextMenu(e, album)} /> | 							<AlbumCard {album} on:showalbumcontextmenu={(e) => showAlbumContextMenu(e, album)} /> | ||||||
| @@ -168,7 +129,7 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 
 | 
 | ||||||
| 			<!-- Empty Message --> | 			<!-- Empty Message --> | ||||||
| 			{#if albums.length === 0} | 			{#if data.albums.length === 0} | ||||||
| 				<div | 				<div | ||||||
| 					class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center" | 					class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center" | ||||||
| 				> | 				> | ||||||
							
								
								
									
										23
									
								
								web/src/routes/albums/[albumId]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/routes/albums/[albumId]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  |  | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  | import { serverApi } from '@api'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent, params }) => { | ||||||
|  | 	const { user } = await parent(); | ||||||
|  |  | ||||||
|  | 	if (!user) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const albumId = params['albumId']; | ||||||
|  |  | ||||||
|  | 	try { | ||||||
|  | 		const { data: album } = await serverApi.albumApi.getAlbumInfo(albumId); | ||||||
|  | 		return { | ||||||
|  | 			album | ||||||
|  | 		}; | ||||||
|  | 	} catch (e) { | ||||||
|  | 		throw redirect(302, '/albums'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										14
									
								
								web/src/routes/albums/[albumId]/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								web/src/routes/albums/[albumId]/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  |  | ||||||
|  | 	export let data: PageData; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <svelte:head> | ||||||
|  | 	<title>{data.album.albumName} - Immich</title> | ||||||
|  | </svelte:head> | ||||||
|  |  | ||||||
|  | <div class="immich-scrollbar"> | ||||||
|  | 	<AlbumViewer album={data.album} /> | ||||||
|  | </div> | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
|  |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
| 	import { AlbumResponseDto } from '@api'; |  | ||||||
|  |  | ||||||
| 	export const load: Load = async ({ fetch, params, session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		try { |  | ||||||
| 			const albumId = params['albumId']; |  | ||||||
|  |  | ||||||
| 			const albumInfo = await fetch(`/data/album/get-album-info?albumId=${albumId}`).then((r) => |  | ||||||
| 				r.json() |  | ||||||
| 			); |  | ||||||
|  |  | ||||||
| 			return { |  | ||||||
| 				status: 200, |  | ||||||
| 				props: { |  | ||||||
| 					album: albumInfo |  | ||||||
| 				} |  | ||||||
| 			}; |  | ||||||
| 		} catch (e: any) { |  | ||||||
| 			if (e.response?.status === 404) { |  | ||||||
| 				return { |  | ||||||
| 					status: 302, |  | ||||||
| 					redirect: '/albums' |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script lang="ts"> |  | ||||||
| 	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte'; |  | ||||||
| 	import { browser } from '$app/env'; |  | ||||||
|  |  | ||||||
| 	export let album: AlbumResponseDto; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <svelte:head> |  | ||||||
| 	<title>{album.albumName} - Immich</title> |  | ||||||
| </svelte:head> |  | ||||||
|  |  | ||||||
| <div class="immich-scrollbar"> |  | ||||||
| 	<AlbumViewer {album} /> |  | ||||||
| </div> |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
| 	import { browser } from '$app/env'; |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| 	export const load: Load = async ({ params, session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		const albumId = params['albumId']; |  | ||||||
|  |  | ||||||
| 		if (albumId) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: `/albums/${albumId}` |  | ||||||
| 			}; |  | ||||||
| 		} else { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: `/photos` |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
							
								
								
									
										18
									
								
								web/src/routes/albums/[albumId]/photos/[assetId]/+page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								web/src/routes/albums/[albumId]/photos/[assetId]/+page.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | export const prerender = false; | ||||||
|  | import type { PageLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: PageLoad = async ({ params, parent }) => { | ||||||
|  | 	const { user } = await parent(); | ||||||
|  | 	if (!user) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const albumId = params['albumId']; | ||||||
|  |  | ||||||
|  | 	if (albumId) { | ||||||
|  | 		throw redirect(302, `/albums/${albumId}`); | ||||||
|  | 	} else { | ||||||
|  | 		throw redirect(302, `/photos`); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										25
									
								
								web/src/routes/auth/change-password/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								web/src/routes/auth/change-password/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import { goto } from '$app/navigation'; | ||||||
|  | 	import { fade } from 'svelte/transition'; | ||||||
|  |  | ||||||
|  | 	import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  |  | ||||||
|  | 	export let data: PageData; | ||||||
|  |  | ||||||
|  | 	const onSuccessHandler = async () => { | ||||||
|  | 		await fetch('auth/logout', { method: 'POST' }); | ||||||
|  |  | ||||||
|  | 		goto('/auth/login'); | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <svelte:head> | ||||||
|  | 	<title>Change Password - Immich</title> | ||||||
|  | </svelte:head> | ||||||
|  |  | ||||||
|  | <section class="h-screen w-screen flex place-items-center place-content-center"> | ||||||
|  | 	<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}> | ||||||
|  | 		<ChangePasswordForm user={data.user} on:success={onSuccessHandler} /> | ||||||
|  | 	</div> | ||||||
|  | </section> | ||||||
							
								
								
									
										21
									
								
								web/src/routes/auth/change-password/+page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/routes/auth/change-password/+page.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import { api } from '@api'; | ||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | export const prerender = false; | ||||||
|  |  | ||||||
|  | import type { PageLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: PageLoad = async () => { | ||||||
|  | 	try { | ||||||
|  | 		const { data: userInfo } = await api.userApi.getMyUserInfo(); | ||||||
|  |  | ||||||
|  | 		if (userInfo.shouldChangePassword) { | ||||||
|  | 			return { | ||||||
|  | 				user: userInfo | ||||||
|  | 			}; | ||||||
|  | 		} else { | ||||||
|  | 			throw redirect(302, '/photos'); | ||||||
|  | 		} | ||||||
|  | 	} catch (e) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
| @@ -1,56 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
|  |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| 	export const load: Load = async () => { |  | ||||||
| 		try { |  | ||||||
| 			const { data: userInfo } = await api.userApi.getMyUserInfo(); |  | ||||||
|  |  | ||||||
| 			if (userInfo.shouldChangePassword) { |  | ||||||
| 				return { |  | ||||||
| 					status: 200, |  | ||||||
| 					props: { |  | ||||||
| 						user: userInfo |  | ||||||
| 					} |  | ||||||
| 				}; |  | ||||||
| 			} else { |  | ||||||
| 				return { |  | ||||||
| 					status: 302, |  | ||||||
| 					redirect: '/photos' |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 		} catch (e) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script lang="ts"> |  | ||||||
| 	import { goto } from '$app/navigation'; |  | ||||||
| 	import { fade } from 'svelte/transition'; |  | ||||||
|  |  | ||||||
| 	import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte'; |  | ||||||
| 	import { api, UserResponseDto } from '@api'; |  | ||||||
|  |  | ||||||
| 	export let user: UserResponseDto; |  | ||||||
|  |  | ||||||
| 	const onSuccessHandler = async () => { |  | ||||||
| 		await fetch('auth/logout', { method: 'POST' }); |  | ||||||
| 		 |  | ||||||
| 		goto('/auth/login'); |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <svelte:head> |  | ||||||
| 	<title>Change Password - Immich</title> |  | ||||||
| </svelte:head> |  | ||||||
|  |  | ||||||
| <section class="h-screen w-screen flex place-items-center place-content-center"> |  | ||||||
| 	<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}> |  | ||||||
| 		<ChangePasswordForm {user} on:success={onSuccessHandler} /> |  | ||||||
| 	</div> |  | ||||||
| </section> |  | ||||||
| @@ -4,7 +4,7 @@ | |||||||
| 
 | 
 | ||||||
| 	import LoginForm from '$lib/components/forms/login-form.svelte'; | 	import LoginForm from '$lib/components/forms/login-form.svelte'; | ||||||
| 
 | 
 | ||||||
| 	const onLoginSuccess = async () => { | 	const onLoginSuccess = () => { | ||||||
| 		goto('/photos'); | 		goto('/photos'); | ||||||
| 	}; | 	}; | ||||||
| </script> | </script> | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| import { api, serverApi } from '@api'; |  | ||||||
| import type { RequestHandler } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const POST: RequestHandler = async () => { |  | ||||||
| 	api.removeAccessToken(); |  | ||||||
| 	serverApi.removeAccessToken(); |  | ||||||
|  |  | ||||||
| 	return { |  | ||||||
| 		headers: { |  | ||||||
| 			'Set-Cookie': [ |  | ||||||
| 				'immich_is_authenticated=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;', |  | ||||||
| 				'immich_access_token=delete; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT' |  | ||||||
| 			] |  | ||||||
| 		}, |  | ||||||
| 		body: { |  | ||||||
| 			ok: true |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| }; |  | ||||||
							
								
								
									
										27
									
								
								web/src/routes/auth/logout/+server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/routes/auth/logout/+server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | import { json } from '@sveltejs/kit'; | ||||||
|  | import { api, serverApi } from '@api'; | ||||||
|  | import type { RequestHandler } from '@sveltejs/kit'; | ||||||
|  |  | ||||||
|  | export const POST: RequestHandler = async () => { | ||||||
|  | 	api.removeAccessToken(); | ||||||
|  | 	serverApi.removeAccessToken(); | ||||||
|  |  | ||||||
|  | 	const headers = new Headers(); | ||||||
|  |  | ||||||
|  | 	headers.append( | ||||||
|  | 		'set-cookie', | ||||||
|  | 		'immich_is_authenticated=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;' | ||||||
|  | 	); | ||||||
|  | 	headers.append( | ||||||
|  | 		'set-cookie', | ||||||
|  | 		'immich_access_token=delete; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT' | ||||||
|  | 	); | ||||||
|  | 	return json( | ||||||
|  | 		{ | ||||||
|  | 			ok: true | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			headers | ||||||
|  | 		} | ||||||
|  | 	); | ||||||
|  | }; | ||||||
							
								
								
									
										13
									
								
								web/src/routes/auth/register/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								web/src/routes/auth/register/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  | import { serverApi } from '@api'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async () => { | ||||||
|  | 	const { data } = await serverApi.userApi.getUserCount(); | ||||||
|  | 	if (data.userCount != 0) { | ||||||
|  | 		// Admin has been registered, redirect to login | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return; | ||||||
|  | }; | ||||||
							
								
								
									
										11
									
								
								web/src/routes/auth/register/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								web/src/routes/auth/register/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte'; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <svelte:head> | ||||||
|  | 	<title>Admin Registration - Immich</title> | ||||||
|  | </svelte:head> | ||||||
|  |  | ||||||
|  | <section class="h-screen w-screen flex place-items-center place-content-center"> | ||||||
|  | 	<AdminRegistrationForm /> | ||||||
|  | </section> | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| 	export const load: Load = async () => { |  | ||||||
| 		const { data } = await api.userApi.getUserCount(); |  | ||||||
| 		if (data.userCount != 0) { |  | ||||||
| 			// Admin has been registered, redirect to login |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return { |  | ||||||
| 			status: 200 |  | ||||||
| 		}; |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script lang="ts"> |  | ||||||
| 	import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte'; |  | ||||||
| 	import { api } from '@api'; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <svelte:head> |  | ||||||
| 	<title>Admin Registration - Immich</title> |  | ||||||
| </svelte:head> |  | ||||||
|  |  | ||||||
| <section class="h-screen w-screen flex place-items-center place-content-center"> |  | ||||||
| 	<AdminRegistrationForm /> |  | ||||||
| </section> |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| This directory contain SSR endpoints to user serverApi instance to make request directly to DNS |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| import { AlbumResponseDto, serverApi } from '@api'; |  | ||||||
| import type { RequestEvent, RequestHandlerOutput } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const GET = async ({ |  | ||||||
| 	url |  | ||||||
| }: RequestEvent): Promise<RequestHandlerOutput<AlbumResponseDto>> => { |  | ||||||
| 	try { |  | ||||||
| 		const albumId = url.searchParams.get('albumId') || ''; |  | ||||||
| 		const { data } = await serverApi.albumApi.getAlbumInfo(albumId); |  | ||||||
| 		return { |  | ||||||
| 			body: data |  | ||||||
| 		}; |  | ||||||
| 	} catch { |  | ||||||
| 		return { |  | ||||||
| 			status: 500 |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| import { AlbumResponseDto, serverApi } from '@api'; |  | ||||||
| import type { RequestEvent, RequestHandler, RequestHandlerOutput } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const GET = async ({ |  | ||||||
| 	url |  | ||||||
| }: RequestEvent): Promise<RequestHandlerOutput<AlbumResponseDto[]>> => { |  | ||||||
| 	try { |  | ||||||
| 		const isShared = url.searchParams.get('isShared') === 'true' || undefined; |  | ||||||
| 		const { data } = await serverApi.albumApi.getAllAlbums(isShared); |  | ||||||
| 		return { |  | ||||||
| 			body: data |  | ||||||
| 		}; |  | ||||||
| 	} catch { |  | ||||||
| 		return { |  | ||||||
| 			status: 500 |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| import { AssetResponseDto, serverApi } from '@api'; |  | ||||||
| import type { RequestHandlerOutput } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const GET = async (): Promise<RequestHandlerOutput<AssetResponseDto[]>> => { |  | ||||||
| 	try { |  | ||||||
| 		const { data } = await serverApi.assetApi.getAllAssets(); |  | ||||||
| 		return { |  | ||||||
| 			body: data |  | ||||||
| 		}; |  | ||||||
| 	} catch { |  | ||||||
| 		return { |  | ||||||
| 			status: 500 |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| import { serverApi, UserResponseDto } from '@api'; |  | ||||||
| import type { RequestEvent, RequestHandlerOutput } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const GET = async ({url} : RequestEvent): Promise<RequestHandlerOutput<UserResponseDto[]>> => { |  | ||||||
| 	try { |  | ||||||
|     const isAll = url.searchParams.get('isAll') === 'true'; |  | ||||||
|  |  | ||||||
| 		const { data } = await serverApi.userApi.getAllUsers(isAll); |  | ||||||
| 		return { |  | ||||||
| 			body: data |  | ||||||
| 		}; |  | ||||||
| 	} catch { |  | ||||||
| 		return { |  | ||||||
| 			status: 500 |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| import { serverApi, UserResponseDto } from '@api'; |  | ||||||
| import type { RequestHandlerOutput } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| export const GET = async (): Promise<RequestHandlerOutput<UserResponseDto>> => { |  | ||||||
| 	try { |  | ||||||
| 		const { data } = await serverApi.userApi.getMyUserInfo(); |  | ||||||
| 		return { |  | ||||||
| 			body: data |  | ||||||
| 		}; |  | ||||||
| 	} catch { |  | ||||||
| 		return { |  | ||||||
| 			status: 500 |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -1,58 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
|   export const prerender = false; |  | ||||||
|   import type { Load } from '@sveltejs/kit'; |  | ||||||
|   import { api } from '@api'; |  | ||||||
|   import { browser } from '$app/env'; |  | ||||||
|  |  | ||||||
|   export const load: Load = async () => { |  | ||||||
|     if (browser) { |  | ||||||
|       try { |  | ||||||
|         const {data: user} = await api.userApi.getMyUserInfo(); |  | ||||||
|  |  | ||||||
|         return { |  | ||||||
|           status: 302, |  | ||||||
|           redirect: '/photos' |  | ||||||
|         }; |  | ||||||
|       } catch (e) { |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       const {data} = await api.userApi.getUserCount(); |  | ||||||
|  |  | ||||||
|       return { |  | ||||||
|         status: 200, |  | ||||||
|         props: { |  | ||||||
|           isAdminUserExist: data.userCount != 0 |  | ||||||
|         } |  | ||||||
|       }; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script lang="ts"> |  | ||||||
|   import { goto } from '$app/navigation'; |  | ||||||
|  |  | ||||||
|   export let isAdminUserExist: boolean; |  | ||||||
|  |  | ||||||
|   async function onGettingStartedClicked() { |  | ||||||
|     isAdminUserExist ? await goto('/auth/login') : await goto('/auth/register'); |  | ||||||
|   } |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <svelte:head> |  | ||||||
|     <title>Welcome 🎉 - Immich</title> |  | ||||||
|     <meta name="description" content="Immich Web Interface"/> |  | ||||||
| </svelte:head> |  | ||||||
|  |  | ||||||
| <section class="h-screen w-screen flex place-items-center place-content-center"> |  | ||||||
|     <div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]"> |  | ||||||
|         <div class="flex place-items-center place-content-center "> |  | ||||||
|             <img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo"/> |  | ||||||
|         </div> |  | ||||||
|         <h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1> |  | ||||||
|         <button |  | ||||||
|                 class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]" |  | ||||||
|                 on:click={onGettingStartedClicked}>Getting Started |  | ||||||
|         </button |  | ||||||
|         > |  | ||||||
|     </div> |  | ||||||
| </section> |  | ||||||
							
								
								
									
										21
									
								
								web/src/routes/photos/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								web/src/routes/photos/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | import { serverApi } from './../../api/api'; | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  | import { redirect, error } from '@sveltejs/kit'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent }) => { | ||||||
|  | 	try { | ||||||
|  | 		const { user } = await parent(); | ||||||
|  | 		if (!user) { | ||||||
|  | 			throw error(400, 'Not logged in'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const { data: assets } = await serverApi.assetApi.getAllAssets(); | ||||||
|  |  | ||||||
|  | 		return { | ||||||
|  | 			user, | ||||||
|  | 			assets | ||||||
|  | 		}; | ||||||
|  | 	} catch (e) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
| @@ -1,60 +1,28 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
| 
 |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
| 	import { setAssetInfo } from '$lib/stores/assets'; |  | ||||||
| 
 |  | ||||||
| 	export const load: Load = async ({ fetch, session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		try { |  | ||||||
| 			const [userInfo, assets] = await Promise.all([ |  | ||||||
| 				fetch('/data/user/get-my-user-info').then((r) => r.json()), |  | ||||||
| 				fetch('/data/asset/get-all-assets').then((r) => r.json()) |  | ||||||
| 			]); |  | ||||||
| 
 |  | ||||||
| 			setAssetInfo(assets); |  | ||||||
| 
 |  | ||||||
| 			return { |  | ||||||
| 				status: 200, |  | ||||||
| 				props: { |  | ||||||
| 					user: userInfo |  | ||||||
| 				} |  | ||||||
| 			}; |  | ||||||
| 		} catch (e) { |  | ||||||
| 			console.log('ERROR load photos index'); |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; | 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; | ||||||
| 	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte'; | 	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte'; | ||||||
| 	import { fly } from 'svelte/transition'; | 	import { fly } from 'svelte/transition'; | ||||||
| 	import { assetsGroupByDate, flattenAssetGroupByDate, assets } from '$lib/stores/assets'; | 	import { | ||||||
|  | 		assetsGroupByDate, | ||||||
|  | 		flattenAssetGroupByDate, | ||||||
|  | 		assets, | ||||||
|  | 		setAssetInfo | ||||||
|  | 	} from '$lib/stores/assets'; | ||||||
| 	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte'; | 	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte'; | ||||||
| 	import moment from 'moment'; | 	import moment from 'moment'; | ||||||
| 	import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte'; | 	import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte'; | ||||||
| 	import { openFileUploadDialog, UploadType } from '$lib/utils/file-uploader'; | 	import { openFileUploadDialog, UploadType } from '$lib/utils/file-uploader'; | ||||||
| 	import { api, AssetResponseDto, UserResponseDto } from '@api'; | 	import { api, AssetResponseDto } from '@api'; | ||||||
| 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | ||||||
| 	import CircleOutline from 'svelte-material-icons/CircleOutline.svelte'; | 	import CircleOutline from 'svelte-material-icons/CircleOutline.svelte'; | ||||||
| 	import CircleIconButton from '$lib/components/shared-components/circle-icon-button.svelte'; | 	import CircleIconButton from '$lib/components/shared-components/circle-icon-button.svelte'; | ||||||
| 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte'; | 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte'; | ||||||
| 	import Close from 'svelte-material-icons/Close.svelte'; | 	import Close from 'svelte-material-icons/Close.svelte'; | ||||||
| 	import { browser } from '$app/env'; |  | ||||||
| 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; | 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  | 	import { onMount } from 'svelte'; | ||||||
| 
 | 
 | ||||||
| 	export let user: UserResponseDto; | 	export let data: PageData; | ||||||
| 
 | 
 | ||||||
| 	let selectedGroupThumbnail: number | null; | 	let selectedGroupThumbnail: number | null; | ||||||
| 	let isMouseOverGroup: boolean; | 	let isMouseOverGroup: boolean; | ||||||
| @@ -73,6 +41,10 @@ | |||||||
| 	let currentViewAssetIndex = 0; | 	let currentViewAssetIndex = 0; | ||||||
| 	let selectedAsset: AssetResponseDto; | 	let selectedAsset: AssetResponseDto; | ||||||
| 
 | 
 | ||||||
|  | 	onMount(() => { | ||||||
|  | 		setAssetInfo(data.assets); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	const thumbnailMouseEventHandler = (event: CustomEvent) => { | 	const thumbnailMouseEventHandler = (event: CustomEvent) => { | ||||||
| 		const { selectedGroupIndex }: { selectedGroupIndex: number } = event.detail; | 		const { selectedGroupIndex }: { selectedGroupIndex: number } = event.detail; | ||||||
| 
 | 
 | ||||||
| @@ -234,7 +206,10 @@ | |||||||
| 	{/if} | 	{/if} | ||||||
| 
 | 
 | ||||||
| 	{#if !isMultiSelectionMode} | 	{#if !isMultiSelectionMode} | ||||||
| 		<NavigationBar {user} on:uploadClicked={() => openFileUploadDialog(UploadType.GENERAL)} /> | 		<NavigationBar | ||||||
|  | 			user={data.user} | ||||||
|  | 			on:uploadClicked={() => openFileUploadDialog(UploadType.GENERAL)} | ||||||
|  | 		/> | ||||||
| 	{/if} | 	{/if} | ||||||
| </section> | </section> | ||||||
| 
 | 
 | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
| 	export const prerender = false; |  | ||||||
|  |  | ||||||
| 	import { browser } from '$app/env'; |  | ||||||
| 	import type { Load } from '@sveltejs/kit'; |  | ||||||
|  |  | ||||||
| 	export const load: Load = async ({ session }) => { |  | ||||||
| 		if (!browser && !session.user) { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/auth/login' |  | ||||||
| 			}; |  | ||||||
| 		} else { |  | ||||||
| 			return { |  | ||||||
| 				status: 302, |  | ||||||
| 				redirect: '/photos' |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| </script> |  | ||||||
							
								
								
									
										14
									
								
								web/src/routes/photos/[assetId]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								web/src/routes/photos/[assetId]/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | export const prerender = false; | ||||||
|  |  | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent }) => { | ||||||
|  | 	const { user } = await parent(); | ||||||
|  |  | ||||||
|  | 	if (!user) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} else { | ||||||
|  | 		throw redirect(302, '/photos'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										0
									
								
								web/src/routes/photos/[assetId]/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								web/src/routes/photos/[assetId]/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										23
									
								
								web/src/routes/sharing/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web/src/routes/sharing/+page.server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | import { redirect } from '@sveltejs/kit'; | ||||||
|  | export const prerender = false; | ||||||
|  |  | ||||||
|  | import { serverApi } from '@api'; | ||||||
|  | import type { PageServerLoad } from './$types'; | ||||||
|  |  | ||||||
|  | export const load: PageServerLoad = async ({ parent }) => { | ||||||
|  | 	try { | ||||||
|  | 		const { user } = await parent(); | ||||||
|  | 		if (!user) { | ||||||
|  | 			throw redirect(302, '/auth/login'); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const { data: sharedAlbums } = await serverApi.albumApi.getAllAlbums(true); | ||||||
|  |  | ||||||
|  | 		return { | ||||||
|  | 			user: user, | ||||||
|  | 			sharedAlbums: sharedAlbums | ||||||
|  | 		}; | ||||||
|  | 	} catch (e) { | ||||||
|  | 		throw redirect(302, '/auth/login'); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										83
									
								
								web/src/routes/sharing/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								web/src/routes/sharing/+page.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | <script lang="ts"> | ||||||
|  | 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; | ||||||
|  | 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; | ||||||
|  | 	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte'; | ||||||
|  | 	import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte'; | ||||||
|  | 	import { goto } from '$app/navigation'; | ||||||
|  | 	import { api } from '@api'; | ||||||
|  | 	import type { PageData } from './$types'; | ||||||
|  |  | ||||||
|  | 	export let data: PageData; | ||||||
|  |  | ||||||
|  | 	const createSharedAlbum = async () => { | ||||||
|  | 		try { | ||||||
|  | 			const { data: newAlbum } = await api.albumApi.createAlbum({ | ||||||
|  | 				albumName: 'Untitled' | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 			goto('/albums/' + newAlbum.id); | ||||||
|  | 		} catch (e) { | ||||||
|  | 			console.log('Error [createAlbum] ', e); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <svelte:head> | ||||||
|  | 	<title>Albums - Immich</title> | ||||||
|  | </svelte:head> | ||||||
|  |  | ||||||
|  | <section> | ||||||
|  | 	<NavigationBar user={data.user} on:uploadClicked={() => {}} /> | ||||||
|  | </section> | ||||||
|  |  | ||||||
|  | <section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg"> | ||||||
|  | 	<SideBar /> | ||||||
|  |  | ||||||
|  | 	<section class="overflow-y-auto relative"> | ||||||
|  | 		<section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg"> | ||||||
|  | 			<!-- Main Section --> | ||||||
|  | 			<div class="px-4 flex justify-between place-items-center"> | ||||||
|  | 				<div> | ||||||
|  | 					<p class="font-medium">Sharing</p> | ||||||
|  | 				</div> | ||||||
|  |  | ||||||
|  | 				<div> | ||||||
|  | 					<button | ||||||
|  | 						on:click={createSharedAlbum} | ||||||
|  | 						class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700" | ||||||
|  | 					> | ||||||
|  | 						<span> | ||||||
|  | 							<PlusBoxOutline size="18" /> | ||||||
|  | 						</span> | ||||||
|  | 						<p>Create shared album</p> | ||||||
|  | 					</button> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  |  | ||||||
|  | 			<div class="my-4"> | ||||||
|  | 				<hr /> | ||||||
|  | 			</div> | ||||||
|  |  | ||||||
|  | 			<!-- Share Album List --> | ||||||
|  | 			<div class="w-full flex flex-col place-items-center"> | ||||||
|  | 				{#each data.sharedAlbums as album} | ||||||
|  | 					<a sveltekit:prefetch href={`albums/${album.id}`}> | ||||||
|  | 						<SharedAlbumListTile {album} user={data.user} /> | ||||||
|  | 					</a> | ||||||
|  | 				{/each} | ||||||
|  | 			</div> | ||||||
|  |  | ||||||
|  | 			<!-- Empty List --> | ||||||
|  | 			{#if data.sharedAlbums.length === 0} | ||||||
|  | 				<div | ||||||
|  | 					class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center" | ||||||
|  | 				> | ||||||
|  | 					<img src="/empty-2.svg" alt="Empty shared album" width="500" /> | ||||||
|  | 					<p class="text-center text-immich-text-gray-500"> | ||||||
|  | 						Create a shared album to share photos and videos with people in your network | ||||||
|  | 					</p> | ||||||
|  | 				</div> | ||||||
|  | 			{/if} | ||||||
|  | 		</section> | ||||||
|  | 	</section> | ||||||
|  | </section> | ||||||
| @@ -1,120 +0,0 @@ | |||||||
| <script context="module" lang="ts"> |  | ||||||
|   export const prerender = false; |  | ||||||
|  |  | ||||||
|   import type { Load } from '@sveltejs/kit'; |  | ||||||
|   import { AlbumResponseDto, api, UserResponseDto } from '@api'; |  | ||||||
|   import { browser } from '$app/env'; |  | ||||||
|  |  | ||||||
|   export const load: Load = async ({fetch, session}) => { |  | ||||||
|     if (!browser && !session.user) { |  | ||||||
|       return { |  | ||||||
|         status: 302, |  | ||||||
|         redirect: '/auth/login' |  | ||||||
|       }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     try { |  | ||||||
|       const [user, sharedAlbums] = await Promise.all([ |  | ||||||
|         fetch('/data/user/get-my-user-info').then((r) => r.json()), |  | ||||||
|         fetch('/data/album/get-all-albums?isShared=true').then((r) => r.json()) |  | ||||||
|       ]); |  | ||||||
|  |  | ||||||
|       return { |  | ||||||
|         status: 200, |  | ||||||
|         props: { |  | ||||||
|           user: user, |  | ||||||
|           sharedAlbums: sharedAlbums |  | ||||||
|         } |  | ||||||
|       }; |  | ||||||
|     } catch (e) { |  | ||||||
|       return { |  | ||||||
|         status: 302, |  | ||||||
|         redirect: '/auth/login' |  | ||||||
|       }; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <script lang="ts"> |  | ||||||
|   import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte'; |  | ||||||
|   import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte'; |  | ||||||
|   import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte'; |  | ||||||
|   import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte'; |  | ||||||
|   import { goto } from '$app/navigation'; |  | ||||||
|  |  | ||||||
|   export let user: UserResponseDto; |  | ||||||
|   export let sharedAlbums: AlbumResponseDto[]; |  | ||||||
|  |  | ||||||
|   const createSharedAlbum = async () => { |  | ||||||
|     try { |  | ||||||
|       const {data: newAlbum} = await api.albumApi.createAlbum({ |  | ||||||
|         albumName: 'Untitled' |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|       goto('/albums/' + newAlbum.id); |  | ||||||
|     } catch (e) { |  | ||||||
|       console.log('Error [createAlbum] ', e); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| </script> |  | ||||||
|  |  | ||||||
| <svelte:head> |  | ||||||
|     <title>Albums - Immich</title> |  | ||||||
| </svelte:head> |  | ||||||
|  |  | ||||||
| <section> |  | ||||||
|     <NavigationBar {user} on:uploadClicked={() => {}}/> |  | ||||||
| </section> |  | ||||||
|  |  | ||||||
| <section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg"> |  | ||||||
|     <SideBar/> |  | ||||||
|  |  | ||||||
|     <section class="overflow-y-auto relative"> |  | ||||||
|         <section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg"> |  | ||||||
|             <!-- Main Section --> |  | ||||||
|             <div class="px-4 flex justify-between place-items-center"> |  | ||||||
|                 <div> |  | ||||||
|                     <p class="font-medium">Sharing</p> |  | ||||||
|                 </div> |  | ||||||
|  |  | ||||||
|                 <div> |  | ||||||
|                     <button |  | ||||||
|                             on:click={createSharedAlbum} |  | ||||||
|                             class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700" |  | ||||||
|                     > |  | ||||||
| 						<span> |  | ||||||
| 							<PlusBoxOutline size="18"/> |  | ||||||
| 						</span> |  | ||||||
|                         <p>Create shared album</p> |  | ||||||
|                     </button> |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|  |  | ||||||
|             <div class="my-4"> |  | ||||||
|                 <hr/> |  | ||||||
|             </div> |  | ||||||
|  |  | ||||||
|             <!-- Share Album List --> |  | ||||||
|             <div class="w-full flex flex-col place-items-center"> |  | ||||||
|                 {#each sharedAlbums as album} |  | ||||||
|                     <a sveltekit:prefetch href={`albums/${album.id}`}> |  | ||||||
|                         <SharedAlbumListTile {album} {user}/> |  | ||||||
|                     </a |  | ||||||
|                     > |  | ||||||
|                 {/each} |  | ||||||
|             </div> |  | ||||||
|  |  | ||||||
|             <!-- Empty List --> |  | ||||||
|             {#if sharedAlbums.length === 0} |  | ||||||
|                 <div |  | ||||||
|                         class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center" |  | ||||||
|                 > |  | ||||||
|                     <img src="/empty-2.svg" alt="Empty shared album" width="500"/> |  | ||||||
|                     <p class="text-center text-immich-text-gray-500"> |  | ||||||
|                         Create a shared album to share photos and videos with people in your network |  | ||||||
|                     </p> |  | ||||||
|                 </div> |  | ||||||
|             {/if} |  | ||||||
|         </section> |  | ||||||
|     </section> |  | ||||||
| </section> |  | ||||||
		Reference in New Issue
	
	Block a user