refactor: move cli to package folder (#28356)
parent
fb0a54d548
commit
db589455f4
|
|
@ -75,7 +75,7 @@
|
|||
{
|
||||
"label": "Build Immich CLI",
|
||||
"type": "shell",
|
||||
"command": "pnpm --filter cli build:dev"
|
||||
"command": "pnpm --filter @immich/cli build:dev"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
cli:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- cli/src/**
|
||||
- packages/cli/src/**
|
||||
|
||||
documentation:
|
||||
- changed-files:
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ on:
|
|||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'cli/**'
|
||||
- 'packages/cli/**'
|
||||
- '.github/workflows/cli.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'cli/**'
|
||||
- 'packages/cli/**'
|
||||
- '.github/workflows/cli.yml'
|
||||
release:
|
||||
types: [published]
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
packages: write
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
|
|
@ -89,7 +89,7 @@ jobs:
|
|||
- name: Get package version
|
||||
id: package-version
|
||||
run: |
|
||||
version=$(jq -r '.version' cli/package.json)
|
||||
version=$(jq -r '.version' packages/cli/package.json)
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate docker image tags
|
||||
|
|
@ -107,7 +107,7 @@ jobs:
|
|||
- name: Build and push image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
file: cli/Dockerfile
|
||||
file: packages/cli/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
cache-from: type=gha
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
- 'server/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
cli:
|
||||
- 'cli/**'
|
||||
- 'packages/cli/**'
|
||||
- 'packages/sdk/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
e2e:
|
||||
|
|
@ -95,7 +95,7 @@ jobs:
|
|||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
|
|
@ -126,7 +126,7 @@ jobs:
|
|||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./cli
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||
|
|
@ -379,19 +379,14 @@ jobs:
|
|||
cache: 'pnpm'
|
||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||
|
||||
- name: Setup @immich/sdk
|
||||
run: pnpm --filter @immich/sdk install --frozen-lockfile && pnpm --filter @immich/sdk build
|
||||
- name: Setup packages
|
||||
run: pnpm --filter "@immich/*" install --frozen-lockfile && pnpm --filter "@immich/*" build
|
||||
|
||||
- name: Run setup web
|
||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
||||
working-directory: ./web
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run setup cli
|
||||
run: pnpm install --frozen-lockfile && pnpm build
|
||||
working-directory: ./cli
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
if: ${{ !cancelled() }}
|
||||
|
|
|
|||
|
|
@ -23,15 +23,17 @@
|
|||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Immich CLI",
|
||||
"program": "${workspaceFolder}/cli/dist/index.js",
|
||||
"program": "${workspaceFolder}/packages/cli/dist/index.js",
|
||||
"args": ["upload", "--help"],
|
||||
"runtimeArgs": ["--enable-source-maps"],
|
||||
"console": "integratedTerminal",
|
||||
"resolveSourceMapLocations": ["${workspaceFolder}/cli/dist/**/*.js.map"],
|
||||
"resolveSourceMapLocations": [
|
||||
"${workspaceFolder}/packages/cli/dist/**/*.js.map"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"outFiles": ["${workspaceFolder}/cli/dist/**/*.js"],
|
||||
"outFiles": ["${workspaceFolder}/packages/cli/dist/**/*.js"],
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"preLaunchTask": "Build Immich CLI"
|
||||
"preLaunchTask": "Build @immich/cli"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -66,7 +66,7 @@ VOLUME_DIRS = \
|
|||
./packages/sdk/node_modules \
|
||||
./.github/node_modules \
|
||||
./node_modules \
|
||||
./cli/node_modules
|
||||
./packages/cli/node_modules
|
||||
|
||||
# Include .env file if it exists
|
||||
-include docker/.env
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ services:
|
|||
- server_node_modules:/usr/src/app/server/node_modules
|
||||
- web_node_modules:/usr/src/app/web/node_modules
|
||||
- github_node_modules:/usr/src/app/.github/node_modules
|
||||
- cli_node_modules:/usr/src/app/cli/node_modules
|
||||
- cli_node_modules:/usr/src/app/packages/cli/node_modules
|
||||
- docs_node_modules:/usr/src/app/docs/node_modules
|
||||
- e2e_node_modules:/usr/src/app/e2e/node_modules
|
||||
- sdk_node_modules:/usr/src/app/packages/sdk/node_modules
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](ht
|
|||
| :------------------ | :------------------------------------------------------------------- |
|
||||
| `.github/` | Github templates and action workflows |
|
||||
| `.vscode/` | VSCode debug launch profiles |
|
||||
| `cli/` | Source code for the work-in-progress CLI rewrite |
|
||||
| `packages/cli` | Source code for the CLI |
|
||||
| `packages/sdk` | Source code for the generated OpenAPI SDK |
|
||||
| `docker/` | Docker compose resources for dev, test, production |
|
||||
| `design/` | Screenshots and logos for the README |
|
||||
| `docs/` | Source code for the [https://immich.app](https://immich.app) website |
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
|
|||
import { immichCli } from 'src/utils';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const pkg = JSON.parse(readFileSync('../cli/package.json', 'utf8'));
|
||||
const pkg = JSON.parse(readFileSync('../packages/cli/package.json', 'utf8'));
|
||||
|
||||
describe(`immich --version`, () => {
|
||||
describe('immich --version', () => {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export const tempDir = tmpdir();
|
|||
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
||||
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
||||
export const immichCli = (args: string[]) =>
|
||||
executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../cli' }).promise;
|
||||
executeCommand('pnpm', ['exec', 'immich', '-d', `/${tempDir}/immich/`, ...args], { cwd: '../packages/cli' }).promise;
|
||||
export const dockerExec = (args: string[]) =>
|
||||
executeCommand('docker', ['exec', '-i', 'immich-e2e-server', '/bin/bash', '-c', args.join(' ')]);
|
||||
export const immichAdmin = (args: string[]) => dockerExec([`immich-admin ${args.join(' ')}`]);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
|||
pnpm version "$NEXT_SERVER" --no-git-tag-version
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix server
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix i18n
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix cli
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix packages/cli
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix web
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix e2e
|
||||
pnpm version "$NEXT_SERVER" --no-git-tag-version --prefix packages/sdk
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ experimental_monorepo_root = true
|
|||
config_roots = [
|
||||
"plugins",
|
||||
"server",
|
||||
"cli",
|
||||
"packages/cli",
|
||||
"deployment",
|
||||
"mobile",
|
||||
"e2e",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ FROM node:24.1.0-alpine3.20@sha256:8fe019e0d57dbdce5f5c27c0b63d2775cf34b00e3755a
|
|||
|
||||
WORKDIR /usr/src/app
|
||||
COPY package* pnpm* .pnpmfile.cjs ./
|
||||
COPY ./cli ./cli/
|
||||
COPY ./packages ./packages/
|
||||
RUN corepack enable pnpm && \
|
||||
pnpm install --filter @immich/sdk --filter @immich/cli --frozen-lockfile && \
|
||||
pnpm --filter @immich/sdk build && \
|
||||
pnpm --filter @immich/cli build
|
||||
pnpm --filter @immich/sdk --filter @immich/cli install --frozen-lockfile && \
|
||||
pnpm --filter @immich/sdk --filter @immich/cli build
|
||||
|
||||
WORKDIR /import
|
||||
|
||||
ENTRYPOINT ["node", "/usr/src/app/cli/dist"]
|
||||
ENTRYPOINT ["node", "/usr/src/app/packages/cli/dist"]
|
||||
|
|
@ -2,6 +2,11 @@
|
|||
"name": "@immich/cli",
|
||||
"version": "2.7.5",
|
||||
"description": "Command Line Interface (CLI) for Immich",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/immich.git",
|
||||
"directory": "packages/cli"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": "./dist/index.js",
|
||||
"bin": {
|
||||
|
|
@ -52,11 +57,6 @@
|
|||
"format:fix": "prettier --cache --write --list-different .",
|
||||
"check": "tsc --noEmit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/immich.git",
|
||||
"directory": "cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
198
pnpm-lock.yaml
198
pnpm-lock.yaml
|
|
@ -24,103 +24,6 @@ importers:
|
|||
specifier: ^3.7.4
|
||||
version: 3.8.3
|
||||
|
||||
cli:
|
||||
dependencies:
|
||||
chokidar:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
fast-glob:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.3
|
||||
fastq:
|
||||
specifier: ^1.17.1
|
||||
version: 1.20.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.18.1
|
||||
micromatch:
|
||||
specifier: ^4.0.8
|
||||
version: 4.0.8
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.1(eslint@10.2.1(jiti@2.6.1))
|
||||
'@immich/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/sdk
|
||||
'@types/byte-size':
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.2
|
||||
'@types/cli-progress':
|
||||
specifier: ^3.11.0
|
||||
version: 3.11.6
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/micromatch':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.10
|
||||
'@types/mock-fs':
|
||||
specifier: ^4.13.1
|
||||
version: 4.13.4
|
||||
'@types/node':
|
||||
specifier: ^24.12.2
|
||||
version: 24.12.2
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(vitest@4.1.5)
|
||||
byte-size:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.1
|
||||
cli-progress:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
commander:
|
||||
specifier: ^12.0.0
|
||||
version: 12.1.0
|
||||
eslint:
|
||||
specifier: ^10.0.0
|
||||
version: 10.2.1(jiti@2.6.1)
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.8
|
||||
version: 10.1.8(eslint@10.2.1(jiti@2.6.1))
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.1.3
|
||||
version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.2.1(jiti@2.6.1)))(eslint@10.2.1(jiti@2.6.1))(prettier@3.8.3)
|
||||
eslint-plugin-unicorn:
|
||||
specifier: ^64.0.0
|
||||
version: 64.0.0(eslint@10.2.1(jiti@2.6.1))
|
||||
globals:
|
||||
specifier: ^17.0.0
|
||||
version: 17.5.0
|
||||
mock-fs:
|
||||
specifier: ^5.2.0
|
||||
version: 5.5.0
|
||||
prettier:
|
||||
specifier: ^3.7.4
|
||||
version: 3.8.3
|
||||
prettier-plugin-organize-imports:
|
||||
specifier: ^4.0.0
|
||||
version: 4.3.0(prettier@3.8.3)(typescript@6.0.3)
|
||||
typescript:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.58.0
|
||||
version: 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)
|
||||
vite:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest:
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest-fetch-mock:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.5(vitest@4.1.5)
|
||||
yaml:
|
||||
specifier: ^2.3.1
|
||||
version: 2.8.3
|
||||
|
||||
docs:
|
||||
dependencies:
|
||||
'@docusaurus/core':
|
||||
|
|
@ -201,7 +104,7 @@ importers:
|
|||
version: 10.4.0
|
||||
'@immich/cli':
|
||||
specifier: workspace:*
|
||||
version: link:../cli
|
||||
version: link:../packages/cli
|
||||
'@immich/e2e-auth-server':
|
||||
specifier: workspace:*
|
||||
version: link:../e2e-auth-server
|
||||
|
|
@ -314,6 +217,103 @@ importers:
|
|||
specifier: ^4.1.1
|
||||
version: 4.2.0(prettier@3.8.3)
|
||||
|
||||
packages/cli:
|
||||
dependencies:
|
||||
chokidar:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
fast-glob:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.3
|
||||
fastq:
|
||||
specifier: ^1.17.1
|
||||
version: 1.20.1
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.18.1
|
||||
micromatch:
|
||||
specifier: ^4.0.8
|
||||
version: 4.0.8
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.1(eslint@10.2.1(jiti@2.6.1))
|
||||
'@immich/sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../sdk
|
||||
'@types/byte-size':
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.2
|
||||
'@types/cli-progress':
|
||||
specifier: ^3.11.0
|
||||
version: 3.11.6
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.12
|
||||
version: 4.17.12
|
||||
'@types/micromatch':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.10
|
||||
'@types/mock-fs':
|
||||
specifier: ^4.13.1
|
||||
version: 4.13.4
|
||||
'@types/node':
|
||||
specifier: ^24.12.2
|
||||
version: 24.12.2
|
||||
'@vitest/coverage-v8':
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(vitest@4.1.5)
|
||||
byte-size:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.1
|
||||
cli-progress:
|
||||
specifier: ^3.12.0
|
||||
version: 3.12.0
|
||||
commander:
|
||||
specifier: ^12.0.0
|
||||
version: 12.1.0
|
||||
eslint:
|
||||
specifier: ^10.0.0
|
||||
version: 10.2.1(jiti@2.6.1)
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.8
|
||||
version: 10.1.8(eslint@10.2.1(jiti@2.6.1))
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.1.3
|
||||
version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.2.1(jiti@2.6.1)))(eslint@10.2.1(jiti@2.6.1))(prettier@3.8.3)
|
||||
eslint-plugin-unicorn:
|
||||
specifier: ^64.0.0
|
||||
version: 64.0.0(eslint@10.2.1(jiti@2.6.1))
|
||||
globals:
|
||||
specifier: ^17.0.0
|
||||
version: 17.5.0
|
||||
mock-fs:
|
||||
specifier: ^5.2.0
|
||||
version: 5.5.0
|
||||
prettier:
|
||||
specifier: ^3.7.4
|
||||
version: 3.8.3
|
||||
prettier-plugin-organize-imports:
|
||||
specifier: ^4.0.0
|
||||
version: 4.3.0(prettier@3.8.3)(typescript@6.0.3)
|
||||
typescript:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.58.0
|
||||
version: 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)
|
||||
vite:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
|
||||
vitest:
|
||||
specifier: ^4.0.0
|
||||
version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest-fetch-mock:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.5(vitest@4.1.5)
|
||||
yaml:
|
||||
specifier: ^2.3.1
|
||||
version: 2.8.3
|
||||
|
||||
packages/sdk:
|
||||
dependencies:
|
||||
'@oazapfts/runtime':
|
||||
|
|
@ -18018,7 +18018,7 @@ snapshots:
|
|||
obug: 2.1.1
|
||||
std-env: 4.1.0
|
||||
tinyrainbow: 3.1.0
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(happy-dom@20.9.0)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(vite@8.0.10(@types/node@24.12.2)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
|
||||
|
||||
'@vitest/expect@3.2.4':
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
packages:
|
||||
- packages/**
|
||||
- cli
|
||||
- docs
|
||||
- e2e
|
||||
- e2e-auth-server
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ RUN --mount=type=cache,id=pnpm-web,target=/buildcache/pnpm-store \
|
|||
|
||||
FROM builder AS cli
|
||||
|
||||
COPY ./cli ./cli/
|
||||
COPY ./packages ./packages/
|
||||
RUN --mount=type=cache,id=pnpm-cli,target=/buildcache/pnpm-store \
|
||||
--mount=type=bind,source=package.json,target=package.json \
|
||||
|
|
|
|||
Loading…
Reference in New Issue