mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
Merge branch 'main' into rknn-toolkit-lite2
This commit is contained in:
commit
4e42fbc091
@ -1,4 +1,4 @@
|
|||||||
FROM node:22.12.0-alpine3.20@sha256:96cc8323e25c8cc6ddcb8b965e135cfd57846e8003ec0d7bcec16c5fd5f6d39f AS core
|
FROM node:22.13.0-alpine3.20@sha256:db8dcb90326a0116375414e9a7c068a6b87a4422b7da37b5c6cd026f7c7835d3 AS core
|
||||||
|
|
||||||
WORKDIR /usr/src/open-api/typescript-sdk
|
WORKDIR /usr/src/open-api/typescript-sdk
|
||||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||||
|
@ -71,6 +71,7 @@ services:
|
|||||||
- ../web:/usr/src/app
|
- ../web:/usr/src/app
|
||||||
- ../i18n:/usr/src/i18n
|
- ../i18n:/usr/src/i18n
|
||||||
- ../open-api/:/usr/src/open-api/
|
- ../open-api/:/usr/src/open-api/
|
||||||
|
# - ../../ui:/usr/ui
|
||||||
- /usr/src/app/node_modules
|
- /usr/src/app/node_modules
|
||||||
ulimits:
|
ulimits:
|
||||||
nofile:
|
nofile:
|
||||||
|
@ -63,6 +63,17 @@ If you only want to do web development connected to an existing, remote backend,
|
|||||||
IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev
|
IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `@immich/ui`
|
||||||
|
|
||||||
|
To see local changes to `@immich/ui` in Immich, do the following:
|
||||||
|
|
||||||
|
1. Install `@immich/ui` as a sibling to `immich/`, for example `/home/user/immich` and `/home/user/ui`
|
||||||
|
1. Build the `@immich/ui` project via `npm run build`
|
||||||
|
1. Uncomment the corresponding volume in web service of the `docker/docker-compose.dev.yaml` file (`../../ui:/usr/ui`)
|
||||||
|
1. Uncomment the corresponding alias in the `web/vite.config.js` file (`'@immich/ui': path.resolve(\_\_dirname, '../../ui')`)
|
||||||
|
1. Start up the stack via `make dev`
|
||||||
|
1. After making changes in `@immich/ui`, rebuild it (`npm run build`)
|
||||||
|
|
||||||
### Mobile app
|
### Mobile app
|
||||||
|
|
||||||
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x to be installed on your system.
|
The mobile app `(/mobile)` will required Flutter toolchain 3.13.x to be installed on your system.
|
||||||
|
@ -148,8 +148,9 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
|
|
||||||
## Machine Learning
|
## Machine Learning
|
||||||
|
|
||||||
|
|
||||||
| Variable | Description | Default | Containers |
|
| Variable | Description | Default | Containers |
|
||||||
| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------: | :--------------- |
|
| :---------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :-----------------------------: | :--------------- |
|
||||||
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
| `MACHINE_LEARNING_MODEL_TTL` | Inactivity time (s) before a model is unloaded (disabled if \<= 0) | `300` | machine learning |
|
||||||
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
| `MACHINE_LEARNING_MODEL_TTL_POLL_S` | Interval (s) between checks for the model TTL (disabled if \<= 0) | `10` | machine learning |
|
||||||
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
| `MACHINE_LEARNING_CACHE_FOLDER` | Directory where models are downloaded | `/cache` | machine learning |
|
||||||
@ -159,8 +160,10 @@ Redis (Sentinel) URL example JSON before encoding:
|
|||||||
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
| `MACHINE_LEARNING_WORKERS`<sup>\*2</sup> | Number of worker processes to spawn | `1` | machine learning |
|
||||||
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup> | HTTP Keep-alive time in seconds | `2` | machine learning |
|
| `MACHINE_LEARNING_HTTP_KEEPALIVE_TIMEOUT_S`<sup>\*3</sup> | HTTP Keep-alive time in seconds | `2` | machine learning |
|
||||||
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO) | machine learning |
|
| `MACHINE_LEARNING_WORKER_TIMEOUT` | Maximum time (s) of unresponsiveness before a worker is killed | `120` (`300` if using OpenVINO) | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__CLIP` | Name of a CLIP model to be preloaded and kept in cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL` | Name of the textual CLIP model to be preloaded and kept in cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION` | Name of a facial recognition model to be preloaded and kept in cache | | machine learning |
|
| `MACHINE_LEARNING_PRELOAD__CLIP__VISUAL` | Name of the visual CLIP model to be preloaded and kept in cache | | machine learning |
|
||||||
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION` | Name of the recognition portion of the facial recognition model to be preloaded and kept in cache | | machine learning |
|
||||||
|
| `MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION` | Name of the detection portion of the facial recognition model to be preloaded and kept in cache | | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
| `MACHINE_LEARNING_ANN` | Enable ARM-NN hardware acceleration if supported | `True` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
| `MACHINE_LEARNING_ANN_FP16_TURBO` | Execute operations in FP16 precision: increasing speed, reducing precision (applies only to ARM-NN) | `False` | machine learning |
|
||||||
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
| `MACHINE_LEARNING_ANN_TUNING_LEVEL` | ARM-NN GPU tuning level (1: rapid, 2: normal, 3: exhaustive) | `2` | machine learning |
|
||||||
|
@ -13,8 +13,8 @@ import request from 'supertest';
|
|||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const authServer = {
|
const authServer = {
|
||||||
internal: 'http://auth-server:3000',
|
internal: 'http://auth-server:2286',
|
||||||
external: 'http://127.0.0.1:3000',
|
external: 'http://127.0.0.1:2286',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redirect';
|
const mobileOverrideRedirectUri = 'https://photos.immich.app/oauth/mobile-redirect';
|
||||||
|
@ -51,7 +51,7 @@ const setup = async () => {
|
|||||||
const { privateKey, publicKey } = await generateKeyPair('RS256');
|
const { privateKey, publicKey } = await generateKeyPair('RS256');
|
||||||
|
|
||||||
const redirectUris = ['http://127.0.0.1:2285/auth/login', 'https://photos.immich.app/oauth/mobile-redirect'];
|
const redirectUris = ['http://127.0.0.1:2285/auth/login', 'https://photos.immich.app/oauth/mobile-redirect'];
|
||||||
const port = 3000;
|
const port = 2286;
|
||||||
const host = '0.0.0.0';
|
const host = '0.0.0.0';
|
||||||
const oidc = new Provider(`http://${host}:${port}`, {
|
const oidc = new Provider(`http://${host}:${port}`, {
|
||||||
renderError: async (ctx, out, error) => {
|
renderError: async (ctx, out, error) => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
ARG DEVICE=cpu
|
ARG DEVICE=cpu
|
||||||
|
|
||||||
FROM python:3.11-bookworm@sha256:b337e1fd27dbacda505219f713789bf82766694095876769ea10c2d34b4f470b AS builder-cpu
|
FROM python:3.11-bookworm@sha256:f997d3f71b7dcff3f937703c02861437f2b41a94e1ddbd1b5fa357ee99f5cce4 AS builder-cpu
|
||||||
|
|
||||||
FROM builder-cpu AS builder-openvino
|
FROM builder-cpu AS builder-openvino
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ RUN python3 -m venv /opt/venv
|
|||||||
COPY poetry.lock pyproject.toml ./
|
COPY poetry.lock pyproject.toml ./
|
||||||
RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev
|
RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev
|
||||||
|
|
||||||
FROM python:3.11-slim-bookworm@sha256:873952659a04188d2a62d5f7e30fd673d2559432a847a8ad5fcaf9cbd085e9ed AS prod-cpu
|
FROM python:3.11-slim-bookworm@sha256:6ed5bff4d7d377e2a27d9285553b8c21cfccc4f00881de1b24c9bc8d90016e82 AS prod-cpu
|
||||||
|
|
||||||
FROM prod-cpu AS prod-openvino
|
FROM prod-cpu AS prod-openvino
|
||||||
|
|
||||||
|
@ -14,9 +14,29 @@ from uvicorn import Server
|
|||||||
from uvicorn.workers import UvicornWorker
|
from uvicorn.workers import UvicornWorker
|
||||||
|
|
||||||
|
|
||||||
|
class ClipSettings(BaseModel):
|
||||||
|
textual: str | None = None
|
||||||
|
visual: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class FacialRecognitionSettings(BaseModel):
|
||||||
|
recognition: str | None = None
|
||||||
|
detection: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class PreloadModelData(BaseModel):
|
class PreloadModelData(BaseModel):
|
||||||
clip: str | None = None
|
clip_fallback: str | None = os.getenv("MACHINE_LEARNING_PRELOAD__CLIP", None)
|
||||||
facial_recognition: str | None = None
|
facial_recognition_fallback: str | None = os.getenv("MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION", None)
|
||||||
|
if clip_fallback is not None:
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL"] = clip_fallback
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__VISUAL"] = clip_fallback
|
||||||
|
del os.environ["MACHINE_LEARNING_PRELOAD__CLIP"]
|
||||||
|
if facial_recognition_fallback is not None:
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION"] = facial_recognition_fallback
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION"] = facial_recognition_fallback
|
||||||
|
del os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"]
|
||||||
|
clip: ClipSettings = ClipSettings()
|
||||||
|
facial_recognition: FacialRecognitionSettings = FacialRecognitionSettings()
|
||||||
|
|
||||||
|
|
||||||
class MaxBatchSize(BaseModel):
|
class MaxBatchSize(BaseModel):
|
||||||
|
@ -75,21 +75,46 @@ async def lifespan(_: FastAPI) -> AsyncGenerator[None, None]:
|
|||||||
|
|
||||||
|
|
||||||
async def preload_models(preload: PreloadModelData) -> None:
|
async def preload_models(preload: PreloadModelData) -> None:
|
||||||
log.info(f"Preloading models: {preload}")
|
log.info(f"Preloading models: clip:{preload.clip} facial_recognition:{preload.facial_recognition}")
|
||||||
if preload.clip is not None:
|
|
||||||
model = await model_cache.get(preload.clip, ModelType.TEXTUAL, ModelTask.SEARCH)
|
if preload.clip.textual is not None:
|
||||||
|
model = await model_cache.get(preload.clip.textual, ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
await load(model)
|
await load(model)
|
||||||
|
|
||||||
model = await model_cache.get(preload.clip, ModelType.VISUAL, ModelTask.SEARCH)
|
if preload.clip.visual is not None:
|
||||||
|
model = await model_cache.get(preload.clip.visual, ModelType.VISUAL, ModelTask.SEARCH)
|
||||||
await load(model)
|
await load(model)
|
||||||
|
|
||||||
if preload.facial_recognition is not None:
|
if preload.facial_recognition.detection is not None:
|
||||||
model = await model_cache.get(preload.facial_recognition, ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)
|
model = await model_cache.get(
|
||||||
|
preload.facial_recognition.detection,
|
||||||
|
ModelType.DETECTION,
|
||||||
|
ModelTask.FACIAL_RECOGNITION,
|
||||||
|
)
|
||||||
await load(model)
|
await load(model)
|
||||||
|
|
||||||
model = await model_cache.get(preload.facial_recognition, ModelType.RECOGNITION, ModelTask.FACIAL_RECOGNITION)
|
if preload.facial_recognition.recognition is not None:
|
||||||
|
model = await model_cache.get(
|
||||||
|
preload.facial_recognition.recognition,
|
||||||
|
ModelType.RECOGNITION,
|
||||||
|
ModelTask.FACIAL_RECOGNITION,
|
||||||
|
)
|
||||||
await load(model)
|
await load(model)
|
||||||
|
|
||||||
|
if preload.clip_fallback is not None:
|
||||||
|
log.warning(
|
||||||
|
"Deprecated env variable: 'MACHINE_LEARNING_PRELOAD__CLIP'. "
|
||||||
|
"Use 'MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL' and "
|
||||||
|
"'MACHINE_LEARNING_PRELOAD__CLIP__VISUAL' instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
if preload.facial_recognition_fallback is not None:
|
||||||
|
log.warning(
|
||||||
|
"Deprecated env variable: 'MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION'. "
|
||||||
|
"Use 'MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION' and "
|
||||||
|
"'MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION' instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_state() -> Iterator[None]:
|
def update_state() -> Iterator[None]:
|
||||||
global active_requests, last_called
|
global active_requests, last_called
|
||||||
|
@ -750,11 +750,13 @@ class TestCache:
|
|||||||
await model_cache.get("test_model_name", ModelType.TEXTUAL, ModelTask.SEARCH)
|
await model_cache.get("test_model_name", ModelType.TEXTUAL, ModelTask.SEARCH)
|
||||||
|
|
||||||
async def test_preloads_clip_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
async def test_preloads_clip_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__CLIP"] = "ViT-B-32__openai"
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL"] = "ViT-B-32__openai"
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__VISUAL"] = "ViT-B-32__openai"
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
assert settings.preload is not None
|
assert settings.preload is not None
|
||||||
assert settings.preload.clip == "ViT-B-32__openai"
|
assert settings.preload.clip.textual == "ViT-B-32__openai"
|
||||||
|
assert settings.preload.clip.visual == "ViT-B-32__openai"
|
||||||
|
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
monkeypatch.setattr("app.main.model_cache", model_cache)
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
@ -771,11 +773,13 @@ class TestCache:
|
|||||||
async def test_preloads_facial_recognition_models(
|
async def test_preloads_facial_recognition_models(
|
||||||
self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock
|
self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock
|
||||||
) -> None:
|
) -> None:
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"] = "buffalo_s"
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION"] = "buffalo_s"
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION"] = "buffalo_s"
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
assert settings.preload is not None
|
assert settings.preload is not None
|
||||||
assert settings.preload.facial_recognition == "buffalo_s"
|
assert settings.preload.facial_recognition.detection == "buffalo_s"
|
||||||
|
assert settings.preload.facial_recognition.recognition == "buffalo_s"
|
||||||
|
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
monkeypatch.setattr("app.main.model_cache", model_cache)
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
@ -790,13 +794,17 @@ class TestCache:
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def test_preloads_all_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
async def test_preloads_all_models(self, monkeypatch: MonkeyPatch, mock_get_model: mock.Mock) -> None:
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__CLIP"] = "ViT-B-32__openai"
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__TEXTUAL"] = "ViT-B-32__openai"
|
||||||
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION"] = "buffalo_s"
|
os.environ["MACHINE_LEARNING_PRELOAD__CLIP__VISUAL"] = "ViT-B-32__openai"
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__RECOGNITION"] = "buffalo_s"
|
||||||
|
os.environ["MACHINE_LEARNING_PRELOAD__FACIAL_RECOGNITION__DETECTION"] = "buffalo_s"
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
assert settings.preload is not None
|
assert settings.preload is not None
|
||||||
assert settings.preload.clip == "ViT-B-32__openai"
|
assert settings.preload.clip.visual == "ViT-B-32__openai"
|
||||||
assert settings.preload.facial_recognition == "buffalo_s"
|
assert settings.preload.clip.textual == "ViT-B-32__openai"
|
||||||
|
assert settings.preload.facial_recognition.recognition == "buffalo_s"
|
||||||
|
assert settings.preload.facial_recognition.detection == "buffalo_s"
|
||||||
|
|
||||||
model_cache = ModelCache()
|
model_cache = ModelCache()
|
||||||
monkeypatch.setattr("app.main.model_cache", model_cache)
|
monkeypatch.setattr("app.main.model_cache", model_cache)
|
||||||
|
6
machine-learning/poetry.lock
generated
6
machine-learning/poetry.lock
generated
@ -2498,13 +2498,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "2.10.4"
|
version = "2.10.5"
|
||||||
description = "Data validation using Python type hints"
|
description = "Data validation using Python type hints"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"},
|
{file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"},
|
||||||
{file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"},
|
{file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
android:resource="@drawable/notification_icon" />
|
android:resource="@drawable/notification_icon" />
|
||||||
|
|
||||||
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop"
|
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTask"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||||
@ -61,6 +61,34 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
|
||||||
|
<!--TODO:
|
||||||
|
Add this filter if you want to handle shared images-->
|
||||||
|
<intent-filter android:label="Upload to Immich">
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter android:label="Upload to Immich">
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!--TODO:
|
||||||
|
Add this filter if you want to handle shared videos-->
|
||||||
|
<intent-filter android:label="Upload to Immich">
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="video/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter android:label="Upload to Immich">
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="video/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
|
@ -657,5 +657,15 @@
|
|||||||
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
"viewer_stack_use_as_main_asset": "Use as Main Asset",
|
||||||
"viewer_unstack": "Un-Stack",
|
"viewer_unstack": "Un-Stack",
|
||||||
"wifi_name": "WiFi Name",
|
"wifi_name": "WiFi Name",
|
||||||
"your_wifi_name": "Your WiFi name"
|
"your_wifi_name": "Your WiFi name",
|
||||||
|
"upload": "Upload",
|
||||||
|
"uploading": "Uploading",
|
||||||
|
"shared_intent_upload_button_progress_text": "{} / {} Uploaded",
|
||||||
|
"enqueued": "Enqueued",
|
||||||
|
"not_selected": "Not selected",
|
||||||
|
"completed": "Completed",
|
||||||
|
"failed": "Failed",
|
||||||
|
"paused": "Paused",
|
||||||
|
"canceled": "Canceled",
|
||||||
|
"upload_to_immich": "Upload to Immich ({})"
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,13 @@ target 'Runner' do
|
|||||||
use_modular_headers!
|
use_modular_headers!
|
||||||
|
|
||||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
|
||||||
|
# share_handler addition start
|
||||||
|
target 'ShareExtension' do
|
||||||
|
inherit! :search_paths
|
||||||
|
pod "share_handler_ios_models", :path => ".symlinks/plugins/share_handler_ios/ios/Models"
|
||||||
|
end
|
||||||
|
# share_handler addition end
|
||||||
end
|
end
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
|
@ -82,9 +82,17 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- SAMKeychain (1.5.3)
|
- SAMKeychain (1.5.3)
|
||||||
- SDWebImage (5.19.4):
|
- SDWebImage (5.20.0):
|
||||||
- SDWebImage/Core (= 5.19.4)
|
- SDWebImage/Core (= 5.20.0)
|
||||||
- SDWebImage/Core (5.19.4)
|
- SDWebImage/Core (5.20.0)
|
||||||
|
- share_handler_ios (0.0.14):
|
||||||
|
- Flutter
|
||||||
|
- share_handler_ios/share_handler_ios_models (= 0.0.14)
|
||||||
|
- share_handler_ios_models
|
||||||
|
- share_handler_ios/share_handler_ios_models (0.0.14):
|
||||||
|
- Flutter
|
||||||
|
- share_handler_ios_models
|
||||||
|
- share_handler_ios_models (0.0.9)
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
@ -94,7 +102,7 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- SwiftyGif (5.4.5)
|
- SwiftyGif (5.4.5)
|
||||||
- Toast (4.0.0)
|
- Toast (4.1.1)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- wakelock_plus (0.0.1):
|
- wakelock_plus (0.0.1):
|
||||||
@ -123,6 +131,8 @@ DEPENDENCIES:
|
|||||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||||
|
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
|
||||||
|
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
@ -184,6 +194,10 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
photo_manager:
|
photo_manager:
|
||||||
:path: ".symlinks/plugins/photo_manager/ios"
|
:path: ".symlinks/plugins/photo_manager/ios"
|
||||||
|
share_handler_ios:
|
||||||
|
:path: ".symlinks/plugins/share_handler_ios/ios"
|
||||||
|
share_handler_ios_models:
|
||||||
|
:path: ".symlinks/plugins/share_handler_ios/ios/Models"
|
||||||
share_plus:
|
share_plus:
|
||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@ -222,15 +236,17 @@ SPEC CHECKSUMS:
|
|||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||||
SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
|
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
|
||||||
|
share_handler_ios: 6dd3a4ac5ca0d955274aec712ba0ecdcaf583e7c
|
||||||
|
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
|
||||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||||
|
|
||||||
PODFILE CHECKSUM: 2282844f7aed70427ae663932332dad1225156c8
|
PODFILE CHECKSUM: 03b7eead4ee77b9e778179eeb0f3b5513617451c
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */; };
|
||||||
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */; };
|
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */; };
|
||||||
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */; };
|
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
@ -16,8 +17,21 @@
|
|||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; };
|
D218389C4A4C4693F141F7D1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */; };
|
||||||
|
FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = FAC6F8902D287C890078CB2F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC6F8B52D287F120078CB2F /* ShareViewController.swift */; };
|
||||||
|
FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
FAC6F8982D287C890078CB2F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = FAC6F88F2D287C890078CB2F;
|
||||||
|
remoteInfo = ShareExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
@ -29,13 +43,26 @@
|
|||||||
name = "Embed Frameworks";
|
name = "Embed Frameworks";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed Foundation Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.release.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundServicePlugin.swift; sourceTree = "<group>"; };
|
65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundServicePlugin.swift; sourceTree = "<group>"; };
|
||||||
65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundSyncWorker.swift; sourceTree = "<group>"; };
|
65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundSyncWorker.swift; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
@ -49,9 +76,16 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
FA9973382CF6DF4B000EF859 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
FA9973382CF6DF4B000EF859 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||||
|
FAC6F8902D287C890078CB2F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
FAC6F8B12D287F120078CB2F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
FAC6F8B22D287F120078CB2F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
|
FAC6F8B42D287F120078CB2F /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||||
|
FAC6F8B52D287F120078CB2F /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||||
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
|
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -64,6 +98,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
FAC6F88D2D287C890078CB2F /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
3B6A31FED0FC846D6BD69BBC /* Pods_ShareExtension.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
@ -73,6 +115,9 @@
|
|||||||
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */,
|
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */,
|
||||||
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */,
|
E0E99CDC17B3EB7FA8BA2332 /* Pods-Runner.release.xcconfig */,
|
||||||
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */,
|
F7101BB0391A314774615E89 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */,
|
||||||
|
571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */,
|
||||||
|
B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */,
|
||||||
);
|
);
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -81,6 +126,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */,
|
886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */,
|
||||||
|
357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -110,6 +156,7 @@
|
|||||||
children = (
|
children = (
|
||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
|
FAC6F8B62D287F120078CB2F /* ShareExtension */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
0FB772A5B9601143383626CA /* Pods */,
|
0FB772A5B9601143383626CA /* Pods */,
|
||||||
1754452DD81DA6620E279E51 /* Frameworks */,
|
1754452DD81DA6620E279E51 /* Frameworks */,
|
||||||
@ -120,6 +167,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
97C146EE1CF9000F007C117D /* Immich-Debug.app */,
|
97C146EE1CF9000F007C117D /* Immich-Debug.app */,
|
||||||
|
FAC6F8902D287C890078CB2F /* ShareExtension.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -142,6 +190,17 @@
|
|||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
FAC6F8B62D287F120078CB2F /* ShareExtension */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FAC6F8B12D287F120078CB2F /* Info.plist */,
|
||||||
|
FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */,
|
||||||
|
FAC6F8B42D287F120078CB2F /* ShareExtension.entitlements */,
|
||||||
|
FAC6F8B52D287F120078CB2F /* ShareViewController.swift */,
|
||||||
|
);
|
||||||
|
path = ShareExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -155,6 +214,7 @@
|
|||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
|
FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
|
D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */,
|
||||||
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */,
|
6724EEB7D74949FA08581154 /* [CP] Copy Pods Resources */,
|
||||||
@ -162,12 +222,31 @@
|
|||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
FAC6F8992D287C890078CB2F /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */;
|
productReference = 97C146EE1CF9000F007C117D /* Immich-Debug.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
FAC6F88F2D287C890078CB2F /* ShareExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */,
|
||||||
|
FAC6F88C2D287C890078CB2F /* Sources */,
|
||||||
|
FAC6F88D2D287C890078CB2F /* Frameworks */,
|
||||||
|
FAC6F88E2D287C890078CB2F /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = ShareExtension;
|
||||||
|
productName = ShareExtension;
|
||||||
|
productReference = FAC6F8902D287C890078CB2F /* ShareExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@ -175,6 +254,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = YES;
|
BuildIndependentTargetsInParallel = YES;
|
||||||
|
LastSwiftUpdateCheck = 1600;
|
||||||
LastUpgradeCheck = 1510;
|
LastUpgradeCheck = 1510;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
@ -182,6 +262,9 @@
|
|||||||
CreatedOnToolsVersion = 7.3.1;
|
CreatedOnToolsVersion = 7.3.1;
|
||||||
LastSwiftMigration = 1100;
|
LastSwiftMigration = 1100;
|
||||||
};
|
};
|
||||||
|
FAC6F88F2D287C890078CB2F = {
|
||||||
|
CreatedOnToolsVersion = 16.0;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||||
@ -198,6 +281,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
97C146ED1CF9000F007C117D /* Runner */,
|
97C146ED1CF9000F007C117D /* Runner */,
|
||||||
|
FAC6F88F2D287C890078CB2F /* ShareExtension */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@ -214,6 +298,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
FAC6F88E2D287C890078CB2F /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
@ -233,6 +325,28 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
3BEF3D71D97E337D921C0EB5 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-ShareExtension-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */ = {
|
4044AF030EF7D8721844FFBA /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -318,8 +432,24 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
FAC6F88C2D287C890078CB2F /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
FAC6F8992D287C890078CB2F /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = FAC6F88F2D287C890078CB2F /* ShareExtension */;
|
||||||
|
targetProxy = FAC6F8982D287C890078CB2F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@ -337,6 +467,14 @@
|
|||||||
name = LaunchScreen.storyboard;
|
name = LaunchScreen.storyboard;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
FAC6F8B22D287F120078CB2F /* Base */,
|
||||||
|
);
|
||||||
|
name = MainInterface.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
@ -404,6 +542,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 187;
|
CURRENT_PROJECT_VERSION = 187;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -547,6 +686,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 187;
|
CURRENT_PROJECT_VERSION = 187;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -576,6 +716,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 187;
|
CURRENT_PROJECT_VERSION = 187;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -594,6 +735,129 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
FAC6F89C2D287C890078CB2F /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = F8A35EA3C3E01BD66AFDE0E5 /* Pods-ShareExtension.debug.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
FAC6F89D2D287C890078CB2F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 571EAA93D77181C7C98C2EA6 /* Pods-ShareExtension.release.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
FAC6F89E2D287C890078CB2F /* Profile */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B1FBA9EE014DE20271B0FE77 /* Pods-ShareExtension.profile.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
CUSTOM_GROUP_ID = group.app.immich.share;
|
||||||
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Profile;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@ -617,6 +881,16 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
FAC6F8A02D287C890078CB2F /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
FAC6F89C2D287C890078CB2F /* Debug */,
|
||||||
|
FAC6F89D2D287C890078CB2F /* Release */,
|
||||||
|
FAC6F89E2D287C890078CB2F /* Profile */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>AppGroupId</key>
|
||||||
|
<string>$(CUSTOM_GROUP_ID)</string>
|
||||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
<array>
|
<array>
|
||||||
<string>app.alextran.immich.backgroundFetch</string>
|
<string>app.alextran.immich.backgroundFetch</string>
|
||||||
@ -13,6 +15,24 @@
|
|||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>${PRODUCT_NAME}</string>
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>ShareHandler</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>public.file-url</string>
|
||||||
|
<string>public.image</string>
|
||||||
|
<string>public.text</string>
|
||||||
|
<string>public.movie</string>
|
||||||
|
<string>public.url</string>
|
||||||
|
<string>public.data</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@ -61,6 +81,17 @@
|
|||||||
<string>1.124.0</string>
|
<string>1.124.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>187</string>
|
<string>187</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
@ -73,6 +104,8 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<string>No</string>
|
||||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
@ -94,6 +127,10 @@
|
|||||||
<string>We need to manage backup your photos album</string>
|
<string>We need to manage backup your photos album</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>We need to manage backup your photos album</string>
|
<string>We need to manage backup your photos album</string>
|
||||||
|
<key>NSUserActivityTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>INSendMessageIntent</string>
|
||||||
|
</array>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
|
@ -4,5 +4,9 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.developer.networking.wifi-info</key>
|
<key>com.apple.developer.networking.wifi-info</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.app.immich.share</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -6,5 +6,9 @@
|
|||||||
<string>development</string>
|
<string>development</string>
|
||||||
<key>com.apple.developer.networking.wifi-info</key>
|
<key>com.apple.developer.networking.wifi-info</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.app.immich.share</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Share View Controller-->
|
||||||
|
<scene sceneID="ceB-am-kn3">
|
||||||
|
<objects>
|
||||||
|
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
35
mobile/ios/ShareExtension/Info.plist
Normal file
35
mobile/ios/ShareExtension/Info.plist
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>AppGroupId</key>
|
||||||
|
<string>$(CUSTOM_GROUP_ID)</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>IntentsSupported</key>
|
||||||
|
<array>
|
||||||
|
<string>INSendMessageIntent</string>
|
||||||
|
</array>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
|
||||||
|
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|
||||||
|
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
|
||||||
|
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
|
||||||
|
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
|
||||||
|
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count > 0
|
||||||
|
).@count > 0 </string>
|
||||||
|
<key>PHSupportedMediaTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>Video</string>
|
||||||
|
<string>Image</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
10
mobile/ios/ShareExtension/ShareExtension.entitlements
Normal file
10
mobile/ios/ShareExtension/ShareExtension.entitlements
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.app.immich.share</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
3
mobile/ios/ShareExtension/ShareViewController.swift
Normal file
3
mobile/ios/ShareExtension/ShareViewController.swift
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import share_handler_ios_models
|
||||||
|
|
||||||
|
class ShareViewController: ShareHandlerIosViewController {}
|
@ -1 +1,3 @@
|
|||||||
const int noDbId = -9223372036854775808; // from Isar
|
const int noDbId = -9223372036854775808; // from Isar
|
||||||
|
const double downloadCompleted = -1;
|
||||||
|
const double downloadFailed = -2;
|
||||||
|
@ -13,6 +13,7 @@ abstract interface class IAlbumRepository implements IDatabaseRepository {
|
|||||||
String name, {
|
String name, {
|
||||||
bool? shared,
|
bool? shared,
|
||||||
bool? remote,
|
bool? remote,
|
||||||
|
bool? owner,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<Album>> getAll({
|
Future<List<Album>> getAll({
|
||||||
|
7
mobile/lib/interfaces/share_handler.interface.dart
Normal file
7
mobile/lib/interfaces/share_handler.interface.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
|
|
||||||
|
abstract interface class IShareHandlerRepository {
|
||||||
|
void Function(List<ShareIntentAttachment>)? onSharedMedia;
|
||||||
|
|
||||||
|
Future<void> init();
|
||||||
|
}
|
11
mobile/lib/interfaces/upload.interface.dart
Normal file
11
mobile/lib/interfaces/upload.interface.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
|
|
||||||
|
abstract interface class IUploadRepository {
|
||||||
|
void Function(TaskStatusUpdate)? onUploadStatus;
|
||||||
|
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||||
|
|
||||||
|
Future<bool> upload(UploadTask task);
|
||||||
|
Future<bool> cancel(String id);
|
||||||
|
Future<void> deleteAllTrackingRecords();
|
||||||
|
Future<void> deleteRecordsWithIds(List<String> id);
|
||||||
|
}
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:timezone/data/latest.dart';
|
import 'package:timezone/data/latest.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
@ -107,10 +108,12 @@ Future<void> initApp() async {
|
|||||||
progressBar: true,
|
progressBar: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
FileDownloader().trackTasksInGroup(
|
await FileDownloader().trackTasksInGroup(
|
||||||
downloadGroupLivePhoto,
|
downloadGroupLivePhoto,
|
||||||
markDownloadedComplete: false,
|
markDownloadedComplete: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await FileDownloader().trackTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Isar> loadDb() async {
|
Future<Isar> loadDb() async {
|
||||||
@ -208,6 +211,8 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
|||||||
// needs to be delayed so that EasyLocalization is working
|
// needs to be delayed so that EasyLocalization is working
|
||||||
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ref.read(shareIntentUploadProvider.notifier).init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
114
mobile/lib/models/upload/share_intent_attachment.model.dart
Normal file
114
mobile/lib/models/upload/share_intent_attachment.model.dart
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/utils/bytes_units.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
|
enum ShareIntentAttachmentType {
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UploadStatus {
|
||||||
|
enqueued,
|
||||||
|
running,
|
||||||
|
complete,
|
||||||
|
notFound,
|
||||||
|
failed,
|
||||||
|
canceled,
|
||||||
|
waitingtoRetry,
|
||||||
|
paused,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShareIntentAttachment {
|
||||||
|
final String path;
|
||||||
|
|
||||||
|
// enum
|
||||||
|
final ShareIntentAttachmentType type;
|
||||||
|
|
||||||
|
// enum
|
||||||
|
final UploadStatus status;
|
||||||
|
|
||||||
|
final double uploadProgress;
|
||||||
|
|
||||||
|
final int fileLength;
|
||||||
|
|
||||||
|
ShareIntentAttachment({
|
||||||
|
required this.path,
|
||||||
|
required this.type,
|
||||||
|
required this.status,
|
||||||
|
this.uploadProgress = 0,
|
||||||
|
this.fileLength = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
int get id => hash(path);
|
||||||
|
|
||||||
|
File get file => File(path);
|
||||||
|
|
||||||
|
String get fileName => basename(file.path);
|
||||||
|
|
||||||
|
bool get isImage => type == ShareIntentAttachmentType.image;
|
||||||
|
|
||||||
|
bool get isVideo => type == ShareIntentAttachmentType.video;
|
||||||
|
|
||||||
|
String? _fileSize;
|
||||||
|
|
||||||
|
String get fileSize => _fileSize ??= formatHumanReadableBytes(fileLength, 2);
|
||||||
|
|
||||||
|
ShareIntentAttachment copyWith({
|
||||||
|
String? path,
|
||||||
|
ShareIntentAttachmentType? type,
|
||||||
|
UploadStatus? status,
|
||||||
|
double? uploadProgress,
|
||||||
|
}) {
|
||||||
|
return ShareIntentAttachment(
|
||||||
|
path: path ?? this.path,
|
||||||
|
type: type ?? this.type,
|
||||||
|
status: status ?? this.status,
|
||||||
|
uploadProgress: uploadProgress ?? this.uploadProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'path': path,
|
||||||
|
'type': type.index,
|
||||||
|
'status': status.index,
|
||||||
|
'uploadProgress': uploadProgress,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ShareIntentAttachment.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ShareIntentAttachment(
|
||||||
|
path: map['path'] as String,
|
||||||
|
type: ShareIntentAttachmentType.values[map['type'] as int],
|
||||||
|
status: UploadStatus.values[map['status'] as int],
|
||||||
|
uploadProgress: map['uploadProgress'] as double,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJson() => json.encode(toMap());
|
||||||
|
|
||||||
|
factory ShareIntentAttachment.fromJson(String source) =>
|
||||||
|
ShareIntentAttachment.fromMap(
|
||||||
|
json.decode(source) as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ShareIntentAttachment(path: $path, type: $type, status: $status, uploadProgress: $uploadProgress)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant ShareIntentAttachment other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.path == path && other.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return path.hashCode ^ type.hashCode;
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.pushRoute(AlbumOptionsRoute()),
|
onTap: () => context.pushRoute(const AlbumOptionsRoute()),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 50,
|
height: 50,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
@ -13,6 +13,9 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
horizontal: 16.0,
|
horizontal: 16.0,
|
||||||
),
|
),
|
||||||
this.borderRadius = 20.0,
|
this.borderRadius = 20.0,
|
||||||
|
this.trailing,
|
||||||
|
this.selected = false,
|
||||||
|
this.disabled = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Widget leading;
|
final Widget leading;
|
||||||
@ -21,12 +24,21 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
final EdgeInsetsGeometry leadingPadding;
|
final EdgeInsetsGeometry leadingPadding;
|
||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
|
final Widget? trailing;
|
||||||
|
final bool selected;
|
||||||
|
final bool disabled;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
onTap: onTap,
|
onTap: disabled ? null : onTap,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: selected
|
||||||
|
? Theme.of(context).primaryColor.withAlpha(30)
|
||||||
|
: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -34,7 +46,8 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
padding: leadingPadding,
|
padding: leadingPadding,
|
||||||
child: leading,
|
child: leading,
|
||||||
),
|
),
|
||||||
Column(
|
Expanded(
|
||||||
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -44,8 +57,11 @@ class LargeLeadingTile extends StatelessWidget {
|
|||||||
subtitle ?? const SizedBox.shrink(),
|
subtitle ?? const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (trailing != null) trailing!,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,12 @@ class PhotosPage extends HookConsumerWidget {
|
|||||||
Future(() => ref.read(assetProvider.notifier).getAllAsset());
|
Future(() => ref.read(assetProvider.notifier).getAllAsset());
|
||||||
Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums());
|
Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums());
|
||||||
ref.read(serverInfoProvider.notifier).getServerInfo();
|
ref.read(serverInfoProvider.notifier).getServerInfo();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget buildLoadingIndicator() {
|
Widget buildLoadingIndicator() {
|
||||||
Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1);
|
Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1);
|
||||||
|
|
||||||
|
263
mobile/lib/pages/share_intent/share_intent.page.dart
Normal file
263
mobile/lib/pages/share_intent/share_intent.page.dart
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
|
import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart';
|
||||||
|
import 'package:immich_mobile/entities/store.entity.dart' as db_store;
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class ShareIntentPage extends HookConsumerWidget {
|
||||||
|
const ShareIntentPage({super.key, required this.attachments});
|
||||||
|
|
||||||
|
final List<ShareIntentAttachment> attachments;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final currentEndpoint =
|
||||||
|
db_store.Store.get(db_store.StoreKey.serverEndpoint);
|
||||||
|
final candidates = ref.watch(shareIntentUploadProvider);
|
||||||
|
final isUploaded = useState(false);
|
||||||
|
|
||||||
|
void removeAttachment(ShareIntentAttachment attachment) {
|
||||||
|
ref.read(shareIntentUploadProvider.notifier).removeAttachment(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAttachments(List<ShareIntentAttachment> attachments) {
|
||||||
|
ref.read(shareIntentUploadProvider.notifier).addAttachments(attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void upload() async {
|
||||||
|
for (final attachment in candidates) {
|
||||||
|
await ref
|
||||||
|
.read(shareIntentUploadProvider.notifier)
|
||||||
|
.upload(attachment.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
isUploaded.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSelected(ShareIntentAttachment attachment) {
|
||||||
|
return candidates.contains(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleSelection(ShareIntentAttachment attachment) {
|
||||||
|
if (isSelected(attachment)) {
|
||||||
|
removeAttachment(attachment);
|
||||||
|
} else {
|
||||||
|
addAttachments([attachment]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Column(
|
||||||
|
children: [
|
||||||
|
const Text('upload_to_immich').tr(
|
||||||
|
args: [
|
||||||
|
candidates.length.toString(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
currentEndpoint,
|
||||||
|
style: context.textTheme.labelMedium?.copyWith(
|
||||||
|
color: context.colorScheme.onSurface.withAlpha(200),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: ListView.builder(
|
||||||
|
itemCount: attachments.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final attachment = attachments[index];
|
||||||
|
final target = candidates.firstWhere(
|
||||||
|
(element) => element.id == attachment.id,
|
||||||
|
orElse: () => attachment,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 4.0,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
child: LargeLeadingTile(
|
||||||
|
onTap: () => toggleSelection(attachment),
|
||||||
|
disabled: isUploaded.value,
|
||||||
|
selected: isSelected(attachment),
|
||||||
|
leading: Stack(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
child: attachment.isImage
|
||||||
|
? Image.file(
|
||||||
|
attachment.file,
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: const SizedBox(
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.videocam,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (attachment.isImage)
|
||||||
|
const Positioned(
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
child: Icon(
|
||||||
|
Icons.image,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 20,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
offset: Offset(0, 0),
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Colors.black45,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
attachment.fileName,
|
||||||
|
style: context.textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
attachment.fileSize,
|
||||||
|
style: context.textTheme.labelLarge,
|
||||||
|
),
|
||||||
|
trailing: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: UploadStatusIcon(
|
||||||
|
selected: isSelected(attachment),
|
||||||
|
status: target.status,
|
||||||
|
progress: target.uploadProgress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
bottomNavigationBar: SafeArea(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: isUploaded.value ? null : upload,
|
||||||
|
child: isUploaded.value
|
||||||
|
? UploadingText(candidates: candidates)
|
||||||
|
: const Text('upload').tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UploadingText extends StatelessWidget {
|
||||||
|
const UploadingText({super.key, required this.candidates});
|
||||||
|
final List<ShareIntentAttachment> candidates;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final uploadedCount = candidates.where((element) {
|
||||||
|
return element.status == UploadStatus.complete;
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
return const Text("shared_intent_upload_button_progress_text")
|
||||||
|
.tr(args: [uploadedCount.toString(), candidates.length.toString()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UploadStatusIcon extends StatelessWidget {
|
||||||
|
const UploadStatusIcon({
|
||||||
|
super.key,
|
||||||
|
required this.status,
|
||||||
|
required this.selected,
|
||||||
|
this.progress = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UploadStatus status;
|
||||||
|
final double progress;
|
||||||
|
final bool selected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (!selected) {
|
||||||
|
return Icon(
|
||||||
|
Icons.check_circle_outline_rounded,
|
||||||
|
color: context.colorScheme.onSurface.withAlpha(100),
|
||||||
|
semanticLabel: 'not_selected'.tr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final statusIcon = switch (status) {
|
||||||
|
UploadStatus.enqueued => Icon(
|
||||||
|
Icons.check_circle_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
semanticLabel: 'enqueued'.tr(),
|
||||||
|
),
|
||||||
|
UploadStatus.running => Stack(
|
||||||
|
alignment: AlignmentDirectional.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
child: TweenAnimationBuilder(
|
||||||
|
tween: Tween<double>(begin: 0.0, end: progress),
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
builder: (context, value, _) => CircularProgressIndicator(
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainerLow,
|
||||||
|
strokeWidth: 3,
|
||||||
|
value: value,
|
||||||
|
semanticsLabel: 'uploading'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
(progress * 100).toStringAsFixed(0),
|
||||||
|
style: context.textTheme.labelSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
UploadStatus.complete => Icon(
|
||||||
|
Icons.check_circle_rounded,
|
||||||
|
color: Colors.green,
|
||||||
|
semanticLabel: 'completed'.tr(),
|
||||||
|
),
|
||||||
|
UploadStatus.notFound || UploadStatus.failed => Icon(
|
||||||
|
Icons.error_rounded,
|
||||||
|
color: Colors.red,
|
||||||
|
semanticLabel: 'failed'.tr(),
|
||||||
|
),
|
||||||
|
UploadStatus.canceled => Icon(
|
||||||
|
Icons.cancel_rounded,
|
||||||
|
color: Colors.red,
|
||||||
|
semanticLabel: 'canceled'.tr(),
|
||||||
|
),
|
||||||
|
UploadStatus.waitingtoRetry || UploadStatus.paused => Icon(
|
||||||
|
Icons.pause_circle_rounded,
|
||||||
|
color: context.primaryColor,
|
||||||
|
semanticLabel: 'paused'.tr(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return statusIcon;
|
||||||
|
}
|
||||||
|
}
|
@ -46,8 +46,18 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
) =>
|
) =>
|
||||||
_albumService.createAlbum(albumTitle, assets, []);
|
_albumService.createAlbum(albumTitle, assets, []);
|
||||||
|
|
||||||
Future<Album?> getAlbumByName(String albumName, {bool remoteOnly = false}) =>
|
Future<Album?> getAlbumByName(
|
||||||
_albumService.getAlbumByName(albumName, remoteOnly);
|
String albumName, {
|
||||||
|
bool? remote,
|
||||||
|
bool? shared,
|
||||||
|
bool? owner,
|
||||||
|
}) =>
|
||||||
|
_albumService.getAlbumByName(
|
||||||
|
albumName,
|
||||||
|
remote: remote,
|
||||||
|
shared: shared,
|
||||||
|
owner: owner,
|
||||||
|
);
|
||||||
|
|
||||||
/// Create an album on the server with the same name as the selected album for backup
|
/// Create an album on the server with the same name as the selected album for backup
|
||||||
/// First this will check if the album already exists on the server with name
|
/// First this will check if the album already exists on the server with name
|
||||||
@ -55,7 +65,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
|||||||
Future<void> createSyncAlbum(
|
Future<void> createSyncAlbum(
|
||||||
String albumName,
|
String albumName,
|
||||||
) async {
|
) async {
|
||||||
final album = await getAlbumByName(albumName, remoteOnly: true);
|
final album = await getAlbumByName(albumName, remote: true, owner: true);
|
||||||
if (album != null) {
|
if (album != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
|
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/services/share_intent_service.dart';
|
||||||
|
import 'package:immich_mobile/services/upload.service.dart';
|
||||||
|
|
||||||
|
final shareIntentUploadProvider = StateNotifierProvider<
|
||||||
|
ShareIntentUploadStateNotifier, List<ShareIntentAttachment>>(
|
||||||
|
((ref) => ShareIntentUploadStateNotifier(
|
||||||
|
ref.watch(appRouterProvider),
|
||||||
|
ref.watch(uploadServiceProvider),
|
||||||
|
ref.watch(shareIntentServiceProvider),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
class ShareIntentUploadStateNotifier
|
||||||
|
extends StateNotifier<List<ShareIntentAttachment>> {
|
||||||
|
final AppRouter router;
|
||||||
|
final UploadService _uploadService;
|
||||||
|
final ShareIntentService _shareIntentService;
|
||||||
|
|
||||||
|
ShareIntentUploadStateNotifier(
|
||||||
|
this.router,
|
||||||
|
this._uploadService,
|
||||||
|
this._shareIntentService,
|
||||||
|
) : super([]) {
|
||||||
|
_uploadService.onUploadStatus = _uploadStatusCallback;
|
||||||
|
_uploadService.onTaskProgress = _taskProgressCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
_shareIntentService.onSharedMedia = onSharedMedia;
|
||||||
|
_shareIntentService.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSharedMedia(List<ShareIntentAttachment> attachments) {
|
||||||
|
router.removeWhere((route) => route.name == "ShareIntentRoute");
|
||||||
|
clearAttachments();
|
||||||
|
addAttachments(attachments);
|
||||||
|
router.push(ShareIntentRoute(attachments: attachments));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAttachments(List<ShareIntentAttachment> attachments) {
|
||||||
|
if (attachments.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = [...state, ...attachments];
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeAttachment(ShareIntentAttachment attachment) {
|
||||||
|
final updatedState =
|
||||||
|
state.where((element) => element != attachment).toList();
|
||||||
|
if (updatedState.length != state.length) {
|
||||||
|
state = updatedState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearAttachments() {
|
||||||
|
if (state.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateUploadStatus(TaskStatusUpdate task, TaskStatus status) async {
|
||||||
|
if (status == TaskStatus.canceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final taskId = task.task.taskId;
|
||||||
|
final uploadStatus = switch (task.status) {
|
||||||
|
TaskStatus.complete => UploadStatus.complete,
|
||||||
|
TaskStatus.failed => UploadStatus.failed,
|
||||||
|
TaskStatus.canceled => UploadStatus.canceled,
|
||||||
|
TaskStatus.enqueued => UploadStatus.enqueued,
|
||||||
|
TaskStatus.running => UploadStatus.running,
|
||||||
|
TaskStatus.paused => UploadStatus.paused,
|
||||||
|
TaskStatus.notFound => UploadStatus.notFound,
|
||||||
|
TaskStatus.waitingToRetry => UploadStatus.waitingtoRetry
|
||||||
|
};
|
||||||
|
|
||||||
|
state = [
|
||||||
|
for (final attachment in state)
|
||||||
|
if (attachment.id == taskId.toInt())
|
||||||
|
attachment.copyWith(status: uploadStatus)
|
||||||
|
else
|
||||||
|
attachment,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
void _uploadStatusCallback(TaskStatusUpdate update) {
|
||||||
|
_updateUploadStatus(update, update.status);
|
||||||
|
|
||||||
|
switch (update.status) {
|
||||||
|
case TaskStatus.complete:
|
||||||
|
if (update.responseStatusCode == 200) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint("[COMPLETE] ${update.task.taskId} - DUPLICATE");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint("[COMPLETE] ${update.task.taskId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _taskProgressCallback(TaskProgressUpdate update) {
|
||||||
|
// Ignore if the task is cancled or completed
|
||||||
|
if (update.progress == downloadFailed ||
|
||||||
|
update.progress == downloadCompleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final taskId = update.task.taskId;
|
||||||
|
state = [
|
||||||
|
for (final attachment in state)
|
||||||
|
if (attachment.id == taskId.toInt())
|
||||||
|
attachment.copyWith(uploadProgress: update.progress)
|
||||||
|
else
|
||||||
|
attachment,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> upload(File file) {
|
||||||
|
return _uploadService.upload(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> cancelUpload(String id) {
|
||||||
|
return _uploadService.cancelUpload(id);
|
||||||
|
}
|
||||||
|
}
|
@ -34,11 +34,25 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
|
|||||||
Future<Album> create(Album album) => txn(() => db.albums.store(album));
|
Future<Album> create(Album album) => txn(() => db.albums.store(album));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Album?> getByName(String name, {bool? shared, bool? remote}) {
|
Future<Album?> getByName(
|
||||||
|
String name, {
|
||||||
|
bool? shared,
|
||||||
|
bool? remote,
|
||||||
|
bool? owner,
|
||||||
|
}) {
|
||||||
var query = db.albums.filter().nameEqualTo(name);
|
var query = db.albums.filter().nameEqualTo(name);
|
||||||
if (shared != null) {
|
if (shared != null) {
|
||||||
query = query.sharedEqualTo(shared);
|
query = query.sharedEqualTo(shared);
|
||||||
}
|
}
|
||||||
|
if (owner == true) {
|
||||||
|
query = query.owner(
|
||||||
|
(q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
|
||||||
|
);
|
||||||
|
} else if (owner == false) {
|
||||||
|
query = query.owner(
|
||||||
|
(q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (remote == true) {
|
if (remote == true) {
|
||||||
query = query.localIdIsNull();
|
query = query.localIdIsNull();
|
||||||
} else if (remote == false) {
|
} else if (remote == false) {
|
||||||
|
63
mobile/lib/repositories/share_handler.repository.dart
Normal file
63
mobile/lib/repositories/share_handler.repository.dart
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/interfaces/share_handler.interface.dart';
|
||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
|
import 'package:share_handler/share_handler.dart';
|
||||||
|
|
||||||
|
final shareHandlerRepositoryProvider = Provider(
|
||||||
|
(ref) => ShareHandlerRepository(),
|
||||||
|
);
|
||||||
|
|
||||||
|
class ShareHandlerRepository implements IShareHandlerRepository {
|
||||||
|
ShareHandlerRepository();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void Function(List<ShareIntentAttachment> attachments)? onSharedMedia;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
final handler = ShareHandlerPlatform.instance;
|
||||||
|
final media = await handler.getInitialSharedMedia();
|
||||||
|
|
||||||
|
if (media != null && media.attachments != null) {
|
||||||
|
onSharedMedia?.call(_buildPayload(media.attachments!));
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.sharedMediaStream.listen((SharedMedia media) {
|
||||||
|
if (media.attachments != null) {
|
||||||
|
onSharedMedia?.call(_buildPayload(media.attachments!));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShareIntentAttachment> _buildPayload(
|
||||||
|
List<SharedAttachment?> attachments,
|
||||||
|
) {
|
||||||
|
final payload = <ShareIntentAttachment>[];
|
||||||
|
|
||||||
|
for (final attachment in attachments) {
|
||||||
|
if (attachment == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final type = attachment.type == SharedAttachmentType.image
|
||||||
|
? ShareIntentAttachmentType.image
|
||||||
|
: ShareIntentAttachmentType.video;
|
||||||
|
|
||||||
|
final fileLength = File(attachment.path).lengthSync();
|
||||||
|
|
||||||
|
payload.add(
|
||||||
|
ShareIntentAttachment(
|
||||||
|
path: attachment.path,
|
||||||
|
type: type,
|
||||||
|
status: UploadStatus.enqueued,
|
||||||
|
uploadProgress: 0.0,
|
||||||
|
fileLength: fileLength,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
42
mobile/lib/repositories/upload.repository.dart
Normal file
42
mobile/lib/repositories/upload.repository.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/interfaces/upload.interface.dart';
|
||||||
|
import 'package:immich_mobile/utils/upload.dart';
|
||||||
|
|
||||||
|
final uploadRepositoryProvider = Provider((ref) => UploadRepository());
|
||||||
|
|
||||||
|
class UploadRepository implements IUploadRepository {
|
||||||
|
@override
|
||||||
|
void Function(TaskStatusUpdate)? onUploadStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||||
|
|
||||||
|
UploadRepository() {
|
||||||
|
FileDownloader().registerCallbacks(
|
||||||
|
group: uploadGroup,
|
||||||
|
taskStatusCallback: (update) => onUploadStatus?.call(update),
|
||||||
|
taskProgressCallback: (update) => onTaskProgress?.call(update),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> upload(UploadTask task) {
|
||||||
|
return FileDownloader().enqueue(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteAllTrackingRecords() {
|
||||||
|
return FileDownloader().database.deleteAllRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> cancel(String id) {
|
||||||
|
return FileDownloader().cancelTaskWithId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteRecordsWithIds(List<String> ids) {
|
||||||
|
return FileDownloader().database.deleteRecordsWithIds(ids);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ import 'package:immich_mobile/entities/user.entity.dart';
|
|||||||
import 'package:immich_mobile/models/memories/memory.model.dart';
|
import 'package:immich_mobile/models/memories/memory.model.dart';
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
import 'package:immich_mobile/models/search/search_filter.model.dart';
|
||||||
import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
|
import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
|
||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
import 'package:immich_mobile/pages/backup/album_preview.page.dart';
|
import 'package:immich_mobile/pages/backup/album_preview.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart';
|
import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart';
|
||||||
import 'package:immich_mobile/pages/backup/backup_controller.page.dart';
|
import 'package:immich_mobile/pages/backup/backup_controller.page.dart';
|
||||||
@ -57,6 +58,7 @@ import 'package:immich_mobile/pages/library/partner/partner.page.dart';
|
|||||||
import 'package:immich_mobile/pages/library/partner/partner_detail.page.dart';
|
import 'package:immich_mobile/pages/library/partner/partner_detail.page.dart';
|
||||||
import 'package:immich_mobile/pages/library/shared_link/shared_link.page.dart';
|
import 'package:immich_mobile/pages/library/shared_link/shared_link.page.dart';
|
||||||
import 'package:immich_mobile/pages/library/shared_link/shared_link_edit.page.dart';
|
import 'package:immich_mobile/pages/library/shared_link/shared_link_edit.page.dart';
|
||||||
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
import 'package:immich_mobile/routing/auth_guard.dart';
|
import 'package:immich_mobile/routing/auth_guard.dart';
|
||||||
@ -277,6 +279,10 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: NativeVideoViewerRoute.page,
|
page: NativeVideoViewerRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: ShareIntentRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,15 +136,10 @@ class AlbumAssetSelectionRouteArgs {
|
|||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [AlbumOptionsPage]
|
/// [AlbumOptionsPage]
|
||||||
class AlbumOptionsRoute extends PageRouteInfo<AlbumOptionsRouteArgs> {
|
class AlbumOptionsRoute extends PageRouteInfo<void> {
|
||||||
AlbumOptionsRoute({
|
const AlbumOptionsRoute({List<PageRouteInfo>? children})
|
||||||
Key? key,
|
: super(
|
||||||
List<PageRouteInfo>? children,
|
|
||||||
}) : super(
|
|
||||||
AlbumOptionsRoute.name,
|
AlbumOptionsRoute.name,
|
||||||
args: AlbumOptionsRouteArgs(
|
|
||||||
key: key,
|
|
||||||
),
|
|
||||||
initialChildren: children,
|
initialChildren: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -153,25 +148,11 @@ class AlbumOptionsRoute extends PageRouteInfo<AlbumOptionsRouteArgs> {
|
|||||||
static PageInfo page = PageInfo(
|
static PageInfo page = PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
final args = data.argsAs<AlbumOptionsRouteArgs>();
|
return const AlbumOptionsPage();
|
||||||
return AlbumOptionsPage(
|
|
||||||
key: args.key,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlbumOptionsRouteArgs {
|
|
||||||
const AlbumOptionsRouteArgs({this.key});
|
|
||||||
|
|
||||||
final Key? key;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'AlbumOptionsRouteArgs{key: $key}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [AlbumPreviewPage]
|
/// [AlbumPreviewPage]
|
||||||
class AlbumPreviewRoute extends PageRouteInfo<AlbumPreviewRouteArgs> {
|
class AlbumPreviewRoute extends PageRouteInfo<AlbumPreviewRouteArgs> {
|
||||||
@ -1453,6 +1434,52 @@ class SettingsSubRouteArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [ShareIntentPage]
|
||||||
|
class ShareIntentRoute extends PageRouteInfo<ShareIntentRouteArgs> {
|
||||||
|
ShareIntentRoute({
|
||||||
|
Key? key,
|
||||||
|
required List<ShareIntentAttachment> attachments,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
ShareIntentRoute.name,
|
||||||
|
args: ShareIntentRouteArgs(
|
||||||
|
key: key,
|
||||||
|
attachments: attachments,
|
||||||
|
),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'ShareIntentRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<ShareIntentRouteArgs>();
|
||||||
|
return ShareIntentPage(
|
||||||
|
key: args.key,
|
||||||
|
attachments: args.attachments,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShareIntentRouteArgs {
|
||||||
|
const ShareIntentRouteArgs({
|
||||||
|
this.key,
|
||||||
|
required this.attachments,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final List<ShareIntentAttachment> attachments;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ShareIntentRouteArgs{key: $key, attachments: $attachments}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [SharedLinkEditPage]
|
/// [SharedLinkEditPage]
|
||||||
class SharedLinkEditRoute extends PageRouteInfo<SharedLinkEditRouteArgs> {
|
class SharedLinkEditRoute extends PageRouteInfo<SharedLinkEditRouteArgs> {
|
||||||
|
@ -170,7 +170,12 @@ class AlbumService {
|
|||||||
try {
|
try {
|
||||||
await _userService.refreshUsers();
|
await _userService.refreshUsers();
|
||||||
final (sharedAlbum, ownedAlbum) = await (
|
final (sharedAlbum, ownedAlbum) = await (
|
||||||
|
// Note: `shared: true` is required to get albums that don't belong to
|
||||||
|
// us due to unusual behaviour on the API but this will also return our
|
||||||
|
// own shared albums
|
||||||
_albumApiRepository.getAll(shared: true),
|
_albumApiRepository.getAll(shared: true),
|
||||||
|
// Passing null (or nothing) for `shared` returns only albums that
|
||||||
|
// explicitly belong to us
|
||||||
_albumApiRepository.getAll(shared: null)
|
_albumApiRepository.getAll(shared: null)
|
||||||
).wait;
|
).wait;
|
||||||
|
|
||||||
@ -212,7 +217,7 @@ class AlbumService {
|
|||||||
for (int round = 0;; round++) {
|
for (int round = 0;; round++) {
|
||||||
final proposedName = "$baseName${round == 0 ? "" : " ($round)"}";
|
final proposedName = "$baseName${round == 0 ? "" : " ($round)"}";
|
||||||
|
|
||||||
if (null == await _albumRepository.getByName(proposedName)) {
|
if (null == await _albumRepository.getByName(proposedName, owner: true)) {
|
||||||
return proposedName;
|
return proposedName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,8 +413,18 @@ class AlbumService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Album?> getAlbumByName(String name, bool remoteOnly) =>
|
Future<Album?> getAlbumByName(
|
||||||
_albumRepository.getByName(name, remote: remoteOnly ? true : null);
|
String name, {
|
||||||
|
bool? remote,
|
||||||
|
bool? shared,
|
||||||
|
bool? owner,
|
||||||
|
}) =>
|
||||||
|
_albumRepository.getByName(
|
||||||
|
name,
|
||||||
|
remote: remote,
|
||||||
|
shared: shared,
|
||||||
|
owner: owner,
|
||||||
|
);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Add the uploaded asset to the selected albums
|
/// Add the uploaded asset to the selected albums
|
||||||
@ -419,7 +434,7 @@ class AlbumService {
|
|||||||
List<String> assetIds,
|
List<String> assetIds,
|
||||||
) async {
|
) async {
|
||||||
for (final albumName in albumNames) {
|
for (final albumName in albumNames) {
|
||||||
Album? album = await getAlbumByName(albumName, true);
|
Album? album = await getAlbumByName(albumName, remote: true, owner: true);
|
||||||
album ??= await createAlbum(albumName, []);
|
album ??= await createAlbum(albumName, []);
|
||||||
if (album != null && album.remoteId != null) {
|
if (album != null && album.remoteId != null) {
|
||||||
await _albumApiRepository.addAssets(album.remoteId!, assetIds);
|
await _albumApiRepository.addAssets(album.remoteId!, assetIds);
|
||||||
|
23
mobile/lib/services/share_intent_service.dart
Normal file
23
mobile/lib/services/share_intent_service.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
|
import 'package:immich_mobile/repositories/share_handler.repository.dart';
|
||||||
|
|
||||||
|
final shareIntentServiceProvider = Provider(
|
||||||
|
(ref) => ShareIntentService(
|
||||||
|
ref.watch(shareHandlerRepositoryProvider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class ShareIntentService {
|
||||||
|
final ShareHandlerRepository shareHandlerRepository;
|
||||||
|
void Function(List<ShareIntentAttachment> attachments)? onSharedMedia;
|
||||||
|
|
||||||
|
ShareIntentService(
|
||||||
|
this.shareHandlerRepository,
|
||||||
|
);
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
shareHandlerRepository.onSharedMedia = onSharedMedia;
|
||||||
|
shareHandlerRepository.init();
|
||||||
|
}
|
||||||
|
}
|
94
mobile/lib/services/upload.service.dart
Normal file
94
mobile/lib/services/upload.service.dart
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/interfaces/upload.interface.dart';
|
||||||
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
|
import 'package:immich_mobile/utils/upload.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
// import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
final uploadServiceProvider = Provider(
|
||||||
|
(ref) => UploadService(
|
||||||
|
ref.watch(uploadRepositoryProvider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class UploadService {
|
||||||
|
final IUploadRepository _uploadRepository;
|
||||||
|
// final Logger _log = Logger("UploadService");
|
||||||
|
void Function(TaskStatusUpdate)? onUploadStatus;
|
||||||
|
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||||
|
|
||||||
|
UploadService(
|
||||||
|
this._uploadRepository,
|
||||||
|
) {
|
||||||
|
_uploadRepository.onUploadStatus = _onUploadCallback;
|
||||||
|
_uploadRepository.onTaskProgress = _onTaskProgressCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onTaskProgressCallback(TaskProgressUpdate update) {
|
||||||
|
onTaskProgress?.call(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onUploadCallback(TaskStatusUpdate update) {
|
||||||
|
onUploadStatus?.call(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> cancelUpload(String id) {
|
||||||
|
return FileDownloader().cancelTaskWithId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> upload(File file) async {
|
||||||
|
final task = await _buildUploadTask(
|
||||||
|
hash(file.path).toString(),
|
||||||
|
file,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _uploadRepository.upload(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<UploadTask> _buildUploadTask(
|
||||||
|
String id,
|
||||||
|
File file, {
|
||||||
|
Map<String, String>? fields,
|
||||||
|
}) async {
|
||||||
|
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||||
|
final url = Uri.parse('$serverEndpoint/assets').toString();
|
||||||
|
final headers = ApiService.getRequestHeaders();
|
||||||
|
final deviceId = Store.get(StoreKey.deviceId);
|
||||||
|
|
||||||
|
final (baseDirectory, directory, filename) =
|
||||||
|
await Task.split(filePath: file.path);
|
||||||
|
final stats = await file.stat();
|
||||||
|
final fileCreatedAt = stats.changed;
|
||||||
|
final fileModifiedAt = stats.modified;
|
||||||
|
|
||||||
|
final fieldsMap = {
|
||||||
|
'filename': filename,
|
||||||
|
'deviceAssetId': id,
|
||||||
|
'deviceId': deviceId,
|
||||||
|
'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(),
|
||||||
|
'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(),
|
||||||
|
'isFavorite': 'false',
|
||||||
|
'duration': '0',
|
||||||
|
if (fields != null) ...fields,
|
||||||
|
};
|
||||||
|
|
||||||
|
return UploadTask(
|
||||||
|
taskId: id,
|
||||||
|
httpRequestMethod: 'POST',
|
||||||
|
url: url,
|
||||||
|
headers: headers,
|
||||||
|
filename: filename,
|
||||||
|
fields: fieldsMap,
|
||||||
|
baseDirectory: baseDirectory,
|
||||||
|
directory: directory,
|
||||||
|
fileField: 'assetData',
|
||||||
|
group: uploadGroup,
|
||||||
|
updates: Updates.statusAndProgress,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
String formatBytes(int bytes) {
|
String formatBytes(int bytes) {
|
||||||
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
|
||||||
|
|
||||||
@ -14,3 +16,10 @@ String formatBytes(int bytes) {
|
|||||||
|
|
||||||
return "${remainder.toStringAsFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}";
|
return "${remainder.toStringAsFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String formatHumanReadableBytes(int bytes, int decimals) {
|
||||||
|
if (bytes <= 0) return "0 B";
|
||||||
|
const suffixes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
var i = (log(bytes) / log(1024)).floor();
|
||||||
|
return '${(bytes / pow(1024, i)).toStringAsFixed(decimals)} ${suffixes[i]}';
|
||||||
|
}
|
||||||
|
1
mobile/lib/utils/upload.dart
Normal file
1
mobile/lib/utils/upload.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
const uploadGroup = 'upload_group';
|
@ -206,7 +206,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.settings_rounded),
|
leading: const Icon(Icons.settings_rounded),
|
||||||
onTap: () => context.navigateTo(AlbumOptionsRoute()),
|
onTap: () => context.navigateTo(const AlbumOptionsRoute()),
|
||||||
title: const Text(
|
title: const Text(
|
||||||
"translated_text_options",
|
"translated_text_options",
|
||||||
style: TextStyle(fontWeight: FontWeight.w500),
|
style: TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
@ -1328,6 +1328,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.8"
|
version: "0.3.8"
|
||||||
|
share_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: share_handler
|
||||||
|
sha256: "76575533be04df3fecbebd3c5b5325a8271b5973131f8b8b0ab8490c395a5d37"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.22"
|
||||||
|
share_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_handler_android
|
||||||
|
sha256: "124dcc914fb7ecd89076d3dc28435b98fe2129a988bf7742f7a01dcb66a95667"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.9"
|
||||||
|
share_handler_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_handler_ios
|
||||||
|
sha256: cdc21f88f336a944157a8e9ceb191525cee3b082d6eb6c2082488e4f09dc3ece
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.15"
|
||||||
|
share_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_handler_platform_interface
|
||||||
|
sha256: "7a4df95a87b326b2f07458d937f2281874567c364b7b7ebe4e7d50efaae5f106"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.6"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -77,6 +77,7 @@ dependencies:
|
|||||||
image_picker: ^1.0.7 # only used to select user profile image from system gallery -> we can simply select an image from within immich?
|
image_picker: ^1.0.7 # only used to select user profile image from system gallery -> we can simply select an image from within immich?
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
file_picker: ^8.0.0+1
|
file_picker: ^8.0.0+1
|
||||||
|
share_handler: ^0.0.22
|
||||||
|
|
||||||
# This is uncommented in F-Droid build script
|
# This is uncommented in F-Droid build script
|
||||||
# Taken from https://github.com/Myzel394/locus/blob/445013d22ec1d759027d4303bd65b30c5c8588c8/pubspec.yaml#L105
|
# Taken from https://github.com/Myzel394/locus/blob/445013d22ec1d759027d4303bd65b30c5c8588c8/pubspec.yaml#L105
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
],
|
],
|
||||||
"minimumReleaseAge": "5 days",
|
"minimumReleaseAge": "5 days",
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"groupName": "@immich/ui",
|
||||||
|
"matchPackageNames": ["@immich/ui"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"matchFileNames": [
|
"matchFileNames": [
|
||||||
"cli/**",
|
"cli/**",
|
||||||
|
@ -10,6 +10,7 @@ node_modules
|
|||||||
coverage
|
coverage
|
||||||
dist
|
dist
|
||||||
**/migrations/**
|
**/migrations/**
|
||||||
|
db.d.ts
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
# Ignore files for PNPM, NPM and YARN
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# dev build
|
# dev build
|
||||||
FROM ghcr.io/immich-app/base-server-dev:20250107@sha256:d00ab37e1c1ed87b799d6509fbc825a721ca0723c59c67955217826882017d38 AS dev
|
FROM ghcr.io/immich-app/base-server-dev:20250114@sha256:fce0404484bde5afc38a4399c6b25895eb079a666d269f199c93dfbfdd5b26b6 AS dev
|
||||||
|
|
||||||
RUN apt-get install --no-install-recommends -yqq tini
|
RUN apt-get install --no-install-recommends -yqq tini
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@ -25,7 +25,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
|
|||||||
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
|
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
|
||||||
|
|
||||||
# web build
|
# web build
|
||||||
FROM node:22.12.0-alpine3.20@sha256:96cc8323e25c8cc6ddcb8b965e135cfd57846e8003ec0d7bcec16c5fd5f6d39f AS web
|
FROM node:22.13.0-alpine3.20@sha256:db8dcb90326a0116375414e9a7c068a6b87a4422b7da37b5c6cd026f7c7835d3 AS web
|
||||||
|
|
||||||
WORKDIR /usr/src/open-api/typescript-sdk
|
WORKDIR /usr/src/open-api/typescript-sdk
|
||||||
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
|
||||||
@ -42,7 +42,7 @@ RUN npm run build
|
|||||||
|
|
||||||
|
|
||||||
# prod build
|
# prod build
|
||||||
FROM ghcr.io/immich-app/base-server-prod:20250107@sha256:78e92f113103271d43a3b050370b21b31c3c14792d3d23b18b542581a440c72b
|
FROM ghcr.io/immich-app/base-server-prod:20250114@sha256:94ec8a36cdf11691810c4aeccee1b49b00348e17f6b6781d87dd48a74e6c6787
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/bin/database.js",
|
"typeorm:migrations:revert": "typeorm migration:revert -d ./dist/bin/database.js",
|
||||||
"typeorm:schema:drop": "typeorm query -d ./dist/bin/database.js 'DROP schema public cascade; CREATE schema public;'",
|
"typeorm:schema:drop": "typeorm query -d ./dist/bin/database.js 'DROP schema public cascade; CREATE schema public;'",
|
||||||
"typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run",
|
"typeorm:schema:reset": "npm run typeorm:schema:drop && npm run typeorm:migrations:run",
|
||||||
|
"kysely:codegen": "npx kysely-codegen --include-pattern=\"(public|vectors).*\" --dialect postgres --url postgres://postgres:postgres@localhost/immich --log-level debug --out-file=./src/db.d.ts",
|
||||||
"sync:open-api": "node ./dist/bin/sync-open-api.js",
|
"sync:open-api": "node ./dist/bin/sync-open-api.js",
|
||||||
"sync:sql": "node ./dist/bin/sync-sql.js",
|
"sync:sql": "node ./dist/bin/sync-sql.js",
|
||||||
"email:dev": "email dev -p 3050 --dir src/emails"
|
"email:dev": "email dev -p 3050 --dir src/emails"
|
||||||
|
@ -20,14 +20,14 @@ import { ErrorInterceptor } from 'src/middleware/error.interceptor';
|
|||||||
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
||||||
import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
|
import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter';
|
||||||
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
|
import { LoggingInterceptor } from 'src/middleware/logging.interceptor';
|
||||||
import { repositories } from 'src/repositories';
|
import { providers, repositories } from 'src/repositories';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { teardownTelemetry } from 'src/repositories/telemetry.repository';
|
import { teardownTelemetry } from 'src/repositories/telemetry.repository';
|
||||||
import { services } from 'src/services';
|
import { services } from 'src/services';
|
||||||
import { CliService } from 'src/services/cli.service';
|
import { CliService } from 'src/services/cli.service';
|
||||||
import { DatabaseService } from 'src/services/database.service';
|
import { DatabaseService } from 'src/services/database.service';
|
||||||
|
|
||||||
const common = [...services, ...repositories];
|
const common = [...services, ...providers, ...repositories];
|
||||||
|
|
||||||
const middleware = [
|
const middleware = [
|
||||||
FileUploadInterceptor,
|
FileUploadInterceptor,
|
||||||
@ -73,7 +73,7 @@ class BaseModule implements OnModuleInit, OnModuleDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
async onModuleInit() {
|
||||||
this.telemetryRepository.setup({ repositories: repositories.map(({ useClass }) => useClass) });
|
this.telemetryRepository.setup({ repositories: [...providers.map(({ useClass }) => useClass), ...repositories] });
|
||||||
|
|
||||||
this.jobRepository.setup({ services });
|
this.jobRepository.setup({ services });
|
||||||
if (this.worker === ImmichWorker.MICROSERVICES) {
|
if (this.worker === ImmichWorker.MICROSERVICES) {
|
||||||
|
@ -12,7 +12,7 @@ import { format } from 'sql-formatter';
|
|||||||
import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators';
|
import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators';
|
||||||
import { entities } from 'src/entities';
|
import { entities } from 'src/entities';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { repositories } from 'src/repositories';
|
import { providers, repositories } from 'src/repositories';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ConfigRepository } from 'src/repositories/config.repository';
|
import { ConfigRepository } from 'src/repositories/config.repository';
|
||||||
import { AuthService } from 'src/services/auth.service';
|
import { AuthService } from 'src/services/auth.service';
|
||||||
@ -43,7 +43,7 @@ export class SqlLogger implements Logger {
|
|||||||
|
|
||||||
const reflector = new Reflector();
|
const reflector = new Reflector();
|
||||||
|
|
||||||
type Repository = (typeof repositories)[0]['useClass'];
|
type Repository = (typeof providers)[0]['useClass'];
|
||||||
type Provider = { provide: any; useClass: Repository };
|
type Provider = { provide: any; useClass: Repository };
|
||||||
type SqlGeneratorOptions = { targetDir: string };
|
type SqlGeneratorOptions = { targetDir: string };
|
||||||
|
|
||||||
@ -57,7 +57,11 @@ class SqlGenerator {
|
|||||||
async run() {
|
async run() {
|
||||||
try {
|
try {
|
||||||
await this.setup();
|
await this.setup();
|
||||||
for (const repository of repositories) {
|
const targets = [
|
||||||
|
...providers,
|
||||||
|
...repositories.map((repository) => ({ provide: repository, useClass: repository as any })),
|
||||||
|
];
|
||||||
|
for (const repository of targets) {
|
||||||
if (repository.provide === ILoggerRepository) {
|
if (repository.provide === ILoggerRepository) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -86,6 +90,7 @@ class SqlGenerator {
|
|||||||
this.sqlLogger.logQuery(event.query.sql);
|
this.sqlLogger.logQuery(event.query.sql);
|
||||||
} else if (event.level === 'error') {
|
} else if (event.level === 'error') {
|
||||||
this.sqlLogger.logQueryError(event.error as Error, event.query.sql);
|
this.sqlLogger.logQueryError(event.error as Error, event.query.sql);
|
||||||
|
this.sqlLogger.logQuery(event.query.sql);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -98,7 +103,7 @@ class SqlGenerator {
|
|||||||
TypeOrmModule.forFeature(entities),
|
TypeOrmModule.forFeature(entities),
|
||||||
OpenTelemetryModule.forRoot(otel),
|
OpenTelemetryModule.forRoot(otel),
|
||||||
],
|
],
|
||||||
providers: [...repositories, AuthService, SchedulerRegistry],
|
providers: [...providers, ...repositories, AuthService, SchedulerRegistry],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
this.app = await moduleFixture.createNestApplication().init();
|
this.app = await moduleFixture.createNestApplication().init();
|
||||||
|
3
server/src/database.ts
Normal file
3
server/src/database.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const columns = {
|
||||||
|
userDto: ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'],
|
||||||
|
} as const;
|
23
server/src/db.d.ts
vendored
23
server/src/db.d.ts
vendored
@ -3,16 +3,21 @@
|
|||||||
* Please do not edit it manually.
|
* Please do not edit it manually.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ColumnType } from 'kysely';
|
import type { ColumnType } from "kysely";
|
||||||
|
|
||||||
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
|
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[]
|
||||||
|
? U[]
|
||||||
|
: ArrayTypeImpl<T>;
|
||||||
|
|
||||||
export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S[], I[], U[]> : T[];
|
export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U>
|
||||||
|
? ColumnType<S[], I[], U[]>
|
||||||
|
: T[];
|
||||||
|
|
||||||
export type AssetsStatusEnum = 'active' | 'deleted' | 'trashed';
|
export type AssetsStatusEnum = "active" | "deleted" | "trashed";
|
||||||
|
|
||||||
export type Generated<T> =
|
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
|
||||||
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>;
|
? ColumnType<S, I | undefined, U>
|
||||||
|
: ColumnType<T, T | undefined, T>;
|
||||||
|
|
||||||
export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>;
|
export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>;
|
||||||
|
|
||||||
@ -28,7 +33,7 @@ export type JsonPrimitive = boolean | number | string | null;
|
|||||||
|
|
||||||
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
||||||
|
|
||||||
export type Sourcetype = 'exif' | 'machine-learning';
|
export type Sourcetype = "exif" | "machine-learning";
|
||||||
|
|
||||||
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
||||||
|
|
||||||
@ -257,7 +262,7 @@ export interface NaturalearthCountries {
|
|||||||
admin: string;
|
admin: string;
|
||||||
admin_a3: string;
|
admin_a3: string;
|
||||||
coordinates: string;
|
coordinates: string;
|
||||||
id: number;
|
id: Generated<number>;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +438,6 @@ export interface DB {
|
|||||||
tags_closure: TagsClosure;
|
tags_closure: TagsClosure;
|
||||||
user_metadata: UserMetadata;
|
user_metadata: UserMetadata;
|
||||||
users: Users;
|
users: Users;
|
||||||
'vectors.pg_vector_index_stat': VectorsPgVectorIndexStat;
|
"vectors.pg_vector_index_stat": VectorsPgVectorIndexStat;
|
||||||
version_history: VersionHistory;
|
version_history: VersionHistory;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
|
import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
|
||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
|
import { mapUser, UserResponseDto } from 'src/dtos/user.dto';
|
||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
|
import { ActivityItem } from 'src/types';
|
||||||
import { Optional, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export enum ReactionType {
|
export enum ReactionType {
|
||||||
@ -67,13 +68,13 @@ export class ActivityCreateDto extends ActivityDto {
|
|||||||
comment?: string;
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapActivity(activity: ActivityEntity): ActivityResponseDto {
|
export const mapActivity = (activity: ActivityItem): ActivityResponseDto => {
|
||||||
return {
|
return {
|
||||||
id: activity.id,
|
id: activity.id,
|
||||||
assetId: activity.assetId,
|
assetId: activity.assetId,
|
||||||
createdAt: activity.createdAt,
|
createdAt: activity.createdAt,
|
||||||
comment: activity.comment,
|
comment: activity.comment,
|
||||||
type: activity.isLiked ? ReactionType.LIKE : ReactionType.COMMENT,
|
type: activity.isLiked ? ReactionType.LIKE : ReactionType.COMMENT,
|
||||||
user: mapUser(activity.user),
|
user: mapUser(activity.user as unknown as UserEntity),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import { Insertable } from 'kysely';
|
|
||||||
import { Activity } from 'src/db';
|
|
||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
|
||||||
import { ActivitySearch } from 'src/repositories/activity.repository';
|
|
||||||
|
|
||||||
export const IActivityRepository = 'IActivityRepository';
|
|
||||||
|
|
||||||
export interface IActivityRepository {
|
|
||||||
search(options: ActivitySearch): Promise<ActivityEntity[]>;
|
|
||||||
create(activity: Insertable<Activity>): Promise<ActivityEntity>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
getStatistics(options: { albumId: string; assetId?: string }): Promise<number>;
|
|
||||||
}
|
|
@ -1,14 +1,18 @@
|
|||||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
import { Insertable, Selectable, Updateable } from 'kysely';
|
||||||
|
import { AlbumsSharedUsersUsers } from 'src/db';
|
||||||
|
|
||||||
export const IAlbumUserRepository = 'IAlbumUserRepository';
|
export const IAlbumUserRepository = 'IAlbumUserRepository';
|
||||||
|
|
||||||
export type AlbumPermissionId = {
|
export type AlbumPermissionId = {
|
||||||
albumId: string;
|
albumsId: string;
|
||||||
userId: string;
|
usersId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IAlbumUserRepository {
|
export interface IAlbumUserRepository {
|
||||||
create(albumUser: Partial<AlbumUserEntity>): Promise<AlbumUserEntity>;
|
create(albumUser: Insertable<AlbumsSharedUsersUsers>): Promise<Selectable<AlbumsSharedUsersUsers>>;
|
||||||
update({ userId, albumId }: AlbumPermissionId, albumPermission: Partial<AlbumUserEntity>): Promise<AlbumUserEntity>;
|
update(
|
||||||
delete({ userId, albumId }: AlbumPermissionId): Promise<void>;
|
id: AlbumPermissionId,
|
||||||
|
albumPermission: Updateable<AlbumsSharedUsersUsers>,
|
||||||
|
): Promise<Selectable<AlbumsSharedUsersUsers>>;
|
||||||
|
delete(id: AlbumPermissionId): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Insertable, Updateable } from 'kysely';
|
||||||
|
import { Libraries } from 'src/db';
|
||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
|
|
||||||
@ -6,10 +8,10 @@ export const ILibraryRepository = 'ILibraryRepository';
|
|||||||
export interface ILibraryRepository {
|
export interface ILibraryRepository {
|
||||||
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
getAll(withDeleted?: boolean): Promise<LibraryEntity[]>;
|
||||||
getAllDeleted(): Promise<LibraryEntity[]>;
|
getAllDeleted(): Promise<LibraryEntity[]>;
|
||||||
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | null>;
|
get(id: string, withDeleted?: boolean): Promise<LibraryEntity | undefined>;
|
||||||
create(library: Partial<LibraryEntity>): Promise<LibraryEntity>;
|
create(library: Insertable<Libraries>): Promise<LibraryEntity>;
|
||||||
delete(id: string): Promise<void>;
|
delete(id: string): Promise<void>;
|
||||||
softDelete(id: string): Promise<void>;
|
softDelete(id: string): Promise<void>;
|
||||||
update(library: Partial<LibraryEntity>): Promise<LibraryEntity>;
|
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity>;
|
||||||
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined>;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { MemoryEntity } from 'src/entities/memory.entity';
|
import { Insertable, Updateable } from 'kysely';
|
||||||
|
import { Memories } from 'src/db';
|
||||||
|
import { MemoryEntity, OnThisDayData } from 'src/entities/memory.entity';
|
||||||
import { IBulkAsset } from 'src/utils/asset.util';
|
import { IBulkAsset } from 'src/utils/asset.util';
|
||||||
|
|
||||||
export const IMemoryRepository = 'IMemoryRepository';
|
export const IMemoryRepository = 'IMemoryRepository';
|
||||||
@ -6,7 +8,10 @@ export const IMemoryRepository = 'IMemoryRepository';
|
|||||||
export interface IMemoryRepository extends IBulkAsset {
|
export interface IMemoryRepository extends IBulkAsset {
|
||||||
search(ownerId: string): Promise<MemoryEntity[]>;
|
search(ownerId: string): Promise<MemoryEntity[]>;
|
||||||
get(id: string): Promise<MemoryEntity | null>;
|
get(id: string): Promise<MemoryEntity | null>;
|
||||||
create(memory: Partial<MemoryEntity>): Promise<MemoryEntity>;
|
create(
|
||||||
update(memory: Partial<MemoryEntity>): Promise<MemoryEntity>;
|
memory: Omit<Insertable<Memories>, 'data'> & { data: OnThisDayData },
|
||||||
|
assetIds: Set<string>,
|
||||||
|
): Promise<MemoryEntity>;
|
||||||
|
update(id: string, memory: Updateable<Memories>): Promise<MemoryEntity>;
|
||||||
delete(id: string): Promise<void>;
|
delete(id: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Updateable } from 'kysely';
|
||||||
|
import { Partners } from 'src/db';
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||||
|
|
||||||
export interface PartnerIds {
|
export interface PartnerIds {
|
||||||
@ -14,8 +16,8 @@ export const IPartnerRepository = 'IPartnerRepository';
|
|||||||
|
|
||||||
export interface IPartnerRepository {
|
export interface IPartnerRepository {
|
||||||
getAll(userId: string): Promise<PartnerEntity[]>;
|
getAll(userId: string): Promise<PartnerEntity[]>;
|
||||||
get(partner: PartnerIds): Promise<PartnerEntity | null>;
|
get(partner: PartnerIds): Promise<PartnerEntity | undefined>;
|
||||||
create(partner: PartnerIds): Promise<PartnerEntity>;
|
create(partner: PartnerIds): Promise<PartnerEntity>;
|
||||||
remove(entity: PartnerEntity): Promise<void>;
|
remove(partner: PartnerIds): Promise<void>;
|
||||||
update(entity: Partial<PartnerEntity>): Promise<PartnerEntity>;
|
update(partner: PartnerIds, entity: Updateable<Partners>): Promise<PartnerEntity>;
|
||||||
}
|
}
|
||||||
|
@ -1,180 +1,137 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
-- AccessRepository.activity.checkOwnerAccess
|
-- AccessRepository.activity.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"ActivityEntity"."id" AS "ActivityEntity_id"
|
"activity"."id"
|
||||||
FROM
|
from
|
||||||
"activity" "ActivityEntity"
|
"activity"
|
||||||
WHERE
|
where
|
||||||
(
|
"activity"."id" in ($1)
|
||||||
("ActivityEntity"."id" IN ($1))
|
and "activity"."userId" = $2
|
||||||
AND ("ActivityEntity"."userId" = $2)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.activity.checkAlbumOwnerAccess
|
-- AccessRepository.activity.checkAlbumOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"ActivityEntity"."id" AS "ActivityEntity_id"
|
"activity"."id"
|
||||||
FROM
|
from
|
||||||
"activity" "ActivityEntity"
|
"activity"
|
||||||
LEFT JOIN "albums" "ActivityEntity__ActivityEntity_album" ON "ActivityEntity__ActivityEntity_album"."id" = "ActivityEntity"."albumId"
|
left join "albums" on "activity"."albumId" = "albums"."id"
|
||||||
AND (
|
and "albums"."deletedAt" is null
|
||||||
"ActivityEntity__ActivityEntity_album"."deletedAt" IS NULL
|
where
|
||||||
)
|
"activity"."id" in ($1)
|
||||||
WHERE
|
and "albums"."ownerId" = $2::uuid
|
||||||
(
|
|
||||||
("ActivityEntity"."id" IN ($1))
|
|
||||||
AND (
|
|
||||||
(
|
|
||||||
(
|
|
||||||
"ActivityEntity__ActivityEntity_album"."ownerId" = $2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.activity.checkCreateAccess
|
-- AccessRepository.activity.checkCreateAccess
|
||||||
SELECT
|
select
|
||||||
"album"."id" AS "album_id"
|
"albums"."id"
|
||||||
FROM
|
from
|
||||||
"albums" "album"
|
"albums"
|
||||||
LEFT JOIN "albums_shared_users_users" "album_albumUsers_users" ON "album_albumUsers_users"."albumsId" = "album"."id"
|
left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
|
||||||
LEFT JOIN "users" "albumUsers" ON "albumUsers"."id" = "album_albumUsers_users"."usersId"
|
left join "users" on "users"."id" = "albumUsers"."usersId"
|
||||||
AND ("albumUsers"."deletedAt" IS NULL)
|
and "users"."deletedAt" is null
|
||||||
WHERE
|
where
|
||||||
(
|
"albums"."id" in ($1)
|
||||||
"album"."id" IN ($1)
|
and "albums"."isActivityEnabled" = $2
|
||||||
AND "album"."isActivityEnabled" = true
|
and (
|
||||||
AND (
|
"albums"."ownerId" = $3
|
||||||
"album"."ownerId" = $2
|
or "users"."id" = $4
|
||||||
OR "albumUsers"."id" = $2
|
|
||||||
)
|
)
|
||||||
)
|
and "albums"."deletedAt" is null
|
||||||
AND ("album"."deletedAt" IS NULL)
|
|
||||||
|
|
||||||
-- AccessRepository.album.checkOwnerAccess
|
-- AccessRepository.album.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"AlbumEntity"."id" AS "AlbumEntity_id"
|
"albums"."id"
|
||||||
FROM
|
from
|
||||||
"albums" "AlbumEntity"
|
"albums"
|
||||||
WHERE
|
where
|
||||||
(
|
"albums"."id" in ($1)
|
||||||
(
|
and "albums"."ownerId" = $2
|
||||||
("AlbumEntity"."id" IN ($1))
|
and "albums"."deletedAt" is null
|
||||||
AND ("AlbumEntity"."ownerId" = $2)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND ("AlbumEntity"."deletedAt" IS NULL)
|
|
||||||
|
|
||||||
-- AccessRepository.album.checkSharedAlbumAccess
|
-- AccessRepository.album.checkSharedAlbumAccess
|
||||||
SELECT
|
select
|
||||||
"AlbumEntity"."id" AS "AlbumEntity_id"
|
"albums"."id"
|
||||||
FROM
|
from
|
||||||
"albums" "AlbumEntity"
|
"albums"
|
||||||
LEFT JOIN "albums_shared_users_users" "AlbumEntity__AlbumEntity_albumUsers" ON "AlbumEntity__AlbumEntity_albumUsers"."albumsId" = "AlbumEntity"."id"
|
left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
|
||||||
LEFT JOIN "users" "a641d58cf46d4a391ba060ac4dc337665c69ffea" ON "a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = "AlbumEntity__AlbumEntity_albumUsers"."usersId"
|
left join "users" on "users"."id" = "albumUsers"."usersId"
|
||||||
AND (
|
and "users"."deletedAt" is null
|
||||||
"a641d58cf46d4a391ba060ac4dc337665c69ffea"."deletedAt" IS NULL
|
where
|
||||||
)
|
"albums"."id" in ($1)
|
||||||
WHERE
|
and "albums"."deletedAt" is null
|
||||||
(
|
and "users"."id" = $2
|
||||||
(
|
and "albumUsers"."role" in ($3, $4)
|
||||||
("AlbumEntity"."id" IN ($1))
|
|
||||||
AND (
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
"a641d58cf46d4a391ba060ac4dc337665c69ffea"."id" = $2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND (
|
|
||||||
"AlbumEntity__AlbumEntity_albumUsers"."role" IN ($3, $4)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND ("AlbumEntity"."deletedAt" IS NULL)
|
|
||||||
|
|
||||||
-- AccessRepository.album.checkSharedLinkAccess
|
-- AccessRepository.album.checkSharedLinkAccess
|
||||||
SELECT
|
select
|
||||||
"SharedLinkEntity"."albumId" AS "SharedLinkEntity_albumId",
|
"shared_links"."albumId"
|
||||||
"SharedLinkEntity"."id" AS "SharedLinkEntity_id"
|
from
|
||||||
FROM
|
"shared_links"
|
||||||
"shared_links" "SharedLinkEntity"
|
where
|
||||||
WHERE
|
"shared_links"."id" = $1
|
||||||
(
|
and "shared_links"."albumId" in ($2)
|
||||||
("SharedLinkEntity"."id" = $1)
|
|
||||||
AND ("SharedLinkEntity"."albumId" IN ($2))
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.asset.checkAlbumAccess
|
-- AccessRepository.asset.checkAlbumAccess
|
||||||
SELECT
|
select
|
||||||
"asset"."id" AS "assetId",
|
"assets"."id",
|
||||||
"asset"."livePhotoVideoId" AS "livePhotoVideoId"
|
"assets"."livePhotoVideoId"
|
||||||
FROM
|
from
|
||||||
"albums" "album"
|
"albums"
|
||||||
INNER JOIN "albums_assets_assets" "album_asset" ON "album_asset"."albumsId" = "album"."id"
|
inner join "albums_assets_assets" as "albumAssets" on "albums"."id" = "albumAssets"."albumsId"
|
||||||
INNER JOIN "assets" "asset" ON "asset"."id" = "album_asset"."assetsId"
|
inner join "assets" on "assets"."id" = "albumAssets"."assetsId"
|
||||||
AND ("asset"."deletedAt" IS NULL)
|
and "assets"."deletedAt" is null
|
||||||
LEFT JOIN "albums_shared_users_users" "album_albumUsers_users" ON "album_albumUsers_users"."albumsId" = "album"."id"
|
left join "albums_shared_users_users" as "albumUsers" on "albumUsers"."albumsId" = "albums"."id"
|
||||||
LEFT JOIN "users" "albumUsers" ON "albumUsers"."id" = "album_albumUsers_users"."usersId"
|
left join "users" on "users"."id" = "albumUsers"."usersId"
|
||||||
AND ("albumUsers"."deletedAt" IS NULL)
|
and "users"."deletedAt" is null
|
||||||
WHERE
|
where
|
||||||
(
|
array["assets"."id", "assets"."livePhotoVideoId"] && array[$1]::uuid []
|
||||||
array["asset"."id", "asset"."livePhotoVideoId"] && array[$1]::uuid []
|
and (
|
||||||
AND (
|
"albums"."ownerId" = $2
|
||||||
"album"."ownerId" = $2
|
or "users"."id" = $3
|
||||||
OR "albumUsers"."id" = $2
|
|
||||||
)
|
)
|
||||||
)
|
and "albums"."deletedAt" is null
|
||||||
AND ("album"."deletedAt" IS NULL)
|
|
||||||
|
|
||||||
-- AccessRepository.asset.checkOwnerAccess
|
-- AccessRepository.asset.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"AssetEntity"."id" AS "AssetEntity_id"
|
"assets"."id"
|
||||||
FROM
|
from
|
||||||
"assets" "AssetEntity"
|
"assets"
|
||||||
WHERE
|
where
|
||||||
(
|
"assets"."id" in ($1)
|
||||||
("AssetEntity"."id" IN ($1))
|
and "assets"."ownerId" = $2
|
||||||
AND ("AssetEntity"."ownerId" = $2)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.asset.checkPartnerAccess
|
-- AccessRepository.asset.checkPartnerAccess
|
||||||
SELECT
|
select
|
||||||
"asset"."id" AS "assetId"
|
"assets"."id"
|
||||||
FROM
|
from
|
||||||
"partners" "partner"
|
"partners" as "partner"
|
||||||
INNER JOIN "users" "sharedBy" ON "sharedBy"."id" = "partner"."sharedById"
|
inner join "users" as "sharedBy" on "sharedBy"."id" = "partner"."sharedById"
|
||||||
AND ("sharedBy"."deletedAt" IS NULL)
|
and "sharedBy"."deletedAt" is null
|
||||||
INNER JOIN "assets" "asset" ON "asset"."ownerId" = "sharedBy"."id"
|
inner join "assets" on "assets"."ownerId" = "sharedBy"."id"
|
||||||
AND ("asset"."deletedAt" IS NULL)
|
and "assets"."deletedAt" is null
|
||||||
WHERE
|
where
|
||||||
"partner"."sharedWithId" = $1
|
"partner"."sharedWithId" = $1
|
||||||
AND "asset"."isArchived" = false
|
and "assets"."isArchived" = $2
|
||||||
AND "asset"."id" IN ($2)
|
and "assets"."id" in ($3)
|
||||||
|
|
||||||
-- AccessRepository.asset.checkSharedLinkAccess
|
-- AccessRepository.asset.checkSharedLinkAccess
|
||||||
SELECT
|
select
|
||||||
"assets"."id" AS "assetId",
|
"assets"."id" as "assetId",
|
||||||
"assets"."livePhotoVideoId" AS "assetLivePhotoVideoId",
|
"assets"."livePhotoVideoId" as "assetLivePhotoVideoId",
|
||||||
"albumAssets"."id" AS "albumAssetId",
|
"albumAssets"."id" as "albumAssetId",
|
||||||
"albumAssets"."livePhotoVideoId" AS "albumAssetLivePhotoVideoId"
|
"albumAssets"."livePhotoVideoId" as "albumAssetLivePhotoVideoId"
|
||||||
FROM
|
from
|
||||||
"shared_links" "sharedLink"
|
"shared_links"
|
||||||
LEFT JOIN "albums" "album" ON "album"."id" = "sharedLink"."albumId"
|
left join "albums" on "albums"."id" = "shared_links"."albumId"
|
||||||
AND ("album"."deletedAt" IS NULL)
|
and "albums"."deletedAt" is null
|
||||||
LEFT JOIN "shared_link__asset" "assets_sharedLink" ON "assets_sharedLink"."sharedLinksId" = "sharedLink"."id"
|
left join "shared_link__asset" on "shared_link__asset"."sharedLinksId" = "shared_links"."id"
|
||||||
LEFT JOIN "assets" "assets" ON "assets"."id" = "assets_sharedLink"."assetsId"
|
left join "assets" on "assets"."id" = "shared_link__asset"."assetsId"
|
||||||
AND ("assets"."deletedAt" IS NULL)
|
and "assets"."deletedAt" is null
|
||||||
LEFT JOIN "albums_assets_assets" "album_albumAssets" ON "album_albumAssets"."albumsId" = "album"."id"
|
left join "albums_assets_assets" on "albums_assets_assets"."albumsId" = "albums"."id"
|
||||||
LEFT JOIN "assets" "albumAssets" ON "albumAssets"."id" = "album_albumAssets"."assetsId"
|
left join "assets" as "albumAssets" on "albumAssets"."id" = "albums_assets_assets"."assetsId"
|
||||||
AND ("albumAssets"."deletedAt" IS NULL)
|
and "albumAssets"."deletedAt" is null
|
||||||
WHERE
|
where
|
||||||
"sharedLink"."id" = $1
|
"shared_links"."id" = $1
|
||||||
AND array[
|
and array[
|
||||||
"assets"."id",
|
"assets"."id",
|
||||||
"assets"."livePhotoVideoId",
|
"assets"."livePhotoVideoId",
|
||||||
"albumAssets"."id",
|
"albumAssets"."id",
|
||||||
@ -182,100 +139,76 @@ WHERE
|
|||||||
] && array[$2]::uuid []
|
] && array[$2]::uuid []
|
||||||
|
|
||||||
-- AccessRepository.authDevice.checkOwnerAccess
|
-- AccessRepository.authDevice.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"SessionEntity"."id" AS "SessionEntity_id"
|
"sessions"."id"
|
||||||
FROM
|
from
|
||||||
"sessions" "SessionEntity"
|
"sessions"
|
||||||
WHERE
|
where
|
||||||
(
|
"sessions"."userId" = $1
|
||||||
("SessionEntity"."userId" = $1)
|
and "sessions"."id" in ($2)
|
||||||
AND ("SessionEntity"."id" IN ($2))
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.memory.checkOwnerAccess
|
-- AccessRepository.memory.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"MemoryEntity"."id" AS "MemoryEntity_id"
|
"memories"."id"
|
||||||
FROM
|
from
|
||||||
"memories" "MemoryEntity"
|
"memories"
|
||||||
WHERE
|
where
|
||||||
(
|
"memories"."id" in ($1)
|
||||||
(
|
and "memories"."ownerId" = $2
|
||||||
("MemoryEntity"."id" IN ($1))
|
and "memories"."deletedAt" is null
|
||||||
AND ("MemoryEntity"."ownerId" = $2)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND ("MemoryEntity"."deletedAt" IS NULL)
|
|
||||||
|
|
||||||
-- AccessRepository.person.checkOwnerAccess
|
-- AccessRepository.person.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"PersonEntity"."id" AS "PersonEntity_id"
|
"person"."id"
|
||||||
FROM
|
from
|
||||||
"person" "PersonEntity"
|
"person"
|
||||||
WHERE
|
where
|
||||||
(
|
"person"."id" in ($1)
|
||||||
("PersonEntity"."id" IN ($1))
|
and "person"."ownerId" = $2
|
||||||
AND ("PersonEntity"."ownerId" = $2)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.person.checkFaceOwnerAccess
|
-- AccessRepository.person.checkFaceOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"AssetFaceEntity"."id" AS "AssetFaceEntity_id"
|
"asset_faces"."id"
|
||||||
FROM
|
from
|
||||||
"asset_faces" "AssetFaceEntity"
|
"asset_faces"
|
||||||
LEFT JOIN "assets" "AssetFaceEntity__AssetFaceEntity_asset" ON "AssetFaceEntity__AssetFaceEntity_asset"."id" = "AssetFaceEntity"."assetId"
|
left join "assets" on "assets"."id" = "asset_faces"."assetId"
|
||||||
AND (
|
and "assets"."deletedAt" is null
|
||||||
"AssetFaceEntity__AssetFaceEntity_asset"."deletedAt" IS NULL
|
where
|
||||||
)
|
"asset_faces"."id" in ($1)
|
||||||
WHERE
|
and "assets"."ownerId" = $2
|
||||||
(
|
|
||||||
("AssetFaceEntity"."id" IN ($1))
|
|
||||||
AND (
|
|
||||||
(
|
|
||||||
(
|
|
||||||
"AssetFaceEntity__AssetFaceEntity_asset"."ownerId" = $2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.partner.checkUpdateAccess
|
-- AccessRepository.partner.checkUpdateAccess
|
||||||
SELECT
|
select
|
||||||
"partner"."sharedById" AS "partner_sharedById",
|
"partners"."sharedById"
|
||||||
"partner"."sharedWithId" AS "partner_sharedWithId"
|
from
|
||||||
FROM
|
"partners"
|
||||||
"partners" "partner"
|
where
|
||||||
WHERE
|
"partners"."sharedById" in ($1)
|
||||||
"partner"."sharedById" IN ($1)
|
and "partners"."sharedWithId" = $2
|
||||||
AND "partner"."sharedWithId" = $2
|
|
||||||
|
|
||||||
-- AccessRepository.stack.checkOwnerAccess
|
-- AccessRepository.stack.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"StackEntity"."id" AS "StackEntity_id"
|
"stacks"."id"
|
||||||
FROM
|
from
|
||||||
"asset_stack" "StackEntity"
|
"asset_stack" as "stacks"
|
||||||
WHERE
|
where
|
||||||
(
|
"stacks"."id" in ($1)
|
||||||
("StackEntity"."id" IN ($1))
|
and "stacks"."ownerId" = $2
|
||||||
AND ("StackEntity"."ownerId" = $2)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.tag.checkOwnerAccess
|
-- AccessRepository.tag.checkOwnerAccess
|
||||||
SELECT
|
select
|
||||||
"TagEntity"."id" AS "TagEntity_id"
|
"tags"."id"
|
||||||
FROM
|
from
|
||||||
"tags" "TagEntity"
|
"tags"
|
||||||
WHERE
|
where
|
||||||
(
|
"tags"."id" in ($1)
|
||||||
("TagEntity"."id" IN ($1))
|
and "tags"."userId" = $2
|
||||||
AND ("TagEntity"."userId" = $2)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- AccessRepository.timeline.checkPartnerAccess
|
-- AccessRepository.timeline.checkPartnerAccess
|
||||||
SELECT
|
select
|
||||||
"partner"."sharedById" AS "partner_sharedById",
|
"partners"."sharedById"
|
||||||
"partner"."sharedWithId" AS "partner_sharedWithId"
|
from
|
||||||
FROM
|
"partners"
|
||||||
"partners" "partner"
|
where
|
||||||
WHERE
|
"partners"."sharedById" in ($1)
|
||||||
"partner"."sharedById" IN ($1)
|
and "partners"."sharedWithId" = $2
|
||||||
AND "partner"."sharedWithId" = $2
|
|
||||||
|
@ -9,7 +9,11 @@ select
|
|||||||
from
|
from
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
*
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
from
|
from
|
||||||
"users"
|
"users"
|
||||||
where
|
where
|
||||||
|
25
server/src/queries/album.user.repository.sql
Normal file
25
server/src/queries/album.user.repository.sql
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- AlbumUserRepository.create
|
||||||
|
insert into
|
||||||
|
"albums_shared_users_users" ("usersId", "albumsId")
|
||||||
|
values
|
||||||
|
($1, $2)
|
||||||
|
returning
|
||||||
|
*
|
||||||
|
|
||||||
|
-- AlbumUserRepository.update
|
||||||
|
update "albums_shared_users_users"
|
||||||
|
set
|
||||||
|
"role" = $1
|
||||||
|
where
|
||||||
|
"usersId" = $2
|
||||||
|
and "albumsId" = $3
|
||||||
|
returning
|
||||||
|
*
|
||||||
|
|
||||||
|
-- AlbumUserRepository.delete
|
||||||
|
delete from "albums_shared_users_users"
|
||||||
|
where
|
||||||
|
"usersId" = $1
|
||||||
|
and "albumsId" = $2
|
@ -1,150 +1,137 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
-- LibraryRepository.get
|
-- LibraryRepository.get
|
||||||
SELECT DISTINCT
|
select
|
||||||
"distinctAlias"."LibraryEntity_id" AS "ids_LibraryEntity_id"
|
"libraries".*,
|
||||||
FROM
|
|
||||||
(
|
(
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
to_json(obj)
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
from
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
(
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
select
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
"users"."id",
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
"users"."email",
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
"users"."createdAt",
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
where
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
"users"."id" = "libraries"."ownerId"
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
) as obj
|
||||||
FROM
|
) as "owner"
|
||||||
"libraries" "LibraryEntity"
|
from
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
"libraries"
|
||||||
AND (
|
where
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" IS NULL
|
"libraries"."id" = $1
|
||||||
)
|
and "libraries"."deletedAt" is null
|
||||||
WHERE
|
|
||||||
((("LibraryEntity"."id" = $1)))
|
|
||||||
AND ("LibraryEntity"."deletedAt" IS NULL)
|
|
||||||
) "distinctAlias"
|
|
||||||
ORDER BY
|
|
||||||
"LibraryEntity_id" ASC
|
|
||||||
LIMIT
|
|
||||||
1
|
|
||||||
|
|
||||||
-- LibraryRepository.getAll
|
-- LibraryRepository.getAll
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
"libraries".*,
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
(
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
select
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
to_json(obj)
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
from
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
(
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
select
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."id",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."email",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."createdAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
"users"
|
||||||
FROM
|
where
|
||||||
"libraries" "LibraryEntity"
|
"users"."id" = "libraries"."ownerId"
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
) as obj
|
||||||
AND (
|
) as "owner"
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" IS NULL
|
from
|
||||||
)
|
"libraries"
|
||||||
WHERE
|
where
|
||||||
"LibraryEntity"."deletedAt" IS NULL
|
"libraries"."deletedAt" is null
|
||||||
ORDER BY
|
order by
|
||||||
"LibraryEntity"."createdAt" ASC
|
"createdAt" asc
|
||||||
|
|
||||||
-- LibraryRepository.getAllDeleted
|
-- LibraryRepository.getAllDeleted
|
||||||
SELECT
|
select
|
||||||
"LibraryEntity"."id" AS "LibraryEntity_id",
|
"libraries".*,
|
||||||
"LibraryEntity"."name" AS "LibraryEntity_name",
|
(
|
||||||
"LibraryEntity"."ownerId" AS "LibraryEntity_ownerId",
|
select
|
||||||
"LibraryEntity"."importPaths" AS "LibraryEntity_importPaths",
|
to_json(obj)
|
||||||
"LibraryEntity"."exclusionPatterns" AS "LibraryEntity_exclusionPatterns",
|
from
|
||||||
"LibraryEntity"."createdAt" AS "LibraryEntity_createdAt",
|
(
|
||||||
"LibraryEntity"."updatedAt" AS "LibraryEntity_updatedAt",
|
select
|
||||||
"LibraryEntity"."deletedAt" AS "LibraryEntity_deletedAt",
|
"users"."id",
|
||||||
"LibraryEntity"."refreshedAt" AS "LibraryEntity_refreshedAt",
|
"users"."email",
|
||||||
"LibraryEntity__LibraryEntity_owner"."id" AS "LibraryEntity__LibraryEntity_owner_id",
|
"users"."createdAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."name" AS "LibraryEntity__LibraryEntity_owner_name",
|
"users"."profileImagePath",
|
||||||
"LibraryEntity__LibraryEntity_owner"."isAdmin" AS "LibraryEntity__LibraryEntity_owner_isAdmin",
|
"users"."isAdmin",
|
||||||
"LibraryEntity__LibraryEntity_owner"."email" AS "LibraryEntity__LibraryEntity_owner_email",
|
"users"."shouldChangePassword",
|
||||||
"LibraryEntity__LibraryEntity_owner"."storageLabel" AS "LibraryEntity__LibraryEntity_owner_storageLabel",
|
"users"."deletedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."oauthId" AS "LibraryEntity__LibraryEntity_owner_oauthId",
|
"users"."oauthId",
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileImagePath" AS "LibraryEntity__LibraryEntity_owner_profileImagePath",
|
"users"."updatedAt",
|
||||||
"LibraryEntity__LibraryEntity_owner"."shouldChangePassword" AS "LibraryEntity__LibraryEntity_owner_shouldChangePassword",
|
"users"."storageLabel",
|
||||||
"LibraryEntity__LibraryEntity_owner"."createdAt" AS "LibraryEntity__LibraryEntity_owner_createdAt",
|
"users"."name",
|
||||||
"LibraryEntity__LibraryEntity_owner"."deletedAt" AS "LibraryEntity__LibraryEntity_owner_deletedAt",
|
"users"."quotaSizeInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."status" AS "LibraryEntity__LibraryEntity_owner_status",
|
"users"."quotaUsageInBytes",
|
||||||
"LibraryEntity__LibraryEntity_owner"."updatedAt" AS "LibraryEntity__LibraryEntity_owner_updatedAt",
|
"users"."status",
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaSizeInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaSizeInBytes",
|
"users"."profileChangedAt"
|
||||||
"LibraryEntity__LibraryEntity_owner"."quotaUsageInBytes" AS "LibraryEntity__LibraryEntity_owner_quotaUsageInBytes",
|
from
|
||||||
"LibraryEntity__LibraryEntity_owner"."profileChangedAt" AS "LibraryEntity__LibraryEntity_owner_profileChangedAt"
|
"users"
|
||||||
FROM
|
where
|
||||||
"libraries" "LibraryEntity"
|
"users"."id" = "libraries"."ownerId"
|
||||||
LEFT JOIN "users" "LibraryEntity__LibraryEntity_owner" ON "LibraryEntity__LibraryEntity_owner"."id" = "LibraryEntity"."ownerId"
|
) as obj
|
||||||
WHERE
|
) as "owner"
|
||||||
((NOT ("LibraryEntity"."deletedAt" IS NULL)))
|
from
|
||||||
ORDER BY
|
"libraries"
|
||||||
"LibraryEntity"."createdAt" ASC
|
where
|
||||||
|
"libraries"."deletedAt" is not null
|
||||||
|
order by
|
||||||
|
"createdAt" asc
|
||||||
|
|
||||||
-- LibraryRepository.getStatistics
|
-- LibraryRepository.getStatistics
|
||||||
SELECT
|
select
|
||||||
"libraries"."id" AS "libraries_id",
|
count("assets"."id") filter (
|
||||||
"libraries"."name" AS "libraries_name",
|
where
|
||||||
"libraries"."ownerId" AS "libraries_ownerId",
|
(
|
||||||
"libraries"."importPaths" AS "libraries_importPaths",
|
"assets"."type" = $1
|
||||||
"libraries"."exclusionPatterns" AS "libraries_exclusionPatterns",
|
and "assets"."isVisible" = $2
|
||||||
"libraries"."createdAt" AS "libraries_createdAt",
|
)
|
||||||
"libraries"."updatedAt" AS "libraries_updatedAt",
|
) as "photos",
|
||||||
"libraries"."deletedAt" AS "libraries_deletedAt",
|
count(*) filter (
|
||||||
"libraries"."refreshedAt" AS "libraries_refreshedAt",
|
where
|
||||||
COUNT("assets"."id") FILTER (
|
(
|
||||||
WHERE
|
"assets"."type" = $3
|
||||||
"assets"."type" = 'IMAGE'
|
and "assets"."isVisible" = $4
|
||||||
AND "assets"."isVisible"
|
)
|
||||||
) AS "photos",
|
) as "videos",
|
||||||
COUNT("assets"."id") FILTER (
|
coalesce(sum("exif"."fileSizeInByte"), $5) as "usage"
|
||||||
WHERE
|
from
|
||||||
"assets"."type" = 'VIDEO'
|
"libraries"
|
||||||
AND "assets"."isVisible"
|
inner join "assets" on "assets"."libraryId" = "libraries"."id"
|
||||||
) AS "videos",
|
inner join "exif" on "exif"."assetId" = "assets"."id"
|
||||||
COALESCE(SUM("exif"."fileSizeInByte"), 0) AS "usage"
|
where
|
||||||
FROM
|
"libraries"."id" = $6
|
||||||
"libraries" "libraries"
|
group by
|
||||||
LEFT JOIN "assets" "assets" ON "assets"."libraryId" = "libraries"."id"
|
|
||||||
AND ("assets"."deletedAt" IS NULL)
|
|
||||||
LEFT JOIN "exif" "exif" ON "exif"."assetId" = "assets"."id"
|
|
||||||
WHERE
|
|
||||||
("libraries"."id" = $1)
|
|
||||||
AND ("libraries"."deletedAt" IS NULL)
|
|
||||||
GROUP BY
|
|
||||||
"libraries"."id"
|
"libraries"."id"
|
||||||
|
@ -1,10 +1,85 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- MemoryRepository.search
|
||||||
|
select
|
||||||
|
*
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"ownerId" = $1
|
||||||
|
order by
|
||||||
|
"memoryAt" desc
|
||||||
|
|
||||||
|
-- MemoryRepository.get
|
||||||
|
select
|
||||||
|
"memories".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"assets".*
|
||||||
|
from
|
||||||
|
"assets"
|
||||||
|
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
|
||||||
|
where
|
||||||
|
"memories_assets_assets"."memoriesId" = "memories"."id"
|
||||||
|
and "assets"."deletedAt" is null
|
||||||
|
) as agg
|
||||||
|
) as "assets"
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"id" = $1
|
||||||
|
and "deletedAt" is null
|
||||||
|
|
||||||
|
-- MemoryRepository.update
|
||||||
|
update "memories"
|
||||||
|
set
|
||||||
|
"ownerId" = $1,
|
||||||
|
"isSaved" = $2
|
||||||
|
where
|
||||||
|
"id" = $3
|
||||||
|
select
|
||||||
|
"memories".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"assets".*
|
||||||
|
from
|
||||||
|
"assets"
|
||||||
|
inner join "memories_assets_assets" on "assets"."id" = "memories_assets_assets"."assetsId"
|
||||||
|
where
|
||||||
|
"memories_assets_assets"."memoriesId" = "memories"."id"
|
||||||
|
and "assets"."deletedAt" is null
|
||||||
|
) as agg
|
||||||
|
) as "assets"
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"id" = $1
|
||||||
|
and "deletedAt" is null
|
||||||
|
|
||||||
|
-- MemoryRepository.delete
|
||||||
|
delete from "memories"
|
||||||
|
where
|
||||||
|
"id" = $1
|
||||||
|
|
||||||
-- MemoryRepository.getAssetIds
|
-- MemoryRepository.getAssetIds
|
||||||
SELECT
|
select
|
||||||
"memories_assets"."assetsId" AS "assetId"
|
"assetsId"
|
||||||
FROM
|
from
|
||||||
"memories_assets_assets" "memories_assets"
|
"memories_assets_assets"
|
||||||
WHERE
|
where
|
||||||
"memories_assets"."memoriesId" = $1
|
"memoriesId" = $1
|
||||||
AND "memories_assets"."assetsId" IN ($2)
|
and "assetsId" in ($2)
|
||||||
|
|
||||||
|
-- MemoryRepository.addAssetIds
|
||||||
|
insert into
|
||||||
|
"memories_assets_assets" ("memoriesId", "assetsId")
|
||||||
|
values
|
||||||
|
($1, $2)
|
||||||
|
189
server/src/queries/partner.repository.sql
Normal file
189
server/src/queries/partner.repository.sql
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- PartnerRepository.getAll
|
||||||
|
select
|
||||||
|
"partners".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedBy"
|
||||||
|
where
|
||||||
|
"sharedBy"."id" = "partners"."sharedById"
|
||||||
|
) as obj
|
||||||
|
) as "sharedBy",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedWith"
|
||||||
|
where
|
||||||
|
"sharedWith"."id" = "partners"."sharedWithId"
|
||||||
|
) as obj
|
||||||
|
) as "sharedWith"
|
||||||
|
from
|
||||||
|
"partners"
|
||||||
|
inner join "users" as "sharedBy" on "partners"."sharedById" = "sharedBy"."id"
|
||||||
|
and "sharedBy"."deletedAt" is null
|
||||||
|
inner join "users" as "sharedWith" on "partners"."sharedWithId" = "sharedWith"."id"
|
||||||
|
and "sharedWith"."deletedAt" is null
|
||||||
|
where
|
||||||
|
(
|
||||||
|
"sharedWithId" = $1
|
||||||
|
or "sharedById" = $2
|
||||||
|
)
|
||||||
|
|
||||||
|
-- PartnerRepository.get
|
||||||
|
select
|
||||||
|
"partners".*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedBy"
|
||||||
|
where
|
||||||
|
"sharedBy"."id" = "partners"."sharedById"
|
||||||
|
) as obj
|
||||||
|
) as "sharedBy",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedWith"
|
||||||
|
where
|
||||||
|
"sharedWith"."id" = "partners"."sharedWithId"
|
||||||
|
) as obj
|
||||||
|
) as "sharedWith"
|
||||||
|
from
|
||||||
|
"partners"
|
||||||
|
inner join "users" as "sharedBy" on "partners"."sharedById" = "sharedBy"."id"
|
||||||
|
and "sharedBy"."deletedAt" is null
|
||||||
|
inner join "users" as "sharedWith" on "partners"."sharedWithId" = "sharedWith"."id"
|
||||||
|
and "sharedWith"."deletedAt" is null
|
||||||
|
where
|
||||||
|
"sharedWithId" = $1
|
||||||
|
and "sharedById" = $2
|
||||||
|
|
||||||
|
-- PartnerRepository.create
|
||||||
|
insert into
|
||||||
|
"partners" ("sharedWithId", "sharedById")
|
||||||
|
values
|
||||||
|
($1, $2)
|
||||||
|
returning
|
||||||
|
*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedBy"
|
||||||
|
where
|
||||||
|
"sharedBy"."id" = "partners"."sharedById"
|
||||||
|
) as obj
|
||||||
|
) as "sharedBy",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedWith"
|
||||||
|
where
|
||||||
|
"sharedWith"."id" = "partners"."sharedWithId"
|
||||||
|
) as obj
|
||||||
|
) as "sharedWith"
|
||||||
|
|
||||||
|
-- PartnerRepository.update
|
||||||
|
update "partners"
|
||||||
|
set
|
||||||
|
"inTimeline" = $1
|
||||||
|
where
|
||||||
|
"sharedWithId" = $2
|
||||||
|
and "sharedById" = $3
|
||||||
|
returning
|
||||||
|
*,
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedBy"
|
||||||
|
where
|
||||||
|
"sharedBy"."id" = "partners"."sharedById"
|
||||||
|
) as obj
|
||||||
|
) as "sharedBy",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
to_json(obj)
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"email",
|
||||||
|
"profileImagePath",
|
||||||
|
"profileChangedAt"
|
||||||
|
from
|
||||||
|
"users" as "sharedWith"
|
||||||
|
where
|
||||||
|
"sharedWith"."id" = "partners"."sharedWithId"
|
||||||
|
) as obj
|
||||||
|
) as "sharedWith"
|
||||||
|
|
||||||
|
-- PartnerRepository.remove
|
||||||
|
delete from "partners"
|
||||||
|
where
|
||||||
|
"sharedWithId" = $1
|
||||||
|
and "sharedById" = $2
|
257
server/src/queries/stack.repository.sql
Normal file
257
server/src/queries/stack.repository.sql
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- StackRepository.search
|
||||||
|
SELECT
|
||||||
|
"StackEntity"."id" AS "StackEntity_id",
|
||||||
|
"StackEntity"."ownerId" AS "StackEntity_ownerId",
|
||||||
|
"StackEntity"."primaryAssetId" AS "StackEntity_primaryAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."id" AS "StackEntity__StackEntity_assets_id",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceAssetId" AS "StackEntity__StackEntity_assets_deviceAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."ownerId" AS "StackEntity__StackEntity_assets_ownerId",
|
||||||
|
"StackEntity__StackEntity_assets"."libraryId" AS "StackEntity__StackEntity_assets_libraryId",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceId" AS "StackEntity__StackEntity_assets_deviceId",
|
||||||
|
"StackEntity__StackEntity_assets"."type" AS "StackEntity__StackEntity_assets_type",
|
||||||
|
"StackEntity__StackEntity_assets"."status" AS "StackEntity__StackEntity_assets_status",
|
||||||
|
"StackEntity__StackEntity_assets"."originalPath" AS "StackEntity__StackEntity_assets_originalPath",
|
||||||
|
"StackEntity__StackEntity_assets"."thumbhash" AS "StackEntity__StackEntity_assets_thumbhash",
|
||||||
|
"StackEntity__StackEntity_assets"."encodedVideoPath" AS "StackEntity__StackEntity_assets_encodedVideoPath",
|
||||||
|
"StackEntity__StackEntity_assets"."createdAt" AS "StackEntity__StackEntity_assets_createdAt",
|
||||||
|
"StackEntity__StackEntity_assets"."updatedAt" AS "StackEntity__StackEntity_assets_updatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" AS "StackEntity__StackEntity_assets_deletedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."fileCreatedAt" AS "StackEntity__StackEntity_assets_fileCreatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."localDateTime" AS "StackEntity__StackEntity_assets_localDateTime",
|
||||||
|
"StackEntity__StackEntity_assets"."fileModifiedAt" AS "StackEntity__StackEntity_assets_fileModifiedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."isFavorite" AS "StackEntity__StackEntity_assets_isFavorite",
|
||||||
|
"StackEntity__StackEntity_assets"."isArchived" AS "StackEntity__StackEntity_assets_isArchived",
|
||||||
|
"StackEntity__StackEntity_assets"."isExternal" AS "StackEntity__StackEntity_assets_isExternal",
|
||||||
|
"StackEntity__StackEntity_assets"."isOffline" AS "StackEntity__StackEntity_assets_isOffline",
|
||||||
|
"StackEntity__StackEntity_assets"."checksum" AS "StackEntity__StackEntity_assets_checksum",
|
||||||
|
"StackEntity__StackEntity_assets"."duration" AS "StackEntity__StackEntity_assets_duration",
|
||||||
|
"StackEntity__StackEntity_assets"."isVisible" AS "StackEntity__StackEntity_assets_isVisible",
|
||||||
|
"StackEntity__StackEntity_assets"."livePhotoVideoId" AS "StackEntity__StackEntity_assets_livePhotoVideoId",
|
||||||
|
"StackEntity__StackEntity_assets"."originalFileName" AS "StackEntity__StackEntity_assets_originalFileName",
|
||||||
|
"StackEntity__StackEntity_assets"."sidecarPath" AS "StackEntity__StackEntity_assets_sidecarPath",
|
||||||
|
"StackEntity__StackEntity_assets"."stackId" AS "StackEntity__StackEntity_assets_stackId",
|
||||||
|
"StackEntity__StackEntity_assets"."duplicateId" AS "StackEntity__StackEntity_assets_duplicateId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_assetId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."description" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_description",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageWidth" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageWidth",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageHeight" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageHeight",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fileSizeInByte" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fileSizeInByte",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."orientation" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_orientation",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."dateTimeOriginal" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_dateTimeOriginal",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."modifyDate" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_modifyDate",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."timeZone" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_timeZone",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."latitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_latitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."longitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_longitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."projectionType" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_projectionType",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."city" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_city",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."livePhotoCID" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_livePhotoCID",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."autoStackId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_autoStackId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."state" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_state",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."country" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_country",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."make" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_make",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."model" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_model",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."lensModel" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_lensModel",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fNumber" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fNumber",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."focalLength" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_focalLength",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."iso" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_iso",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exposureTime" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exposureTime",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."profileDescription" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_profileDescription",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."colorspace" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_colorspace",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."bitsPerSample" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_bitsPerSample",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."rating" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_rating",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fps" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fps"
|
||||||
|
FROM
|
||||||
|
"asset_stack" "StackEntity"
|
||||||
|
LEFT JOIN "assets" "StackEntity__StackEntity_assets" ON "StackEntity__StackEntity_assets"."stackId" = "StackEntity"."id"
|
||||||
|
AND (
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" IS NULL
|
||||||
|
)
|
||||||
|
LEFT JOIN "exif" "01db479afeb88793eed8e0d1dde6ccfccf1698b9" ON "01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" = "StackEntity__StackEntity_assets"."id"
|
||||||
|
WHERE
|
||||||
|
(("StackEntity"."ownerId" = $1))
|
||||||
|
|
||||||
|
-- StackRepository.delete
|
||||||
|
SELECT DISTINCT
|
||||||
|
"distinctAlias"."StackEntity_id" AS "ids_StackEntity_id",
|
||||||
|
"distinctAlias"."StackEntity__StackEntity_assets_fileCreatedAt"
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
"StackEntity"."id" AS "StackEntity_id",
|
||||||
|
"StackEntity"."ownerId" AS "StackEntity_ownerId",
|
||||||
|
"StackEntity"."primaryAssetId" AS "StackEntity_primaryAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."id" AS "StackEntity__StackEntity_assets_id",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceAssetId" AS "StackEntity__StackEntity_assets_deviceAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."ownerId" AS "StackEntity__StackEntity_assets_ownerId",
|
||||||
|
"StackEntity__StackEntity_assets"."libraryId" AS "StackEntity__StackEntity_assets_libraryId",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceId" AS "StackEntity__StackEntity_assets_deviceId",
|
||||||
|
"StackEntity__StackEntity_assets"."type" AS "StackEntity__StackEntity_assets_type",
|
||||||
|
"StackEntity__StackEntity_assets"."status" AS "StackEntity__StackEntity_assets_status",
|
||||||
|
"StackEntity__StackEntity_assets"."originalPath" AS "StackEntity__StackEntity_assets_originalPath",
|
||||||
|
"StackEntity__StackEntity_assets"."thumbhash" AS "StackEntity__StackEntity_assets_thumbhash",
|
||||||
|
"StackEntity__StackEntity_assets"."encodedVideoPath" AS "StackEntity__StackEntity_assets_encodedVideoPath",
|
||||||
|
"StackEntity__StackEntity_assets"."createdAt" AS "StackEntity__StackEntity_assets_createdAt",
|
||||||
|
"StackEntity__StackEntity_assets"."updatedAt" AS "StackEntity__StackEntity_assets_updatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" AS "StackEntity__StackEntity_assets_deletedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."fileCreatedAt" AS "StackEntity__StackEntity_assets_fileCreatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."localDateTime" AS "StackEntity__StackEntity_assets_localDateTime",
|
||||||
|
"StackEntity__StackEntity_assets"."fileModifiedAt" AS "StackEntity__StackEntity_assets_fileModifiedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."isFavorite" AS "StackEntity__StackEntity_assets_isFavorite",
|
||||||
|
"StackEntity__StackEntity_assets"."isArchived" AS "StackEntity__StackEntity_assets_isArchived",
|
||||||
|
"StackEntity__StackEntity_assets"."isExternal" AS "StackEntity__StackEntity_assets_isExternal",
|
||||||
|
"StackEntity__StackEntity_assets"."isOffline" AS "StackEntity__StackEntity_assets_isOffline",
|
||||||
|
"StackEntity__StackEntity_assets"."checksum" AS "StackEntity__StackEntity_assets_checksum",
|
||||||
|
"StackEntity__StackEntity_assets"."duration" AS "StackEntity__StackEntity_assets_duration",
|
||||||
|
"StackEntity__StackEntity_assets"."isVisible" AS "StackEntity__StackEntity_assets_isVisible",
|
||||||
|
"StackEntity__StackEntity_assets"."livePhotoVideoId" AS "StackEntity__StackEntity_assets_livePhotoVideoId",
|
||||||
|
"StackEntity__StackEntity_assets"."originalFileName" AS "StackEntity__StackEntity_assets_originalFileName",
|
||||||
|
"StackEntity__StackEntity_assets"."sidecarPath" AS "StackEntity__StackEntity_assets_sidecarPath",
|
||||||
|
"StackEntity__StackEntity_assets"."stackId" AS "StackEntity__StackEntity_assets_stackId",
|
||||||
|
"StackEntity__StackEntity_assets"."duplicateId" AS "StackEntity__StackEntity_assets_duplicateId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_assetId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."description" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_description",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageWidth" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageWidth",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageHeight" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageHeight",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fileSizeInByte" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fileSizeInByte",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."orientation" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_orientation",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."dateTimeOriginal" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_dateTimeOriginal",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."modifyDate" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_modifyDate",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."timeZone" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_timeZone",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."latitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_latitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."longitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_longitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."projectionType" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_projectionType",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."city" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_city",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."livePhotoCID" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_livePhotoCID",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."autoStackId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_autoStackId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."state" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_state",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."country" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_country",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."make" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_make",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."model" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_model",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."lensModel" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_lensModel",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fNumber" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fNumber",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."focalLength" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_focalLength",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."iso" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_iso",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exposureTime" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exposureTime",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."profileDescription" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_profileDescription",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."colorspace" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_colorspace",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."bitsPerSample" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_bitsPerSample",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."rating" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_rating",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fps" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fps",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."id" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_id",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."value" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_value",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."createdAt" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_createdAt",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."updatedAt" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_updatedAt",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."color" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_color",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."parentId" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_parentId",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."userId" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_userId"
|
||||||
|
FROM
|
||||||
|
"asset_stack" "StackEntity"
|
||||||
|
LEFT JOIN "assets" "StackEntity__StackEntity_assets" ON "StackEntity__StackEntity_assets"."stackId" = "StackEntity"."id"
|
||||||
|
AND (
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" IS NULL
|
||||||
|
)
|
||||||
|
LEFT JOIN "exif" "01db479afeb88793eed8e0d1dde6ccfccf1698b9" ON "01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" = "StackEntity__StackEntity_assets"."id"
|
||||||
|
LEFT JOIN "tag_asset" "4f1c9474d4596aede2814ee2eb938eecf7a93b95" ON "4f1c9474d4596aede2814ee2eb938eecf7a93b95"."assetsId" = "StackEntity__StackEntity_assets"."id"
|
||||||
|
LEFT JOIN "tags" "3e3064f11b97177a1e1ce3c77ecf32850343aba1" ON "3e3064f11b97177a1e1ce3c77ecf32850343aba1"."id" = "4f1c9474d4596aede2814ee2eb938eecf7a93b95"."tagsId"
|
||||||
|
WHERE
|
||||||
|
(("StackEntity"."id" = $1))
|
||||||
|
) "distinctAlias"
|
||||||
|
ORDER BY
|
||||||
|
"distinctAlias"."StackEntity__StackEntity_assets_fileCreatedAt" ASC,
|
||||||
|
"StackEntity_id" ASC
|
||||||
|
LIMIT
|
||||||
|
1
|
||||||
|
|
||||||
|
-- StackRepository.getById
|
||||||
|
SELECT DISTINCT
|
||||||
|
"distinctAlias"."StackEntity_id" AS "ids_StackEntity_id",
|
||||||
|
"distinctAlias"."StackEntity__StackEntity_assets_fileCreatedAt"
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
"StackEntity"."id" AS "StackEntity_id",
|
||||||
|
"StackEntity"."ownerId" AS "StackEntity_ownerId",
|
||||||
|
"StackEntity"."primaryAssetId" AS "StackEntity_primaryAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."id" AS "StackEntity__StackEntity_assets_id",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceAssetId" AS "StackEntity__StackEntity_assets_deviceAssetId",
|
||||||
|
"StackEntity__StackEntity_assets"."ownerId" AS "StackEntity__StackEntity_assets_ownerId",
|
||||||
|
"StackEntity__StackEntity_assets"."libraryId" AS "StackEntity__StackEntity_assets_libraryId",
|
||||||
|
"StackEntity__StackEntity_assets"."deviceId" AS "StackEntity__StackEntity_assets_deviceId",
|
||||||
|
"StackEntity__StackEntity_assets"."type" AS "StackEntity__StackEntity_assets_type",
|
||||||
|
"StackEntity__StackEntity_assets"."status" AS "StackEntity__StackEntity_assets_status",
|
||||||
|
"StackEntity__StackEntity_assets"."originalPath" AS "StackEntity__StackEntity_assets_originalPath",
|
||||||
|
"StackEntity__StackEntity_assets"."thumbhash" AS "StackEntity__StackEntity_assets_thumbhash",
|
||||||
|
"StackEntity__StackEntity_assets"."encodedVideoPath" AS "StackEntity__StackEntity_assets_encodedVideoPath",
|
||||||
|
"StackEntity__StackEntity_assets"."createdAt" AS "StackEntity__StackEntity_assets_createdAt",
|
||||||
|
"StackEntity__StackEntity_assets"."updatedAt" AS "StackEntity__StackEntity_assets_updatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" AS "StackEntity__StackEntity_assets_deletedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."fileCreatedAt" AS "StackEntity__StackEntity_assets_fileCreatedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."localDateTime" AS "StackEntity__StackEntity_assets_localDateTime",
|
||||||
|
"StackEntity__StackEntity_assets"."fileModifiedAt" AS "StackEntity__StackEntity_assets_fileModifiedAt",
|
||||||
|
"StackEntity__StackEntity_assets"."isFavorite" AS "StackEntity__StackEntity_assets_isFavorite",
|
||||||
|
"StackEntity__StackEntity_assets"."isArchived" AS "StackEntity__StackEntity_assets_isArchived",
|
||||||
|
"StackEntity__StackEntity_assets"."isExternal" AS "StackEntity__StackEntity_assets_isExternal",
|
||||||
|
"StackEntity__StackEntity_assets"."isOffline" AS "StackEntity__StackEntity_assets_isOffline",
|
||||||
|
"StackEntity__StackEntity_assets"."checksum" AS "StackEntity__StackEntity_assets_checksum",
|
||||||
|
"StackEntity__StackEntity_assets"."duration" AS "StackEntity__StackEntity_assets_duration",
|
||||||
|
"StackEntity__StackEntity_assets"."isVisible" AS "StackEntity__StackEntity_assets_isVisible",
|
||||||
|
"StackEntity__StackEntity_assets"."livePhotoVideoId" AS "StackEntity__StackEntity_assets_livePhotoVideoId",
|
||||||
|
"StackEntity__StackEntity_assets"."originalFileName" AS "StackEntity__StackEntity_assets_originalFileName",
|
||||||
|
"StackEntity__StackEntity_assets"."sidecarPath" AS "StackEntity__StackEntity_assets_sidecarPath",
|
||||||
|
"StackEntity__StackEntity_assets"."stackId" AS "StackEntity__StackEntity_assets_stackId",
|
||||||
|
"StackEntity__StackEntity_assets"."duplicateId" AS "StackEntity__StackEntity_assets_duplicateId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_assetId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."description" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_description",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageWidth" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageWidth",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exifImageHeight" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exifImageHeight",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fileSizeInByte" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fileSizeInByte",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."orientation" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_orientation",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."dateTimeOriginal" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_dateTimeOriginal",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."modifyDate" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_modifyDate",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."timeZone" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_timeZone",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."latitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_latitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."longitude" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_longitude",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."projectionType" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_projectionType",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."city" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_city",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."livePhotoCID" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_livePhotoCID",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."autoStackId" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_autoStackId",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."state" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_state",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."country" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_country",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."make" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_make",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."model" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_model",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."lensModel" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_lensModel",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fNumber" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fNumber",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."focalLength" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_focalLength",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."iso" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_iso",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."exposureTime" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_exposureTime",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."profileDescription" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_profileDescription",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."colorspace" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_colorspace",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."bitsPerSample" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_bitsPerSample",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."rating" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_rating",
|
||||||
|
"01db479afeb88793eed8e0d1dde6ccfccf1698b9"."fps" AS "01db479afeb88793eed8e0d1dde6ccfccf1698b9_fps",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."id" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_id",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."value" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_value",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."createdAt" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_createdAt",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."updatedAt" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_updatedAt",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."color" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_color",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."parentId" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_parentId",
|
||||||
|
"3e3064f11b97177a1e1ce3c77ecf32850343aba1"."userId" AS "3e3064f11b97177a1e1ce3c77ecf32850343aba1_userId"
|
||||||
|
FROM
|
||||||
|
"asset_stack" "StackEntity"
|
||||||
|
LEFT JOIN "assets" "StackEntity__StackEntity_assets" ON "StackEntity__StackEntity_assets"."stackId" = "StackEntity"."id"
|
||||||
|
AND (
|
||||||
|
"StackEntity__StackEntity_assets"."deletedAt" IS NULL
|
||||||
|
)
|
||||||
|
LEFT JOIN "exif" "01db479afeb88793eed8e0d1dde6ccfccf1698b9" ON "01db479afeb88793eed8e0d1dde6ccfccf1698b9"."assetId" = "StackEntity__StackEntity_assets"."id"
|
||||||
|
LEFT JOIN "tag_asset" "4f1c9474d4596aede2814ee2eb938eecf7a93b95" ON "4f1c9474d4596aede2814ee2eb938eecf7a93b95"."assetsId" = "StackEntity__StackEntity_assets"."id"
|
||||||
|
LEFT JOIN "tags" "3e3064f11b97177a1e1ce3c77ecf32850343aba1" ON "3e3064f11b97177a1e1ce3c77ecf32850343aba1"."id" = "4f1c9474d4596aede2814ee2eb938eecf7a93b95"."tagsId"
|
||||||
|
WHERE
|
||||||
|
(("StackEntity"."id" = $1))
|
||||||
|
) "distinctAlias"
|
||||||
|
ORDER BY
|
||||||
|
"distinctAlias"."StackEntity__StackEntity_assets_fileCreatedAt" ASC,
|
||||||
|
"StackEntity_id" ASC
|
||||||
|
LIMIT
|
||||||
|
1
|
@ -1,21 +1,12 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { Kysely, sql } from 'kysely';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { DB } from 'src/db';
|
||||||
import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
|
||||||
import { AlbumEntity } from 'src/entities/album.entity';
|
|
||||||
import { AssetFaceEntity } from 'src/entities/asset-face.entity';
|
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
|
||||||
import { MemoryEntity } from 'src/entities/memory.entity';
|
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
|
||||||
import { PersonEntity } from 'src/entities/person.entity';
|
|
||||||
import { SessionEntity } from 'src/entities/session.entity';
|
|
||||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
|
||||||
import { TagEntity } from 'src/entities/tag.entity';
|
|
||||||
import { AlbumUserRole } from 'src/enum';
|
import { AlbumUserRole } from 'src/enum';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { Brackets, In, Repository } from 'typeorm';
|
import { asUuid } from 'src/utils/database';
|
||||||
|
|
||||||
type IActivityAccess = IAccessRepository['activity'];
|
type IActivityAccess = IAccessRepository['activity'];
|
||||||
type IAlbumAccess = IAccessRepository['album'];
|
type IAlbumAccess = IAccessRepository['album'];
|
||||||
@ -30,10 +21,7 @@ type ITimelineAccess = IAccessRepository['timeline'];
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class ActivityAccess implements IActivityAccess {
|
class ActivityAccess implements IActivityAccess {
|
||||||
constructor(
|
constructor(private db: Kysely<DB>) {}
|
||||||
private activityRepository: Repository<ActivityEntity>,
|
|
||||||
private albumRepository: Repository<AlbumEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -42,15 +30,16 @@ class ActivityAccess implements IActivityAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.activityRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('activity')
|
||||||
select: { id: true },
|
.select('activity.id')
|
||||||
where: {
|
.where('activity.id', 'in', [...activityIds])
|
||||||
id: In([...activityIds]),
|
.where('activity.userId', '=', userId)
|
||||||
userId,
|
.execute()
|
||||||
},
|
.then((activities) => {
|
||||||
})
|
console.log('activities', activities);
|
||||||
.then((activities) => new Set(activities.map((activity) => activity.id)));
|
return new Set(activities.map((activity) => activity.id));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ -60,16 +49,13 @@ class ActivityAccess implements IActivityAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.activityRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('activity')
|
||||||
select: { id: true },
|
.select('activity.id')
|
||||||
where: {
|
.leftJoin('albums', (join) => join.onRef('activity.albumId', '=', 'albums.id').on('albums.deletedAt', 'is', null))
|
||||||
id: In([...activityIds]),
|
.where('activity.id', 'in', [...activityIds])
|
||||||
album: {
|
.whereRef('albums.ownerId', '=', asUuid(userId))
|
||||||
ownerId: userId,
|
.execute()
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((activities) => new Set(activities.map((activity) => activity.id)));
|
.then((activities) => new Set(activities.map((activity) => activity.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,28 +66,22 @@ class ActivityAccess implements IActivityAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.albumRepository
|
return this.db
|
||||||
.createQueryBuilder('album')
|
.selectFrom('albums')
|
||||||
.select('album.id')
|
.select('albums.id')
|
||||||
.leftJoin('album.albumUsers', 'album_albumUsers_users')
|
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||||
.leftJoin('album_albumUsers_users.user', 'albumUsers')
|
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||||
.where('album.id IN (:...albumIds)', { albumIds: [...albumIds] })
|
.where('albums.id', 'in', [...albumIds])
|
||||||
.andWhere('album.isActivityEnabled = true')
|
.where('albums.isActivityEnabled', '=', true)
|
||||||
.andWhere(
|
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
|
||||||
new Brackets((qb) => {
|
.where('albums.deletedAt', 'is', null)
|
||||||
qb.where('album.ownerId = :userId', { userId }).orWhere('albumUsers.id = :userId', { userId });
|
.execute()
|
||||||
}),
|
|
||||||
)
|
|
||||||
.getMany()
|
|
||||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlbumAccess implements IAlbumAccess {
|
class AlbumAccess implements IAlbumAccess {
|
||||||
constructor(
|
constructor(private db: Kysely<DB>) {}
|
||||||
private albumRepository: Repository<AlbumEntity>,
|
|
||||||
private sharedLinkRepository: Repository<SharedLinkEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -110,14 +90,13 @@ class AlbumAccess implements IAlbumAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.albumRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('albums')
|
||||||
select: { id: true },
|
.select('albums.id')
|
||||||
where: {
|
.where('albums.id', 'in', [...albumIds])
|
||||||
id: In([...albumIds]),
|
.where('albums.ownerId', '=', userId)
|
||||||
ownerId: userId,
|
.where('albums.deletedAt', 'is', null)
|
||||||
},
|
.execute()
|
||||||
})
|
|
||||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,19 +107,19 @@ class AlbumAccess implements IAlbumAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.albumRepository
|
const accessRole =
|
||||||
.find({
|
access === AlbumUserRole.EDITOR ? [AlbumUserRole.EDITOR] : [AlbumUserRole.EDITOR, AlbumUserRole.VIEWER];
|
||||||
select: { id: true },
|
|
||||||
where: {
|
return this.db
|
||||||
id: In([...albumIds]),
|
.selectFrom('albums')
|
||||||
albumUsers: {
|
.select('albums.id')
|
||||||
user: { id: userId },
|
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||||
// If editor access is needed we check for it, otherwise both are accepted
|
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||||
role:
|
.where('albums.id', 'in', [...albumIds])
|
||||||
access === AlbumUserRole.EDITOR ? AlbumUserRole.EDITOR : In([AlbumUserRole.EDITOR, AlbumUserRole.VIEWER]),
|
.where('albums.deletedAt', 'is', null)
|
||||||
},
|
.where('users.id', '=', userId)
|
||||||
},
|
.where('albumUsers.role', 'in', [...accessRole])
|
||||||
})
|
.execute()
|
||||||
.then((albums) => new Set(albums.map((album) => album.id)));
|
.then((albums) => new Set(albums.map((album) => album.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,14 +130,12 @@ class AlbumAccess implements IAlbumAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sharedLinkRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('shared_links')
|
||||||
select: { albumId: true },
|
.select('shared_links.albumId')
|
||||||
where: {
|
.where('shared_links.id', '=', sharedLinkId)
|
||||||
id: sharedLinkId,
|
.where('shared_links.albumId', 'in', [...albumIds])
|
||||||
albumId: In([...albumIds]),
|
.execute()
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(
|
.then(
|
||||||
(sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
|
(sharedLinks) => new Set(sharedLinks.flatMap((sharedLink) => (sharedLink.albumId ? [sharedLink.albumId] : []))),
|
||||||
);
|
);
|
||||||
@ -166,12 +143,7 @@ class AlbumAccess implements IAlbumAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AssetAccess implements IAssetAccess {
|
class AssetAccess implements IAssetAccess {
|
||||||
constructor(
|
constructor(private db: Kysely<DB>) {}
|
||||||
private albumRepository: Repository<AlbumEntity>,
|
|
||||||
private assetRepository: Repository<AssetEntity>,
|
|
||||||
private partnerRepository: Repository<PartnerEntity>,
|
|
||||||
private sharedLinkRepository: Repository<SharedLinkEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -180,30 +152,31 @@ class AssetAccess implements IAssetAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.albumRepository
|
return this.db
|
||||||
.createQueryBuilder('album')
|
.selectFrom('albums')
|
||||||
.innerJoin('album.assets', 'asset')
|
.innerJoin('albums_assets_assets as albumAssets', 'albums.id', 'albumAssets.albumsId')
|
||||||
.leftJoin('album.albumUsers', 'album_albumUsers_users')
|
.innerJoin('assets', (join) =>
|
||||||
.leftJoin('album_albumUsers_users.user', 'albumUsers')
|
join.onRef('assets.id', '=', 'albumAssets.assetsId').on('assets.deletedAt', 'is', null),
|
||||||
.select('asset.id', 'assetId')
|
|
||||||
.addSelect('asset.livePhotoVideoId', 'livePhotoVideoId')
|
|
||||||
.where('array["asset"."id", "asset"."livePhotoVideoId"] && array[:...assetIds]::uuid[]', {
|
|
||||||
assetIds: [...assetIds],
|
|
||||||
})
|
|
||||||
.andWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
qb.where('album.ownerId = :userId', { userId }).orWhere('albumUsers.id = :userId', { userId });
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.getRawMany()
|
.leftJoin('albums_shared_users_users as albumUsers', 'albumUsers.albumsId', 'albums.id')
|
||||||
.then((rows) => {
|
.leftJoin('users', (join) => join.onRef('users.id', '=', 'albumUsers.usersId').on('users.deletedAt', 'is', null))
|
||||||
|
.select(['assets.id', 'assets.livePhotoVideoId'])
|
||||||
|
.where(
|
||||||
|
sql`array["assets"."id", "assets"."livePhotoVideoId"]`,
|
||||||
|
'&&',
|
||||||
|
sql`array[${sql.join([...assetIds])}]::uuid[] `,
|
||||||
|
)
|
||||||
|
.where((eb) => eb.or([eb('albums.ownerId', '=', userId), eb('users.id', '=', userId)]))
|
||||||
|
.where('albums.deletedAt', 'is', null)
|
||||||
|
.execute()
|
||||||
|
.then((assets) => {
|
||||||
const allowedIds = new Set<string>();
|
const allowedIds = new Set<string>();
|
||||||
for (const row of rows) {
|
for (const asset of assets) {
|
||||||
if (row.assetId && assetIds.has(row.assetId)) {
|
if (asset.id && assetIds.has(asset.id)) {
|
||||||
allowedIds.add(row.assetId);
|
allowedIds.add(asset.id);
|
||||||
}
|
}
|
||||||
if (row.livePhotoVideoId && assetIds.has(row.livePhotoVideoId)) {
|
if (asset.livePhotoVideoId && assetIds.has(asset.livePhotoVideoId)) {
|
||||||
allowedIds.add(row.livePhotoVideoId);
|
allowedIds.add(asset.livePhotoVideoId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return allowedIds;
|
return allowedIds;
|
||||||
@ -217,15 +190,12 @@ class AssetAccess implements IAssetAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.assetRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('assets')
|
||||||
select: { id: true },
|
.select('assets.id')
|
||||||
where: {
|
.where('assets.id', 'in', [...assetIds])
|
||||||
id: In([...assetIds]),
|
.where('assets.ownerId', '=', userId)
|
||||||
ownerId: userId,
|
.execute()
|
||||||
},
|
|
||||||
withDeleted: true,
|
|
||||||
})
|
|
||||||
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,16 +206,20 @@ class AssetAccess implements IAssetAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.partnerRepository
|
return this.db
|
||||||
.createQueryBuilder('partner')
|
.selectFrom('partners as partner')
|
||||||
.innerJoin('partner.sharedBy', 'sharedBy')
|
.innerJoin('users as sharedBy', (join) =>
|
||||||
.innerJoin('sharedBy.assets', 'asset')
|
join.onRef('sharedBy.id', '=', 'partner.sharedById').on('sharedBy.deletedAt', 'is', null),
|
||||||
.select('asset.id', 'assetId')
|
)
|
||||||
.where('partner.sharedWithId = :userId', { userId })
|
.innerJoin('assets', (join) =>
|
||||||
.andWhere('asset.isArchived = false')
|
join.onRef('assets.ownerId', '=', 'sharedBy.id').on('assets.deletedAt', 'is', null),
|
||||||
.andWhere('asset.id IN (:...assetIds)', { assetIds: [...assetIds] })
|
)
|
||||||
.getRawMany()
|
.select('assets.id')
|
||||||
.then((rows) => new Set(rows.map((row) => row.assetId)));
|
.where('partner.sharedWithId', '=', userId)
|
||||||
|
.where('assets.isArchived', '=', false)
|
||||||
|
.where('assets.id', 'in', [...assetIds])
|
||||||
|
.execute()
|
||||||
|
.then((assets) => new Set(assets.map((asset) => asset.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ -255,23 +229,32 @@ class AssetAccess implements IAssetAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sharedLinkRepository
|
return this.db
|
||||||
.createQueryBuilder('sharedLink')
|
.selectFrom('shared_links')
|
||||||
.leftJoin('sharedLink.album', 'album')
|
.leftJoin('albums', (join) =>
|
||||||
.leftJoin('sharedLink.assets', 'assets')
|
join.onRef('albums.id', '=', 'shared_links.albumId').on('albums.deletedAt', 'is', null),
|
||||||
.leftJoin('album.assets', 'albumAssets')
|
|
||||||
.select('assets.id', 'assetId')
|
|
||||||
.addSelect('albumAssets.id', 'albumAssetId')
|
|
||||||
.addSelect('assets.livePhotoVideoId', 'assetLivePhotoVideoId')
|
|
||||||
.addSelect('albumAssets.livePhotoVideoId', 'albumAssetLivePhotoVideoId')
|
|
||||||
.where('sharedLink.id = :sharedLinkId', { sharedLinkId })
|
|
||||||
.andWhere(
|
|
||||||
'array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"] && array[:...assetIds]::uuid[]',
|
|
||||||
{
|
|
||||||
assetIds: [...assetIds],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.getRawMany()
|
.leftJoin('shared_link__asset', 'shared_link__asset.sharedLinksId', 'shared_links.id')
|
||||||
|
.leftJoin('assets', (join) =>
|
||||||
|
join.onRef('assets.id', '=', 'shared_link__asset.assetsId').on('assets.deletedAt', 'is', null),
|
||||||
|
)
|
||||||
|
.leftJoin('albums_assets_assets', 'albums_assets_assets.albumsId', 'albums.id')
|
||||||
|
.leftJoin('assets as albumAssets', (join) =>
|
||||||
|
join.onRef('albumAssets.id', '=', 'albums_assets_assets.assetsId').on('albumAssets.deletedAt', 'is', null),
|
||||||
|
)
|
||||||
|
.select([
|
||||||
|
'assets.id as assetId',
|
||||||
|
'assets.livePhotoVideoId as assetLivePhotoVideoId',
|
||||||
|
'albumAssets.id as albumAssetId',
|
||||||
|
'albumAssets.livePhotoVideoId as albumAssetLivePhotoVideoId',
|
||||||
|
])
|
||||||
|
.where('shared_links.id', '=', sharedLinkId)
|
||||||
|
.where(
|
||||||
|
sql`array["assets"."id", "assets"."livePhotoVideoId", "albumAssets"."id", "albumAssets"."livePhotoVideoId"]`,
|
||||||
|
'&&',
|
||||||
|
sql`array[${sql.join([...assetIds])}]::uuid[] `,
|
||||||
|
)
|
||||||
|
.execute()
|
||||||
.then((rows) => {
|
.then((rows) => {
|
||||||
const allowedIds = new Set<string>();
|
const allowedIds = new Set<string>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
@ -294,7 +277,7 @@ class AssetAccess implements IAssetAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AuthDeviceAccess implements IAuthDeviceAccess {
|
class AuthDeviceAccess implements IAuthDeviceAccess {
|
||||||
constructor(private sessionRepository: Repository<SessionEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -303,20 +286,18 @@ class AuthDeviceAccess implements IAuthDeviceAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.sessionRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('sessions')
|
||||||
select: { id: true },
|
.select('sessions.id')
|
||||||
where: {
|
.where('sessions.userId', '=', userId)
|
||||||
userId,
|
.where('sessions.id', 'in', [...deviceIds])
|
||||||
id: In([...deviceIds]),
|
.execute()
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((tokens) => new Set(tokens.map((token) => token.id)));
|
.then((tokens) => new Set(tokens.map((token) => token.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StackAccess implements IStackAccess {
|
class StackAccess implements IStackAccess {
|
||||||
constructor(private stackRepository: Repository<StackEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -325,20 +306,18 @@ class StackAccess implements IStackAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.stackRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('asset_stack as stacks')
|
||||||
select: { id: true },
|
.select('stacks.id')
|
||||||
where: {
|
.where('stacks.id', 'in', [...stackIds])
|
||||||
id: In([...stackIds]),
|
.where('stacks.ownerId', '=', userId)
|
||||||
ownerId: userId,
|
.execute()
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((stacks) => new Set(stacks.map((stack) => stack.id)));
|
.then((stacks) => new Set(stacks.map((stack) => stack.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineAccess implements ITimelineAccess {
|
class TimelineAccess implements ITimelineAccess {
|
||||||
constructor(private partnerRepository: Repository<PartnerEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -347,18 +326,18 @@ class TimelineAccess implements ITimelineAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.partnerRepository
|
return this.db
|
||||||
.createQueryBuilder('partner')
|
.selectFrom('partners')
|
||||||
.select('partner.sharedById')
|
.select('partners.sharedById')
|
||||||
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] })
|
.where('partners.sharedById', 'in', [...partnerIds])
|
||||||
.andWhere('partner.sharedWithId = :userId', { userId })
|
.where('partners.sharedWithId', '=', userId)
|
||||||
.getMany()
|
.execute()
|
||||||
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MemoryAccess implements IMemoryAccess {
|
class MemoryAccess implements IMemoryAccess {
|
||||||
constructor(private memoryRepository: Repository<MemoryEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -367,23 +346,19 @@ class MemoryAccess implements IMemoryAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.memoryRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('memories')
|
||||||
select: { id: true },
|
.select('memories.id')
|
||||||
where: {
|
.where('memories.id', 'in', [...memoryIds])
|
||||||
id: In([...memoryIds]),
|
.where('memories.ownerId', '=', userId)
|
||||||
ownerId: userId,
|
.where('memories.deletedAt', 'is', null)
|
||||||
},
|
.execute()
|
||||||
})
|
|
||||||
.then((memories) => new Set(memories.map((memory) => memory.id)));
|
.then((memories) => new Set(memories.map((memory) => memory.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PersonAccess implements IPersonAccess {
|
class PersonAccess implements IPersonAccess {
|
||||||
constructor(
|
constructor(private db: Kysely<DB>) {}
|
||||||
private assetFaceRepository: Repository<AssetFaceEntity>,
|
|
||||||
private personRepository: Repository<PersonEntity>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -392,14 +367,12 @@ class PersonAccess implements IPersonAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.personRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('person')
|
||||||
select: { id: true },
|
.select('person.id')
|
||||||
where: {
|
.where('person.id', 'in', [...personIds])
|
||||||
id: In([...personIds]),
|
.where('person.ownerId', '=', userId)
|
||||||
ownerId: userId,
|
.execute()
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((persons) => new Set(persons.map((person) => person.id)));
|
.then((persons) => new Set(persons.map((person) => person.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,22 +383,21 @@ class PersonAccess implements IPersonAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.assetFaceRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('asset_faces')
|
||||||
select: { id: true },
|
.select('asset_faces.id')
|
||||||
where: {
|
.leftJoin('assets', (join) =>
|
||||||
id: In([...assetFaceIds]),
|
join.onRef('assets.id', '=', 'asset_faces.assetId').on('assets.deletedAt', 'is', null),
|
||||||
asset: {
|
)
|
||||||
ownerId: userId,
|
.where('asset_faces.id', 'in', [...assetFaceIds])
|
||||||
},
|
.where('assets.ownerId', '=', userId)
|
||||||
},
|
.execute()
|
||||||
})
|
|
||||||
.then((faces) => new Set(faces.map((face) => face.id)));
|
.then((faces) => new Set(faces.map((face) => face.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PartnerAccess implements IPartnerAccess {
|
class PartnerAccess implements IPartnerAccess {
|
||||||
constructor(private partnerRepository: Repository<PartnerEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -434,18 +406,18 @@ class PartnerAccess implements IPartnerAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.partnerRepository
|
return this.db
|
||||||
.createQueryBuilder('partner')
|
.selectFrom('partners')
|
||||||
.select('partner.sharedById')
|
.select('partners.sharedById')
|
||||||
.where('partner.sharedById IN (:...partnerIds)', { partnerIds: [...partnerIds] })
|
.where('partners.sharedById', 'in', [...partnerIds])
|
||||||
.andWhere('partner.sharedWithId = :userId', { userId })
|
.where('partners.sharedWithId', '=', userId)
|
||||||
.getMany()
|
.execute()
|
||||||
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
.then((partners) => new Set(partners.map((partner) => partner.sharedById)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TagAccess implements ITagAccess {
|
class TagAccess implements ITagAccess {
|
||||||
constructor(private tagRepository: Repository<TagEntity>) {}
|
constructor(private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID_SET] })
|
||||||
@ChunkedSet({ paramIndex: 1 })
|
@ChunkedSet({ paramIndex: 1 })
|
||||||
@ -454,14 +426,12 @@ class TagAccess implements ITagAccess {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tagRepository
|
return this.db
|
||||||
.find({
|
.selectFrom('tags')
|
||||||
select: { id: true },
|
.select('tags.id')
|
||||||
where: {
|
.where('tags.id', 'in', [...tagIds])
|
||||||
id: In([...tagIds]),
|
.where('tags.userId', '=', userId)
|
||||||
userId,
|
.execute()
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((tags) => new Set(tags.map((tag) => tag.id)));
|
.then((tags) => new Set(tags.map((tag) => tag.id)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,29 +448,16 @@ export class AccessRepository implements IAccessRepository {
|
|||||||
tag: ITagAccess;
|
tag: ITagAccess;
|
||||||
timeline: ITimelineAccess;
|
timeline: ITimelineAccess;
|
||||||
|
|
||||||
constructor(
|
constructor(@InjectKysely() db: Kysely<DB>) {
|
||||||
@InjectRepository(ActivityEntity) activityRepository: Repository<ActivityEntity>,
|
this.activity = new ActivityAccess(db);
|
||||||
@InjectRepository(AssetEntity) assetRepository: Repository<AssetEntity>,
|
this.album = new AlbumAccess(db);
|
||||||
@InjectRepository(AlbumEntity) albumRepository: Repository<AlbumEntity>,
|
this.asset = new AssetAccess(db);
|
||||||
@InjectRepository(LibraryEntity) libraryRepository: Repository<LibraryEntity>,
|
this.authDevice = new AuthDeviceAccess(db);
|
||||||
@InjectRepository(MemoryEntity) memoryRepository: Repository<MemoryEntity>,
|
this.memory = new MemoryAccess(db);
|
||||||
@InjectRepository(PartnerEntity) partnerRepository: Repository<PartnerEntity>,
|
this.person = new PersonAccess(db);
|
||||||
@InjectRepository(PersonEntity) personRepository: Repository<PersonEntity>,
|
this.partner = new PartnerAccess(db);
|
||||||
@InjectRepository(AssetFaceEntity) assetFaceRepository: Repository<AssetFaceEntity>,
|
this.stack = new StackAccess(db);
|
||||||
@InjectRepository(SharedLinkEntity) sharedLinkRepository: Repository<SharedLinkEntity>,
|
this.tag = new TagAccess(db);
|
||||||
@InjectRepository(SessionEntity) sessionRepository: Repository<SessionEntity>,
|
this.timeline = new TimelineAccess(db);
|
||||||
@InjectRepository(StackEntity) stackRepository: Repository<StackEntity>,
|
|
||||||
@InjectRepository(TagEntity) tagRepository: Repository<TagEntity>,
|
|
||||||
) {
|
|
||||||
this.activity = new ActivityAccess(activityRepository, albumRepository);
|
|
||||||
this.album = new AlbumAccess(albumRepository, sharedLinkRepository);
|
|
||||||
this.asset = new AssetAccess(albumRepository, assetRepository, partnerRepository, sharedLinkRepository);
|
|
||||||
this.authDevice = new AuthDeviceAccess(sessionRepository);
|
|
||||||
this.memory = new MemoryAccess(memoryRepository);
|
|
||||||
this.person = new PersonAccess(assetFaceRepository, personRepository);
|
|
||||||
this.partner = new PartnerAccess(partnerRepository);
|
|
||||||
this.stack = new StackAccess(stackRepository);
|
|
||||||
this.tag = new TagAccess(tagRepository);
|
|
||||||
this.timeline = new TimelineAccess(partnerRepository);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { ExpressionBuilder, Insertable, Kysely } from 'kysely';
|
import { ExpressionBuilder, Insertable, Kysely } from 'kysely';
|
||||||
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { columns } from 'src/database';
|
||||||
import { Activity, DB } from 'src/db';
|
import { Activity, DB } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
|
||||||
import { asUuid } from 'src/utils/database';
|
import { asUuid } from 'src/utils/database';
|
||||||
|
|
||||||
export interface ActivitySearch {
|
export interface ActivitySearch {
|
||||||
@ -19,18 +18,18 @@ const withUser = (eb: ExpressionBuilder<DB, 'activity'>) => {
|
|||||||
return jsonObjectFrom(
|
return jsonObjectFrom(
|
||||||
eb
|
eb
|
||||||
.selectFrom('users')
|
.selectFrom('users')
|
||||||
.selectAll()
|
.select(columns.userDto)
|
||||||
.whereRef('users.id', '=', 'activity.userId')
|
.whereRef('users.id', '=', 'activity.userId')
|
||||||
.where('users.deletedAt', 'is', null),
|
.where('users.deletedAt', 'is', null),
|
||||||
).as('user');
|
).as('user');
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ActivityRepository implements IActivityRepository {
|
export class ActivityRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ albumId: DummyValue.UUID }] })
|
@GenerateSql({ params: [{ albumId: DummyValue.UUID }] })
|
||||||
search(options: ActivitySearch): Promise<ActivityEntity[]> {
|
search(options: ActivitySearch) {
|
||||||
const { userId, assetId, albumId, isLiked } = options;
|
const { userId, assetId, albumId, isLiked } = options;
|
||||||
|
|
||||||
return this.db
|
return this.db
|
||||||
@ -44,14 +43,14 @@ export class ActivityRepository implements IActivityRepository {
|
|||||||
.$if(!!albumId, (qb) => qb.where('activity.albumId', '=', albumId!))
|
.$if(!!albumId, (qb) => qb.where('activity.albumId', '=', albumId!))
|
||||||
.$if(isLiked !== undefined, (qb) => qb.where('activity.isLiked', '=', isLiked!))
|
.$if(isLiked !== undefined, (qb) => qb.where('activity.isLiked', '=', isLiked!))
|
||||||
.orderBy('activity.createdAt', 'asc')
|
.orderBy('activity.createdAt', 'asc')
|
||||||
.execute() as unknown as Promise<ActivityEntity[]>;
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(activity: Insertable<Activity>) {
|
async create(activity: Insertable<Activity>) {
|
||||||
return this.save(activity);
|
return this.save(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string) {
|
||||||
await this.db.deleteFrom('activity').where('id', '=', asUuid(id)).execute();
|
await this.db.deleteFrom('activity').where('id', '=', asUuid(id)).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +78,6 @@ export class ActivityRepository implements IActivityRepository {
|
|||||||
.selectAll('activity')
|
.selectAll('activity')
|
||||||
.select(withUser)
|
.select(withUser)
|
||||||
.where('activity.id', '=', asUuid(id))
|
.where('activity.id', '=', asUuid(id))
|
||||||
.executeTakeFirstOrThrow() as unknown as Promise<ActivityEntity>;
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,40 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { Insertable, Kysely, Selectable, Updateable } from 'kysely';
|
||||||
import { AlbumUserEntity } from 'src/entities/album-user.entity';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { AlbumsSharedUsersUsers, DB } from 'src/db';
|
||||||
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
|
import { AlbumUserRole } from 'src/enum';
|
||||||
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
import { AlbumPermissionId, IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AlbumUserRepository implements IAlbumUserRepository {
|
export class AlbumUserRepository implements IAlbumUserRepository {
|
||||||
constructor(@InjectRepository(AlbumUserEntity) private repository: Repository<AlbumUserEntity>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
async create(albumUser: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
|
||||||
const { userId, albumId } = await this.repository.save(albumUser);
|
create(albumUser: Insertable<AlbumsSharedUsersUsers>): Promise<Selectable<AlbumsSharedUsersUsers>> {
|
||||||
return this.repository.findOneOrFail({ where: { userId, albumId } });
|
return this.db.insertInto('albums_shared_users_users').values(albumUser).returningAll().executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
async update({ userId, albumId }: AlbumPermissionId, dto: Partial<AlbumUserEntity>): Promise<AlbumUserEntity> {
|
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }, { role: AlbumUserRole.VIEWER }] })
|
||||||
await this.repository.update({ userId, albumId }, dto);
|
update(
|
||||||
return this.repository.findOneOrFail({
|
{ usersId, albumsId }: AlbumPermissionId,
|
||||||
where: { userId, albumId },
|
dto: Updateable<AlbumsSharedUsersUsers>,
|
||||||
});
|
): Promise<Selectable<AlbumsSharedUsersUsers>> {
|
||||||
|
return this.db
|
||||||
|
.updateTable('albums_shared_users_users')
|
||||||
|
.set(dto)
|
||||||
|
.where('usersId', '=', usersId)
|
||||||
|
.where('albumsId', '=', albumsId)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete({ userId, albumId }: AlbumPermissionId): Promise<void> {
|
@GenerateSql({ params: [{ usersId: DummyValue.UUID, albumsId: DummyValue.UUID }] })
|
||||||
await this.repository.delete({ userId, albumId });
|
async delete({ usersId, albumsId }: AlbumPermissionId): Promise<void> {
|
||||||
|
await this.db
|
||||||
|
.deleteFrom('albums_shared_users_users')
|
||||||
|
.where('usersId', '=', usersId)
|
||||||
|
.where('albumsId', '=', albumsId)
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ describe('getEnv', () => {
|
|||||||
config: {
|
config: {
|
||||||
kysely: {
|
kysely: {
|
||||||
dialect: expect.any(PostgresJSDialect),
|
dialect: expect.any(PostgresJSDialect),
|
||||||
log: ['error'],
|
log: expect.any(Function),
|
||||||
},
|
},
|
||||||
typeorm: expect.objectContaining({
|
typeorm: expect.objectContaining({
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
|
@ -5,7 +5,7 @@ import { Request, Response } from 'express';
|
|||||||
import { PostgresJSDialect } from 'kysely-postgres-js';
|
import { PostgresJSDialect } from 'kysely-postgres-js';
|
||||||
import { CLS_ID } from 'nestjs-cls';
|
import { CLS_ID } from 'nestjs-cls';
|
||||||
import { join, resolve } from 'node:path';
|
import { join, resolve } from 'node:path';
|
||||||
import postgres from 'postgres';
|
import postgres, { Notice } from 'postgres';
|
||||||
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
import { citiesFile, excludePaths, IWorker } from 'src/constants';
|
||||||
import { Telemetry } from 'src/decorators';
|
import { Telemetry } from 'src/decorators';
|
||||||
import { EnvDto } from 'src/dtos/env.dto';
|
import { EnvDto } from 'src/dtos/env.dto';
|
||||||
@ -99,6 +99,11 @@ const getEnv = (): EnvData => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const driverOptions = {
|
const driverOptions = {
|
||||||
|
onnotice: (notice: Notice) => {
|
||||||
|
if (notice['severity'] !== 'NOTICE') {
|
||||||
|
console.warn('Postgres notice:', notice);
|
||||||
|
}
|
||||||
|
},
|
||||||
max: 10,
|
max: 10,
|
||||||
types: {
|
types: {
|
||||||
date: {
|
date: {
|
||||||
@ -194,7 +199,16 @@ const getEnv = (): EnvData => {
|
|||||||
dialect: new PostgresJSDialect({
|
dialect: new PostgresJSDialect({
|
||||||
postgres: databaseUrl ? postgres(databaseUrl, driverOptions) : postgres({ ...parts, ...driverOptions }),
|
postgres: databaseUrl ? postgres(databaseUrl, driverOptions) : postgres({ ...parts, ...driverOptions }),
|
||||||
}),
|
}),
|
||||||
log: ['error'] as const,
|
log(event) {
|
||||||
|
if (event.level === 'error') {
|
||||||
|
console.error('Query failed :', {
|
||||||
|
durationMs: event.queryDurationMillis,
|
||||||
|
error: event.error,
|
||||||
|
sql: event.query.sql,
|
||||||
|
params: event.query.parameters,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
|
||||||
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||||
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
||||||
@ -78,8 +77,12 @@ import { VersionHistoryRepository } from 'src/repositories/version-history.repos
|
|||||||
import { ViewRepository } from 'src/repositories/view-repository';
|
import { ViewRepository } from 'src/repositories/view-repository';
|
||||||
|
|
||||||
export const repositories = [
|
export const repositories = [
|
||||||
|
//
|
||||||
|
ActivityRepository,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const providers = [
|
||||||
{ provide: IAccessRepository, useClass: AccessRepository },
|
{ provide: IAccessRepository, useClass: AccessRepository },
|
||||||
{ provide: IActivityRepository, useClass: ActivityRepository },
|
|
||||||
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
{ provide: IAlbumRepository, useClass: AlbumRepository },
|
||||||
{ provide: IAlbumUserRepository, useClass: AlbumUserRepository },
|
{ provide: IAlbumUserRepository, useClass: AlbumUserRepository },
|
||||||
{ provide: IAssetRepository, useClass: AssetRepository },
|
{ provide: IAssetRepository, useClass: AssetRepository },
|
||||||
|
@ -1,84 +1,122 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { ExpressionBuilder, Insertable, Kysely, Updateable } from 'kysely';
|
||||||
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { DB, Libraries } from 'src/db';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
import { LibraryStatsResponseDto } from 'src/dtos/library.dto';
|
||||||
import { LibraryEntity } from 'src/entities/library.entity';
|
import { LibraryEntity } from 'src/entities/library.entity';
|
||||||
|
import { AssetType } from 'src/enum';
|
||||||
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
import { ILibraryRepository } from 'src/interfaces/library.interface';
|
||||||
import { IsNull, Not } from 'typeorm';
|
|
||||||
import { Repository } from 'typeorm/repository/Repository.js';
|
const userColumns = [
|
||||||
|
'users.id',
|
||||||
|
'users.email',
|
||||||
|
'users.createdAt',
|
||||||
|
'users.profileImagePath',
|
||||||
|
'users.isAdmin',
|
||||||
|
'users.shouldChangePassword',
|
||||||
|
'users.deletedAt',
|
||||||
|
'users.oauthId',
|
||||||
|
'users.updatedAt',
|
||||||
|
'users.storageLabel',
|
||||||
|
'users.name',
|
||||||
|
'users.quotaSizeInBytes',
|
||||||
|
'users.quotaUsageInBytes',
|
||||||
|
'users.status',
|
||||||
|
'users.profileChangedAt',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const withOwner = (eb: ExpressionBuilder<DB, 'libraries'>) => {
|
||||||
|
return jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'libraries.ownerId').select(userColumns)).as(
|
||||||
|
'owner',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LibraryRepository implements ILibraryRepository {
|
export class LibraryRepository implements ILibraryRepository {
|
||||||
constructor(@InjectRepository(LibraryEntity) private repository: Repository<LibraryEntity>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
get(id: string, withDeleted = false): Promise<LibraryEntity | null> {
|
get(id: string, withDeleted = false): Promise<LibraryEntity | undefined> {
|
||||||
return this.repository.findOneOrFail({
|
return this.db
|
||||||
where: {
|
.selectFrom('libraries')
|
||||||
id,
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
relations: { owner: true },
|
.where('libraries.id', '=', id)
|
||||||
withDeleted,
|
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||||
});
|
.executeTakeFirst() as Promise<LibraryEntity | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [] })
|
@GenerateSql({ params: [] })
|
||||||
getAll(withDeleted = false): Promise<LibraryEntity[]> {
|
getAll(withDeleted = false): Promise<LibraryEntity[]> {
|
||||||
return this.repository.find({
|
return this.db
|
||||||
relations: {
|
.selectFrom('libraries')
|
||||||
owner: true,
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
order: {
|
.orderBy('createdAt', 'asc')
|
||||||
createdAt: 'ASC',
|
.$if(!withDeleted, (qb) => qb.where('libraries.deletedAt', 'is', null))
|
||||||
},
|
.execute() as unknown as Promise<LibraryEntity[]>;
|
||||||
withDeleted,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql()
|
@GenerateSql()
|
||||||
getAllDeleted(): Promise<LibraryEntity[]> {
|
getAllDeleted(): Promise<LibraryEntity[]> {
|
||||||
return this.repository.find({
|
return this.db
|
||||||
where: {
|
.selectFrom('libraries')
|
||||||
deletedAt: Not(IsNull()),
|
.selectAll('libraries')
|
||||||
},
|
.select(withOwner)
|
||||||
relations: {
|
.where('libraries.deletedAt', 'is not', null)
|
||||||
owner: true,
|
.orderBy('createdAt', 'asc')
|
||||||
},
|
.execute() as unknown as Promise<LibraryEntity[]>;
|
||||||
order: {
|
|
||||||
createdAt: 'ASC',
|
|
||||||
},
|
|
||||||
withDeleted: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create(library: Omit<LibraryEntity, 'id' | 'createdAt' | 'updatedAt' | 'ownerId'>): Promise<LibraryEntity> {
|
create(library: Insertable<Libraries>): Promise<LibraryEntity> {
|
||||||
return this.repository.save(library);
|
return this.db
|
||||||
|
.insertInto('libraries')
|
||||||
|
.values(library)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow() as Promise<LibraryEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await this.repository.delete({ id });
|
await this.db.deleteFrom('libraries').where('libraries.id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async softDelete(id: string): Promise<void> {
|
async softDelete(id: string): Promise<void> {
|
||||||
await this.repository.softDelete({ id });
|
await this.db.updateTable('libraries').set({ deletedAt: new Date() }).where('libraries.id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(library: Partial<LibraryEntity>): Promise<LibraryEntity> {
|
update(id: string, library: Updateable<Libraries>): Promise<LibraryEntity> {
|
||||||
return this.save(library);
|
return this.db
|
||||||
|
.updateTable('libraries')
|
||||||
|
.set(library)
|
||||||
|
.where('libraries.id', '=', id)
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow() as Promise<LibraryEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined> {
|
async getStatistics(id: string): Promise<LibraryStatsResponseDto | undefined> {
|
||||||
const stats = await this.repository
|
const stats = await this.db
|
||||||
.createQueryBuilder('libraries')
|
.selectFrom('libraries')
|
||||||
.addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'IMAGE' AND assets.isVisible)`, 'photos')
|
.innerJoin('assets', 'assets.libraryId', 'libraries.id')
|
||||||
.addSelect(`COUNT(assets.id) FILTER (WHERE assets.type = 'VIDEO' AND assets.isVisible)`, 'videos')
|
.innerJoin('exif', 'exif.assetId', 'assets.id')
|
||||||
.addSelect('COALESCE(SUM(exif.fileSizeInByte), 0)', 'usage')
|
.select((eb) =>
|
||||||
.leftJoin('libraries.assets', 'assets')
|
eb.fn
|
||||||
.leftJoin('assets.exifInfo', 'exif')
|
.count('assets.id')
|
||||||
|
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.IMAGE), eb('assets.isVisible', '=', true)]))
|
||||||
|
.as('photos'),
|
||||||
|
)
|
||||||
|
.select((eb) =>
|
||||||
|
eb.fn
|
||||||
|
.countAll()
|
||||||
|
.filterWhere((eb) => eb.and([eb('assets.type', '=', AssetType.VIDEO), eb('assets.isVisible', '=', true)]))
|
||||||
|
.as('videos'),
|
||||||
|
)
|
||||||
|
.select((eb) => eb.fn.coalesce((eb) => eb.fn.sum('exif.fileSizeInByte'), eb.val(0)).as('usage'))
|
||||||
.groupBy('libraries.id')
|
.groupBy('libraries.id')
|
||||||
.where('libraries.id = :id', { id })
|
.where('libraries.id', '=', id)
|
||||||
.getRawOne();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
return;
|
return;
|
||||||
@ -91,9 +129,4 @@ export class LibraryRepository implements ILibraryRepository {
|
|||||||
total: Number(stats.photos) + Number(stats.videos),
|
total: Number(stats.photos) + Number(stats.videos),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async save(library: Partial<LibraryEntity>) {
|
|
||||||
const { id } = await this.repository.save(library);
|
|
||||||
return this.repository.findOneByOrFail({ id });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { getName } from 'i18n-iso-countries';
|
import { getName } from 'i18n-iso-countries';
|
||||||
|
import { Expression, Kysely, sql, SqlBool } from 'kysely';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
import { createReadStream, existsSync } from 'node:fs';
|
import { createReadStream, existsSync } from 'node:fs';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import readLine from 'node:readline';
|
import readLine from 'node:readline';
|
||||||
import { citiesFile } from 'src/constants';
|
import { citiesFile } from 'src/constants';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { DB, GeodataPlaces, NaturalearthCountries } from 'src/db';
|
||||||
import { GeodataPlacesEntity, GeodataPlacesTempEntity } from 'src/entities/geodata-places.entity';
|
import { AssetEntity, withExif } from 'src/entities/asset.entity';
|
||||||
import {
|
import { NaturalEarthCountriesTempEntity } from 'src/entities/natural-earth-countries.entity';
|
||||||
NaturalEarthCountriesEntity,
|
|
||||||
NaturalEarthCountriesTempEntity,
|
|
||||||
} from 'src/entities/natural-earth-countries.entity';
|
|
||||||
import { LogLevel, SystemMetadataKey } from 'src/enum';
|
import { LogLevel, SystemMetadataKey } from 'src/enum';
|
||||||
import { IConfigRepository } from 'src/interfaces/config.interface';
|
import { IConfigRepository } from 'src/interfaces/config.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
@ -23,21 +21,19 @@ import {
|
|||||||
ReverseGeocodeResult,
|
ReverseGeocodeResult,
|
||||||
} from 'src/interfaces/map.interface';
|
} from 'src/interfaces/map.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
import { OptionalBetween } from 'src/utils/database';
|
|
||||||
import { DataSource, In, IsNull, Not, Repository } from 'typeorm';
|
interface MapDB extends DB {
|
||||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
geodata_places_tmp: GeodataPlaces;
|
||||||
|
naturalearth_countries_tmp: NaturalearthCountries;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MapRepository implements IMapRepository {
|
export class MapRepository implements IMapRepository {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
|
|
||||||
@InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository<GeodataPlacesEntity>,
|
|
||||||
@InjectRepository(NaturalEarthCountriesEntity)
|
|
||||||
private naturalEarthCountriesRepository: Repository<NaturalEarthCountriesEntity>,
|
|
||||||
@InjectDataSource() private dataSource: DataSource,
|
|
||||||
@Inject(IConfigRepository) private configRepository: IConfigRepository,
|
@Inject(IConfigRepository) private configRepository: IConfigRepository,
|
||||||
@Inject(ISystemMetadataRepository) private metadataRepository: ISystemMetadataRepository,
|
@Inject(ISystemMetadataRepository) private metadataRepository: ISystemMetadataRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
|
@InjectKysely() private db: Kysely<MapDB>,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(MapRepository.name);
|
this.logger.setContext(MapRepository.name);
|
||||||
}
|
}
|
||||||
@ -70,39 +66,34 @@ export class MapRepository implements IMapRepository {
|
|||||||
): Promise<MapMarker[]> {
|
): Promise<MapMarker[]> {
|
||||||
const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
|
const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
|
||||||
|
|
||||||
const where = {
|
const assets = (await this.db
|
||||||
isVisible: true,
|
.selectFrom('assets')
|
||||||
isArchived,
|
.$call(withExif)
|
||||||
exifInfo: {
|
.select('id')
|
||||||
latitude: Not(IsNull()),
|
.leftJoin('albums_assets_assets', (join) => join.onRef('assets.id', '=', 'albums_assets_assets.assetsId'))
|
||||||
longitude: Not(IsNull()),
|
.where('isVisible', '=', true)
|
||||||
},
|
.$if(isArchived !== undefined, (q) => q.where('isArchived', '=', isArchived!))
|
||||||
isFavorite,
|
.$if(isFavorite !== undefined, (q) => q.where('isFavorite', '=', isFavorite!))
|
||||||
fileCreatedAt: OptionalBetween(fileCreatedAfter, fileCreatedBefore),
|
.$if(fileCreatedAfter !== undefined, (q) => q.where('fileCreatedAt', '>=', fileCreatedAfter!))
|
||||||
};
|
.$if(fileCreatedBefore !== undefined, (q) => q.where('fileCreatedAt', '<=', fileCreatedBefore!))
|
||||||
|
.where('deletedAt', 'is', null)
|
||||||
|
.where('exif.latitude', 'is not', null)
|
||||||
|
.where('exif.longitude', 'is not', null)
|
||||||
|
.where((eb) => {
|
||||||
|
const ors: Expression<SqlBool>[] = [];
|
||||||
|
|
||||||
const assets = await this.assetRepository.find({
|
if (ownerIds.length > 0) {
|
||||||
select: {
|
ors.push(eb('ownerId', 'in', ownerIds));
|
||||||
id: true,
|
}
|
||||||
exifInfo: {
|
|
||||||
city: true,
|
if (albumIds.length > 0) {
|
||||||
state: true,
|
ors.push(eb('albums_assets_assets.albumsId', 'in', albumIds));
|
||||||
country: true,
|
}
|
||||||
latitude: true,
|
|
||||||
longitude: true,
|
return eb.or(ors);
|
||||||
},
|
})
|
||||||
},
|
.orderBy('fileCreatedAt', 'desc')
|
||||||
where: [
|
.execute()) as any as AssetEntity[];
|
||||||
{ ...where, ownerId: In([...ownerIds]) },
|
|
||||||
{ ...where, albums: { id: In([...albumIds]) } },
|
|
||||||
],
|
|
||||||
relations: {
|
|
||||||
exifInfo: true,
|
|
||||||
},
|
|
||||||
order: {
|
|
||||||
fileCreatedAt: 'DESC',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return assets.map((asset) => ({
|
return assets.map((asset) => ({
|
||||||
id: asset.id,
|
id: asset.id,
|
||||||
@ -117,15 +108,19 @@ export class MapRepository implements IMapRepository {
|
|||||||
async reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult> {
|
async reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult> {
|
||||||
this.logger.debug(`Request: ${point.latitude},${point.longitude}`);
|
this.logger.debug(`Request: ${point.latitude},${point.longitude}`);
|
||||||
|
|
||||||
const response = await this.geodataPlacesRepository
|
const response = await this.db
|
||||||
.createQueryBuilder('geoplaces')
|
.selectFrom('geodata_places')
|
||||||
|
.selectAll()
|
||||||
.where(
|
.where(
|
||||||
'earth_box(ll_to_earth_public(:latitude, :longitude), 25000) @> ll_to_earth_public(latitude, longitude)',
|
sql`earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), 25000)`,
|
||||||
point,
|
'@>',
|
||||||
|
sql`ll_to_earth_public(latitude, longitude)`,
|
||||||
|
)
|
||||||
|
.orderBy(
|
||||||
|
sql`(earth_distance(ll_to_earth_public(${point.latitude}, ${point.longitude}), ll_to_earth_public(latitude, longitude)))`,
|
||||||
)
|
)
|
||||||
.orderBy('earth_distance(ll_to_earth_public(:latitude, :longitude), ll_to_earth_public(latitude, longitude))')
|
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.getOne();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
if (this.logger.isLevelEnabled(LogLevel.VERBOSE)) {
|
if (this.logger.isLevelEnabled(LogLevel.VERBOSE)) {
|
||||||
@ -143,11 +138,12 @@ export class MapRepository implements IMapRepository {
|
|||||||
`Response from database for reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`,
|
`Response from database for reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const ne_response = await this.naturalEarthCountriesRepository
|
const ne_response = await this.db
|
||||||
.createQueryBuilder('naturalearth_countries')
|
.selectFrom('naturalearth_countries')
|
||||||
.where('coordinates @> point (:longitude, :latitude)', point)
|
.selectAll()
|
||||||
|
.where('coordinates', '@>', sql<string>`point(${point.longitude}, ${point.latitude})`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.getOne();
|
.executeTakeFirst();
|
||||||
|
|
||||||
if (!ne_response) {
|
if (!ne_response) {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
@ -176,10 +172,11 @@ export class MapRepository implements IMapRepository {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dataSource.query('DROP TABLE IF EXISTS naturalearth_countries_tmp');
|
await this.db.schema.dropTable('naturalearth_countries_tmp').ifExists().execute();
|
||||||
await this.dataSource.query(
|
await sql`CREATE TABLE naturalearth_countries_tmp (LIKE naturalearth_countries INCLUDING ALL EXCLUDING INDEXES)`.execute(
|
||||||
'CREATE TABLE naturalearth_countries_tmp (LIKE naturalearth_countries INCLUDING ALL EXCLUDING INDEXES)',
|
this.db,
|
||||||
);
|
);
|
||||||
|
|
||||||
const entities: Omit<NaturalEarthCountriesTempEntity, 'id'>[] = [];
|
const entities: Omit<NaturalEarthCountriesTempEntity, 'id'>[] = [];
|
||||||
for (const feature of geoJSONData.features) {
|
for (const feature of geoJSONData.features) {
|
||||||
for (const entry of feature.geometry.coordinates) {
|
for (const entry of feature.geometry.coordinates) {
|
||||||
@ -196,14 +193,14 @@ export class MapRepository implements IMapRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.dataSource.manager.insert(NaturalEarthCountriesTempEntity, entities);
|
await this.db.insertInto('naturalearth_countries_tmp').values(entities).execute();
|
||||||
|
|
||||||
await this.dataSource.query(`ALTER TABLE naturalearth_countries_tmp ADD PRIMARY KEY (id) WITH (FILLFACTOR = 100)`);
|
await sql`ALTER TABLE naturalearth_countries_tmp ADD PRIMARY KEY (id) WITH (FILLFACTOR = 100)`.execute(this.db);
|
||||||
|
|
||||||
await this.dataSource.transaction(async (manager) => {
|
await this.db.transaction().execute(async (manager) => {
|
||||||
await manager.query('ALTER TABLE naturalearth_countries RENAME TO naturalearth_countries_old');
|
await manager.schema.alterTable('naturalearth_countries').renameTo('naturalearth_countries_old').execute();
|
||||||
await manager.query('ALTER TABLE naturalearth_countries_tmp RENAME TO naturalearth_countries');
|
await manager.schema.alterTable('naturalearth_countries_tmp').renameTo('naturalearth_countries').execute();
|
||||||
await manager.query('DROP TABLE naturalearth_countries_old');
|
await manager.schema.dropTable('naturalearth_countries_old').execute();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,17 +211,15 @@ export class MapRepository implements IMapRepository {
|
|||||||
this.loadAdmin(resourcePaths.geodata.admin2),
|
this.loadAdmin(resourcePaths.geodata.admin2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await this.dataSource.query('DROP TABLE IF EXISTS geodata_places_tmp');
|
await this.db.schema.dropTable('geodata_places_tmp').ifExists().execute();
|
||||||
await this.dataSource.query(
|
await sql`CREATE TABLE geodata_places_tmp (LIKE geodata_places INCLUDING ALL EXCLUDING INDEXES)`.execute(this.db);
|
||||||
'CREATE TABLE geodata_places_tmp (LIKE geodata_places INCLUDING ALL EXCLUDING INDEXES)',
|
|
||||||
);
|
|
||||||
await this.loadCities500(admin1, admin2);
|
await this.loadCities500(admin1, admin2);
|
||||||
await this.createGeodataIndices();
|
await this.createGeodataIndices();
|
||||||
|
|
||||||
await this.dataSource.transaction(async (manager) => {
|
await this.db.transaction().execute(async (manager) => {
|
||||||
await manager.query('ALTER TABLE geodata_places RENAME TO geodata_places_old');
|
await manager.schema.alterTable('geodata_places').renameTo('geodata_places_old').execute();
|
||||||
await manager.query('ALTER TABLE geodata_places_tmp RENAME TO geodata_places');
|
await manager.schema.alterTable('geodata_places_tmp').renameTo('geodata_places').execute();
|
||||||
await manager.query('DROP TABLE geodata_places_old');
|
await manager.schema.dropTable('geodata_places_old').execute();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +231,7 @@ export class MapRepository implements IMapRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const input = createReadStream(cities500, { highWaterMark: 512 * 1024 * 1024 });
|
const input = createReadStream(cities500, { highWaterMark: 512 * 1024 * 1024 });
|
||||||
let bufferGeodata: QueryDeepPartialEntity<GeodataPlacesTempEntity>[] = [];
|
let bufferGeodata = [];
|
||||||
const lineReader = readLine.createInterface({ input });
|
const lineReader = readLine.createInterface({ input });
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
@ -257,14 +252,18 @@ export class MapRepository implements IMapRepository {
|
|||||||
admin1Code: lineSplit[10],
|
admin1Code: lineSplit[10],
|
||||||
admin2Code: lineSplit[11],
|
admin2Code: lineSplit[11],
|
||||||
modificationDate: lineSplit[18],
|
modificationDate: lineSplit[18],
|
||||||
admin1Name: admin1Map.get(`${lineSplit[8]}.${lineSplit[10]}`),
|
admin1Name: admin1Map.get(`${lineSplit[8]}.${lineSplit[10]}`) ?? null,
|
||||||
admin2Name: admin2Map.get(`${lineSplit[8]}.${lineSplit[10]}.${lineSplit[11]}`),
|
admin2Name: admin2Map.get(`${lineSplit[8]}.${lineSplit[10]}.${lineSplit[11]}`) ?? null,
|
||||||
};
|
};
|
||||||
bufferGeodata.push(geoData);
|
bufferGeodata.push(geoData);
|
||||||
if (bufferGeodata.length >= 5000) {
|
if (bufferGeodata.length >= 5000) {
|
||||||
const curLength = bufferGeodata.length;
|
const curLength = bufferGeodata.length;
|
||||||
futures.push(
|
futures.push(
|
||||||
this.dataSource.manager.insert(GeodataPlacesTempEntity, bufferGeodata).then(() => {
|
this.db
|
||||||
|
.insertInto('geodata_places_tmp')
|
||||||
|
.values(bufferGeodata)
|
||||||
|
.execute()
|
||||||
|
.then(() => {
|
||||||
count += curLength;
|
count += curLength;
|
||||||
if (count % 10_000 === 0) {
|
if (count % 10_000 === 0) {
|
||||||
this.logger.log(`${count} geodata records imported`);
|
this.logger.log(`${count} geodata records imported`);
|
||||||
@ -280,7 +279,7 @@ export class MapRepository implements IMapRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dataSource.manager.insert(GeodataPlacesTempEntity, bufferGeodata);
|
await this.db.insertInto('geodata_places_tmp').values(bufferGeodata).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadAdmin(filePath: string) {
|
private async loadAdmin(filePath: string) {
|
||||||
@ -303,24 +302,28 @@ export class MapRepository implements IMapRepository {
|
|||||||
|
|
||||||
private createGeodataIndices() {
|
private createGeodataIndices() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.dataSource.query(`ALTER TABLE geodata_places_tmp ADD PRIMARY KEY (id) WITH (FILLFACTOR = 100)`),
|
sql`ALTER TABLE geodata_places_tmp ADD PRIMARY KEY (id) WITH (FILLFACTOR = 100)`.execute(this.db),
|
||||||
this.dataSource.query(`
|
sql`
|
||||||
CREATE INDEX IDX_geodata_gist_earthcoord_${randomUUID().replaceAll('-', '_')}
|
CREATE INDEX IDX_geodata_gist_earthcoord_${sql.raw(randomUUID().replaceAll('-', '_'))}
|
||||||
ON geodata_places_tmp
|
ON geodata_places_tmp
|
||||||
USING gist (ll_to_earth_public(latitude, longitude))
|
USING gist (ll_to_earth_public(latitude, longitude))
|
||||||
WITH (fillfactor = 100)`),
|
WITH (fillfactor = 100)
|
||||||
this.dataSource.query(`
|
`.execute(this.db),
|
||||||
CREATE INDEX idx_geodata_places_name_${randomUUID().replaceAll('-', '_')}
|
this.db.schema
|
||||||
ON geodata_places_tmp
|
.createIndex(`idx_geodata_places_country_code_${randomUUID().replaceAll('-', '_')}`)
|
||||||
USING gin (f_unaccent(name) gin_trgm_ops)`),
|
.on('geodata_places_tmp')
|
||||||
this.dataSource.query(`
|
.using('gin (f_unaccent(name) gin_trgm_ops)')
|
||||||
CREATE INDEX idx_geodata_places_admin1_name_${randomUUID().replaceAll('-', '_')}
|
.execute(),
|
||||||
ON geodata_places_tmp
|
this.db.schema
|
||||||
USING gin (f_unaccent("admin1Name") gin_trgm_ops)`),
|
.createIndex(`idx_geodata_places_country_code_${randomUUID().replaceAll('-', '_')}`)
|
||||||
this.dataSource.query(`
|
.on('geodata_places_tmp')
|
||||||
CREATE INDEX idx_geodata_places_admin2_name_${randomUUID().replaceAll('-', '_')}
|
.using('gin (f_unaccent("admin1Name") gin_trgm_ops)')
|
||||||
ON geodata_places_tmp
|
.execute(),
|
||||||
USING gin (f_unaccent("admin2Name") gin_trgm_ops)`),
|
this.db.schema
|
||||||
|
.createIndex(`idx_geodata_places_admin2_name_${randomUUID().replaceAll('-', '_')}`)
|
||||||
|
.on('geodata_places_tmp')
|
||||||
|
.using('gin (f_unaccent("admin2Name") gin_trgm_ops)')
|
||||||
|
.execute(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,55 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
import { Insertable, Kysely, Updateable } from 'kysely';
|
||||||
|
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { DB, Memories } from 'src/db';
|
||||||
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
import { Chunked, ChunkedSet, DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { MemoryEntity } from 'src/entities/memory.entity';
|
import { MemoryEntity } from 'src/entities/memory.entity';
|
||||||
import { IMemoryRepository } from 'src/interfaces/memory.interface';
|
import { IMemoryRepository } from 'src/interfaces/memory.interface';
|
||||||
import { DataSource, In, Repository } from 'typeorm';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MemoryRepository implements IMemoryRepository {
|
export class MemoryRepository implements IMemoryRepository {
|
||||||
constructor(
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
@InjectRepository(MemoryEntity) private repository: Repository<MemoryEntity>,
|
|
||||||
@InjectDataSource() private dataSource: DataSource,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
search(ownerId: string): Promise<MemoryEntity[]> {
|
search(ownerId: string): Promise<MemoryEntity[]> {
|
||||||
return this.repository.find({
|
return this.db
|
||||||
where: {
|
.selectFrom('memories')
|
||||||
ownerId,
|
.selectAll()
|
||||||
},
|
.where('ownerId', '=', ownerId)
|
||||||
order: {
|
.orderBy('memoryAt', 'desc')
|
||||||
memoryAt: 'DESC',
|
.execute() as Promise<MemoryEntity[]>;
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
get(id: string): Promise<MemoryEntity | null> {
|
get(id: string): Promise<MemoryEntity | null> {
|
||||||
return this.repository.findOne({
|
return this.getByIdBuilder(id).executeTakeFirst() as unknown as Promise<MemoryEntity | null>;
|
||||||
where: {
|
}
|
||||||
id,
|
|
||||||
},
|
async create(memory: Insertable<Memories>, assetIds: Set<string>): Promise<MemoryEntity> {
|
||||||
relations: {
|
const id = await this.db.transaction().execute(async (tx) => {
|
||||||
assets: true,
|
const { id } = await tx.insertInto('memories').values(memory).returning('id').executeTakeFirstOrThrow();
|
||||||
},
|
|
||||||
|
if (assetIds.size > 0) {
|
||||||
|
const values = [...assetIds].map((assetId) => ({ memoriesId: id, assetsId: assetId }));
|
||||||
|
await tx.insertInto('memories_assets_assets').values(values).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this.getByIdBuilder(id).executeTakeFirstOrThrow() as unknown as Promise<MemoryEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
create(memory: Partial<MemoryEntity>): Promise<MemoryEntity> {
|
@GenerateSql({ params: [DummyValue.UUID, { ownerId: DummyValue.UUID, isSaved: true }] })
|
||||||
return this.save(memory);
|
async update(id: string, memory: Updateable<Memories>): Promise<MemoryEntity> {
|
||||||
}
|
await this.db.updateTable('memories').set(memory).where('id', '=', id).execute();
|
||||||
|
return this.getByIdBuilder(id).executeTakeFirstOrThrow() as unknown as Promise<MemoryEntity>;
|
||||||
update(memory: Partial<MemoryEntity>): Promise<MemoryEntity> {
|
|
||||||
return this.save(memory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
await this.repository.delete({ id });
|
await this.db.deleteFrom('memories').where('id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||||
@ -53,46 +59,49 @@ export class MemoryRepository implements IMemoryRepository {
|
|||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await this.dataSource
|
const results = await this.db
|
||||||
.createQueryBuilder()
|
.selectFrom('memories_assets_assets')
|
||||||
.select('memories_assets.assetsId', 'assetId')
|
.select(['assetsId'])
|
||||||
.from('memories_assets_assets', 'memories_assets')
|
.where('memoriesId', '=', id)
|
||||||
.where('"memories_assets"."memoriesId" = :memoryId', { memoryId: id })
|
.where('assetsId', 'in', assetIds)
|
||||||
.andWhere('memories_assets.assetsId IN (:...assetIds)', { assetIds })
|
.execute();
|
||||||
.getRawMany<{ assetId: string }>();
|
|
||||||
|
|
||||||
return new Set(results.map(({ assetId }) => assetId));
|
return new Set(results.map(({ assetsId }) => assetsId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||||
async addAssetIds(id: string, assetIds: string[]): Promise<void> {
|
async addAssetIds(id: string, assetIds: string[]): Promise<void> {
|
||||||
await this.dataSource
|
await this.db
|
||||||
.createQueryBuilder()
|
.insertInto('memories_assets_assets')
|
||||||
.insert()
|
|
||||||
.into('memories_assets_assets', ['memoriesId', 'assetsId'])
|
|
||||||
.values(assetIds.map((assetId) => ({ memoriesId: id, assetsId: assetId })))
|
.values(assetIds.map((assetId) => ({ memoriesId: id, assetsId: assetId })))
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Chunked({ paramIndex: 1 })
|
@Chunked({ paramIndex: 1 })
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, [DummyValue.UUID]] })
|
||||||
async removeAssetIds(id: string, assetIds: string[]): Promise<void> {
|
async removeAssetIds(id: string, assetIds: string[]): Promise<void> {
|
||||||
await this.dataSource
|
await this.db
|
||||||
.createQueryBuilder()
|
.deleteFrom('memories_assets_assets')
|
||||||
.delete()
|
.where('memoriesId', '=', id)
|
||||||
.from('memories_assets_assets')
|
.where('assetsId', 'in', assetIds)
|
||||||
.where({
|
|
||||||
memoriesId: id,
|
|
||||||
assetsId: In(assetIds),
|
|
||||||
})
|
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async save(memory: Partial<MemoryEntity>): Promise<MemoryEntity> {
|
private getByIdBuilder(id: string) {
|
||||||
const { id } = await this.repository.save(memory);
|
return this.db
|
||||||
return this.repository.findOneOrFail({
|
.selectFrom('memories')
|
||||||
where: { id },
|
.selectAll('memories')
|
||||||
relations: {
|
.select((eb) =>
|
||||||
assets: true,
|
jsonArrayFrom(
|
||||||
},
|
eb
|
||||||
});
|
.selectFrom('assets')
|
||||||
|
.selectAll('assets')
|
||||||
|
.innerJoin('memories_assets_assets', 'assets.id', 'memories_assets_assets.assetsId')
|
||||||
|
.whereRef('memories_assets_assets.memoriesId', '=', 'memories.id')
|
||||||
|
.where('assets.deletedAt', 'is', null),
|
||||||
|
).as('assets'),
|
||||||
|
)
|
||||||
|
.where('id', '=', id)
|
||||||
|
.where('deletedAt', 'is', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,93 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { ExpressionBuilder, Insertable, JoinBuilder, Kysely, Updateable } from 'kysely';
|
||||||
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { DB, Partners, Users } from 'src/db';
|
||||||
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { PartnerEntity } from 'src/entities/partner.entity';
|
import { PartnerEntity } from 'src/entities/partner.entity';
|
||||||
import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository, PartnerIds } from 'src/interfaces/partner.interface';
|
||||||
import { DeepPartial, Repository } from 'typeorm';
|
|
||||||
|
const columns = ['id', 'name', 'email', 'profileImagePath', 'profileChangedAt'] as const;
|
||||||
|
|
||||||
|
const onSharedBy = (join: JoinBuilder<DB & { sharedBy: Users }, 'partners' | 'sharedBy'>) =>
|
||||||
|
join.onRef('partners.sharedById', '=', 'sharedBy.id').on('sharedBy.deletedAt', 'is', null);
|
||||||
|
|
||||||
|
const onSharedWith = (join: JoinBuilder<DB & { sharedWith: Users }, 'partners' | 'sharedWith'>) =>
|
||||||
|
join.onRef('partners.sharedWithId', '=', 'sharedWith.id').on('sharedWith.deletedAt', 'is', null);
|
||||||
|
|
||||||
|
const withSharedBy = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
||||||
|
return jsonObjectFrom(
|
||||||
|
eb.selectFrom('users as sharedBy').select(columns).whereRef('sharedBy.id', '=', 'partners.sharedById'),
|
||||||
|
).as('sharedBy');
|
||||||
|
};
|
||||||
|
|
||||||
|
const withSharedWith = (eb: ExpressionBuilder<DB, 'partners'>) => {
|
||||||
|
return jsonObjectFrom(
|
||||||
|
eb.selectFrom('users as sharedWith').select(columns).whereRef('sharedWith.id', '=', 'partners.sharedWithId'),
|
||||||
|
).as('sharedWith');
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PartnerRepository implements IPartnerRepository {
|
export class PartnerRepository implements IPartnerRepository {
|
||||||
constructor(@InjectRepository(PartnerEntity) private repository: Repository<PartnerEntity>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getAll(userId: string): Promise<PartnerEntity[]> {
|
getAll(userId: string): Promise<PartnerEntity[]> {
|
||||||
return this.repository.find({ where: [{ sharedWithId: userId }, { sharedById: userId }] });
|
return this.db
|
||||||
|
.selectFrom('partners')
|
||||||
|
.innerJoin('users as sharedBy', onSharedBy)
|
||||||
|
.innerJoin('users as sharedWith', onSharedWith)
|
||||||
|
.selectAll('partners')
|
||||||
|
.select(withSharedBy)
|
||||||
|
.select(withSharedWith)
|
||||||
|
.where((eb) => eb.or([eb('sharedWithId', '=', userId), eb('sharedById', '=', userId)]))
|
||||||
|
.execute() as Promise<PartnerEntity[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
get({ sharedWithId, sharedById }: PartnerIds): Promise<PartnerEntity | null> {
|
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }] })
|
||||||
return this.repository.findOne({ where: { sharedById, sharedWithId } });
|
get({ sharedWithId, sharedById }: PartnerIds): Promise<PartnerEntity | undefined> {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('partners')
|
||||||
|
.innerJoin('users as sharedBy', onSharedBy)
|
||||||
|
.innerJoin('users as sharedWith', onSharedWith)
|
||||||
|
.selectAll('partners')
|
||||||
|
.select(withSharedBy)
|
||||||
|
.select(withSharedWith)
|
||||||
|
.where('sharedWithId', '=', sharedWithId)
|
||||||
|
.where('sharedById', '=', sharedById)
|
||||||
|
.executeTakeFirst() as unknown as Promise<PartnerEntity | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
create({ sharedById, sharedWithId }: PartnerIds): Promise<PartnerEntity> {
|
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }] })
|
||||||
return this.save({ sharedBy: { id: sharedById }, sharedWith: { id: sharedWithId } });
|
create(values: Insertable<Partners>): Promise<PartnerEntity> {
|
||||||
|
return this.db
|
||||||
|
.insertInto('partners')
|
||||||
|
.values(values)
|
||||||
|
.returningAll()
|
||||||
|
.returning(withSharedBy)
|
||||||
|
.returning(withSharedWith)
|
||||||
|
.executeTakeFirstOrThrow() as unknown as Promise<PartnerEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(entity: PartnerEntity): Promise<void> {
|
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }, { inTimeline: true }] })
|
||||||
await this.repository.remove(entity);
|
update({ sharedWithId, sharedById }: PartnerIds, values: Updateable<Partners>): Promise<PartnerEntity> {
|
||||||
|
return this.db
|
||||||
|
.updateTable('partners')
|
||||||
|
.set(values)
|
||||||
|
.where('sharedWithId', '=', sharedWithId)
|
||||||
|
.where('sharedById', '=', sharedById)
|
||||||
|
.returningAll()
|
||||||
|
.returning(withSharedBy)
|
||||||
|
.returning(withSharedWith)
|
||||||
|
.executeTakeFirstOrThrow() as unknown as Promise<PartnerEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(entity: Partial<PartnerEntity>): Promise<PartnerEntity> {
|
@GenerateSql({ params: [{ sharedWithId: DummyValue.UUID, sharedById: DummyValue.UUID }] })
|
||||||
return this.save(entity);
|
async remove({ sharedWithId, sharedById }: PartnerIds): Promise<void> {
|
||||||
}
|
await this.db
|
||||||
|
.deleteFrom('partners')
|
||||||
private async save(entity: DeepPartial<PartnerEntity>): Promise<PartnerEntity> {
|
.where('sharedWithId', '=', sharedWithId)
|
||||||
await this.repository.save(entity);
|
.where('sharedById', '=', sharedById)
|
||||||
return this.repository.findOneOrFail({
|
.execute();
|
||||||
where: { sharedById: entity.sharedById, sharedWithId: entity.sharedWithId },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
import { AssetEntity } from 'src/entities/asset.entity';
|
||||||
import { StackEntity } from 'src/entities/stack.entity';
|
import { StackEntity } from 'src/entities/stack.entity';
|
||||||
import { IStackRepository, StackSearch } from 'src/interfaces/stack.interface';
|
import { IStackRepository, StackSearch } from 'src/interfaces/stack.interface';
|
||||||
@ -12,6 +13,7 @@ export class StackRepository implements IStackRepository {
|
|||||||
@InjectRepository(StackEntity) private repository: Repository<StackEntity>,
|
@InjectRepository(StackEntity) private repository: Repository<StackEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [{ ownerId: DummyValue.UUID }] })
|
||||||
search(query: StackSearch): Promise<StackEntity[]> {
|
search(query: StackSearch): Promise<StackEntity[]> {
|
||||||
return this.repository.find({
|
return this.repository.find({
|
||||||
where: {
|
where: {
|
||||||
@ -80,6 +82,7 @@ export class StackRepository implements IStackRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async delete(id: string): Promise<void> {
|
async delete(id: string): Promise<void> {
|
||||||
const stack = await this.getById(id);
|
const stack = await this.getById(id);
|
||||||
if (!stack) {
|
if (!stack) {
|
||||||
@ -119,6 +122,7 @@ export class StackRepository implements IStackRepository {
|
|||||||
return this.save(entity);
|
return this.save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async getById(id: string): Promise<StackEntity | null> {
|
async getById(id: string): Promise<StackEntity | null> {
|
||||||
return this.repository.findOne({
|
return this.repository.findOne({
|
||||||
where: {
|
where: {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { ReactionType } from 'src/dtos/activity.dto';
|
import { ReactionType } from 'src/dtos/activity.dto';
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
|
||||||
import { ActivityService } from 'src/services/activity.service';
|
import { ActivityService } from 'src/services/activity.service';
|
||||||
|
import { IActivityRepository } from 'src/types';
|
||||||
import { activityStub } from 'test/fixtures/activity.stub';
|
import { activityStub } from 'test/fixtures/activity.stub';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
import { authStub } from 'test/fixtures/auth.stub';
|
||||||
import { IAccessRepositoryMock } from 'test/repositories/access.repository.mock';
|
import { IAccessRepositoryMock } from 'test/repositories/access.repository.mock';
|
||||||
|
@ -5,15 +5,15 @@ import {
|
|||||||
ActivityResponseDto,
|
ActivityResponseDto,
|
||||||
ActivitySearchDto,
|
ActivitySearchDto,
|
||||||
ActivityStatisticsResponseDto,
|
ActivityStatisticsResponseDto,
|
||||||
|
mapActivity,
|
||||||
MaybeDuplicate,
|
MaybeDuplicate,
|
||||||
ReactionLevel,
|
ReactionLevel,
|
||||||
ReactionType,
|
ReactionType,
|
||||||
mapActivity,
|
|
||||||
} from 'src/dtos/activity.dto';
|
} from 'src/dtos/activity.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
import { ActivityItem } from 'src/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ActivityService extends BaseService {
|
export class ActivityService extends BaseService {
|
||||||
@ -43,7 +43,7 @@ export class ActivityService extends BaseService {
|
|||||||
albumId: dto.albumId,
|
albumId: dto.albumId,
|
||||||
};
|
};
|
||||||
|
|
||||||
let activity: ActivityEntity | null = null;
|
let activity: ActivityItem | undefined;
|
||||||
let duplicate = false;
|
let duplicate = false;
|
||||||
|
|
||||||
if (dto.type === ReactionType.LIKE) {
|
if (dto.type === ReactionType.LIKE) {
|
||||||
|
@ -323,18 +323,16 @@ describe(AlbumService.name, () => {
|
|||||||
albumMock.update.mockResolvedValue(albumStub.sharedWithAdmin);
|
albumMock.update.mockResolvedValue(albumStub.sharedWithAdmin);
|
||||||
userMock.get.mockResolvedValue(userStub.user2);
|
userMock.get.mockResolvedValue(userStub.user2);
|
||||||
albumUserMock.create.mockResolvedValue({
|
albumUserMock.create.mockResolvedValue({
|
||||||
userId: userStub.user2.id,
|
usersId: userStub.user2.id,
|
||||||
user: userStub.user2,
|
albumsId: albumStub.sharedWithAdmin.id,
|
||||||
albumId: albumStub.sharedWithAdmin.id,
|
|
||||||
album: albumStub.sharedWithAdmin,
|
|
||||||
role: AlbumUserRole.EDITOR,
|
role: AlbumUserRole.EDITOR,
|
||||||
});
|
});
|
||||||
await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, {
|
await sut.addUsers(authStub.user1, albumStub.sharedWithAdmin.id, {
|
||||||
albumUsers: [{ userId: authStub.user2.user.id }],
|
albumUsers: [{ userId: authStub.user2.user.id }],
|
||||||
});
|
});
|
||||||
expect(albumUserMock.create).toHaveBeenCalledWith({
|
expect(albumUserMock.create).toHaveBeenCalledWith({
|
||||||
userId: authStub.user2.user.id,
|
usersId: authStub.user2.user.id,
|
||||||
albumId: albumStub.sharedWithAdmin.id,
|
albumsId: albumStub.sharedWithAdmin.id,
|
||||||
});
|
});
|
||||||
expect(eventMock.emit).toHaveBeenCalledWith('album.invite', {
|
expect(eventMock.emit).toHaveBeenCalledWith('album.invite', {
|
||||||
id: albumStub.sharedWithAdmin.id,
|
id: albumStub.sharedWithAdmin.id,
|
||||||
@ -361,8 +359,8 @@ describe(AlbumService.name, () => {
|
|||||||
|
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
||||||
albumId: albumStub.sharedWithUser.id,
|
albumsId: albumStub.sharedWithUser.id,
|
||||||
userId: userStub.user1.id,
|
usersId: userStub.user1.id,
|
||||||
});
|
});
|
||||||
expect(albumMock.getById).toHaveBeenCalledWith(albumStub.sharedWithUser.id, { withAssets: false });
|
expect(albumMock.getById).toHaveBeenCalledWith(albumStub.sharedWithUser.id, { withAssets: false });
|
||||||
});
|
});
|
||||||
@ -388,8 +386,8 @@ describe(AlbumService.name, () => {
|
|||||||
|
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
||||||
albumId: albumStub.sharedWithUser.id,
|
albumsId: albumStub.sharedWithUser.id,
|
||||||
userId: authStub.user1.user.id,
|
usersId: authStub.user1.user.id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -400,8 +398,8 @@ describe(AlbumService.name, () => {
|
|||||||
|
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
expect(albumUserMock.delete).toHaveBeenCalledTimes(1);
|
||||||
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
expect(albumUserMock.delete).toHaveBeenCalledWith({
|
||||||
albumId: albumStub.sharedWithUser.id,
|
albumsId: albumStub.sharedWithUser.id,
|
||||||
userId: authStub.user1.user.id,
|
usersId: authStub.user1.user.id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -433,7 +431,7 @@ describe(AlbumService.name, () => {
|
|||||||
role: AlbumUserRole.EDITOR,
|
role: AlbumUserRole.EDITOR,
|
||||||
});
|
});
|
||||||
expect(albumUserMock.update).toHaveBeenCalledWith(
|
expect(albumUserMock.update).toHaveBeenCalledWith(
|
||||||
{ albumId: albumStub.sharedWithAdmin.id, userId: userStub.admin.id },
|
{ albumsId: albumStub.sharedWithAdmin.id, usersId: userStub.admin.id },
|
||||||
{ role: AlbumUserRole.EDITOR },
|
{ role: AlbumUserRole.EDITOR },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -229,7 +229,7 @@ export class AlbumService extends BaseService {
|
|||||||
throw new BadRequestException('User not found');
|
throw new BadRequestException('User not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.albumUserRepository.create({ userId, albumId: id, role });
|
await this.albumUserRepository.create({ usersId: userId, albumsId: id, role });
|
||||||
await this.eventRepository.emit('album.invite', { id, userId });
|
await this.eventRepository.emit('album.invite', { id, userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,12 +257,12 @@ export class AlbumService extends BaseService {
|
|||||||
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.albumUserRepository.delete({ albumId: id, userId });
|
await this.albumUserRepository.delete({ albumsId: id, usersId: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
|
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
|
||||||
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
||||||
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
|
await this.albumUserRepository.update({ albumsId: id, usersId: userId }, { role: dto.role });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async findOrFail(id: string, options: AlbumInfoOptions) {
|
private async findOrFail(id: string, options: AlbumInfoOptions) {
|
||||||
|
@ -7,7 +7,6 @@ import { StorageCore } from 'src/cores/storage.core';
|
|||||||
import { Users } from 'src/db';
|
import { Users } from 'src/db';
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
|
||||||
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
import { IAlbumRepository } from 'src/interfaces/album.interface';
|
||||||
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
import { IKeyRepository } from 'src/interfaces/api-key.interface';
|
||||||
@ -45,6 +44,7 @@ import { ITrashRepository } from 'src/interfaces/trash.interface';
|
|||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface';
|
import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface';
|
||||||
import { IViewRepository } from 'src/interfaces/view.interface';
|
import { IViewRepository } from 'src/interfaces/view.interface';
|
||||||
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { getConfig, updateConfig } from 'src/utils/config';
|
import { getConfig, updateConfig } from 'src/utils/config';
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export class BaseService {
|
|||||||
constructor(
|
constructor(
|
||||||
@Inject(ILoggerRepository) protected logger: ILoggerRepository,
|
@Inject(ILoggerRepository) protected logger: ILoggerRepository,
|
||||||
@Inject(IAccessRepository) protected accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) protected accessRepository: IAccessRepository,
|
||||||
@Inject(IActivityRepository) protected activityRepository: IActivityRepository,
|
protected activityRepository: ActivityRepository,
|
||||||
@Inject(IAuditRepository) protected auditRepository: IAuditRepository,
|
@Inject(IAuditRepository) protected auditRepository: IAuditRepository,
|
||||||
@Inject(IAlbumRepository) protected albumRepository: IAlbumRepository,
|
@Inject(IAlbumRepository) protected albumRepository: IAlbumRepository,
|
||||||
@Inject(IAlbumUserRepository) protected albumUserRepository: IAlbumUserRepository,
|
@Inject(IAlbumUserRepository) protected albumUserRepository: IAlbumUserRepository,
|
||||||
|
@ -87,7 +87,7 @@ describe(LibraryService.name, () => {
|
|||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
||||||
(library) => library.id === id,
|
(library) => library.id === id,
|
||||||
) || null,
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -190,8 +190,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when library can't be found", async () => {
|
it("should fail when library can't be found", async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(sut.handleQueueSyncFiles({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
await expect(sut.handleQueueSyncFiles({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -242,8 +240,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when library can't be found", async () => {
|
it("should fail when library can't be found", async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(sut.handleQueueSyncAssets({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
await expect(sut.handleQueueSyncAssets({ id: libraryStub.externalLibrary1.id })).resolves.toBe(JobStatus.SKIPPED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -630,7 +626,6 @@ describe(LibraryService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when a library is not found', async () => {
|
it('should throw an error when a library is not found', async () => {
|
||||||
libraryMock.get.mockResolvedValue(null);
|
|
||||||
await expect(sut.get(libraryStub.externalLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.get(libraryStub.externalLibrary1.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
expect(libraryMock.get).toHaveBeenCalledWith(libraryStub.externalLibrary1.id);
|
||||||
});
|
});
|
||||||
@ -825,7 +820,10 @@ describe(LibraryService.name, () => {
|
|||||||
await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual(
|
await expect(sut.update('library-id', { importPaths: [`${cwd}/foo/bar`] })).resolves.toEqual(
|
||||||
mapLibrary(libraryStub.externalLibrary1),
|
mapLibrary(libraryStub.externalLibrary1),
|
||||||
);
|
);
|
||||||
expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' }));
|
expect(libraryMock.update).toHaveBeenCalledWith(
|
||||||
|
'library-id',
|
||||||
|
expect.objectContaining({ importPaths: [`${cwd}/foo/bar`] }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1015,7 +1013,7 @@ describe(LibraryService.name, () => {
|
|||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
[libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find(
|
||||||
(library) => library.id === id,
|
(library) => library.id === id,
|
||||||
) || null,
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ export class LibraryService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const library = await this.libraryRepository.update({ id, ...dto });
|
const library = await this.libraryRepository.update(id, dto);
|
||||||
return mapLibrary(library);
|
return mapLibrary(library);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,7 +571,7 @@ export class LibraryService extends BaseService {
|
|||||||
this.logger.debug(`No non-excluded assets found in any import path for library ${library.id}`);
|
this.logger.debug(`No non-excluded assets found in any import path for library ${library.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.libraryRepository.update({ id: job.id, refreshedAt: new Date() });
|
await this.libraryRepository.update(job.id, { refreshedAt: new Date() });
|
||||||
|
|
||||||
return JobStatus.SUCCESS;
|
return JobStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,17 @@ describe(MemoryService.name, () => {
|
|||||||
memoryAt: new Date(2024),
|
memoryAt: new Date(2024),
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({ assets: [] });
|
).resolves.toMatchObject({ assets: [] });
|
||||||
expect(memoryMock.create).toHaveBeenCalledWith(expect.objectContaining({ assets: [] }));
|
expect(memoryMock.create).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
ownerId: 'admin_id',
|
||||||
|
memoryAt: expect.any(Date),
|
||||||
|
type: MemoryType.ON_THIS_DAY,
|
||||||
|
isSaved: undefined,
|
||||||
|
sendAt: undefined,
|
||||||
|
data: { year: 2024 },
|
||||||
|
},
|
||||||
|
new Set(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a memory', async () => {
|
it('should create a memory', async () => {
|
||||||
@ -80,14 +90,14 @@ describe(MemoryService.name, () => {
|
|||||||
type: MemoryType.ON_THIS_DAY,
|
type: MemoryType.ON_THIS_DAY,
|
||||||
data: { year: 2024 },
|
data: { year: 2024 },
|
||||||
assetIds: ['asset1'],
|
assetIds: ['asset1'],
|
||||||
memoryAt: new Date(2024),
|
memoryAt: new Date(2024, 0, 1),
|
||||||
}),
|
}),
|
||||||
).resolves.toBeDefined();
|
).resolves.toBeDefined();
|
||||||
expect(memoryMock.create).toHaveBeenCalledWith(
|
expect(memoryMock.create).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
ownerId: userStub.admin.id,
|
ownerId: userStub.admin.id,
|
||||||
assets: [{ id: 'asset1' }],
|
|
||||||
}),
|
}),
|
||||||
|
new Set(['asset1']),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -115,12 +125,7 @@ describe(MemoryService.name, () => {
|
|||||||
accessMock.memory.checkOwnerAccess.mockResolvedValue(new Set(['memory1']));
|
accessMock.memory.checkOwnerAccess.mockResolvedValue(new Set(['memory1']));
|
||||||
memoryMock.update.mockResolvedValue(memoryStub.memory1);
|
memoryMock.update.mockResolvedValue(memoryStub.memory1);
|
||||||
await expect(sut.update(authStub.admin, 'memory1', { isSaved: true })).resolves.toBeDefined();
|
await expect(sut.update(authStub.admin, 'memory1', { isSaved: true })).resolves.toBeDefined();
|
||||||
expect(memoryMock.update).toHaveBeenCalledWith(
|
expect(memoryMock.update).toHaveBeenCalledWith('memory1', expect.objectContaining({ isSaved: true }));
|
||||||
expect.objectContaining({
|
|
||||||
id: 'memory1',
|
|
||||||
isSaved: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import { BadRequestException, Injectable } from '@nestjs/common';
|
|||||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from 'src/dtos/memory.dto';
|
import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from 'src/dtos/memory.dto';
|
||||||
import { AssetEntity } from 'src/entities/asset.entity';
|
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
@ -29,15 +28,17 @@ export class MemoryService extends BaseService {
|
|||||||
permission: Permission.ASSET_SHARE,
|
permission: Permission.ASSET_SHARE,
|
||||||
ids: assetIds,
|
ids: assetIds,
|
||||||
});
|
});
|
||||||
const memory = await this.memoryRepository.create({
|
const memory = await this.memoryRepository.create(
|
||||||
|
{
|
||||||
ownerId: auth.user.id,
|
ownerId: auth.user.id,
|
||||||
type: dto.type,
|
type: dto.type,
|
||||||
data: dto.data,
|
data: dto.data,
|
||||||
isSaved: dto.isSaved,
|
isSaved: dto.isSaved,
|
||||||
memoryAt: dto.memoryAt,
|
memoryAt: dto.memoryAt,
|
||||||
seenAt: dto.seenAt,
|
seenAt: dto.seenAt,
|
||||||
assets: [...allowedAssetIds].map((id) => ({ id }) as AssetEntity),
|
},
|
||||||
});
|
allowedAssetIds,
|
||||||
|
);
|
||||||
|
|
||||||
return mapMemory(memory);
|
return mapMemory(memory);
|
||||||
}
|
}
|
||||||
@ -45,8 +46,7 @@ export class MemoryService extends BaseService {
|
|||||||
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
|
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
|
||||||
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const memory = await this.memoryRepository.update({
|
const memory = await this.memoryRepository.update(id, {
|
||||||
id,
|
|
||||||
isSaved: dto.isSaved,
|
isSaved: dto.isSaved,
|
||||||
memoryAt: dto.memoryAt,
|
memoryAt: dto.memoryAt,
|
||||||
seenAt: dto.seenAt,
|
seenAt: dto.seenAt,
|
||||||
@ -68,7 +68,7 @@ export class MemoryService extends BaseService {
|
|||||||
|
|
||||||
const hasSuccess = results.find(({ success }) => success);
|
const hasSuccess = results.find(({ success }) => success);
|
||||||
if (hasSuccess) {
|
if (hasSuccess) {
|
||||||
await this.memoryRepository.update({ id, updatedAt: new Date() });
|
await this.memoryRepository.update(id, { updatedAt: new Date() });
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@ -86,7 +86,7 @@ export class MemoryService extends BaseService {
|
|||||||
|
|
||||||
const hasSuccess = results.find(({ success }) => success);
|
const hasSuccess = results.find(({ success }) => success);
|
||||||
if (hasSuccess) {
|
if (hasSuccess) {
|
||||||
await this.memoryRepository.update({ id, updatedAt: new Date() });
|
await this.memoryRepository.update(id, { id, updatedAt: new Date() });
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -37,7 +37,7 @@ describe(PartnerService.name, () => {
|
|||||||
|
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
it('should create a new partner', async () => {
|
it('should create a new partner', async () => {
|
||||||
partnerMock.get.mockResolvedValue(null);
|
partnerMock.get.mockResolvedValue(void 0);
|
||||||
partnerMock.create.mockResolvedValue(partnerStub.adminToUser1);
|
partnerMock.create.mockResolvedValue(partnerStub.adminToUser1);
|
||||||
|
|
||||||
await expect(sut.create(authStub.admin, authStub.user1.user.id)).resolves.toBeDefined();
|
await expect(sut.create(authStub.admin, authStub.user1.user.id)).resolves.toBeDefined();
|
||||||
@ -67,7 +67,7 @@ describe(PartnerService.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when the partner does not exist', async () => {
|
it('should throw an error when the partner does not exist', async () => {
|
||||||
partnerMock.get.mockResolvedValue(null);
|
partnerMock.get.mockResolvedValue(void 0);
|
||||||
|
|
||||||
await expect(sut.remove(authStub.admin, authStub.user1.user.id)).rejects.toBeInstanceOf(BadRequestException);
|
await expect(sut.remove(authStub.admin, authStub.user1.user.id)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
@ -87,11 +87,10 @@ describe(PartnerService.name, () => {
|
|||||||
partnerMock.update.mockResolvedValue(partnerStub.adminToUser1);
|
partnerMock.update.mockResolvedValue(partnerStub.adminToUser1);
|
||||||
|
|
||||||
await expect(sut.update(authStub.admin, 'shared-by-id', { inTimeline: true })).resolves.toBeDefined();
|
await expect(sut.update(authStub.admin, 'shared-by-id', { inTimeline: true })).resolves.toBeDefined();
|
||||||
expect(partnerMock.update).toHaveBeenCalledWith({
|
expect(partnerMock.update).toHaveBeenCalledWith(
|
||||||
sharedById: 'shared-by-id',
|
{ sharedById: 'shared-by-id', sharedWithId: authStub.admin.user.id },
|
||||||
sharedWithId: authStub.admin.user.id,
|
{ inTimeline: true },
|
||||||
inTimeline: true,
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -43,7 +43,7 @@ export class PartnerService extends BaseService {
|
|||||||
await this.requireAccess({ auth, permission: Permission.PARTNER_UPDATE, ids: [sharedById] });
|
await this.requireAccess({ auth, permission: Permission.PARTNER_UPDATE, ids: [sharedById] });
|
||||||
const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id };
|
const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id };
|
||||||
|
|
||||||
const entity = await this.partnerRepository.update({ ...partnerId, inTimeline: dto.inTimeline });
|
const entity = await this.partnerRepository.update(partnerId, { inTimeline: dto.inTimeline });
|
||||||
return this.mapPartner(entity, PartnerDirection.SharedWith);
|
return this.mapPartner(entity, PartnerDirection.SharedWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { UserEntity } from 'src/entities/user.entity';
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
|
|
||||||
export type AuthApiKey = {
|
export type AuthApiKey = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -7,3 +8,11 @@ export type AuthApiKey = {
|
|||||||
user: UserEntity;
|
user: UserEntity;
|
||||||
permissions: Permission[];
|
permissions: Permission[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RepositoryInterface<T extends object> = Pick<T, keyof T>;
|
||||||
|
|
||||||
|
export type IActivityRepository = RepositoryInterface<ActivityRepository>;
|
||||||
|
|
||||||
|
export type ActivityItem =
|
||||||
|
| Awaited<ReturnType<IActivityRepository['create']>>
|
||||||
|
| Awaited<ReturnType<IActivityRepository['search']>>[0];
|
||||||
|
32
server/test/fixtures/activity.stub.ts
vendored
32
server/test/fixtures/activity.stub.ts
vendored
@ -1,33 +1,39 @@
|
|||||||
import { ActivityEntity } from 'src/entities/activity.entity';
|
import { ActivityItem } from 'src/types';
|
||||||
import { albumStub } from 'test/fixtures/album.stub';
|
import { albumStub } from 'test/fixtures/album.stub';
|
||||||
import { assetStub } from 'test/fixtures/asset.stub';
|
import { assetStub } from 'test/fixtures/asset.stub';
|
||||||
import { authStub } from 'test/fixtures/auth.stub';
|
|
||||||
import { userStub } from 'test/fixtures/user.stub';
|
|
||||||
|
|
||||||
export const activityStub = {
|
export const activityStub = {
|
||||||
oneComment: Object.freeze<ActivityEntity>({
|
oneComment: Object.freeze<ActivityItem>({
|
||||||
id: 'activity-1',
|
id: 'activity-1',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
isLiked: false,
|
isLiked: false,
|
||||||
userId: authStub.admin.user.id,
|
userId: 'admin_id',
|
||||||
user: userStub.admin,
|
user: {
|
||||||
|
id: 'admin_id',
|
||||||
|
name: 'admin',
|
||||||
|
email: 'admin@test.com',
|
||||||
|
profileImagePath: '',
|
||||||
|
profileChangedAt: new Date('2021-01-01'),
|
||||||
|
},
|
||||||
assetId: assetStub.image.id,
|
assetId: assetStub.image.id,
|
||||||
asset: assetStub.image,
|
|
||||||
albumId: albumStub.oneAsset.id,
|
albumId: albumStub.oneAsset.id,
|
||||||
album: albumStub.oneAsset,
|
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
}),
|
}),
|
||||||
liked: Object.freeze<ActivityEntity>({
|
liked: Object.freeze<ActivityItem>({
|
||||||
id: 'activity-2',
|
id: 'activity-2',
|
||||||
comment: null,
|
comment: null,
|
||||||
isLiked: true,
|
isLiked: true,
|
||||||
userId: authStub.admin.user.id,
|
userId: 'admin_id',
|
||||||
user: userStub.admin,
|
user: {
|
||||||
|
id: 'admin_id',
|
||||||
|
name: 'admin',
|
||||||
|
email: 'admin@test.com',
|
||||||
|
profileImagePath: '',
|
||||||
|
profileChangedAt: new Date('2021-01-01'),
|
||||||
|
},
|
||||||
assetId: assetStub.image.id,
|
assetId: assetStub.image.id,
|
||||||
asset: assetStub.image,
|
|
||||||
albumId: albumStub.oneAsset.id,
|
albumId: albumStub.oneAsset.id,
|
||||||
album: albumStub.oneAsset,
|
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
}),
|
}),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
import { IActivityRepository } from 'src/types';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
export const newActivityRepositoryMock = (): Mocked<IActivityRepository> => {
|
export const newActivityRepositoryMock = (): Mocked<IActivityRepository> => {
|
||||||
|
@ -3,7 +3,9 @@ import { Writable } from 'node:stream';
|
|||||||
import { PNG } from 'pngjs';
|
import { PNG } from 'pngjs';
|
||||||
import { ImmichWorker } from 'src/enum';
|
import { ImmichWorker } from 'src/enum';
|
||||||
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
|
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
|
||||||
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
|
import { IActivityRepository } from 'src/types';
|
||||||
import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
|
import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
|
||||||
import { newActivityRepositoryMock } from 'test/repositories/activity.repository.mock';
|
import { newActivityRepositoryMock } from 'test/repositories/activity.repository.mock';
|
||||||
import { newAlbumUserRepositoryMock } from 'test/repositories/album-user.repository.mock';
|
import { newAlbumUserRepositoryMock } from 'test/repositories/album-user.repository.mock';
|
||||||
@ -104,7 +106,7 @@ export const newTestService = <T extends BaseService>(
|
|||||||
const sut = new Service(
|
const sut = new Service(
|
||||||
loggerMock,
|
loggerMock,
|
||||||
accessMock,
|
accessMock,
|
||||||
activityMock,
|
activityMock as IActivityRepository as ActivityRepository,
|
||||||
auditMock,
|
auditMock,
|
||||||
albumMock,
|
albumMock,
|
||||||
albumUserMock,
|
albumUserMock,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:22.12.0-alpine3.20@sha256:96cc8323e25c8cc6ddcb8b965e135cfd57846e8003ec0d7bcec16c5fd5f6d39f
|
FROM node:22.13.0-alpine3.20@sha256:db8dcb90326a0116375414e9a7c068a6b87a4422b7da37b5c6cd026f7c7835d3
|
||||||
|
|
||||||
RUN apk add --no-cache tini
|
RUN apk add --no-cache tini
|
||||||
USER node
|
USER node
|
||||||
|
@ -81,6 +81,7 @@ export default [
|
|||||||
'unicorn/prevent-abbreviations': 'off',
|
'unicorn/prevent-abbreviations': 'off',
|
||||||
'unicorn/no-nested-ternary': 'off',
|
'unicorn/no-nested-ternary': 'off',
|
||||||
'unicorn/consistent-function-scoping': 'off',
|
'unicorn/consistent-function-scoping': 'off',
|
||||||
|
'unicorn/filename-case': 'off',
|
||||||
'unicorn/prefer-top-level-await': 'off',
|
'unicorn/prefer-top-level-await': 'off',
|
||||||
'unicorn/import-style': 'off',
|
'unicorn/import-style': 'off',
|
||||||
'svelte/button-has-type': 'error',
|
'svelte/button-has-type': 'error',
|
||||||
|
317
web/package-lock.json
generated
317
web/package-lock.json
generated
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
|
"@immich/ui": "^0.13.0",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.11.5",
|
"@photo-sphere-viewer/core": "^5.11.5",
|
||||||
@ -104,7 +105,6 @@
|
|||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@ -793,6 +793,31 @@
|
|||||||
"npm": ">=9.0.0"
|
"npm": ">=9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
||||||
|
"integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.6.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz",
|
||||||
|
"integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.6.0",
|
||||||
|
"@floating-ui/utils": "^0.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@formatjs/ecma402-abstract": {
|
"node_modules/@formatjs/ecma402-abstract": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.2.tgz",
|
||||||
@ -1279,11 +1304,34 @@
|
|||||||
"resolved": "../open-api/typescript-sdk",
|
"resolved": "../open-api/typescript-sdk",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@immich/ui": {
|
||||||
|
"version": "0.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.13.0.tgz",
|
||||||
|
"integrity": "sha512-kO6uDbO+UpwRdzDI4FSyXkB7UXNDcnMo86gyLfdjZj6on9fy5Eam9KpJlt/zvVDNAqyGQzrBmdQSQl6n+S1JuA==",
|
||||||
|
"license": "GNU Affero General Public License version 3",
|
||||||
|
"dependencies": {
|
||||||
|
"@mdi/js": "^7.4.47",
|
||||||
|
"bits-ui": "^1.0.0-next.46",
|
||||||
|
"tailwind-merge": "^2.5.4",
|
||||||
|
"tailwind-variants": "^0.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@internationalized/date": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-+z6ti+CcJnRlLHok/emGEsWQhe7kfSmEW+/6qCzvKY67YPh7YOBfvc7+/+NXq+zJlbArg30tYpqLjNgcAYv2YQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^5.1.2",
|
"string-width": "^5.1.2",
|
||||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||||
@ -1300,7 +1348,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -1312,7 +1359,6 @@
|
|||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -1323,14 +1369,12 @@
|
|||||||
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||||
"version": "9.2.2",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eastasianwidth": "^0.2.0",
|
"eastasianwidth": "^0.2.0",
|
||||||
"emoji-regex": "^9.2.2",
|
"emoji-regex": "^9.2.2",
|
||||||
@ -1347,7 +1391,6 @@
|
|||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^6.0.1"
|
"ansi-regex": "^6.0.1"
|
||||||
},
|
},
|
||||||
@ -1362,7 +1405,6 @@
|
|||||||
"version": "8.1.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^6.1.0",
|
"ansi-styles": "^6.1.0",
|
||||||
"string-width": "^5.0.1",
|
"string-width": "^5.0.1",
|
||||||
@ -1542,7 +1584,6 @@
|
|||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "2.0.5",
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
"run-parallel": "^1.1.9"
|
"run-parallel": "^1.1.9"
|
||||||
@ -1555,7 +1596,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
@ -1564,7 +1604,6 @@
|
|||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.scandir": "2.1.5",
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
@ -1605,7 +1644,6 @@
|
|||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
@ -2017,6 +2055,15 @@
|
|||||||
"vite": "^5.0.0"
|
"vite": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||||
|
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.2.0",
|
"version": "10.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.2.0.tgz",
|
||||||
@ -2826,7 +2873,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@ -2846,14 +2892,12 @@
|
|||||||
"node_modules/any-promise": {
|
"node_modules/any-promise": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@ -2865,8 +2909,7 @@
|
|||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@ -2967,18 +3010,40 @@
|
|||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bits-ui": {
|
||||||
|
"version": "1.0.0-next.77",
|
||||||
|
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.0.0-next.77.tgz",
|
||||||
|
"integrity": "sha512-IV0AyVEvsRkXv4s/fl4iea5E9W2b9EBf98s9mRMKMc1xHxM9MmtM2r6MZMqftHQ/c+gHTIt3A9EKuTlh7uay8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.6.4",
|
||||||
|
"@floating-ui/dom": "^1.6.7",
|
||||||
|
"@internationalized/date": "^3.5.6",
|
||||||
|
"esm-env": "^1.1.2",
|
||||||
|
"runed": "^0.22.0",
|
||||||
|
"svelte-toolbelt": "^0.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18",
|
||||||
|
"pnpm": ">=8.7.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/huntabyte"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
@ -2993,7 +3058,6 @@
|
|||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.1.1"
|
"fill-range": "^7.1.1"
|
||||||
@ -3093,7 +3157,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@ -3164,7 +3227,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
@ -3189,7 +3251,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -3350,7 +3411,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@ -3388,7 +3448,6 @@
|
|||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"path-key": "^3.1.0",
|
"path-key": "^3.1.0",
|
||||||
@ -3416,7 +3475,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"cssesc": "bin/cssesc"
|
"cssesc": "bin/cssesc"
|
||||||
},
|
},
|
||||||
@ -3580,14 +3638,12 @@
|
|||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/dlv": {
|
"node_modules/dlv": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/dom-accessibility-api": {
|
"node_modules/dom-accessibility-api": {
|
||||||
"version": "0.5.16",
|
"version": "0.5.16",
|
||||||
@ -3621,8 +3677,7 @@
|
|||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.74",
|
"version": "1.5.74",
|
||||||
@ -3634,8 +3689,7 @@
|
|||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/engine.io-client": {
|
"node_modules/engine.io-client": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
@ -4146,9 +4200,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esm-env": {
|
"node_modules/esm-env": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||||
"integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==",
|
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/esniff": {
|
"node_modules/esniff": {
|
||||||
@ -4306,7 +4360,6 @@
|
|||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||||
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nodelib/fs.stat": "^2.0.2",
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
@ -4323,7 +4376,6 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -4347,7 +4399,6 @@
|
|||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||||
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
@ -4374,7 +4425,6 @@
|
|||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
@ -4424,7 +4474,6 @@
|
|||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
|
||||||
"integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
|
"integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "^7.0.0",
|
"cross-spawn": "^7.0.0",
|
||||||
"signal-exit": "^4.0.1"
|
"signal-exit": "^4.0.1"
|
||||||
@ -4469,7 +4518,6 @@
|
|||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@ -4482,8 +4530,7 @@
|
|||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/geojson-vt": {
|
"node_modules/geojson-vt": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
@ -4527,7 +4574,6 @@
|
|||||||
"version": "10.4.5",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"foreground-child": "^3.1.0",
|
"foreground-child": "^3.1.0",
|
||||||
@ -4548,7 +4594,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.3"
|
"is-glob": "^4.0.3"
|
||||||
},
|
},
|
||||||
@ -4560,7 +4605,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0"
|
"balanced-match": "^1.0.0"
|
||||||
@ -4570,7 +4614,6 @@
|
|||||||
"version": "9.0.5",
|
"version": "9.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^2.0.1"
|
"brace-expansion": "^2.0.1"
|
||||||
@ -4666,7 +4709,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
},
|
},
|
||||||
@ -4852,6 +4894,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
|
||||||
},
|
},
|
||||||
|
"node_modules/inline-style-parser": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/internmap": {
|
"node_modules/internmap": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||||
@ -4882,7 +4930,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
},
|
},
|
||||||
@ -4909,7 +4956,6 @@
|
|||||||
"version": "2.13.0",
|
"version": "2.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
|
||||||
"integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
|
"integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
},
|
},
|
||||||
@ -4944,7 +4990,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -4953,7 +4998,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@ -4962,7 +5006,6 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
},
|
},
|
||||||
@ -4974,7 +5017,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
@ -5113,7 +5155,6 @@
|
|||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/cliui": "^8.0.2"
|
"@isaacs/cliui": "^8.0.2"
|
||||||
},
|
},
|
||||||
@ -5128,7 +5169,6 @@
|
|||||||
"version": "1.21.6",
|
"version": "1.21.6",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
||||||
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
@ -5303,8 +5343,7 @@
|
|||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/locate-character": {
|
"node_modules/locate-character": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@ -5546,7 +5585,6 @@
|
|||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
@ -5555,7 +5593,6 @@
|
|||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.3",
|
"braces": "^3.0.3",
|
||||||
@ -5623,7 +5660,6 @@
|
|||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16 || 14 >=14.17"
|
"node": ">=16 || 14 >=14.17"
|
||||||
}
|
}
|
||||||
@ -5660,7 +5696,6 @@
|
|||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0",
|
"any-promise": "^1.0.0",
|
||||||
"object-assign": "^4.0.1",
|
"object-assign": "^4.0.1",
|
||||||
@ -5671,7 +5706,6 @@
|
|||||||
"version": "3.3.8",
|
"version": "3.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -5734,7 +5768,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -5760,7 +5793,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -5769,7 +5801,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@ -5850,8 +5881,7 @@
|
|||||||
"node_modules/package-json-from-dist": {
|
"node_modules/package-json-from-dist": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
|
||||||
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
|
"integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -5910,7 +5940,6 @@
|
|||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@ -5918,14 +5947,12 @@
|
|||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/path-scurry": {
|
"node_modules/path-scurry": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
@ -5940,8 +5967,7 @@
|
|||||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||||
"version": "10.4.3",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/pathe": {
|
"node_modules/pathe": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@ -5976,14 +6002,12 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.6"
|
"node": ">=8.6"
|
||||||
},
|
},
|
||||||
@ -5995,7 +6019,6 @@
|
|||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -6004,7 +6027,6 @@
|
|||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
|
||||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@ -6031,7 +6053,6 @@
|
|||||||
"version": "8.5.0",
|
"version": "8.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.0.tgz",
|
||||||
"integrity": "sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==",
|
"integrity": "sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -6060,7 +6081,6 @@
|
|||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
"read-cache": "^1.0.0",
|
"read-cache": "^1.0.0",
|
||||||
@ -6077,7 +6097,6 @@
|
|||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camelcase-css": "^2.0.1"
|
"camelcase-css": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -6125,7 +6144,6 @@
|
|||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -6194,7 +6212,6 @@
|
|||||||
"version": "6.1.2",
|
"version": "6.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
@ -6207,8 +6224,7 @@
|
|||||||
"node_modules/postcss-value-parser": {
|
"node_modules/postcss-value-parser": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/potpack": {
|
"node_modules/potpack": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -6341,7 +6357,6 @@
|
|||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -6372,7 +6387,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pify": "^2.3.0"
|
"pify": "^2.3.0"
|
||||||
}
|
}
|
||||||
@ -6483,7 +6497,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
},
|
},
|
||||||
@ -6561,7 +6574,6 @@
|
|||||||
"version": "1.22.8",
|
"version": "1.22.8",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||||
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-core-module": "^2.13.0",
|
"is-core-module": "^2.13.0",
|
||||||
@ -6587,7 +6599,6 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"iojs": ">=1.0.0",
|
"iojs": ">=1.0.0",
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -6697,7 +6708,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -6716,6 +6726,21 @@
|
|||||||
"queue-microtask": "^1.2.2"
|
"queue-microtask": "^1.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/runed": {
|
||||||
|
"version": "0.22.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/runed/-/runed-0.22.0.tgz",
|
||||||
|
"integrity": "sha512-ZWVXWhOr0P5xdNgtviz6D1ivLUDWKLCbeC5SUEJ3zBkqLReVqWHenFxMNFeFaiC5bfxhFxyxzyzB+98uYFtwdA==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/huntabyte",
|
||||||
|
"https://github.com/sponsors/tglide"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"esm-env": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rw": {
|
"node_modules/rw": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||||
@ -6843,7 +6868,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shebang-regex": "^3.0.0"
|
"shebang-regex": "^3.0.0"
|
||||||
},
|
},
|
||||||
@ -6855,7 +6879,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@ -6870,7 +6893,6 @@
|
|||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
@ -6979,7 +7001,6 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@ -7078,7 +7099,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
@ -7093,7 +7113,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
@ -7107,7 +7126,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
@ -7120,7 +7138,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
},
|
},
|
||||||
@ -7152,11 +7169,19 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-to-object": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inline-style-parser": "0.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sucrase": {
|
"node_modules/sucrase": {
|
||||||
"version": "3.35.0",
|
"version": "3.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||||
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
@ -7199,7 +7224,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@ -7841,6 +7865,41 @@
|
|||||||
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
|
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte-toolbelt": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-i/Tv4NwAWWqJnK5H0F8y/ubDnogDYlwwyzKhrspTUFzrFuGnYshqd2g4/R43ds841wmaFiSW/HsdsdWhPOlrAA==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/huntabyte"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"runed": "^0.20.0",
|
||||||
|
"style-to-object": "^1.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18",
|
||||||
|
"pnpm": ">=8.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svelte-toolbelt/node_modules/runed": {
|
||||||
|
"version": "0.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/runed/-/runed-0.20.0.tgz",
|
||||||
|
"integrity": "sha512-YqPxaUdWL5nUXuSF+/v8a+NkVN8TGyEGbQwTA25fLY35MR/2bvZ1c6sCbudoo1kT4CAJPh4kUkcgGVxW127WKw==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/huntabyte",
|
||||||
|
"https://github.com/sponsors/tglide"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"esm-env": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svelte/node_modules/aria-query": {
|
"node_modules/svelte/node_modules/aria-query": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
|
||||||
@ -7858,11 +7917,36 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tailwind-variants": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-ho2k5kn+LB1fT5XdNS3Clb96zieWxbStE9wNLK7D0AV64kdZMaYzAKo0fWl6fXLPY99ffF9oBJnIj5escEl/8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tailwind-merge": "^2.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.x",
|
||||||
|
"pnpm": ">=7.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.17",
|
"version": "3.4.17",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||||
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
@ -7900,7 +7984,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
@ -7913,7 +7996,6 @@
|
|||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
||||||
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -7949,7 +8031,6 @@
|
|||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
||||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
@ -8000,7 +8081,6 @@
|
|||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0"
|
"any-promise": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -8009,7 +8089,6 @@
|
|||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
},
|
},
|
||||||
@ -8098,7 +8177,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
@ -8163,8 +8241,7 @@
|
|||||||
"node_modules/ts-interface-checker": {
|
"node_modules/ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
@ -8308,8 +8385,7 @@
|
|||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/validate-npm-package-license": {
|
"node_modules/validate-npm-package-license": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
@ -8581,7 +8657,6 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
},
|
},
|
||||||
@ -8635,7 +8710,6 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^4.0.0",
|
"ansi-styles": "^4.0.0",
|
||||||
"string-width": "^4.1.0",
|
"string-width": "^4.1.0",
|
||||||
@ -8652,7 +8726,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -8667,7 +8740,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
},
|
},
|
||||||
@ -8678,8 +8750,7 @@
|
|||||||
"node_modules/wrap-ansi-cjs/node_modules/color-name": {
|
"node_modules/wrap-ansi-cjs/node_modules/color-name": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
|
"@immich/ui": "^0.13.0",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
"@mapbox/mapbox-gl-rtl-text": "0.2.3",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@photo-sphere-viewer/core": "^5.11.5",
|
"@photo-sphere-viewer/core": "^5.11.5",
|
||||||
|
@ -22,6 +22,30 @@
|
|||||||
--immich-dark-success: 56 142 60;
|
--immich-dark-success: 56 142 60;
|
||||||
--immich-dark-warning: 245 124 0;
|
--immich-dark-warning: 245 124 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* light */
|
||||||
|
--immich-ui-primary: 66 80 175;
|
||||||
|
--immich-ui-dark: 58 58 58;
|
||||||
|
--immich-ui-light: 255 255 255;
|
||||||
|
--immich-ui-success: 34 197 94;
|
||||||
|
--immich-ui-danger: 180 0 0;
|
||||||
|
--immich-ui-warning: 255 170 0;
|
||||||
|
--immich-ui-info: 14 165 233;
|
||||||
|
--immich-ui-default-border: 209 213 219;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
/* dark */
|
||||||
|
--immich-ui-primary: 172 203 250;
|
||||||
|
--immich-ui-light: 0 0 0;
|
||||||
|
--immich-ui-dark: 229 231 235;
|
||||||
|
/* --immich-success: 56 142 60; */
|
||||||
|
--immich-ui-danger: 239 68 68;
|
||||||
|
--immich-ui-warning: 255 170 0;
|
||||||
|
--immich-ui-info: 14 165 233;
|
||||||
|
--immich-ui-default-border: 55 65 81;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user