diff --git a/.dockerignore b/.dockerignore
index d22e30d..7828809 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,4 +1,7 @@
img/
grafana/
.github/
-Dockerfile
\ No newline at end of file
+Dockerfile
+README.md
+.env*
+.gitignore
\ No newline at end of file
diff --git a/.env.example b/.env.example
index 755743f..c153499 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,2 @@
-IMMICH_USERNAME=
-IMMICH_PASSWORD=
+IMMICH_API_KEY=
IMMICH_BASE_URL=
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..5539eb9
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,17 @@
+name: Build
+on:
+ workflow_dispatch:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+jobs:
+
+ build:
+ name: Run build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+ - name: Build
+ run: go build -o ./immich.out ./src
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..d052439
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,34 @@
+name: CodeQL
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'go' ]
+
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v2
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
\ No newline at end of file
diff --git a/.github/workflows/push_docker.yml b/.github/workflows/docker.yml
similarity index 59%
rename from .github/workflows/push_docker.yml
rename to .github/workflows/docker.yml
index d535850..2c506cf 100644
--- a/.github/workflows/push_docker.yml
+++ b/.github/workflows/docker.yml
@@ -8,9 +8,7 @@ on:
type: string
jobs:
-
-
- build_push_monolith:
+ build_docker_release:
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -19,29 +17,34 @@ jobs:
ref: 'main'
fetch-depth: 0
-
-
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2.1.0
+ uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
id: buildx
- uses: docker/setup-buildx-action@v2.2.1
+ uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: martabal
password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Login to GitHub Container Registry
+ uses: docker/login-action@v2
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GH_TOKEN }}
- - name: Build and push immich
- uses: docker/build-push-action@v3.2.0
+ - name: Build and push
+ uses: docker/build-push-action@v4
with:
context: ./
- file: ./Dockerfile
platforms: linux/arm/v7,linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: |
martabal/immich-exporter:${{ inputs.tags }}
martabal/immich-exporter:latest
-
+ ghcr.io/${{ github.repository_owner }}/immich-exporter:${{ inputs.tags }}
+ ghcr.io/${{ github.repository_owner }}/immich-exporter:latest
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..7f73fbd
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,17 @@
+name: Test
+on:
+ workflow_dispatch:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+jobs:
+
+ test:
+ name: Run tests
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+ - name: Run formatter
+ run: test -z $(gofmt -l ./src)
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 96dd709..d611b63 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,16 +1,17 @@
-FROM golang:1.19-alpine3.17 AS builder
+FROM golang:1.20-alpine3.18 AS builder
WORKDIR /app
COPY . .
-RUN go get -d -v ./src/ && \
- go build -o /go/bin/immich-exporter ./src
+RUN go build -o /go/bin/immich-exporter ./src
-FROM alpine:3.17
+
+FROM alpine:3.18
COPY --from=builder /go/bin/immich-exporter /go/bin/immich-exporter
COPY package.json /go/bin/
WORKDIR /go/bin
-CMD ["/go/bin/immich-exporter"]
+
+CMD ["/go/bin/immich-exporter"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 9eea173..eeaee4d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# immich-exporter
-[![Publish Release](https://github.com/martabal/immich-exporter/actions/workflows/push_docker.yml/badge.svg)](https://github.com/martabal/immich-exporter/actions/workflows/push_docker.yml)
+[![Publish Release](https://github.com/martabal/immich-exporter/actions/workflows/docker.yml/badge.svg)](https://github.com/martabal/immich-exporter/actions/workflows/docker.yml)
+[![Build](https://github.com/martabal/immich-exporter/actions/workflows/build.yml/badge.svg)](https://github.com/martabal/immich-exporter/actions/workflows/build.yml)
+[![Test](https://github.com/martabal/immich-exporter/actions/workflows/test.yml/badge.svg)](https://github.com/martabal/immich-exporter/actions/workflows/test.yml)
@@ -11,13 +13,14 @@ This app is made to be integrated with the [immich-grafana-dashboard](https://gi
## Run it
+Create an API key in your Immich settings and set `IMMICH_API_KEY` to is value.
+
### Docker-cli ([click here for more info](https://docs.docker.com/engine/reference/commandline/cli/))
```sh
docker run --name=immich-exporter \
-e IMMICH_URL=http://192.168.1.10:8080 \
- -e IMMICH_PASSWORD='' \
- -e IMMICH_USERNAME=admin \
+ -e IMMICH_API_KEY= \
-p 8090:8090 \
martabal/immich-exporter
```
@@ -32,8 +35,7 @@ services:
container_name: immich-exporter
environment:
- IMMICH_URL=http://192.168.1.10:8080
- - IMMICH_PASSWORD=''
- - IMMICH_USERNAME=admin
+ - IMMICH_API_KEY=
ports:
- 8090:8090
restart: unless-stopped
@@ -63,12 +65,24 @@ If you want to use an .env file, edit `.env.example` to match your setup, rename
| Parameters | Function |
| :-----: | ----- |
| `-p 8090` | Webservice port |
-| `-e IMMICH_USERNAME` | Immich username |
-| `-e IMMICH_PASSWORD` | Immich password |
| `-e IMMICH_BASE_URL` | Immich base URL |
+| `-e IMMICH_API_KEY` | Immich API key |
+| `-e EXPORTER_PORT` | qbittorrent export port (optional) | `8090` |
+| `-e LOG_LEVEL` | App log level (`TRACE`, `DEBUG`, `INFO`, `WARN` and `ERROR`) | `INFO` |
### Arguments
| Arguments | Function |
| :-----: | ----- |
-| -e | Use a .env file containing environment variables (.env file must be placed in the same directory) |
+| -e | If qbittorrent-exporter detects a .env file in the same directory, the values in the .env will be used, `-e` forces the usage of environment variables |
+
+### Setup
+
+Add the target to your `scrape_configs` in your `prometheus.yml` file of your Prometheus instance.
+
+```yaml
+scrape_configs:
+ - job_name: 'immich'
+ static_configs:
+ - targets: [ ':8090' ]
+```
diff --git a/go.mod b/go.mod
index c50fe88..3e94810 100644
--- a/go.mod
+++ b/go.mod
@@ -1,15 +1,19 @@
-module immich-exporter
+module immich-exp
go 1.20
-require github.com/joho/godotenv v1.5.1
+require (
+ github.com/joho/godotenv v1.5.1
+ github.com/prometheus/client_golang v1.14.0
+ github.com/sirupsen/logrus v1.6.0
+)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
- github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
diff --git a/go.sum b/go.sum
index 906d70e..94823d4 100644
--- a/go.sum
+++ b/go.sum
@@ -52,6 +52,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -109,6 +110,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -138,6 +140,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -155,6 +158,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -185,11 +189,13 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -465,6 +471,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/package.json b/package.json
index 229dbf9..c9585c9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "immich-exporter",
- "version": "0.1.2",
+ "version": "1.0.0",
"description": "exporter for immich",
"main": "src/main.go",
"scripts": {
diff --git a/src/immich/auth.go b/src/immich/auth.go
deleted file mode 100644
index ef9e221..0000000
--- a/src/immich/auth.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package immich
-
-import (
- "encoding/json"
- "fmt"
- "immich-exporter/src/models"
- "io/ioutil"
- "log"
- "net/http"
- "strings"
-)
-
-func Auth() {
-
- url := models.GetURL() + "/api/auth/login"
- method := "POST"
-
- payload := strings.NewReader(`{
- "email": "` + models.GetUsername() + `",
- "password": "` + models.Getpassword() + `"}`)
-
- client := &http.Client{}
- req, err := http.NewRequest(method, url, payload)
-
- if err != nil {
- fmt.Println(err)
- return
- } else {
- req.Header.Add("Content-Type", "application/json")
- req.Header.Add("Accept", "application/json")
-
- res, err := client.Do(req)
- if err != nil {
- log.Println(err)
- return
- } else {
- defer res.Body.Close()
-
- body, err := ioutil.ReadAll(res.Body)
- if err != nil {
- log.Println(err)
- return
- }
-
- if res.StatusCode == 400 {
- log.Fatalln("Incorrect login")
- } else {
- var result models.StructLogin
- if err := json.Unmarshal(body, &result); err != nil {
- log.Println("Can not unmarshal JSON")
- }
-
- models.SetAccessToken(result.AccessToken)
- }
-
- }
-
- }
-
-}
diff --git a/src/immich/data.go b/src/immich/data.go
index dc4640f..35420e4 100644
--- a/src/immich/data.go
+++ b/src/immich/data.go
@@ -3,7 +3,7 @@ package immich
import (
"encoding/json"
"fmt"
- "immich-exporter/src/models"
+ "immich-exp/src/models"
"io/ioutil"
"log"
"net/http"
@@ -25,9 +25,10 @@ func Allrequests(r *prometheus.Registry) {
func Analyze(r *prometheus.Registry) {
defer wg.Done()
- allusers := make(chan func() (*models.StructAllUsers, error))
+ allusers := make(chan func() (*models.StructAllUsers, error))
serverinfo := make(chan func() (*models.StructServerInfo, error))
+
wg.Add(1)
go GetAllUsers(allusers)
res1, err := (<-allusers)()
@@ -47,16 +48,7 @@ func Analyze(r *prometheus.Registry) {
func GetAllUsers(c chan func() (*models.StructAllUsers, error)) {
defer wg.Done()
resp, err := Apirequest("/api/user?isAll=true", "GET")
- if err != nil {
- if err.Error() == "403" {
- log.Println("Cookie changed, try to reconnect ...")
- Auth()
- } else {
- if models.GetPromptError() == false {
- log.Println("Error : ", err)
- }
- }
- } else {
+ if err == nil {
if models.GetPromptError() == true {
models.SetPromptError(false)
}
@@ -71,25 +63,14 @@ func GetAllUsers(c chan func() (*models.StructAllUsers, error)) {
}
c <- (func() (*models.StructAllUsers, error) { return result, nil })
-
}
}
-
}
func ServerVersion(r *prometheus.Registry) {
defer wg.Done()
resp, err := Apirequest("/api/server-info/version", "GET")
- if err != nil {
- if err.Error() == "403" {
- log.Println("Cookie changed, try to reconnect ...")
- Auth()
- } else {
- if models.GetPromptError() == false {
- log.Println("Error : ", err)
- }
- }
- } else {
+ if err == nil {
if models.GetPromptError() == true {
models.SetPromptError(false)
}
@@ -100,14 +81,12 @@ func ServerVersion(r *prometheus.Registry) {
var result models.StructServerVersion
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
- log.Println("Can not unmarshal JSON")
+ log.Println("Can not unmarshal JSON for version")
}
SendBackMessageserverVersion(&result, r)
-
}
}
-
}
func ServerInfo(c chan func() (*models.StructServerInfo, error)) {
@@ -116,7 +95,6 @@ func ServerInfo(c chan func() (*models.StructServerInfo, error)) {
if err != nil {
if err.Error() == "403" {
log.Println("Cookie changed, try to reconnect ...")
- Auth()
} else {
if models.GetPromptError() == false {
log.Println("Error : ", err)
@@ -134,23 +112,22 @@ func ServerInfo(c chan func() (*models.StructServerInfo, error)) {
result := new(models.StructServerInfo)
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
- log.Println("Can not unmarshal JSON")
+ log.Println("Can not unmarshal JSON for server infos")
}
c <- (func() (*models.StructServerInfo, error) { return result, nil })
}
}
-
}
func Apirequest(uri string, method string) (*http.Response, error) {
- req, err := http.NewRequest(method, models.GetURL()+uri, nil)
+ req, err := http.NewRequest(method, models.Getbaseurl()+uri, nil)
if err != nil {
log.Fatalln("Error with url")
}
-
- req.AddCookie(&http.Cookie{Name: "immich_access_token", Value: models.GetAccessToken()})
+ req.Header.Add("Accept", "application/json")
+ req.Header.Add("x-api-key", models.GetApiKey())
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
@@ -164,23 +141,16 @@ func Apirequest(uri string, method string) (*http.Response, error) {
return resp, err
} else {
- models.SetPromptError(false)
if resp.StatusCode == 200 {
-
+ models.SetPromptError(false)
return resp, nil
-
} else {
err := fmt.Errorf("%d", resp.StatusCode)
if models.GetPromptError() == false {
models.SetPromptError(true)
-
- log.Println("Error code", err.Error())
-
+ log.Println("Error code", err.Error(), " for ", models.Getbaseurl()+uri)
}
return resp, err
-
}
-
}
-
}
diff --git a/src/immich/sendbackmessage.go b/src/immich/sendbackmessage.go
index a90d81c..65c869e 100644
--- a/src/immich/sendbackmessage.go
+++ b/src/immich/sendbackmessage.go
@@ -1,7 +1,7 @@
package immich
import (
- "immich-exporter/src/models"
+ "immich-exp/src/models"
"strconv"
"github.com/prometheus/client_golang/prometheus"
@@ -52,14 +52,14 @@ func SendBackMessagePreference(result *models.StructServerInfo, result2 *models.
r.MustRegister(user_photos)
total_photos.Add(float64((*result).Photos))
total_videos.Add(float64((*result).Videos))
- total_usage.Add(float64((*result).UsageRaw))
+ total_usage.Add(float64((*result).Usage))
total_users.Add(float64(len((*result).UsageByUser)))
for i := 0; i < len((*result).UsageByUser); i++ {
var myuser = GetName((*result).UsageByUser[i].UserID, result2)
- user_info.With(prometheus.Labels{"videos": strconv.Itoa((*result).UsageByUser[i].Videos), "photos": strconv.Itoa((*result).UsageByUser[i].Photos), "uid": (*result).UsageByUser[i].UserID, "usage": strconv.Itoa(int((*result).UsageByUser[i].UsageRaw)), "firstname": myuser.FirstName, "lastname": myuser.LastName}).Inc()
+ user_info.With(prometheus.Labels{"videos": strconv.Itoa((*result).UsageByUser[i].Videos), "photos": strconv.Itoa((*result).UsageByUser[i].Photos), "uid": (*result).UsageByUser[i].UserID, "usage": strconv.Itoa(int((*result).UsageByUser[i].Usage)), "firstname": myuser.FirstName, "lastname": myuser.LastName}).Inc()
user_photos.With(prometheus.Labels{"uid": (*result).UsageByUser[i].UserID, "firstname": myuser.FirstName, "lastname": myuser.LastName}).Set(float64((*result).UsageByUser[i].Photos))
- user_usage.With(prometheus.Labels{"uid": (*result).UsageByUser[i].UserID, "firstname": myuser.FirstName, "lastname": myuser.LastName}).Set(float64((*result).UsageByUser[i].UsageRaw))
+ user_usage.With(prometheus.Labels{"uid": (*result).UsageByUser[i].UserID, "firstname": myuser.FirstName, "lastname": myuser.LastName}).Set(float64((*result).UsageByUser[i].Usage))
user_videos.With(prometheus.Labels{"uid": (*result).UsageByUser[i].UserID, "firstname": myuser.FirstName, "lastname": myuser.LastName}).Set(float64((*result).UsageByUser[i].Videos))
}
diff --git a/src/init.go b/src/init.go
index c8a4e72..bca5e4f 100644
--- a/src/init.go
+++ b/src/init.go
@@ -3,31 +3,39 @@ package main
import (
"encoding/json"
"flag"
- "immich-exporter/src/immich"
- "immich-exporter/src/models"
- "io/ioutil"
- "net/http"
+ "fmt"
+ "immich-exp/src/immich"
+ "immich-exp/src/models"
+
+ "net/http"
+ "strconv"
+ "strings"
- "log"
"os"
"github.com/joho/godotenv"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
+ log "github.com/sirupsen/logrus"
)
-func main() {
- startup()
- log.Println("Immich URL :", models.GetURL())
- log.Println("username :", models.GetUsername())
- log.Println("password :", models.Getpasswordmasked())
- log.Println("Started")
+const DEFAULTPORT = 8090
+func main() {
+ loadenv()
+ projectinfo()
+ log.Info("Immich URL: ", models.Getbaseurl())
+ log.Info("Started")
http.HandleFunc("/metrics", metrics)
- http.ListenAndServe(":8090", nil)
+ addr := ":" + strconv.Itoa(models.GetPort())
+ if models.GetPort() != DEFAULTPORT {
+ log.Info("Listening on port", models.GetPort())
+ }
+ http.ListenAndServe(addr, nil)
}
func metrics(w http.ResponseWriter, r *http.Request) {
+ log.Trace("New request")
registry := prometheus.NewRegistry()
immich.Allrequests(registry)
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
@@ -35,80 +43,86 @@ func metrics(w http.ResponseWriter, r *http.Request) {
}
-func startup() {
- projectinfo()
- var envfile bool
-
- flag.BoolVar(&envfile, "e", false, "Use .env file")
- flag.Parse()
- if envfile {
- useenvfile()
- } else {
- initenv()
- }
-
- immich.Auth()
-
-}
-
func projectinfo() {
- fileContent, err := os.Open("./package.json")
-
+ fileContent, err := os.ReadFile("./package.json")
if err != nil {
log.Fatal(err)
return
}
- defer fileContent.Close()
-
- byteResult, _ := ioutil.ReadAll(fileContent)
-
var res map[string]interface{}
- json.Unmarshal([]byte(byteResult), &res)
- log.Println("Author :", res["author"])
- log.Println(res["name"], "version", res["version"])
+ err = json.Unmarshal(fileContent, &res)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ fmt.Print(res["name"], " (version ", res["version"], ")\n")
+ fmt.Print("Author: ", res["author"], "\n")
+ fmt.Print("Using log level: ", log.GetLevel(), "\n")
}
-func useenvfile() {
- myEnv, err := godotenv.Read()
+func loadenv() {
+ var envfile bool
+ flag.BoolVar(&envfile, "e", false, "Use .env file")
+ flag.Parse()
+ _, err := os.Stat(".env")
+ if !os.IsNotExist(err) && !envfile {
+ err := godotenv.Load(".env")
+ if err != nil {
+ log.Panic("Error loading .env file:", err)
+ }
+ // fmt.Println("Using .env file")
+ }
- username := myEnv["IMMICH_USERNAME"]
- password := myEnv["IMMICH_PASSWORD"]
- immich_url := myEnv["IMMICH_BASE_URL"]
- if myEnv["IMMICH_USERNAME"] == "" {
- log.Panic("Immich username is not set.")
- }
- if myEnv["IMMICH_PASSWORD"] == "" {
- log.Panic("Immich password is not set.")
- }
- if myEnv["IMMICH_BASE_URL"] == "" {
- log.Panic("IMMICH base_url is not set.")
- }
- models.Setuser(username, password, immich_url)
+ immichapikey := getEnv("IMMICH_API_KEY", "", false, "Immich API Key is not set", true)
+ immichURL := getEnv("IMMICH_BASE_URL", "http://localhost:8080", true, "Qbittorrent base_url is not set. Using default base_url", false)
+ exporterPort := getEnv("EXPORTER_PORT", strconv.Itoa(DEFAULTPORT), false, "", false)
+
+ num, err := strconv.Atoi(exporterPort)
if err != nil {
- log.Fatal("Error loading .env file")
+ log.Panic("EXPORTER_PORT must be an integer")
}
- log.Println("Using .env file")
+ if num < 0 || num > 65353 {
+ log.Panic("EXPORTER_PORT must be > 0 and < 65353")
+ }
+
+ setLogLevel(getEnv("LOG_LEVEL", "INFO", false, "", false))
+ models.SetApp(num, false)
+ models.Setuser(immichURL, immichapikey)
+}
+func setLogLevel(logLevel string) {
+ logLevels := map[string]log.Level{
+ "TRACE": log.TraceLevel,
+ "DEBUG": log.DebugLevel,
+ "INFO": log.InfoLevel,
+ "WARN": log.WarnLevel,
+ "ERROR": log.ErrorLevel,
+ }
+
+ level, found := logLevels[strings.ToUpper(logLevel)]
+ if !found {
+ level = log.InfoLevel
+ }
+
+ log.SetLevel(level)
+ log.SetFormatter(&log.TextFormatter{
+ DisableColors: false,
+ FullTimestamp: true,
+ })
}
-func initenv() {
-
- username := os.Getenv("IMMICH_USERNAME")
- password := os.Getenv("IMMICH_PASSWORD")
- immich_url := os.Getenv("IMMICH_BASE_URL")
- if os.Getenv("IMMICH_USERNAME") == "" {
- log.Panic("Immich username is not set.")
-
+func getEnv(key string, fallback string, printLog bool, logPrinted string, needed bool) string {
+ value, ok := os.LookupEnv(key)
+ if !ok || value == "" {
+ if needed {
+ log.Panicln("Please set a value for", key)
+ }
+ if printLog {
+ log.Warn(logPrinted)
+ }
+ return fallback
}
- if os.Getenv("IMMICH_PASSWORD") == "" {
- log.Panic("Immich password is not set.")
-
- }
- if os.Getenv("IMMICH_BASE_URL") == "" {
- log.Panic("Immich base_url is not set.")
-
- }
- models.Setuser(username, password, immich_url)
-
+ return value
}
diff --git a/src/models/api.go b/src/models/api.go
new file mode 100644
index 0000000..1dc0047
--- /dev/null
+++ b/src/models/api.go
@@ -0,0 +1,64 @@
+package models
+
+import "time"
+
+type StructLogin struct {
+ AccessToken string `json:"accessToken"`
+ UserID string `json:"userId"`
+ UserEmail string `json:"userEmail"`
+ FirstName string `json:"firstName"`
+ LastName string `json:"lastName"`
+ IsAdmin bool `json:"isAdmin"`
+ ShouldChangePassword bool `json:"shouldChangePassword"`
+}
+
+type StructServerInfo struct {
+ Photos int `json:"photos"`
+ Videos int `json:"videos"`
+ Usage int64 `json:"usage"`
+ UsageByUser []struct {
+ UserID string `json:"userId"`
+ UserFirstName string `json:"userFirstName"`
+ UserLastName string `json:"userLastName"`
+ Photos int `json:"photos"`
+ Videos int `json:"videos"`
+ Usage int `json:"usage"`
+ } `json:"usageByUser"`
+}
+
+type StructDiskInfo struct {
+ DiskAvailable string `json:"diskAvailable"`
+ DiskSize string `json:"diskSize"`
+ DiskUse string `json:"diskUse"`
+ DiskAvailableRaw int64 `json:"diskAvailableRaw"`
+ DiskSizeRaw int64 `json:"diskSizeRaw"`
+ DiskUseRaw int64 `json:"diskUseRaw"`
+ DiskUsagePercentage float64 `json:"diskUsagePercentage"`
+}
+
+type StructServerVersion struct {
+ Major int `json:"major"`
+ Minor int `json:"minor"`
+ Patch int `json:"patch"`
+}
+
+type StructAllUsers []struct {
+ ID string `json:"id"`
+ Email string `json:"email"`
+ FirstName string `json:"firstName"`
+ LastName string `json:"lastName"`
+ CreatedAt time.Time `json:"createdAt"`
+ ProfileImagePath string `json:"profileImagePath"`
+ ShouldChangePassword bool `json:"shouldChangePassword"`
+ IsAdmin bool `json:"isAdmin"`
+ DeletedAt time.Time `json:"deletedAt"`
+ OauthID string `json:"oauthId"`
+}
+
+type StructCustomUser struct {
+ Email string
+ ID string
+ FirstName string
+ LastName string
+ IsAdmin bool
+}
diff --git a/src/models/app.go b/src/models/app.go
new file mode 100644
index 0000000..bc9139a
--- /dev/null
+++ b/src/models/app.go
@@ -0,0 +1,27 @@
+package models
+
+type TypeAppConfig struct {
+ Port int
+ Error bool
+}
+
+var AppConfig TypeAppConfig
+
+func SetApp(setport int, seterror bool) {
+ AppConfig = TypeAppConfig{
+ Port: setport,
+ Error: seterror,
+ }
+}
+
+func GetPort() int {
+ return AppConfig.Port
+}
+
+func SetPromptError(prompt bool) {
+ AppConfig.Error = prompt
+}
+
+func GetPromptError() bool {
+ return AppConfig.Error
+}
diff --git a/src/models/error.go b/src/models/error.go
deleted file mode 100644
index f60f316..0000000
--- a/src/models/error.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package models
-
-var myerr bool
-
-func SetPromptError(prompt bool) {
- myerr = prompt
-}
-
-func GetPromptError() bool {
- return myerr
-}
diff --git a/src/models/immich.go b/src/models/immich.go
index 2b1a329..802d064 100644
--- a/src/models/immich.go
+++ b/src/models/immich.go
@@ -1,64 +1,22 @@
package models
-import "time"
-
-type StructLogin struct {
- AccessToken string `json:"accessToken"`
- UserID string `json:"userId"`
- UserEmail string `json:"userEmail"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- IsAdmin bool `json:"isAdmin"`
- ShouldChangePassword bool `json:"shouldChangePassword"`
+type StructImmich struct {
+ APIKey string
+ URL string
}
-type StructServerInfo struct {
- Photos int `json:"photos"`
- Videos int `json:"videos"`
- UsageByUser []struct {
- UserID string `json:"userId"`
- Videos int `json:"videos"`
- Photos int `json:"photos"`
- UsageRaw int64 `json:"usageRaw"`
- Usage string `json:"usage"`
- } `json:"usageByUser"`
- UsageRaw int64 `json:"usageRaw"`
- Usage string `json:"usage"`
+var myuser StructImmich
+
+func Setuser(url string, apikey string) {
+ myuser.URL = url
+ myuser.APIKey = apikey
+
}
-type StructDiskInfo struct {
- DiskAvailable string `json:"diskAvailable"`
- DiskSize string `json:"diskSize"`
- DiskUse string `json:"diskUse"`
- DiskAvailableRaw int64 `json:"diskAvailableRaw"`
- DiskSizeRaw int64 `json:"diskSizeRaw"`
- DiskUseRaw int64 `json:"diskUseRaw"`
- DiskUsagePercentage float64 `json:"diskUsagePercentage"`
+func Getbaseurl() string {
+ return myuser.URL
}
-type StructServerVersion struct {
- Major int `json:"major"`
- Minor int `json:"minor"`
- Patch int `json:"patch"`
-}
-
-type StructAllUsers []struct {
- ID string `json:"id"`
- Email string `json:"email"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- CreatedAt time.Time `json:"createdAt"`
- ProfileImagePath string `json:"profileImagePath"`
- ShouldChangePassword bool `json:"shouldChangePassword"`
- IsAdmin bool `json:"isAdmin"`
- DeletedAt time.Time `json:"deletedAt"`
- OauthID string `json:"oauthId"`
-}
-
-type StructCustomUser struct {
- Email string
- ID string
- FirstName string
- LastName string
- IsAdmin bool
+func GetApiKey() string {
+ return myuser.APIKey
}
diff --git a/src/models/models.go b/src/models/models.go
deleted file mode 100644
index 3a5d47b..0000000
--- a/src/models/models.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package models
-
-type StructImmich struct {
- Username string
- Password string
- URL string
- AccessToken string
-}
-
-var myuser StructImmich
-
-func Getuser() (string, string) {
- return myuser.Username, myuser.Password
-}
-
-func Setuser(username string, password string, url string) {
- myuser.Username = username
- myuser.Password = password
- myuser.URL = url
-}
-
-func GetUsername() string {
- return myuser.Username
-}
-
-func Getpassword() string {
- return myuser.Password
-}
-func Getpasswordmasked() string {
- hide := ""
- for i := 0; i < len(myuser.Password); i++ {
- hide += "*"
- }
- return hide
-}
-
-func SetAccessToken(accessToken string) {
- myuser.AccessToken = accessToken
-}
-
-func GetAccessToken() string {
- return myuser.AccessToken
-}
-
-func GetURL() string {
- return myuser.URL
-}