forked from dark_thunder/immich
feat(web)!: SPA (#5069)
* feat(web): SPA * chore: remove unnecessary prune * feat(web): merge with immich-server * Correct method name * fix: bugs, docs, workflows, etc. * chore: keep dockerignore for dev * chore: remove license * fix: expose 2283 --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
20
.dockerignore
Normal file
20
.dockerignore
Normal file
@ -0,0 +1,20 @@
|
||||
.vscode/
|
||||
cli/
|
||||
design/
|
||||
docker/
|
||||
docs/
|
||||
fastlane/
|
||||
machine-learning/
|
||||
misc/
|
||||
mobile/
|
||||
|
||||
server/node_modules
|
||||
server/coverage/
|
||||
server/.reverse-geocoding-dump/
|
||||
server/upload/
|
||||
server/dist/
|
||||
|
||||
web/node_modules/
|
||||
web/coverage/
|
||||
web/.svelte-kit
|
||||
web/build/
|
10
.github/workflows/docker-cleanup.yml
vendored
10
.github/workflows/docker-cleanup.yml
vendored
@ -29,14 +29,11 @@ jobs:
|
||||
include:
|
||||
- primary-name: "immich-server"
|
||||
- primary-name: "immich-machine-learning"
|
||||
- primary-name: "immich-web"
|
||||
- primary-name: "immich-proxy"
|
||||
env:
|
||||
# Requires a personal access token with the OAuth scope delete:packages
|
||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
||||
steps:
|
||||
-
|
||||
name: Clean temporary images
|
||||
- name: Clean temporary images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.4.0
|
||||
with:
|
||||
@ -60,15 +57,12 @@ jobs:
|
||||
include:
|
||||
- primary-name: "immich-server"
|
||||
- primary-name: "immich-machine-learning"
|
||||
- primary-name: "immich-web"
|
||||
- primary-name: "immich-proxy"
|
||||
- primary-name: "immich-build-cache"
|
||||
env:
|
||||
# Requires a personal access token with the OAuth scope delete:packages
|
||||
TOKEN: ${{ secrets.PACKAGE_DELETE_TOKEN }}
|
||||
steps:
|
||||
-
|
||||
name: Clean untagged images
|
||||
- name: Clean untagged images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.4.0
|
||||
with:
|
||||
|
11
.github/workflows/docker.yml
vendored
11
.github/workflows/docker.yml
vendored
@ -24,16 +24,12 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- context: "web"
|
||||
image: "immich-web"
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
- context: "machine-learning"
|
||||
file: "machine-learning/Dockerfile"
|
||||
image: "immich-machine-learning"
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
- context: "nginx"
|
||||
image: "immich-proxy"
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
- context: "server"
|
||||
- context: "."
|
||||
file: "server/Dockerfile"
|
||||
image: "immich-server"
|
||||
platforms: "linux/arm64,linux/amd64"
|
||||
|
||||
@ -103,6 +99,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5.0.0
|
||||
with:
|
||||
context: ${{ matrix.context }}
|
||||
file: ${{ matrix.file }}
|
||||
platforms: ${{ matrix.platforms }}
|
||||
# Skip pushing when PR from a fork
|
||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
|
@ -6,31 +6,34 @@ version: "3.8"
|
||||
|
||||
name: immich-dev
|
||||
|
||||
x-server-build: &server-common
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
target: dev
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:debug immich
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
<<: *server-common
|
||||
ports:
|
||||
- 3001:3001
|
||||
- 9230:9230
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
@ -38,30 +41,13 @@ services:
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: immich-microservices:latest
|
||||
command: npm run start:debug microservices
|
||||
<<: *server-common
|
||||
# extends:
|
||||
# file: hwaccel.yml
|
||||
# service: hwaccel
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:debug microservices
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 9231:9230
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 1048576
|
||||
hard: 1048576
|
||||
depends_on:
|
||||
- database
|
||||
- immich-server
|
||||
@ -73,12 +59,11 @@ services:
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
command: npm run dev --host
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 3000:3000
|
||||
- 2283:3000
|
||||
- 24678:24678
|
||||
volumes:
|
||||
- ../web:/usr/src/app
|
||||
@ -139,22 +124,5 @@ services:
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: immich-proxy-dev:latest
|
||||
environment:
|
||||
# Make sure these values get passed through from the env file
|
||||
- IMMICH_SERVER_URL
|
||||
- IMMICH_WEB_URL
|
||||
build:
|
||||
context: ../nginx
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 2283:8080
|
||||
depends_on:
|
||||
- immich-server
|
||||
- immich-web
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
model-cache:
|
||||
|
@ -2,19 +2,25 @@ version: "3.8"
|
||||
|
||||
name: immich-prod
|
||||
|
||||
x-server-build: &server-common
|
||||
image: immich-server:latest
|
||||
build:
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: immich-server:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
command: [ "./start-server.sh" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
<<: *server-common
|
||||
ports:
|
||||
- 2283:3001
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
@ -22,35 +28,15 @@ services:
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: immich-microservices:latest
|
||||
command: [ "./start-microservices.sh" ]
|
||||
<<: *server-common
|
||||
# extends:
|
||||
# file: hwaccel.yml
|
||||
# service: hwaccel
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
command: [ "./start-microservices.sh" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}/photos:/usr/src/app/upload
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- immich-server
|
||||
- typesense
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: immich-web:latest
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
depends_on:
|
||||
- immich-server
|
||||
|
||||
immich-machine-learning:
|
||||
@ -95,23 +81,5 @@ services:
|
||||
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: immich-proxy:latest
|
||||
environment:
|
||||
# Make sure these values get passed through from the env file
|
||||
- IMMICH_SERVER_URL
|
||||
- IMMICH_WEB_URL
|
||||
build:
|
||||
context: ../nginx
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 2283:8080
|
||||
logging:
|
||||
driver: none
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
model-cache:
|
||||
|
@ -6,9 +6,9 @@ services:
|
||||
immich-server:
|
||||
image: immich-server-dev:latest
|
||||
build:
|
||||
context: ../server
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
context: ../
|
||||
dockerfile: server/Dockerfile
|
||||
target: dev
|
||||
command: npm run test:e2e
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
|
@ -12,6 +12,8 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- 2283:3001
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
@ -45,13 +47,6 @@ services:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: ghcr.io/immich-app/immich-web:${IMMICH_VERSION:-release}
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.1@sha256:9bcff2b829f12074426ca044b56160ca9d777a0c488303469143dd9f8259d4dd
|
||||
@ -82,16 +77,6 @@ services:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: ghcr.io/immich-app/immich-proxy:${IMMICH_VERSION:-release}
|
||||
ports:
|
||||
- 2283:8080
|
||||
depends_on:
|
||||
- immich-server
|
||||
- immich-web
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
model-cache:
|
||||
|
@ -1,21 +1,6 @@
|
||||
# Reverse Proxy
|
||||
|
||||
When deploying Immich it is important to understand that a reverse proxy is required in front of the server and web container. The reverse proxy acts as an intermediary between the user and container, forwarding requests to the correct container based on the URL path.
|
||||
|
||||
## Default Reverse Proxy
|
||||
|
||||
Immich provides a default nginx reverse proxy preconfigured to perform the correct routing and set the necessary headers for the server and web container to use. These headers are crucial to redirect to the correct URL and determine the client's IP address.
|
||||
|
||||
## Using a Different Reverse Proxy
|
||||
|
||||
While the reverse proxy provided by Immich works well for basic deployments, some users may want to use a different reverse proxy. Fortunately, Immich is flexible enough to accommodate different reverse proxies. Users can either:
|
||||
|
||||
1. Add another reverse proxy on top of Immich's reverse proxy
|
||||
2. Completely replace the default reverse proxy
|
||||
|
||||
## Adding a Custom Reverse Proxy
|
||||
|
||||
Users can deploy a custom reverse proxy that forwards requests to Immich's reverse proxy. This way, the new reverse proxy can handle TLS termination, load balancing, or other advanced features, while still delegating routing decisions to Immich's reverse proxy. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.
|
||||
Users can deploy a custom reverse proxy that forwards requests to Immich. This way, the reverse proxy can handle TLS termination, load balancing, or other advanced features. All reverse proxies between Immich and the user must forward all headers and set the `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto` and `X-Forwarded-For` headers to their appropriate values. Additionally, your reverse proxy should allow for big enough uploads. By following these practices, you ensure that all custom reverse proxies are fully compatible with Immich.
|
||||
|
||||
### Nginx example config
|
||||
|
||||
@ -43,7 +28,3 @@ server {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Replacing the Default Reverse Proxy
|
||||
|
||||
Replacing Immich's default reverse proxy is an advanced deployment and support may be limited. When replacing Immich's default proxy it is important to ensure that requests to `/api/*` are routed to the server container and all other requests to the web container. Additionally, the previously mentioned headers should be configured accordingly. You may find our [nginx configuration file](https://github.com/immich-app/immich/blob/main/nginx/templates/default.conf.template) a helpful reference.
|
||||
|
@ -17,6 +17,5 @@ Our [GitHub Repository](https://github.com/immich-app/immich) is a [monorepo](ht
|
||||
| `machine-learning/` | Source code for the `immich-machine-learning` docker image |
|
||||
| `misc/release/` | Scripts for version pumps and draft releases |
|
||||
| `mobile/` | Source code for the mobile app, both Android and iOS |
|
||||
| `nginx/` | Source code for the `immich-proxy` docker image |
|
||||
| `server/` | Source code for the `immich-server` docker image |
|
||||
| `web/` | Source code for the `immich-web` docker image |
|
||||
| `web/` | Source code for the `web` |
|
||||
|
@ -52,7 +52,7 @@ If you only want to do web development connected to an existing, remote backend,
|
||||
3. Start the web development server
|
||||
|
||||
```
|
||||
PUBLIC_IMMICH_SERVER_URL=https://demo.immich.app/api npm run dev
|
||||
IMMICH_SERVER_URL=https://demo.immich.app/api npm run dev
|
||||
```
|
||||
|
||||
## IDE setup
|
||||
|
@ -13,7 +13,3 @@ Running Immich on Windows can be frustrating and there are lots of ways it can g
|
||||
### NTFS Mounted Volumes
|
||||
|
||||
The docker-compose.dev.yml and docker-compose.prod.yml use volume mounts for the postgres database. On start-up, postgres will try to `chown` the data directory, but fail. See [this post](https://forums.docker.com/t/data-directory-var-lib-postgresql-data-pgdata-has-wrong-ownership/17963/24) for more information about this issue and possible solutions.
|
||||
|
||||
### `Cannot read properties of null (reading 'split')`
|
||||
|
||||
This error occurs when trying to access the app via port `3000` instead of `2283`. During development `immich-proxy` runs on port 2283, while `immich-web` runs on `3000`.
|
||||
|
@ -122,28 +122,6 @@ TYPESENSE_API_KEY=some-random-text
|
||||
|
||||
PUBLIC_LOGIN_PAGE_MESSAGE="My Family Photos and Videos Backup Server"
|
||||
|
||||
####################################################################################
|
||||
# Alternative Service Addresses - Optional
|
||||
#
|
||||
# This is an advanced feature for users who may be running their immich services on different hosts.
|
||||
# It will not change which address or port that services bind to within their containers, but it will change where other services look for their peers.
|
||||
# Note: immich-microservices is bound to 3002, but no references are made
|
||||
####################################################################################
|
||||
|
||||
IMMICH_WEB_URL=http://immich-web:3000
|
||||
IMMICH_SERVER_URL=http://immich-server:3001
|
||||
|
||||
####################################################################################
|
||||
# Alternative API's External Address - Optional
|
||||
#
|
||||
# This is an advanced feature used to control the public server endpoint returned to clients during Well-known discovery.
|
||||
# You should only use this if you want mobile apps to access the immich API over a custom URL. Do not include trailing slash.
|
||||
# NOTE: At this time, the web app will not be affected by this setting and will continue to use the relative path: /api
|
||||
# Examples: http://localhost:3001, http://immich-api.example.com, etc
|
||||
####################################################################################
|
||||
|
||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
||||
|
||||
###################################################################################
|
||||
# Immich Version - Optional
|
||||
#
|
||||
|
@ -63,21 +63,6 @@ These environment variables are used by the `docker-compose.yml` file and do **N
|
||||
| `MACHINE_LEARNING_HOST` | Machine Learning Host | `0.0.0.0` | machine learning |
|
||||
| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning |
|
||||
|
||||
## URLs
|
||||
|
||||
| Variable | Description | Default | Services |
|
||||
| :------------------------- | :---------------------- | :-------------------------: | :--------- |
|
||||
| `IMMICH_WEB_URL` | Immich Web URL | `http://immich-web:3000` | proxy |
|
||||
| `IMMICH_SERVER_URL` | Immich Server URL | `http://immich-server:3001` | web, proxy |
|
||||
| `PUBLIC_IMMICH_SERVER_URL` | Public Immich URL | `http://immich-server:3001` | web |
|
||||
| `IMMICH_API_URL_EXTERNAL` | Immich API URL External | `/api` | web |
|
||||
|
||||
:::info
|
||||
|
||||
The above paths are modifying the internal paths of the containers.
|
||||
|
||||
:::
|
||||
|
||||
## Database
|
||||
|
||||
| Variable | Description | Default | Services |
|
||||
|
@ -98,12 +98,12 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
||||
|
||||
> Note: This can take several minutes depending on your Internet speed and Unraid hardware
|
||||
|
||||
9. Once on the Docker page you will see several Immich containers, one of them will be labelled `immich_proxy` and will have a port mapping. Visit the `IP:PORT` displayed in your web browser and you should see the Immich admin setup page.
|
||||
9. Once on the Docker page you will see several Immich containers, one of them will be labelled `immich_web` and will have a port mapping. Visit the `IP:PORT` displayed in your web browser and you should see the Immich admin setup page.
|
||||
|
||||
<img
|
||||
src={require('./img/unraid06.webp').default}
|
||||
width="80%"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-proxy"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-web"
|
||||
/>
|
||||
|
||||
<details >
|
||||
@ -112,12 +112,12 @@ alt="Go to Docker Tab and visit the address listed next to immich-proxy"
|
||||
<img
|
||||
src={require('./img/unraid07.webp').default}
|
||||
width="80%"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-proxy"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-web"
|
||||
/>
|
||||
<img
|
||||
src={require('./img/unraid08.webp').default}
|
||||
width="90%"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-proxy"
|
||||
alt="Go to Docker Tab and visit the address listed next to immich-web"
|
||||
/>
|
||||
|
||||
</details>
|
||||
|
@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# vim:sw=4:ts=4:et
|
||||
|
||||
set -e
|
||||
|
||||
entrypoint_log() {
|
||||
if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
ME=$(basename $0)
|
||||
DEFAULT_CONF_FILE="etc/nginx/conf.d/default.conf"
|
||||
|
||||
# check if we have ipv6 available
|
||||
if [ ! -f "/proc/net/if_inet6" ]; then
|
||||
entrypoint_log "$ME: info: ipv6 not available"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "/$DEFAULT_CONF_FILE" ]; then
|
||||
entrypoint_log "$ME: info: /$DEFAULT_CONF_FILE is not a file or does not exist"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# check if the file can be modified, e.g. not on a r/o filesystem
|
||||
touch /$DEFAULT_CONF_FILE 2>/dev/null || { entrypoint_log "$ME: info: can not modify /$DEFAULT_CONF_FILE (read-only file system?)"; exit 0; }
|
||||
|
||||
# check if the file is already modified, e.g. on a container restart
|
||||
grep -q "listen \[::]\:8080;" /$DEFAULT_CONF_FILE && { entrypoint_log "$ME: info: IPv6 listen already enabled"; exit 0; }
|
||||
|
||||
if [ -f "/etc/os-release" ]; then
|
||||
. /etc/os-release
|
||||
else
|
||||
entrypoint_log "$ME: info: can not guess the operating system"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# enable ipv6 on default.conf listen sockets
|
||||
sed -i -E 's,listen 8080;,listen 8080;\n listen [::]:8080;,' /$DEFAULT_CONF_FILE
|
||||
|
||||
entrypoint_log "$ME: info: Enabled listen on IPv6 in /$DEFAULT_CONF_FILE"
|
||||
|
||||
exit 0
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
export IMMICH_WEB_URL="${IMMICH_WEB_URL:-http://immich-web:3000}"
|
||||
IMMICH_WEB_SCHEME=$(echo "$IMMICH_WEB_URL" | grep -Eo '^https?://' || echo "http://")
|
||||
export IMMICH_WEB_SCHEME
|
||||
IMMICH_WEB_HOST=$(echo "$IMMICH_WEB_URL" | cut -d '/' -f 3)
|
||||
export IMMICH_WEB_HOST
|
||||
export IMMICH_SERVER_URL="${IMMICH_SERVER_URL:-http://immich-server:3001}"
|
||||
IMMICH_SERVER_SCHEME=$(echo "$IMMICH_WEB_URL" | grep -Eo '^https?://' || echo "http://")
|
||||
export IMMICH_SERVER_SCHEME
|
||||
IMMICH_SERVER_HOST=$(echo "$IMMICH_SERVER_URL" | cut -d '/' -f 3)
|
||||
export IMMICH_SERVER_HOST
|
@ -1,9 +0,0 @@
|
||||
FROM ghcr.io/nginxinc/nginx-unprivileged:1.25.1-alpine3.17@sha256:c38e27fdba47f725f49177b88fdd1fd2feef11b13dc11dea3695c3feb2c6d96d
|
||||
|
||||
COPY LICENSE /licenses/LICENSE.txt
|
||||
COPY LICENSE /LICENSE
|
||||
|
||||
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
|
||||
COPY 15-set-env-variables.envsh /docker-entrypoint.d
|
||||
|
||||
COPY templates/ /etc/nginx/templates
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hau Tran
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,72 +0,0 @@
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
map $http_x_forwarded_proto $forwarded_protocol {
|
||||
default $scheme;
|
||||
|
||||
# Only allow the values 'http' and 'https' for the X-Forwarded-Proto header.
|
||||
http http;
|
||||
https https;
|
||||
}
|
||||
|
||||
upstream server {
|
||||
server ${IMMICH_SERVER_HOST};
|
||||
keepalive 2;
|
||||
}
|
||||
|
||||
upstream web {
|
||||
server ${IMMICH_WEB_HOST};
|
||||
keepalive 2;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
|
||||
access_log off;
|
||||
client_max_body_size 50000M;
|
||||
|
||||
# Compression
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_proxied any;
|
||||
gzip_vary on;
|
||||
gunzip on;
|
||||
|
||||
# text/html is included by default
|
||||
gzip_types
|
||||
application/javascript
|
||||
application/json
|
||||
font/ttf
|
||||
image/svg+xml
|
||||
text/css;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_busy_buffers_size 24k;
|
||||
proxy_buffers 64 4k;
|
||||
proxy_force_ranges on;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $forwarded_protocol;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
|
||||
location /api {
|
||||
|
||||
rewrite /api/(.*) /$1 break;
|
||||
|
||||
proxy_pass ${IMMICH_SERVER_SCHEME}server;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
||||
proxy_pass ${IMMICH_WEB_SCHEME}web;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
node_modules/
|
||||
upload/
|
||||
dist/
|
||||
coverage/
|
||||
.reverse-geocoding-dump
|
@ -1,33 +1,42 @@
|
||||
FROM ghcr.io/immich-app/base-server-dev:20231109 as builder
|
||||
# dev build
|
||||
FROM ghcr.io/immich-app/base-server-dev:20231109 as dev
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
WORKDIR /usr/src/app
|
||||
COPY server/package.json server/package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
COPY server .
|
||||
|
||||
|
||||
FROM builder as prod
|
||||
FROM dev AS prod
|
||||
|
||||
RUN npm run build
|
||||
RUN npm prune --omit=dev --omit=optional
|
||||
|
||||
# web build
|
||||
FROM node:20.8-alpine3.18 as web
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY web/package.json web/package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY web .
|
||||
RUN npm run build
|
||||
|
||||
|
||||
# prod build
|
||||
FROM ghcr.io/immich-app/base-server-prod:20231109
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production
|
||||
|
||||
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||
COPY --from=prod /usr/src/app/dist ./dist
|
||||
COPY --from=prod /usr/src/app/bin ./bin
|
||||
COPY ./assets ./assets
|
||||
|
||||
COPY --from=web /usr/src/app/build ./www
|
||||
COPY server/assets assets
|
||||
COPY server/package.json server/package-lock.json ./
|
||||
COPY server/start*.sh ./
|
||||
RUN npm link && npm cache clean --force
|
||||
COPY LICENSE /licenses/LICENSE.txt
|
||||
COPY LICENSE /LICENSE
|
||||
COPY package.json package-lock.json ./
|
||||
COPY start*.sh ./
|
||||
|
||||
RUN npm link && npm cache clean --force
|
||||
VOLUME /usr/src/app/upload
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
ENTRYPOINT ["tini", "--", "/bin/sh"]
|
||||
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hau Tran
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -15,7 +15,7 @@ export class EnablePasswordLoginCommand extends CommandRunner {
|
||||
const config = await this.configService.getConfig();
|
||||
config.passwordLogin.enabled = true;
|
||||
await this.configService.updateConfig(config);
|
||||
await axios.post('http://localhost:3001/refresh-config');
|
||||
await axios.post('http://localhost:3001/api/refresh-config');
|
||||
console.log('Password login has been enabled.');
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ export class DisablePasswordLoginCommand extends CommandRunner {
|
||||
const config = await this.configService.getConfig();
|
||||
config.passwordLogin.enabled = false;
|
||||
await this.configService.updateConfig(config);
|
||||
await axios.post('http://localhost:3001/refresh-config');
|
||||
await axios.post('http://localhost:3001/api/refresh-config');
|
||||
console.log('Password login has been disabled.');
|
||||
}
|
||||
}
|
||||
|
@ -306,9 +306,9 @@ describe(SystemConfigService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTheme', () => {
|
||||
describe('getCustomCss', () => {
|
||||
it('should return the default theme', async () => {
|
||||
await expect(sut.getTheme()).resolves.toEqual(defaults.theme);
|
||||
await expect(sut.getCustomCss()).resolves.toEqual(defaults.theme.customCss);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { JobName } from '../job';
|
||||
import { CommunicationEvent, ICommunicationRepository, IJobRepository, ISystemConfigRepository } from '../repositories';
|
||||
import { SystemConfigThemeDto } from './dto/system-config-theme.dto';
|
||||
import { SystemConfigDto, mapConfig } from './dto/system-config.dto';
|
||||
import { SystemConfigTemplateStorageOptionDto } from './response-dto/system-config-template-storage-option.dto';
|
||||
import {
|
||||
@ -31,11 +30,6 @@ export class SystemConfigService {
|
||||
return this.core.config$;
|
||||
}
|
||||
|
||||
async getTheme(): Promise<SystemConfigThemeDto> {
|
||||
const { theme } = await this.core.getConfig();
|
||||
return theme;
|
||||
}
|
||||
|
||||
async getConfig(): Promise<SystemConfigDto> {
|
||||
const config = await this.core.getConfig();
|
||||
return mapConfig(config);
|
||||
@ -87,4 +81,9 @@ export class SystemConfigService {
|
||||
|
||||
return JSON.parse(await this.repository.readFile(`./assets/style-${theme}.json`));
|
||||
}
|
||||
|
||||
async getCustomCss(): Promise<string> {
|
||||
const { theme } = await this.core.getConfig();
|
||||
return theme.customCss;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
SwaggerDocumentOptions,
|
||||
SwaggerModule,
|
||||
} from '@nestjs/swagger';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
@ -56,6 +57,12 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
||||
document.components.schemas = sortKeys(document.components.schemas);
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(document.paths)) {
|
||||
const newKey = key.replace('/api/', '/');
|
||||
delete document.paths[key];
|
||||
document.paths[newKey] = value;
|
||||
}
|
||||
|
||||
for (const path of Object.values(document.paths)) {
|
||||
const operations = {
|
||||
get: path.get,
|
||||
@ -94,6 +101,14 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
||||
return document;
|
||||
};
|
||||
|
||||
export const indexFallback = (excludePaths: string[]) => (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.url.startsWith('/api') || req.method.toLowerCase() !== 'get' || excludePaths.indexOf(req.url) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
res.sendFile('/www/index.html', { root: process.cwd() });
|
||||
}
|
||||
};
|
||||
|
||||
export const useSwagger = (app: INestApplication, isDev: boolean) => {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Immich')
|
||||
|
@ -1,15 +1,34 @@
|
||||
import { SystemConfigService } from '@app/domain';
|
||||
import { Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { Controller, Get, Header, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { ApiExcludeEndpoint } from '@nestjs/swagger';
|
||||
import { PublicRoute } from '../app.guard';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private configService: SystemConfigService) {}
|
||||
constructor(private service: SystemConfigService) {}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@Get('.well-known/immich')
|
||||
getImmichWellKnown() {
|
||||
return {
|
||||
api: {
|
||||
endpoint: '/api',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@PublicRoute()
|
||||
@Get('custom.css')
|
||||
@Header('Content-Type', 'text/css')
|
||||
getCustomCss() {
|
||||
return this.service.getCustomCss();
|
||||
}
|
||||
|
||||
@ApiExcludeEndpoint()
|
||||
@Post('refresh-config')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public reloadConfig() {
|
||||
return this.configService.refreshConfig();
|
||||
return this.service.refreshConfig();
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { json } from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { AppModule } from './app.module';
|
||||
import { useSwagger } from './app.utils';
|
||||
import { indexFallback, useSwagger } from './app.utils';
|
||||
|
||||
const logger = new Logger('ImmichServer');
|
||||
const port = Number(process.env.SERVER_PORT) || 3001;
|
||||
@ -24,6 +24,11 @@ export async function bootstrap() {
|
||||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||
useSwagger(app, isDev);
|
||||
|
||||
const excludePaths = ['/.well-known/immich', '/custom.css'];
|
||||
app.setGlobalPrefix('api', { exclude: excludePaths });
|
||||
app.useStaticAssets('www');
|
||||
app.use(indexFallback(excludePaths));
|
||||
|
||||
const server = await app.listen(port);
|
||||
server.requestTimeout = 30 * 60 * 1000;
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { Logger } from '@nestjs/common';
|
||||
import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
|
||||
@WebSocketGateway({ cors: true })
|
||||
@WebSocketGateway({ cors: true, path: '/api/socket.io' })
|
||||
export class CommunicationRepository implements OnGatewayConnection, OnGatewayDisconnect, ICommunicationRepository {
|
||||
private logger = new Logger(CommunicationRepository.name);
|
||||
private onConnectCallbacks: Callback[] = [];
|
||||
|
@ -1,4 +1,4 @@
|
||||
node_modules/
|
||||
upload/
|
||||
dist/
|
||||
|
||||
coverage/
|
||||
.svelte-kit
|
||||
build/
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user