feat: up version
Signed-off-by: martabal <74269598+martabal@users.noreply.github.com>
This commit is contained in:
parent
cf29943ebe
commit
8a7ebfefa1
20 changed files with 336 additions and 322 deletions
|
@ -1,4 +1,7 @@
|
|||
img/
|
||||
grafana/
|
||||
.github/
|
||||
Dockerfile
|
||||
Dockerfile
|
||||
README.md
|
||||
.env*
|
||||
.gitignore
|
|
@ -1,3 +1,2 @@
|
|||
IMMICH_USERNAME=
|
||||
IMMICH_PASSWORD=
|
||||
IMMICH_API_KEY=
|
||||
IMMICH_BASE_URL=
|
17
.github/workflows/build.yml
vendored
Normal file
17
.github/workflows/build.yml
vendored
Normal file
|
@ -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
|
34
.github/workflows/codeql-analysis.yml
vendored
Normal file
34
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
@ -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
|
|
@ -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
|
17
.github/workflows/test.yml
vendored
Normal file
17
.github/workflows/test.yml
vendored
Normal file
|
@ -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)
|
11
Dockerfile
11
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"]
|
30
README.md
30
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)
|
||||
|
||||
<p align="center">
|
||||
<img src="img/immich.png" width=100> <img src="img/prometheus.png" width=100><img src="img/golang.png" width=100>
|
||||
|
@ -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='<your_password>' \
|
||||
-e IMMICH_USERNAME=admin \
|
||||
-e IMMICH_API_KEY=<your_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='<your_password>'
|
||||
- IMMICH_USERNAME=admin
|
||||
- IMMICH_API_KEY=<your_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: [ '<your_ip_address>:8090' ]
|
||||
```
|
||||
|
|
10
go.mod
10
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
|
||||
|
|
7
go.sum
7
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=
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "immich-exporter",
|
||||
"version": "0.1.2",
|
||||
"version": "1.0.0",
|
||||
"description": "exporter for immich",
|
||||
"main": "src/main.go",
|
||||
"scripts": {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
158
src/init.go
158
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
|
||||
}
|
||||
|
|
64
src/models/api.go
Normal file
64
src/models/api.go
Normal file
|
@ -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
|
||||
}
|
27
src/models/app.go
Normal file
27
src/models/app.go
Normal file
|
@ -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
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package models
|
||||
|
||||
var myerr bool
|
||||
|
||||
func SetPromptError(prompt bool) {
|
||||
myerr = prompt
|
||||
}
|
||||
|
||||
func GetPromptError() bool {
|
||||
return myerr
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue