initial commit

Signed-off-by: martin <martin.labat92@gmail.com>
This commit is contained in:
martin 2023-02-10 22:47:58 +01:00
commit f3eb2cef68
No known key found for this signature in database
GPG key ID: 4544CC55835A3B9E
21 changed files with 1385 additions and 0 deletions

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
img/
grafana/
.github/
Dockerfile

3
.env.example Normal file
View file

@ -0,0 +1,3 @@
IMMICH_USERNAME=
IMMICH_PASSWORD=
IMMICH_BASE_URL=

47
.github/workflows/push_docker.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: manual push
on:
workflow_dispatch:
inputs:
tags:
description: 'version'
required: true
type: string
jobs:
build_push_monolith:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: 'main'
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2.2.1
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: martabal
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push immich
uses: docker/build-push-action@v3.2.0
with:
context: ./
file: ./Dockerfile
platforms: linux/arm/v7,linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: |
martabal/qbitorrent-exporter:${{ inputs.tags }}
martabal/qbitorrent-exporter:latest

23
.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
.env

14
Dockerfile Normal file
View file

@ -0,0 +1,14 @@
FROM golang:1.19-alpine3.17 AS builder
WORKDIR /app
COPY . .
RUN go get -d -v ./src/ && \
go build -o /go/bin/immich-exporter ./src
FROM alpine:3.17
COPY --from=builder /go/bin/immich-exporter /go/bin/immich-exporter
CMD ["/go/bin/immich-exporter"]

74
README.md Normal file
View file

@ -0,0 +1,74 @@
# immich-exporter
[![manual push](https://github.com/martabal/immich-exporter/actions/workflows/push_docker.yml/badge.svg)](https://github.com/martabal/immich-exporter/actions/workflows/push_docker.yml)
<p align="center">
<img src="img/immich.png" width=100> &nbsp; <img src="img/prometheus.png" width=100><img src="img/golang.png" width=100>
</p>
This app is a Prometheus exporter for immich.
This app is made to be integrated with the [immich-grafana-dashboard](https://github.com/martabal/immich-exporter/grafana/dashboard.json)
## Run it
### 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 \
-p 8090:8090 \
martabal/immich-exporter
```
### Docker-compose
```yaml
version: "2.1"
services:
immich:
image: martabal/immich-exporter:latest
container_name: immich-exporter
environment:
- IMMICH_URL=http://192.168.1.10:8080
- IMMICH_PASSWORD='<your_password>'
- IMMICH_USERNAME=admin
ports:
- 8090:8090
restart: unless-stopped
```
### Without docker
```sh
git clone https://github.com/martabal/immich-exporter.git
cd immich-exporter/
go get -d -v
cd src
go build -o ./immich-exporter
./immich-exporter
```
If you want to use an .env file, edit `.env.example` to match your setup, rename it `.env` then run it with :
```sh
./immich-exporter -e
```
## Parameters
### Environment variables
| Parameters | Function |
| :-----: | ----- |
| `-p 8090` | Webservice port |
| `-e IMMICH_USERNAME` | Immich username |
| `-e IMMICH_PASSWORD` | Immich password |
| `-e IMMICH_BASE_URL` | Immich base URL |
### Arguments
| Arguments | Function |
| :-----: | ----- |
| -e | Use a .env file containing environment variables (.env file must be placed in the same directory) |

5
go.mod Normal file
View file

@ -0,0 +1,5 @@
module immich-exporter
go 1.20
require github.com/joho/godotenv v1.5.1

2
go.sum Normal file
View file

@ -0,0 +1,2 @@
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=

698
grafana/dashboard.json Normal file
View file

@ -0,0 +1,698 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 65,
"links": [],
"liveNow": false,
"panels": [
{
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 10,
"title": "Row title",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "string"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 9,
"x": 0,
"y": 1
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"first"
],
"fields": "/^version$/",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "9.3.6",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"exemplar": false,
"expr": "immich_app_version",
"format": "table",
"instant": true,
"legendFormat": "{{version}}",
"range": false,
"refId": "A"
}
],
"title": "Immich version",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "string"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 5,
"x": 9,
"y": 1
},
"id": 13,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"first"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "9.3.6",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"exemplar": false,
"expr": "immich_app_nb_users",
"format": "table",
"instant": true,
"legendFormat": "__auto",
"range": false,
"refId": "A"
}
],
"title": "Number of users",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": false,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Time"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "__name__"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "instance"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "Value"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "firstname"
},
"properties": [
{
"id": "displayName",
"value": "First name"
}
]
},
{
"matcher": {
"id": "byName",
"options": "job"
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "lastname"
},
"properties": [
{
"id": "displayName",
"value": "Last name"
}
]
},
{
"matcher": {
"id": "byName",
"options": "usage"
},
"properties": [
{
"id": "unit",
"value": "bytes"
}
]
}
]
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 7
},
"id": 15,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": false,
"displayName": "Time"
}
]
},
"pluginVersion": "9.3.6",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"exemplar": false,
"expr": "immich_user_info",
"format": "table",
"instant": true,
"legendFormat": "",
"range": false,
"refId": "A"
}
],
"title": "Users details",
"type": "table"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 13
},
"id": 12,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"exemplar": false,
"expr": "immich_user_usage",
"instant": true,
"legendFormat": "{{firstname}} {{lastname}}",
"range": false,
"refId": "A"
}
],
"title": "User usage",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 12,
"y": 13
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"expr": "immich_app_total_photos + immich_app_total_videos",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "Total assets",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": []
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 22
},
"id": 8,
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"expr": "immich_app_total_photos",
"legendFormat": "Photos",
"range": true,
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"expr": "immich_app_total_videos",
"hide": false,
"legendFormat": "Videos",
"range": true,
"refId": "B"
}
],
"title": "Proportion Photos / Videos",
"type": "piechart"
},
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": []
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 22
},
"id": 6,
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "zBtc3iPnk"
},
"editorMode": "code",
"exemplar": false,
"expr": "immich_user_usage",
"instant": true,
"legendFormat": "{{firstname}} {{lastname}}",
"range": false,
"refId": "A"
}
],
"title": "Usage by user",
"type": "piechart"
}
],
"refresh": false,
"schemaVersion": 37,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Immich",
"uid": "9QXCv3AVk",
"version": 7,
"weekStart": ""
}

BIN
img/golang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
img/immich.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
img/prometheus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

19
package.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "immich-exporter",
"version": "0.1.0",
"description": "exporter for immich",
"main": "src/main.go",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"exporter",
"immich",
"grafana",
"dashboard",
"metrics",
"prometheus"
],
"author": "martabal",
"license": "ISC"
}

60
src/immich/auth.go Normal file
View file

@ -0,0 +1,60 @@
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.Login
if err := json.Unmarshal(body, &result); err != nil {
log.Println("Can not unmarshal JSON")
}
models.SetAccessToken(result.AccessToken)
}
}
}
}

166
src/immich/data.go Normal file
View file

@ -0,0 +1,166 @@
package immich
import (
"encoding/json"
"fmt"
"immich-exporter/src/models"
"io/ioutil"
"log"
"net/http"
)
func Allrequests() string {
return serverversion() + Analyze()
}
func Analyze() string {
allusers, err := GetAllUsers()
users, err2 := users()
if err != nil && err2 != nil {
return ""
} else {
return Sendbackmessagepreference(users, allusers)
}
}
func GetAllUsers() (*models.AllUsers, error) {
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 models.GetPromptError() == true {
models.SetPromptError(false)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
} else {
var result models.AllUsers
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
log.Println("Can not unmarshal JSON")
}
return &result, err
}
}
return &models.AllUsers{}, err
}
func serverversion() string {
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 models.GetPromptError() == true {
models.SetPromptError(false)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
} else {
var result models.ServerVersion
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
log.Println("Can not unmarshal JSON")
}
return Sendbackmessageserverversion(&result)
}
}
return ""
}
func users() (*models.Users, error) {
resp, err := Apirequest("/api/server-info/stats", "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 models.GetPromptError() == true {
models.SetPromptError(false)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
} else {
var result models.Users
if err := json.Unmarshal(body, &result); err != nil { // Parse []byte to go struct pointer
log.Println("Can not unmarshal JSON")
}
return &result, nil
}
}
return &models.Users{}, err
}
func Apirequest(uri string, method string) (*http.Response, error) {
req, err := http.NewRequest(method, models.GetURL()+uri, nil)
if err != nil {
log.Fatalln("Error with url")
}
req.AddCookie(&http.Cookie{Name: "immich_access_token", Value: models.GetAccessToken()})
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
err := fmt.Errorf("Can't connect to server")
if models.GetPromptError() == false {
log.Println(err.Error())
models.SetPromptError(true)
}
return resp, err
} else {
models.SetPromptError(false)
if resp.StatusCode == 200 {
return resp, nil
} else {
err := fmt.Errorf("%d", resp.StatusCode)
if models.GetPromptError() == false {
models.SetPromptError(true)
log.Println("Error code", err.Error())
}
return resp, err
}
}
}

View file

@ -0,0 +1,46 @@
package immich
import (
"immich-exporter/src/models"
"strconv"
)
func Sendbackmessagepreference(result *models.Users, result2 *models.AllUsers) string {
total := "# HELP immich_app_number_users The number of immich users\n# TYPE immich_app_max_active_downloads gauge\nimmich_app_nb_users " + strconv.Itoa(len((*result).UsageByUser)) + "\n"
total = total + "# HELP immich_app_total_photos The total number of photos\n# TYPE immich_app_total_photos gauge\nimmich_app_total_photos " + strconv.Itoa((*result).Photos) + "\n"
total = total + "# HELP immich_app_total_videos The total number of videos\n# TYPE immich_app_total_videos gauge\nimmich_app_total_videos " + strconv.Itoa((*result).Videos) + "\n"
total = total + "# HELP immich_app_total_usage The usage of the user\n# TYPE immich_app_total_usage gauge\nimmich_app_total_usage " + strconv.Itoa(int((*result).UsageRaw)) + "\n"
immich_user_videos := "# HELP immich_app_user_videos The number of videos of the user\n# TYPE immich_app_user_videos gauge\n"
immich_user_photos := "# HELP immich_app_user_photos The number of photo of the user\n# TYPE immich_app_user_photos gauge\n"
immich_user_usageRaw := "# HELP immich_app_user_usage The usage of the user\n# TYPE immich_app_user_usage gauge\n"
immich_user_info := "# HELP immich_user_info All info for torrents\n# TYPE immich_user_info gauge\n"
for i := 0; i < len((*result).UsageByUser); i++ {
var myuser = GetName((*result).UsageByUser[i].UserID, result2)
immich_user_info = immich_user_info + `immich_user_info{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 + `"} 1.0` + "\n"
immich_user_usageRaw = immich_user_usageRaw + `immich_user_usage{uid="` + (*result).UsageByUser[i].UserID + `",firstname="` + myuser.FirstName + `",lastname="` + myuser.LastName + `",} ` + strconv.Itoa(int((*result).UsageByUser[i].UsageRaw)) + "\n"
immich_user_photos = immich_user_photos + `immich_user_photos{uid="` + (*result).UsageByUser[i].UserID + `",firstname="` + myuser.FirstName + `",lastname="` + myuser.LastName + `",} ` + strconv.Itoa((*result).UsageByUser[i].Photos) + "\n"
immich_user_videos = immich_user_videos + `immich_user_videos{uid="` + (*result).UsageByUser[i].UserID + `",firstname="` + myuser.FirstName + `",lastname="` + myuser.LastName + `",} ` + strconv.Itoa((*result).UsageByUser[i].Videos) + "\n"
}
return total + immich_user_info + immich_user_videos + immich_user_photos + immich_user_usageRaw
}
func Sendbackmessageserverversion(result *models.ServerVersion) string {
return "# HELP immich_app_version The current immich version\n# TYPE immich_app_version gauge\nimmich_app_version" + `{version="` + strconv.Itoa((*result).Major) + "." + strconv.Itoa((*result).Minor) + "." + strconv.Itoa((*result).Patch) + `",} 1.0` + "\n"
}
func GetName(result string, result2 *models.AllUsers) models.CustomUser {
var myuser models.CustomUser
for i := 0; i < len(*result2); i++ {
if (*result2)[i].ID == result {
myuser.ID = (*result2)[i].ID
myuser.FirstName = (*result2)[i].FirstName
myuser.LastName = (*result2)[i].LastName
myuser.Email = (*result2)[i].Email
myuser.IsAdmin = (*result2)[i].IsAdmin
}
}
return myuser
}

70
src/init.go Normal file
View file

@ -0,0 +1,70 @@
package main
import (
"flag"
"immich-exporter/src/immich"
"immich-exporter/src/models"
"log"
"os"
"github.com/joho/godotenv"
)
func startup() {
var envfile bool
flag.BoolVar(&envfile, "e", false, "Use .env file")
flag.Parse()
log.Println("Loading all parameters")
if envfile {
useenvfile()
} else {
initenv()
}
immich.Auth()
}
func useenvfile() {
myEnv, err := godotenv.Read()
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)
if err != nil {
log.Fatal("Error loading .env file")
}
log.Println("Using .env file")
}
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.")
}
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)
}

28
src/main.go Normal file
View file

@ -0,0 +1,28 @@
package main
import (
"fmt"
"immich-exporter/src/immich"
"immich-exporter/src/models"
"log"
"net/http"
)
func main() {
startup()
log.Println("Immich URL :", models.GetURL())
log.Println("username :", models.GetUsername())
log.Println("password :", models.Getpasswordmasked())
log.Println("Started")
http.HandleFunc("/metrics", metrics)
http.ListenAndServe(":8090", nil)
}
func metrics(w http.ResponseWriter, req *http.Request) {
value := immich.Allrequests()
if value == "" {
value = immich.Allrequests()
}
fmt.Fprintf(w, value)
}

64
src/models/api.go Normal file
View file

@ -0,0 +1,64 @@
package models
import "time"
type Login 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 Users 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"`
}
type ServerInfo 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 ServerVersion struct {
Major int `json:"major"`
Minor int `json:"minor"`
Patch int `json:"patch"`
}
type AllUsers []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 CustomUser struct {
Email string
ID string
FirstName string
LastName string
IsAdmin bool
}

11
src/models/error.go Normal file
View file

@ -0,0 +1,11 @@
package models
var myerr bool
func SetPromptError(prompt bool) {
myerr = prompt
}
func GetPromptError() bool {
return myerr
}

51
src/models/models.go Normal file
View file

@ -0,0 +1,51 @@
package models
type User struct {
Username string
Password string
URL string
accessToken string
}
var myuser User
func mask(input string) string {
hide := ""
for i := 0; i < len(input); i++ {
hide += "*"
}
return hide
}
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 {
return mask(myuser.Password)
}
func SetAccessToken(accessToken string) {
myuser.accessToken = accessToken
}
func GetAccessToken() string {
return myuser.accessToken
}
func GetURL() string {
return myuser.URL
}