mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 13:23:05 +08:00
feat: pnpm-monorepo (#236)
This commit is contained in:
+42
-28
@@ -22,41 +22,43 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
app_name: ['api', 'root', 'my', 'buy', 'get', 'datasets']
|
||||
app_name: ['root', 'api', 'my', 'get', 'buy', 'datasets']
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/${{ matrix.app_name }}.immich.app
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version-file: './.nvmrc'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||
|
||||
- name: Set app
|
||||
run: |
|
||||
touch .env
|
||||
echo IMMICH_APP="${{ matrix.app_name }}.immich.app" >> .env
|
||||
- name: Install
|
||||
run: pnpm install
|
||||
|
||||
- name: Use futopay-test
|
||||
if: ${{ env.ENVIRONMENT == 'dev' }}
|
||||
run: |
|
||||
echo PUBLIC_IMMICH_PAY_HOST=https://futopay-test.azurewebsites.net >> .env
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run build
|
||||
- name: Build
|
||||
env:
|
||||
PUBLIC_IMMICH_PAY_HOST: ${{ env.ENVIRONMENT == 'dev' && 'https://futopay-test.azurewebsites.net' || 'https://pay.futo.org' }}
|
||||
PUBLIC_CF_TURNSTILE_SITE: ${{ secrets.CF_TURNSTILE_DEFAULT_INVISIBLE_SITE_KEY }}
|
||||
run: npm run build
|
||||
run: pnpm build
|
||||
|
||||
- name: Upload build output
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: build-frontend-output-${{ matrix.app_name }}
|
||||
include-hidden-files: true
|
||||
path: build
|
||||
path: apps/${{ matrix.app_name }}.immich.app/build
|
||||
retention-days: 1
|
||||
|
||||
build-backend:
|
||||
@@ -65,22 +67,32 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
app_name: ['datasets']
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/${{ matrix.app_name }}.immich.app/backend
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version-file: './.nvmrc'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
working-directory: 'apps/${{ matrix.app_name }}.immich.app/backend'
|
||||
- name: Run pnpm install
|
||||
run: pnpm install
|
||||
|
||||
- name: Build backend
|
||||
run: npx wrangler build
|
||||
working-directory: 'apps/${{ matrix.app_name }}.immich.app/backend'
|
||||
|
||||
- name: Upload build output
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
@@ -96,7 +108,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
app_name: ['api', 'root', 'my', 'buy', 'get', 'datasets']
|
||||
app_name: ['root', 'api', 'my', 'get', 'buy', 'datasets']
|
||||
env:
|
||||
TF_VAR_app_name: ${{ matrix.app_name }}
|
||||
TF_VAR_stage: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || '' }}
|
||||
@@ -104,7 +116,9 @@ jobs:
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ github.ref == 'refs/heads/main' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get build artifact
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
environment: ['dev', 'prod']
|
||||
name: ['api', 'root', 'my', 'buy', 'get', 'datasets']
|
||||
app_name: ['root', 'api', 'my', 'get', 'buy', 'datasets']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/deployment
|
||||
env:
|
||||
ENVIRONMENT: ${{ matrix.environment }}
|
||||
TF_VAR_app_name: ${{ matrix.name }}
|
||||
TF_VAR_app_name: ${{ matrix.app_name }}
|
||||
TF_VAR_dist_dir: ${{ github.workspace }}/dist
|
||||
TF_VAR_stage: pr-${{ github.event.number }}
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ matrix.environment == 'prod' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
|
||||
|
||||
+28
-22
@@ -15,45 +15,51 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
app_name: ['api', 'root', 'my', 'buy', 'get', 'datasets']
|
||||
app_name: ['root', 'api', 'my', 'get', 'buy', 'datasets']
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: apps/${{ matrix.app_name }}.immich.app
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version-file: './.nvmrc'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||
|
||||
- name: Set app
|
||||
run: |
|
||||
touch .env
|
||||
echo IMMICH_APP="${{ matrix.app_name }}.immich.app" >> .env
|
||||
- name: Install
|
||||
run: pnpm install
|
||||
|
||||
- name: Run npm install
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run formatter
|
||||
run: npm run format
|
||||
- name: Format
|
||||
run: pnpm format
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check
|
||||
- name: Type check
|
||||
run: pnpm check
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run unit tests
|
||||
run: npm test
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Check for app backend
|
||||
id: check-backend
|
||||
run: |
|
||||
if [ -d "apps/${{ matrix.app_name }}.immich.app/backend" ]; then
|
||||
if [ -d "backend" ]; then
|
||||
echo "has_backend=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "has_backend=false" >> $GITHUB_ENV
|
||||
@@ -63,5 +69,5 @@ jobs:
|
||||
if: ${{ env.has_backend == 'true' }}
|
||||
working-directory: 'apps/${{ matrix.app_name }}.immich.app/backend'
|
||||
run: |
|
||||
npm ci
|
||||
npm test
|
||||
pnpm install
|
||||
pnpm test
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ node_modules
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
/.svelte-kit
|
||||
/build
|
||||
apps/*/.svelte-kit
|
||||
apps/*/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
|
||||
# wrangler generated files
|
||||
worker-configuration.d.ts
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
|
||||
@@ -1,38 +1 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
# Static sites
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
bun.lock
|
||||
bun.lockb
|
||||
|
||||
# Miscellaneous
|
||||
/static/
|
||||
|
||||
# wrangler generated files
|
||||
worker-configuration.d.ts
|
||||
|
||||
/.svelte-kit
|
||||
/build
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://api.immich.app/
|
||||
|
||||
Immich link proxy to redirect to your personal instance
|
||||
@@ -4,13 +4,13 @@ import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
/** @type {import('eslint').Linter.FlatConfig[]} */
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
...svelte.configs.prettier,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
@@ -32,7 +32,6 @@ export default [
|
||||
},
|
||||
},
|
||||
{
|
||||
// idk why svelte.ts files are not being parsed correctly
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/', 'apps/**/backend/worker-configuration.d.ts', '**/*.svelte.ts'],
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/'],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "api.immich.app",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite dev",
|
||||
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
|
||||
"preview": "vite preview",
|
||||
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"sync": "svelte-kit sync",
|
||||
"test": "vitest",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.4.0",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@sveltejs/adapter-static": "^3.0.9",
|
||||
"@sveltejs/kit": "^2.43.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@types/node": "^22",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.12.4",
|
||||
"globals": "^16.4.0",
|
||||
"playwright": "^1.55.1",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.1",
|
||||
"vite": "^7.1.7",
|
||||
"vitest": "^3.2.4",
|
||||
"vitest-browser-svelte": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@immich/ui": "^0.35.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"svelte-highlight": "^7.8.4"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
@@ -21,7 +21,7 @@
|
||||
<meta name="twitter:image" content="/img/social-preview.png" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" class="dark bg-light text-dark">
|
||||
<body data-sveltekit-preload-data="hover" class="bg-light text-dark dark">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('sum test', () => {
|
||||
it('adds 1 + 2 to equal 3', () => {
|
||||
expect(1 + 2).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
@import '@immich/ui/theme/default.css';
|
||||
@import '$common/theme/theme.css';
|
||||
@source '../../node_modules/@immich/ui/dist';
|
||||
@source '../../../../common';
|
||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,4 @@
|
||||
<span
|
||||
class="text-dark rounded-lg bg-teal-200 px-2 py-1 dark:bg-teal-900 dark:text-teal-50"
|
||||
title="Endpoint can only be used by Administrators">Admin Endpoint</span
|
||||
>
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { playgroundManager } from '$lib/api/services/playground-manager.svelte';
|
||||
import { playgroundManager } from '$lib/services/playground-manager.svelte';
|
||||
import { Button, Code, Field, HStack, Input, Modal, ModalBody, ModalFooter, Stack, Text } from '@immich/ui';
|
||||
import { mdiConnection } from '@mdi/js';
|
||||
|
||||
@@ -59,14 +59,14 @@
|
||||
<form autocomplete="off" novalidate id="configure-form" {onsubmit}>
|
||||
<Stack gap={4}>
|
||||
{#if playgroundManager.connected}
|
||||
<div class="flex gap-1 items-center">
|
||||
<div class="flex items-center gap-1">
|
||||
<Text>
|
||||
Connected to <Code>{playgroundManager.serverUrl}</Code>
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{#if playgroundManager.serverVersion}
|
||||
<div class="flex gap-1 items-center">
|
||||
<div class="flex items-center gap-1">
|
||||
<Text>
|
||||
Server version <Code>{playgroundManager.serverVersion.value}</Code>
|
||||
</Text>
|
||||
@@ -74,7 +74,7 @@
|
||||
{/if}
|
||||
{:else}
|
||||
{#if errorMessage}
|
||||
<Text class="pb-4 bg-red-100 dark:bg-red-900 border border-red-900 dark:border-red-100 rounded-lg p-4"
|
||||
<Text class="rounded-lg border border-red-900 bg-red-100 p-4 pb-4 dark:border-red-100 dark:bg-red-900"
|
||||
>{errorMessage}</Text
|
||||
>
|
||||
{/if}
|
||||
@@ -95,7 +95,7 @@
|
||||
<hr class="my-1 border border-gray-500" />
|
||||
|
||||
<Text
|
||||
class="pb-4 bg-yellow-100 dark:bg-yellow-900 border border-yellow-900 dark:border-yellow-100 rounded-lg p-4"
|
||||
class="rounded-lg border border-yellow-900 bg-yellow-100 p-4 pb-4 dark:border-yellow-100 dark:bg-yellow-900"
|
||||
>Note: In order to connect, CORS has to be enabled on the remote server.</Text
|
||||
>
|
||||
{/if}
|
||||
@@ -0,0 +1,3 @@
|
||||
<span class="rounded-lg bg-red-200 px-2 py-1 dark:bg-red-900 dark:text-red-50" title="Endpoint is deprecated"
|
||||
>Deprecated</span
|
||||
>
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import ApiNextStepCard from '$lib/api/components/ApiNextStepCard.svelte';
|
||||
import ApiNextStepCard from '$lib/components/ApiNextStepCard.svelte';
|
||||
import { Heading, Stack } from '@immich/ui';
|
||||
|
||||
type Props = {
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<Stack gap={4}>
|
||||
<Heading>Next Steps</Heading>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
{#each nextSteps as nextStep (nextStep.title)}
|
||||
<ApiNextStepCard {...nextStep} />
|
||||
{/each}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import ApiNextSteps from '$lib/api/components/ApiNextSteps.svelte';
|
||||
import ApiNextSteps from '$lib/components/ApiNextSteps.svelte';
|
||||
import { Heading, Stack, Text } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
</script>
|
||||
|
||||
<Stack gap={8}>
|
||||
<div class="flex justify-between place-items-end">
|
||||
<div class="flex place-items-end justify-between">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Heading size="giant" tag="h1">{title}</Heading>
|
||||
<Text color="muted" size="giant" fontWeight="bold">{description}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border border-subtle" />
|
||||
<hr class="border-subtle border" />
|
||||
|
||||
{@render children?.()}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { ParameterObject } from '$lib/api/services/open-api.d';
|
||||
import type { ParameterObject } from '$lib/services/open-api.d';
|
||||
import ApiParamsInternal from './ApiParamsInternal.svelte';
|
||||
|
||||
type Props = {
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ApiSchemaType from '$lib/api/components/ApiSchemaType.svelte';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/api/services/open-api.d';
|
||||
import ApiSchemaType from '$lib/components/ApiSchemaType.svelte';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/services/open-api.d';
|
||||
import { Card, CardBody, Text } from '@immich/ui';
|
||||
|
||||
type InternalParam = {
|
||||
@@ -20,9 +20,9 @@
|
||||
<div class="flex flex-col gap-2 lg:hidden">
|
||||
{#each params as param, i (i)}
|
||||
<Card color="secondary">
|
||||
<CardBody class="py-2 flex flex-col gap-2">
|
||||
<CardBody class="flex flex-col gap-2 py-2">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex justify-between w-full place-items-center">
|
||||
<div class="flex w-full place-items-center justify-between">
|
||||
<Text>
|
||||
<code>{param.name} </code>
|
||||
{#if param.required}
|
||||
@@ -44,14 +44,14 @@
|
||||
|
||||
<div class="hidden lg:block">
|
||||
<Card color="secondary">
|
||||
<div class="grid grid-cols-12 py-2 px-4 font-bold bg-subtle text-dark mb-2 border-b">
|
||||
<div class="bg-subtle text-dark mb-2 grid grid-cols-12 border-b px-4 py-2 font-bold">
|
||||
<div class="col-span-3">Property</div>
|
||||
<div class="col-span-3">Type</div>
|
||||
<div class="col-span-3">Required</div>
|
||||
<div class="col-span-3">Description</div>
|
||||
</div>
|
||||
{#each params as param, i (i)}
|
||||
<div class="grid grid-cols-12 py-2 px-4">
|
||||
<div class="grid grid-cols-12 px-4 py-2">
|
||||
<div class="col-span-3">
|
||||
<code>{param.name}</code>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
<div class="col-span-3">
|
||||
{#if param.required}
|
||||
<span class="py-1 px-2 dark:bg-red-900 dark:text-red-50 bg-red-200 rounded-lg" title="Param is required"
|
||||
<span class="rounded-lg bg-red-200 px-2 py-1 dark:bg-red-900 dark:text-red-50" title="Param is required"
|
||||
>Required</span
|
||||
>
|
||||
{/if}
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
</script>
|
||||
|
||||
<span
|
||||
class="py-1 px-2 dark:bg-purple-900 dark:text-purple-50 bg-purple-200 text-dark rounded-lg"
|
||||
class="text-dark rounded-lg bg-purple-200 px-2 py-1 dark:bg-purple-900 dark:text-purple-50"
|
||||
title="The {value} permission"
|
||||
>
|
||||
<span class="font-mono">{value}</span>
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { type ApiEndpoint } from '$lib/api/services/open-api';
|
||||
import { playgroundManager } from '$lib/api/services/playground-manager.svelte';
|
||||
import CodeBlock from '$lib/components/CodeBlock.svelte';
|
||||
import { type ApiEndpoint } from '$lib/services/open-api';
|
||||
import { playgroundManager } from '$lib/services/playground-manager.svelte';
|
||||
import {
|
||||
Button,
|
||||
Code,
|
||||
CodeBlock,
|
||||
Field,
|
||||
Heading,
|
||||
Icon,
|
||||
@@ -63,7 +63,7 @@
|
||||
</script>
|
||||
|
||||
<section class="flex flex-col gap-2">
|
||||
<div class="flex justify-between place-items-center gap-2">
|
||||
<div class="flex place-items-center justify-between gap-2">
|
||||
<Heading size="small" tag="h2" class="flex items-center gap-1">
|
||||
Live Response
|
||||
<Icon icon={mdiLightningBolt} size="1.5rem" class="text-yellow-600 dark:text-yellow-400" />
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<span
|
||||
class="py-1 px-2 dark:bg-blue-900 dark:text-blue-50 bg-blue-200 text-dark rounded-lg"
|
||||
class="text-dark rounded-lg bg-blue-200 px-2 py-1 dark:bg-blue-900 dark:text-blue-50"
|
||||
title="Endpoint can be accessed without any authentication">Public Endpoint</span
|
||||
>
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import ApiSchemaEnum from '$lib/api/components/ApiSchemaEnum.svelte';
|
||||
import ApiSchemaObject from '$lib/api/components/ApiSchemaObject.svelte';
|
||||
import ApiSchemaRef from '$lib/api/components/ApiSchemaRef.svelte';
|
||||
import ApiSchemaType from '$lib/api/components/ApiSchemaType.svelte';
|
||||
import { isRef } from '$lib/api/services/open-api';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/api/services/open-api.d';
|
||||
import ApiSchemaEnum from '$lib/components/ApiSchemaEnum.svelte';
|
||||
import ApiSchemaObject from '$lib/components/ApiSchemaObject.svelte';
|
||||
import ApiSchemaRef from '$lib/components/ApiSchemaRef.svelte';
|
||||
import ApiSchemaType from '$lib/components/ApiSchemaType.svelte';
|
||||
import { isRef } from '$lib/services/open-api';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/services/open-api.d';
|
||||
|
||||
type Props = {
|
||||
schema?: SchemaObject | ReferenceObject;
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/api/services/open-api.d';
|
||||
import { getRefHref, getRefName, isRef } from '$lib/api/services/open-api';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/services/open-api.d';
|
||||
import { getRefHref, getRefName, isRef } from '$lib/services/open-api';
|
||||
import { Link, Text } from '@immich/ui';
|
||||
|
||||
type Props = {
|
||||
@@ -11,7 +11,7 @@
|
||||
</script>
|
||||
|
||||
{#if isRef(schema)}
|
||||
<span class="py-1 px-2 dark:bg-pink-900 dark:text-pink-50 bg-pink-200 rounded-lg">
|
||||
<span class="rounded-lg bg-pink-200 px-2 py-1 dark:bg-pink-900 dark:text-pink-50">
|
||||
<Link href={getRefHref(schema)}>{getRefName(schema)}[]</Link>
|
||||
</span>
|
||||
{:else}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { SchemaObject } from '$lib/api/services/open-api.d';
|
||||
import ApiParamsInternal from './ApiParamsInternal.svelte';
|
||||
import ApiParamsInternal from '$lib/components/ApiParamsInternal.svelte';
|
||||
import type { SchemaObject } from '$lib/services/open-api.d';
|
||||
|
||||
type Props = {
|
||||
schema: SchemaObject;
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { ReferenceObject } from '$lib/api/services/open-api.d';
|
||||
import { getOpenApi, getRefHref, getRefName } from '$lib/api/services/open-api';
|
||||
import type { ReferenceObject } from '$lib/services/open-api.d';
|
||||
import { getOpenApi, getRefHref, getRefName } from '$lib/services/open-api';
|
||||
import { Link, Text } from '@immich/ui';
|
||||
|
||||
type Props = {
|
||||
@@ -13,7 +13,7 @@
|
||||
</script>
|
||||
|
||||
{#if fromRef(ref)}
|
||||
<span class="py-1 px-2 dark:bg-pink-900 dark:text-pink-50 bg-pink-200 rounded-lg">
|
||||
<span class="rounded-lg bg-pink-200 px-2 py-1 dark:bg-pink-900 dark:text-pink-50">
|
||||
<Link href={getRefHref(ref)}>{getRefName(ref)}</Link>
|
||||
</span>
|
||||
{:else}
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import ApiSchemaType from '$lib/api/components/ApiSchemaType.svelte';
|
||||
import { getOpenApi, getRefHref, isRef } from '$lib/api/services/open-api';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/api/services/open-api.d';
|
||||
import { cleanClass } from '$lib/utils';
|
||||
import { cleanClass } from '$lib';
|
||||
import ApiSchemaType from '$lib/components/ApiSchemaType.svelte';
|
||||
import { getOpenApi, getRefHref, isRef } from '$lib/services/open-api';
|
||||
import type { ReferenceObject, SchemaObject } from '$lib/services/open-api.d';
|
||||
import { Link, Text } from '@immich/ui';
|
||||
|
||||
type Props = {
|
||||
@@ -65,7 +65,7 @@
|
||||
const schema = $derived(schemaOrRef as SchemaObject);
|
||||
</script>
|
||||
|
||||
<div class="flex gap-1 place-items-center">
|
||||
<div class="flex place-items-center gap-1">
|
||||
{#if isRef(schema)}
|
||||
{@const ref = fromRef(schema)}
|
||||
{#if ref}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<span
|
||||
class="py-1 px-2 dark:bg-yellow-900 dark:text-yellow-50 bg-yellow-200 text-dark rounded-lg"
|
||||
class="text-dark rounded-lg bg-yellow-200 px-2 py-1 dark:bg-yellow-900 dark:text-yellow-50"
|
||||
title="Endpoint can be accessed by shared link authentication">Shared Link Route</span
|
||||
>
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getEndpointColor, type ApiEndpointTag } from '$lib/api/services/open-api';
|
||||
import { getEndpointColor, type ApiEndpointTag } from '$lib/services/open-api';
|
||||
import { Button, Card, CardTitle, Heading, Text } from '@immich/ui';
|
||||
|
||||
type Props = {
|
||||
@@ -10,15 +10,15 @@
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Heading class="flex gap-2 py-4 items-center">
|
||||
<Heading class="flex items-center gap-2 py-4">
|
||||
<a href={tag.href}>{tag.name}</a>
|
||||
</Heading>
|
||||
<Card color="secondary">
|
||||
{#each tag.endpoints as endpoint, i (i)}
|
||||
<Button href={endpoint.href} shape="rectangle" color="secondary" fullWidth variant="ghost" class="p-4">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex w-full flex-col">
|
||||
<CardTitle>
|
||||
<span class="group flex gap-2 justify-between {endpoint.deprecated ? 'text-gray-500 italic' : ''}">
|
||||
<span class="group flex justify-between gap-2 {endpoint.deprecated ? 'italic text-gray-500' : ''}">
|
||||
<span class="flex gap-2">
|
||||
<span class={getEndpointColor(endpoint)}>{endpoint.method}</span>
|
||||
<span>{endpoint.route}</span>
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$lib/utils';
|
||||
import { cleanClass } from '$lib';
|
||||
import { Heading } from '@immich/ui';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { ComponentProps, Snippet } from 'svelte';
|
||||
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
||||
|
||||
type Props = {
|
||||
@@ -26,6 +26,16 @@ import {
|
||||
mdiTrashCanOutline,
|
||||
} from '@mdi/js';
|
||||
|
||||
export enum ApiPage {
|
||||
Introduction = '/introduction',
|
||||
GettingStarted = '/getting-started',
|
||||
Authentication = '/authentication',
|
||||
Permissions = '/permissions',
|
||||
Sdk = '/sdk',
|
||||
Endpoints = '/endpoints',
|
||||
Models = '/models',
|
||||
}
|
||||
|
||||
const icons: Record<string, string> = {
|
||||
'API Keys': mdiKeyOutline,
|
||||
Activities: mdiForumOutline,
|
||||
@@ -58,3 +68,15 @@ const icons: Record<string, string> = {
|
||||
};
|
||||
|
||||
export const getIcon = (name: string) => icons[name] || mdiCodeJson;
|
||||
|
||||
export const cleanClass = (...classNames: unknown[]) => {
|
||||
return classNames
|
||||
.filter((className) => {
|
||||
if (!className || typeof className === 'boolean') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof className === 'string';
|
||||
})
|
||||
.join(' ');
|
||||
};
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { Pathname } from '$app/types';
|
||||
import { PUBLIC_IMMICH_SPEC_URL } from '$env/static/public';
|
||||
import { ApiPage } from '$lib';
|
||||
import type {
|
||||
OpenAPIObject,
|
||||
ParameterObject,
|
||||
ReferenceObject,
|
||||
RequestBodyObject,
|
||||
SchemaObject,
|
||||
} from '$lib/api/services/open-api.d';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
} from '$lib/services/open-api.d';
|
||||
|
||||
export type ApiMethod = 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
|
||||
export type AuthenticationMethod = 'ApiKey' | 'Cookie' | 'Bearer';
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import ApiConfigureModal from '$lib/api/modals/ApiConfigureModal.svelte';
|
||||
import ApiConfigureModal from '$lib/components/ApiConfigureModal.svelte';
|
||||
import { modalManager } from '@immich/ui';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'immich-api:connection';
|
||||
+6
-7
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { beforeNavigate } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { getIcon } from '$lib/utils/icons';
|
||||
import Header from '$common/components/Header.svelte';
|
||||
import PageContent from '$common/components/PageContent.svelte';
|
||||
import { ApiPage, getIcon } from '$lib';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import { AppShell, AppShellHeader, AppShellSidebar, NavbarGroup, NavbarItem } from '@immich/ui';
|
||||
import {
|
||||
mdiApi,
|
||||
@@ -48,7 +47,7 @@
|
||||
</AppShellHeader>
|
||||
|
||||
<AppShellSidebar bind:open class="border-e">
|
||||
<div class="mt-8 pe-6 flex flex-col gap-4">
|
||||
<div class="mt-8 flex flex-col gap-4 pe-6">
|
||||
<div>
|
||||
<NavbarItem title="Introduction" href={ApiPage.Introduction} icon={mdiNoteOutline} activeIcon={mdiNote} />
|
||||
<NavbarItem
|
||||
@@ -83,7 +82,7 @@
|
||||
</div>
|
||||
</AppShellSidebar>
|
||||
|
||||
<PageContent class="mx-auto w-full max-w-(--breakpoint-lg)">
|
||||
<PageContent class="max-w-(--breakpoint-lg) mx-auto w-full">
|
||||
{@render children?.()}
|
||||
</PageContent>
|
||||
</AppShell>
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import ApiPermission from '$lib/api/components/ApiPermission.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import ApiPermission from '$lib/components/ApiPermission.svelte';
|
||||
import { Alert, Card, CardBody, CardHeader, Code, CodeBlock, Heading, Link, Text } from '@immich/ui';
|
||||
import { mdiLightbulbOnOutline } from '@mdi/js';
|
||||
import { plaintext } from 'svelte-highlight/languages';
|
||||
@@ -34,7 +34,7 @@
|
||||
</Text>
|
||||
|
||||
<Card class="mt-2">
|
||||
<CardHeader class="py-1 bg-subtle">
|
||||
<CardHeader class="bg-subtle py-1">
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="font-bold">Type</div>
|
||||
<div class="font-bold">Value</div>
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import ApiTagSummary from '$lib/api/components/ApiTagSummary.svelte';
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import ApiTagSummary from '$lib/components/ApiTagSummary.svelte';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
|
||||
const { tags } = getOpenApi();
|
||||
</script>
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import ApiTagSummary from '$lib/api/components/ApiTagSummary.svelte';
|
||||
import { Text, Button, Heading, Stack, Icon } from '@immich/ui';
|
||||
import { type PageData } from './$types';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiTagSummary from '$lib/components/ApiTagSummary.svelte';
|
||||
import { Button, Heading, Icon, Stack, Text } from '@immich/ui';
|
||||
import { mdiArrowLeft, mdiArrowRight } from '@mdi/js';
|
||||
import { type PageData } from './$types';
|
||||
|
||||
type Props = {
|
||||
data: PageData;
|
||||
@@ -15,7 +15,7 @@
|
||||
</script>
|
||||
|
||||
<Stack gap={4}>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center justify-between">
|
||||
<Heading size="giant">API Endpoints</Heading>
|
||||
<Button href={ApiPage.Endpoints} color="secondary">View All</Button>
|
||||
</div>
|
||||
@@ -23,7 +23,7 @@
|
||||
<ApiTagSummary {tag} />
|
||||
|
||||
<Stack gap={4}>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
{#if tag.previous}
|
||||
<Button href={tag.previous.href} size="giant" fullWidth color="secondary" variant="outline">
|
||||
<Stack gap={2} class="w-full">
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
{#if tag.next}
|
||||
<Button href={tag.next.href} size="giant" fullWidth color="secondary" variant="outline">
|
||||
<div class="w-full flex flex-col place-items-end">
|
||||
<div class="flex w-full flex-col place-items-end">
|
||||
<Text size="small">Next</Text>
|
||||
<div class="flex items-center gap-2">
|
||||
<Text>{tag.next.name}</Text>
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import { ApiPage } from '$lib';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
|
||||
export const load = (async ({ params, parent }) => {
|
||||
await parent();
|
||||
+16
-16
@@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import ApiAdminRouteBadge from '$lib/api/components/ApiAdminRouteBadge.svelte';
|
||||
import ApiDeprecatedBadge from '$lib/api/components/ApiDeprecatedBadge.svelte';
|
||||
import ApiParams from '$lib/api/components/ApiParams.svelte';
|
||||
import ApiPermission from '$lib/api/components/ApiPermission.svelte';
|
||||
import ApiPlayground from '$lib/api/components/ApiPlayground.svelte';
|
||||
import ApiPublicRouteBadge from '$lib/api/components/ApiPublicRouteBadge.svelte';
|
||||
import ApiSchema from '$lib/api/components/ApiSchema.svelte';
|
||||
import ApiSharedLinkRouteBadge from '$lib/api/components/ApiSharedLinkRouteBadge.svelte';
|
||||
import { getEndpointColor, getOpenApi, isRef } from '$lib/api/services/open-api';
|
||||
import ApiAdminRouteBadge from '$lib/components/ApiAdminRouteBadge.svelte';
|
||||
import ApiDeprecatedBadge from '$lib/components/ApiDeprecatedBadge.svelte';
|
||||
import ApiParams from '$lib/components/ApiParams.svelte';
|
||||
import ApiPermission from '$lib/components/ApiPermission.svelte';
|
||||
import ApiPlayground from '$lib/components/ApiPlayground.svelte';
|
||||
import ApiPublicRouteBadge from '$lib/components/ApiPublicRouteBadge.svelte';
|
||||
import ApiSchema from '$lib/components/ApiSchema.svelte';
|
||||
import ApiSharedLinkRouteBadge from '$lib/components/ApiSharedLinkRouteBadge.svelte';
|
||||
import { getEndpointColor, getOpenApi, isRef } from '$lib/services/open-api';
|
||||
import LinkableHeading from '$lib/components/LinkableHeading.svelte';
|
||||
import { Button, Heading, Icon, Stack, Text } from '@immich/ui';
|
||||
import { mdiArrowLeft, mdiArrowRight } from '@mdi/js';
|
||||
@@ -28,8 +28,8 @@
|
||||
<Stack gap={8}>
|
||||
<section>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Heading size="large" tag="h1" class="flex flex-col md:flex-row md:justify-between md:items-center">
|
||||
<span class="group flex items-center gap-1 {endpoint.deprecated ? 'text-gray-500 italic' : ''}">
|
||||
<Heading size="large" tag="h1" class="flex flex-col md:flex-row md:items-center md:justify-between">
|
||||
<span class="group flex items-center gap-1 {endpoint.deprecated ? 'italic text-gray-500' : ''}">
|
||||
<span class="flex gap-2">
|
||||
<span class={getEndpointColor(endpoint)}>{endpoint.method}</span>
|
||||
<span>{endpoint.route}</span>
|
||||
@@ -40,7 +40,7 @@
|
||||
{#if endpoint.description}
|
||||
<Text color="muted" fontWeight="bold">{endpoint.description}</Text>
|
||||
{/if}
|
||||
<div class="flex flex-col lg:flex-row gap-4 justify-between">
|
||||
<div class="flex flex-col justify-between gap-4 lg:flex-row">
|
||||
<div>
|
||||
{#if endpoint.deprecated}
|
||||
<span>
|
||||
@@ -66,7 +66,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="mt-4 border border-subtle" />
|
||||
<hr class="border-subtle mt-4 border" />
|
||||
</section>
|
||||
|
||||
{#if endpoint.params.length > 0}
|
||||
@@ -97,7 +97,7 @@
|
||||
<ApiSchema schema={fromRef(endpoint.responses[0].schema)} />
|
||||
{:else}
|
||||
<Heading tag="h2" size="small">Responses</Heading>
|
||||
<div class="px-4 flex gap-1">
|
||||
<div class="flex gap-1 px-4">
|
||||
{#each endpoint.responses as response, i (i)}
|
||||
<span>{response.status}</span>
|
||||
{#if response.status === 204 || !response.contentType}
|
||||
@@ -115,7 +115,7 @@
|
||||
<ApiPlayground {endpoint} />
|
||||
|
||||
<Stack gap={4}>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
{#if endpoint.previous}
|
||||
<Button href={endpoint.previous.href} size="giant" fullWidth color="secondary" variant="outline">
|
||||
<Stack gap={2} class="w-full">
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
{#if endpoint.next}
|
||||
<Button href={endpoint.next.href} size="giant" fullWidth color="secondary" variant="outline">
|
||||
<div class="w-full flex flex-col place-items-end">
|
||||
<div class="flex w-full flex-col place-items-end">
|
||||
<Text size="small">Next</Text>
|
||||
<div class="flex items-center gap-2">
|
||||
<Text>{tag.name}.{endpoint.next.operationId}</Text>
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import { ApiPage } from '$lib';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
|
||||
export const load = (async ({ params, parent }) => {
|
||||
await parent();
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import ApiPermission from '$lib/api/components/ApiPermission.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import ApiPermission from '$lib/components/ApiPermission.svelte';
|
||||
import { Alert, Heading, Link, Text } from '@immich/ui';
|
||||
import { mdiLightbulbOnOutline } from '@mdi/js';
|
||||
</script>
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import { Heading, Link, Text } from '@immich/ui';
|
||||
</script>
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import { Card, CardTitle } from '@immich/ui';
|
||||
|
||||
const { models } = getOpenApi();
|
||||
</script>
|
||||
|
||||
<ApiPageContent title="API Models" description="An overview of all the available API models">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
|
||||
{#each models as model (model.href)}
|
||||
<a href={model.href}>
|
||||
<Card color="secondary" class="">
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ApiSchema from '$lib/api/components/ApiSchema.svelte';
|
||||
import type { ApiModel } from '$lib/api/services/open-api';
|
||||
import ApiSchema from '$lib/components/ApiSchema.svelte';
|
||||
import type { ApiModel } from '$lib/services/open-api';
|
||||
import { Code, Heading } from '@immich/ui';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</script>
|
||||
|
||||
<Heading size="large" class="mb-2" tag="h1">
|
||||
<a href="#{model.name}" id={model.name} class="scroll-m-8 flex gap-2">
|
||||
<a href="#{model.name}" id={model.name} class="flex scroll-m-8 gap-2">
|
||||
<span>{model.name}</span>
|
||||
</a>
|
||||
</Heading>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import { Link, Text } from '@immich/ui';
|
||||
</script>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import ApiPageContent from '$lib/api/components/ApiPageContent.svelte';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { ApiPage } from '$lib';
|
||||
import ApiPageContent from '$lib/components/ApiPageContent.svelte';
|
||||
import { Code, CodeBlock, Constants, Heading, Link, Text } from '@immich/ui';
|
||||
import { bash } from 'svelte-highlight/languages';
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate, beforeNavigate } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { getOpenApi } from '$lib/api/services/open-api';
|
||||
import { ApiPage } from '$lib';
|
||||
import '$lib/app.css';
|
||||
import { ApiPage } from '$lib/utils/api';
|
||||
import { getOpenApi } from '$lib/services/open-api';
|
||||
import {
|
||||
asText,
|
||||
CommandPalette,
|
||||
@@ -1,5 +1,5 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { loadOpenApi } from '$lib/api/services/open-api';
|
||||
import { loadOpenApi } from '$lib/services/open-api';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const ssr = false;
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Header from '$common/components/Header.svelte';
|
||||
import PageContent from '$common/components/PageContent.svelte';
|
||||
import stackedBlocksUrl from '$lib/assets/img/stacked-blocks.svg';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import { AppShell, AppShellHeader, Button, Constants, Heading, Link, Logo, Text, VStack } from '@immich/ui';
|
||||
import { mdiCubeOutline, mdiOpenInNew } from '@mdi/js';
|
||||
</script>
|
||||
@@ -21,8 +21,8 @@
|
||||
/>
|
||||
</AppShellHeader>
|
||||
|
||||
<PageContent class="mx-auto w-full max-w-(--breakpoint-lg)">
|
||||
<VStack gap={8} class="mt-4 lg:mt-16 text-center">
|
||||
<PageContent class="max-w-(--breakpoint-lg) mx-auto w-full">
|
||||
<VStack gap={8} class="mt-4 text-center lg:mt-16">
|
||||
<Logo size="giant" variant="logo" class="border" />
|
||||
<div class="flex flex-col gap-2">
|
||||
<Heading size="title" tag="h1">Immich API</Heading>
|
||||
@@ -0,0 +1,3 @@
|
||||
# allow crawling everything by default
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,28 @@
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import dotenv from 'dotenv';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
dotenv.config({ path: '../../.env' });
|
||||
|
||||
process.env.PUBLIC_IMMICH_SPEC_URL = process.env.PUBLIC_IMMICH_SPEC_URL || 'https://docs.immich.app/openapi.json';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
fallback: 'index.html',
|
||||
}),
|
||||
alias: {
|
||||
$common: '../common',
|
||||
'$common/*': '../../common/*',
|
||||
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
|
||||
'@mdi/js': resolve('./node_modules/@mdi/js'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -11,9 +11,9 @@
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
server: {
|
||||
fs: {
|
||||
// Allow serving files from one level up to the project root
|
||||
allow: ['../../common'],
|
||||
},
|
||||
},
|
||||
test: {
|
||||
expect: { requireAssertions: true },
|
||||
environment: 'node',
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
bun.lock
|
||||
bun.lockb
|
||||
|
||||
# Miscellaneous
|
||||
/static/
|
||||
|
||||
# wrangler generated files
|
||||
worker-configuration.d.ts
|
||||
|
||||
/.svelte-kit
|
||||
/build
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://buy.immich.app/
|
||||
|
||||
Support Immich by buying a product key.
|
||||
@@ -0,0 +1,37 @@
|
||||
import js from '@eslint/js';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
...svelte.configs.prettier,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte', '**/*.svelte.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser,
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/'],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "buy.immich.app",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite dev",
|
||||
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
|
||||
"preview": "vite preview",
|
||||
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"sync": "svelte-kit sync",
|
||||
"test": "vitest",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.4.0",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@sveltejs/adapter-static": "^3.0.9",
|
||||
"@sveltejs/kit": "^2.43.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@types/node": "^22",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.12.4",
|
||||
"globals": "^16.4.0",
|
||||
"playwright": "^1.55.1",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.1",
|
||||
"vite": "^7.1.7",
|
||||
"vitest": "^3.2.4",
|
||||
"vitest-browser-svelte": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@immich/ui": "^0.35.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"svelte-highlight": "^7.8.4"
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -21,7 +21,7 @@
|
||||
<meta name="twitter:image" content="/img/social-preview.png" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" class="dark bg-light text-dark">
|
||||
<body data-sveltekit-preload-data="hover" class="bg-light text-dark dark">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('sum test', () => {
|
||||
it('adds 1 + 2 to equal 3', () => {
|
||||
expect(1 + 2).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
@import '@immich/ui/theme/default.css';
|
||||
@import '$common/theme/theme.css';
|
||||
@source '../../node_modules/@immich/ui/dist';
|
||||
@source '../../../../common';
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getRedirectUrl } from '$lib/utils/license';
|
||||
import { getRedirectUrl } from '$lib';
|
||||
import { Button, Card, CardBody, CardFooter, CardHeader, CardTitle, Stack, Text } from '@immich/ui';
|
||||
import { mdiCheckCircleOutline, mdiContentCopy } from '@mdi/js';
|
||||
|
||||
@@ -33,16 +33,16 @@
|
||||
<CardBody>
|
||||
<Stack gap={4}>
|
||||
<div>
|
||||
<div class="bg-primary/10 py-2 px-6 rounded-lg">
|
||||
<div class="bg-primary/10 rounded-lg px-6 py-2">
|
||||
<Text color="primary">{productKey}</Text>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<div class="w-full grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<div class="grid w-full grid-cols-1 gap-2 md:grid-cols-2">
|
||||
<Button href={getRedirectUrl(productKey, 'https://my.immich.app')} fullWidth size="medium">Activate</Button>
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="flex items-center justify-center">
|
||||
{#if clipboardStatus === 'error'}
|
||||
<Text color="danger">Unable to copy to clipboard</Text>
|
||||
{:else if clipboardStatus === 'success'}
|
||||
+22
-22
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import FullPageLayout from '$lib/layouts/FullPageLayout.svelte';
|
||||
import { getCallbackUrl, ImmichLicense } from '$lib/utils/license';
|
||||
import FullPageLayout from '$common/components/FullPageLayout.svelte';
|
||||
import { getCallbackUrl, ImmichLicense } from '$lib';
|
||||
import { Button, Heading, Icon, Logo, Stack, SupporterBadge, Text } from '@immich/ui';
|
||||
import { mdiAccount, mdiCheckCircleOutline, mdiServer } from '@mdi/js';
|
||||
|
||||
@@ -22,19 +22,19 @@
|
||||
<section>
|
||||
<Stack gap={4}>
|
||||
<div
|
||||
class="gradiant-bg dark:bg-gray-900 border border-gray-300 dark:border-gray-800 rounded-2xl w-full hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 p-6"
|
||||
class="gradiant-bg hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 w-full rounded-2xl border border-gray-300 p-6 dark:border-gray-800 dark:bg-gray-900"
|
||||
>
|
||||
<div class="">
|
||||
<div class="w-full flex place-items-center place-content-center">
|
||||
<div class="flex w-full place-content-center place-items-center">
|
||||
<img
|
||||
src="https://imgproxy.fourthwall.com/DuRc1wOto_bXVrM-sqTsf17VxnxBXUFEQ5F_PT6sphQ/w:1920/sm:1/enc/CqJpqXXfIjg43qs5/dYp-7fXLhSxgi23i/vXvGKj_Wr12gyskx/XpH5YYXnecOLkEZD/Df2Y8QRXm5WNGa0M/tTxj3yhuyZNfwLfh/HIAL4muGjJJgH77a/m4-0PI8wYVzKDLfL/aKzTzjksdRXbrDIs/C3FH6IfFkB64QwBV/I4q06GTK8g4kyH_x/R_0WQk6JhX0eicl8/yzswcthmPd9IwLY-/daD-bA"
|
||||
alt="Immich Retro Disk"
|
||||
class="w-full md:w-1/2 aspect-square object-cover rounded-lg"
|
||||
class="aspect-square w-full rounded-lg object-cover md:w-1/2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-1 flex flex-col sm:flex-row gap-4 sm:justify-between sm:min-w-0 mt-6 sm:place-items-center"
|
||||
class="mt-6 flex flex-1 flex-col gap-4 sm:min-w-0 sm:flex-row sm:place-items-center sm:justify-between"
|
||||
>
|
||||
<div class="flex flex-col gap-2 sm:min-w-0 sm:flex-1">
|
||||
<p class="text-primary retro-font text-4xl">Retro Disk</p>
|
||||
@@ -48,7 +48,7 @@
|
||||
</Stack>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:items-end sm:justify-start sm:flex-shrink-0">
|
||||
<div class="flex flex-col sm:flex-shrink-0 sm:items-end sm:justify-start">
|
||||
<Heading size="giant" class="text-6xl sm:whitespace-nowrap">$25</Heading>
|
||||
<Text class="sm:whitespace-nowrap">+ shipping</Text>
|
||||
</div>
|
||||
@@ -61,21 +61,21 @@
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<div class="flex gap-4 justify-between flex-wrap lg:flex-nowrap">
|
||||
<div class="flex flex-wrap justify-between gap-4 lg:flex-nowrap">
|
||||
<!-- SERVER -->
|
||||
<div
|
||||
class="flex flex-col gap-4 border border-gray-300 dark:border-gray-800 w-full p-4 md:p-8 rounded-2xl bg-gray-100 dark:bg-gray-900 hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 transition-all"
|
||||
class="hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 flex w-full flex-col gap-4 rounded-2xl border border-gray-300 bg-gray-100 p-4 transition-all md:p-8 dark:border-gray-800 dark:bg-gray-900"
|
||||
>
|
||||
<div class="flex flex-col sm:flex-row gap-4 sm:items-start lg:flex-col lg:items-start">
|
||||
<div class="flex-shrink-0 w-full sm:w-32 md:w-48 lg:w-full">
|
||||
<div class="flex flex-col gap-1 text-primary items-start">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start lg:flex-col lg:items-start">
|
||||
<div class="w-full flex-shrink-0 sm:w-32 md:w-48 lg:w-full">
|
||||
<div class="text-primary flex flex-col items-start gap-1">
|
||||
<Icon icon={mdiServer} size="56" />
|
||||
<Text fontWeight="bold" size="large">Server</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col sm:flex-row gap-4 sm:justify-between sm:min-w-0 lg:flex-col lg:gap-8">
|
||||
<Stack gap={1} class="sm:flex-1 sm:min-w-0">
|
||||
<div class="flex flex-1 flex-col gap-4 sm:min-w-0 sm:flex-row sm:justify-between lg:flex-col lg:gap-8">
|
||||
<Stack gap={1} class="sm:min-w-0 sm:flex-1">
|
||||
{#each serverFeatures as feature, index (index)}
|
||||
<div class="grid grid-cols-[36px_auto] items-center">
|
||||
<Icon icon={mdiCheckCircleOutline} size="24" class="text-success" />
|
||||
@@ -84,7 +84,7 @@
|
||||
{/each}
|
||||
</Stack>
|
||||
|
||||
<div class="flex flex-col sm:items-end sm:justify-start sm:flex-shrink-0 lg:items-start">
|
||||
<div class="flex flex-col sm:flex-shrink-0 sm:items-end sm:justify-start lg:items-start">
|
||||
<Heading size="giant" class="text-6xl sm:whitespace-nowrap">$100</Heading>
|
||||
<Text class="sm:whitespace-nowrap">per server</Text>
|
||||
</div>
|
||||
@@ -96,18 +96,18 @@
|
||||
|
||||
<!-- INDIVIDUAL -->
|
||||
<div
|
||||
class="flex flex-col gap-4 border border-gray-300 dark:border-gray-800 w-full p-4 md:p-8 rounded-2xl bg-gray-100 dark:bg-gray-900 hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 transition-all"
|
||||
class="hover:bg-immich-primary/10 dark:hover:bg-immich-primary/20 flex w-full flex-col gap-4 rounded-2xl border border-gray-300 bg-gray-100 p-4 transition-all md:p-8 dark:border-gray-800 dark:bg-gray-900"
|
||||
>
|
||||
<div class="flex flex-col sm:flex-row gap-4 sm:items-start lg:flex-col lg:items-start">
|
||||
<div class="flex-shrink-0 w-full sm:w-32 md:w-48 lg:w-full">
|
||||
<div class="flex flex-col gap-1 text-primary items-start">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start lg:flex-col lg:items-start">
|
||||
<div class="w-full flex-shrink-0 sm:w-32 md:w-48 lg:w-full">
|
||||
<div class="text-primary flex flex-col items-start gap-1">
|
||||
<Icon icon={mdiAccount} size="56" />
|
||||
<Text fontWeight="bold" size="large">Individual</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex flex-col sm:flex-row gap-4 sm:justify-between sm:min-w-0 lg:flex-col lg:gap-8">
|
||||
<Stack gap={1} class="sm:flex-1 sm:min-w-0">
|
||||
<div class="flex flex-1 flex-col gap-4 sm:min-w-0 sm:flex-row sm:justify-between lg:flex-col lg:gap-8">
|
||||
<Stack gap={1} class="sm:min-w-0 sm:flex-1">
|
||||
{#each userFeatures as feature, index (index)}
|
||||
<div class="grid grid-cols-[36px_auto] items-center">
|
||||
<Icon icon={mdiCheckCircleOutline} size="24" class="text-success" />
|
||||
@@ -116,7 +116,7 @@
|
||||
{/each}
|
||||
</Stack>
|
||||
|
||||
<div class="flex flex-col sm:items-end sm:justify-start sm:flex-shrink-0 lg:items-start">
|
||||
<div class="flex flex-col sm:flex-shrink-0 sm:items-end sm:justify-start lg:items-start">
|
||||
<Heading size="giant" class="text-6xl sm:whitespace-nowrap">$25</Heading>
|
||||
<Text class="sm:whitespace-nowrap">per user</Text>
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCallbackUrl, ImmichLicense } from '$lib/utils/license';
|
||||
import { getCallbackUrl, ImmichLicense } from '$lib';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import DefaultPageLayout from '$common/components/DefaultPageLayout.svelte';
|
||||
import { getPaymentStatus, getRedirectUrl, PurchaseStatus, type PaymentStatusResponseDto } from '$lib';
|
||||
import LicenseKey from '$lib/components/LicenseKey.svelte';
|
||||
import DefaultPageLayout from '$lib/layouts/DefaultPageLayout.svelte';
|
||||
import { getPaymentStatus, getRedirectUrl, PurchaseStatus, type PaymentStatusResponseDto } from '$lib/utils/license';
|
||||
import { Alert, Button, Card, CardBody, Heading, LoadingSpinner, Stack, Text } from '@immich/ui';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageData } from './$types';
|
||||
@@ -0,0 +1,3 @@
|
||||
# allow crawling everything by default
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,28 @@
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import dotenv from 'dotenv';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
dotenv.config({ path: '../../.env' });
|
||||
|
||||
process.env.PUBLIC_IMMICH_PAY_HOST = process.env.PUBLIC_IMMICH_PAY_HOST || 'https://pay.futo.org';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
fallback: 'index.html',
|
||||
}),
|
||||
alias: {
|
||||
$common: '../common',
|
||||
'$common/*': '../../common/*',
|
||||
'@immich/ui': resolve('./node_modules/@immich/ui/dist'),
|
||||
'@mdi/js': resolve('./node_modules/@mdi/js'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
server: {
|
||||
fs: {
|
||||
// Allow serving files from one level up to the project root
|
||||
allow: ['../../common'],
|
||||
},
|
||||
},
|
||||
test: {
|
||||
expect: { requireAssertions: true },
|
||||
environment: 'node',
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
bun.lock
|
||||
bun.lockb
|
||||
|
||||
# Miscellaneous
|
||||
/static/
|
||||
|
||||
# wrangler generated files
|
||||
worker-configuration.d.ts
|
||||
|
||||
/.svelte-kit
|
||||
/build
|
||||
@@ -0,0 +1,3 @@
|
||||
# https://datasets.immich.app/
|
||||
|
||||
Asset collection website
|
||||
-3797
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
import js from '@eslint/js';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
...svelte.configs.prettier,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte', '**/*.svelte.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser,
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/', 'backend/worker-configuration.d.ts'],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "datasets.immich.app",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite dev",
|
||||
"build": "npm run sync && vite build && node ../../bin/postbuild.js",
|
||||
"preview": "vite preview",
|
||||
"check": "npm run sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"sync": "svelte-kit sync",
|
||||
"test": "vitest",
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:fix": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.4.0",
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@sveltejs/adapter-static": "^3.0.9",
|
||||
"@sveltejs/kit": "^2.43.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@types/node": "^22",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.12.4",
|
||||
"globals": "^16.4.0",
|
||||
"playwright": "^1.55.1",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.44.1",
|
||||
"vite": "^7.1.7",
|
||||
"vitest": "^3.2.4",
|
||||
"vitest-browser-svelte": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@immich/ui": "^0.35.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"exifreader": "^4.32.0",
|
||||
"p-limit": "^7.1.1",
|
||||
"svelte-highlight": "^7.8.4",
|
||||
"svelte-turnstile": "^0.11.0"
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -21,7 +21,7 @@
|
||||
<meta name="twitter:image" content="/img/social-preview.png" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" class="dark bg-light text-dark">
|
||||
<body data-sveltekit-preload-data="hover" class="bg-light text-dark dark">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('sum test', () => {
|
||||
it('adds 1 + 2 to equal 3', () => {
|
||||
expect(1 + 2).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
@import '@immich/ui/theme/default.css';
|
||||
@import '$common/theme/theme.css';
|
||||
@source '../../node_modules/@immich/ui/dist';
|
||||
@source '../../../../common';
|
||||
+1
-1
@@ -157,7 +157,7 @@
|
||||
{#if dragStartTarget}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="fixed inset-0 flex h-full w-full flex-col items-center justify-center bg-gray-100/90 text-immich-dark-gray dark:bg-neutral-900/90 dark:text-immich-gray z-10"
|
||||
class="text-immich-dark-gray dark:text-immich-gray fixed inset-0 z-10 flex h-full w-full flex-col items-center justify-center bg-gray-100/90 dark:bg-neutral-900/90"
|
||||
transition:fade={{ duration: 250 }}
|
||||
ondragover={onDragOver}
|
||||
>
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Button, Modal, ModalBody, ModalFooter } from '@immich/ui';
|
||||
import { Button, Modal, ModalBody, ModalFooter, Text } from '@immich/ui';
|
||||
|
||||
type Props = { onClose: () => void };
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<Modal title="Upload failed" size="medium" {onClose}>
|
||||
<ModalBody>
|
||||
<p>There was an error while trying to upload your dataset. Please try again later.</p>
|
||||
<Text>There was an error while trying to upload your dataset. Please try again later.</Text>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button onclick={onClose} shape="round">Close</Button>
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
import { Button, Checkbox, Field, HelperText, Input, Modal, ModalBody, ModalFooter, Stack } from '@immich/ui';
|
||||
import pLimit from 'p-limit';
|
||||
import { Turnstile } from 'svelte-turnstile';
|
||||
import type { UploadableAssets } from '../../../apps/datasets.immich.app/types/upload-manager';
|
||||
import type { UploadableAssets } from '../../../types/upload-manager';
|
||||
|
||||
interface Props {
|
||||
onClose: (event: { cancelled: boolean; failedIds: string[] }) => void;
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { mdiAccount, mdiCameraBurst, mdiImage, mdiPanoramaVariant, mdiPencil, mdiSphere } from '@mdi/js';
|
||||
import EXIFReader from 'exifreader';
|
||||
import type { ExifDatasetMetadata } from '../../../types/metadata';
|
||||
import type { UploadableAssets } from '../../../types/upload-manager';
|
||||
import type { ExifDatasetMetadata } from '../../types/metadata';
|
||||
import type { UploadableAssets } from '../../types/upload-manager';
|
||||
|
||||
export type AssetType = ExifDatasetMetadata['captureType'];
|
||||
export const AssetTypeNames: Record<AssetType, string> = {
|
||||
+2
-2
@@ -3,9 +3,9 @@
|
||||
import { initializeTheme } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
type Props = {
|
||||
children?: Snippet;
|
||||
}
|
||||
};
|
||||
|
||||
let { children }: Props = $props();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user