mirror of
https://github.com/immich-app/immich.git
synced 2024-11-15 18:08:48 -07:00
Migrate SvelteKit to the latest version 431
(#526)
This commit is contained in:
parent
fb0fa742f5
commit
db2ed2d881
@ -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 . .
|
||||||
|
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') || '');
|
|
||||||
|
|
||||||
if (!cookies['immich_is_authenticated']) {
|
|
||||||
return await resolve(event);
|
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>
|
|
Loading…
Reference in New Issue
Block a user