initial commit
Signed-off-by: martin <martin.labat92@gmail.com>
This commit is contained in:
commit
f3eb2cef68
21 changed files with 1385 additions and 0 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
img/
|
||||||
|
grafana/
|
||||||
|
.github/
|
||||||
|
Dockerfile
|
3
.env.example
Normal file
3
.env.example
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
IMMICH_USERNAME=
|
||||||
|
IMMICH_PASSWORD=
|
||||||
|
IMMICH_BASE_URL=
|
47
.github/workflows/push_docker.yml
vendored
Normal file
47
.github/workflows/push_docker.yml
vendored
Normal 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
23
.gitignore
vendored
Normal 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
14
Dockerfile
Normal 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
74
README.md
Normal 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> <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
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module immich-exporter
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require github.com/joho/godotenv v1.5.1
|
2
go.sum
Normal file
2
go.sum
Normal 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
698
grafana/dashboard.json
Normal 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
BIN
img/golang.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
img/immich.png
Normal file
BIN
img/immich.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
BIN
img/prometheus.png
Normal file
BIN
img/prometheus.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
19
package.json
Normal file
19
package.json
Normal 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
60
src/immich/auth.go
Normal 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
166
src/immich/data.go
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/immich/sendbackmessage.go
Normal file
46
src/immich/sendbackmessage.go
Normal 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
70
src/init.go
Normal 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
28
src/main.go
Normal 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
64
src/models/api.go
Normal 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
11
src/models/error.go
Normal 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
51
src/models/models.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue