feat: qr-code api, major refactor, objectGUID as user id and group id
Build and Push Docker image / build-and-push (push) Failing after 29s Details

This commit is contained in:
Valentin Kolb 2024-03-27 15:36:45 +01:00
parent 382388eb91
commit d51a92f6e1
16 changed files with 306 additions and 71 deletions

View File

@ -1,5 +1,6 @@
.env.local
.gitignore
.gitlab-ci.yml
.gitea
.pb_data
docker-compose.yml
Readme.md

View File

@ -0,0 +1,32 @@
name: Build and Push Docker image
on:
push:
branches:
- main
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v2
- name: Log in to StuVe Gitea Container Registry
uses: docker/login-action@v1
with:
registry: git.stuve.uni-ulm.de
username: ${{ secrets.USER_NAME }}
password: ${{ secrets.USER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend:latest
#- name: Trigger webhook
# run: curl -X POST ${{ secrets.WEBHOOK_URL }}

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ pb_data
# env
*.env.local
*.env
# macOS
.DS_Store

View File

@ -1,15 +0,0 @@
stages:
- build
build-container:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [ "" ]
script:
- /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
only:
- main

View File

@ -2,6 +2,10 @@
# This is the first stage of a multi-stage build.
FROM golang:1.19 as builder
# Set labels
LABEL maintainer="Valentin Kolb"
LABEL version="1.0"
# Set the working directory inside the container.
WORKDIR /build

View File

@ -3,7 +3,7 @@
## Übersicht
Als Backend für die StuVe IT Tools wird [Pocketbase](https://pocketbase.io)
verwendet. Pocketbase ist ein Backend-as-a-Service, der auf SQLite basiert.
verwendet. Pocketbase ist ein self-hosted Backend-as-a-Service, der auf SQLite basiert.
## Was ist Pocketbase?
@ -54,9 +54,9 @@ Funktionen für die einzelnen Tabellen und User erlaubt sind.
## Entwickeln
```bash
git clone ...
cd backend
go run main.go serve # --debug=0 for no debug output
git clone https://git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend.git
cd stuve-it-backend
go run main/main.go serve # --debug=0 for no debug output
```
## Installation
@ -84,8 +84,10 @@ Die einzelnen custom go Packages sind weiter unten beschrieben.
│ ├── models.go # Structs für LDAP User und Group
│ ├── tables.go # Erstellt die benötigten Tabellen in der Datenbank (Users, Groups, Logs)
│ └── ldapSync.go # Sync Funktionen (Sync Users, Groups, ...)
└── ldapLogin # LDAP Login Package
└── main.go # Login Funktionen (Login Route)
├── ldapLogin # LDAP Login Package
│ └── main.go # Login Funktionen (Login Route)
└── qrApi # QR API Package
└── main.go # Api Route
```
## Custom Packages
@ -98,7 +100,46 @@ wurden einige Packages erstellt, die Pocketbase erweitern.
Das Logger Package ist ein custom Logger, der die Log-Nachrichten
farblich in die Konsole schreibt.
Diese Package ist sehr minimal gehalten und bietet nur die
Funktionen `Info`, `Warn` und `Error` an.
Funktionen `Success`, `Info` und `Error` an.
#### Beispiel
```go
package main
import "git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
func main() {
logger.LogInfoF("Info Message")
logger.LogErrorF("Warn Message")
logger.LogErrorF("Error Message")
}
```
### QR API
Das QR API Package ist ein einfaches Package, das eine API Route
zum Generieren von QR-Codes in SVG anbietet.
#### API Route
Das Package bietet die Route `GET /api/qr/v1` an, welche die folgenden
Parameter als json entgegennimmt:
| Parameter | Datentyp | Beschreibung |
|-------------------|-------------|------------------------------------------------------------------------------------------------|
| `data` | string | Der Text, der im QR-Code codiert werden soll (**required**) |
| `ecc` | string-enum | Der Error Correction Level des QR-Codes (`L`, `M` - default, `Q`, `H`) |
| `scale` | int | Der Scale des QR-Codes (default `1`) |
| `border` | int | Der Border des QR-Codes (default `1`), eine Border Uni ist die Größe von einem QR-Code 'pixel' |
| `color` | hex-string | Die Farbe des QR-Codes (default `#000000`) |
| `colorBackground` | hex-string | Die Hintergrundfarbe des QR-Codes (default `#FFFFFF`) |
#### Beispiel
```bash
curl -X GET https://it.stuve.uni-ulm.de/api/qr/v1 -d '{"data": "https://stuve.uni-ulm.de"}'
```
### LDAP Sync
@ -108,6 +149,36 @@ die LDAP Datenbank mit der Pocketbase Datenbank.
Dabei werden alle LDAP User und Gruppen in die Pocketbase Datenbank
kopiert.
Dieses Package erfordert, dass alle Gruppen und User das Attribut `objectGUID` haben. Dies ist in der Regel bei Windows
ADs der Fall.
#### LDAP Attribute
Die folgenden LDAP Attribute werden in der Pocketbase Datenbank gespeichert.
##### User
| Attribut | Pocketbase Name | Beschreibung |
|----------------|--------------------------------------------|-------------------------------------------------------------------|
| objectGUID | id | Eindeutige ID des Users (nur die ersten 15 Zeichen werden genutzt |
| givenName | givenName | Vorname des Users |
| sn | sn | Nachname des Users |
| mail | email | E-Mail des Users |
| memberOf | memberOf (in Pocketbase eine SQL Relation) | Gruppenzugehörigkeit des Users |
| accountExpires | accountExpires (in Pocketbase UTC) | Account Ablaufdatum |
| dn | dn | Distinguished Name des Users |
| cn | cn & username | Common Name des Users |
##### Group
| Attribut | Pocketbase Name | Beschreibung |
|-------------|--------------------------------------------|--------------------------------------------------------------------|
| objectGUID | id | Eindeutige ID der Gruppe (nur die ersten 15 Zeichen werden genutzt |
| cn | cn | Common Name der Gruppe |
| dn | dn | Distinguished Name der Gruppe |
| description | description | Beschreibung der Gruppe |
| memberOf | memberOf (in Pocketbase eine SQL Relation) | Gruppenzugehörigkeit der Gruppe |
#### Konfiguration
Das Package benötigt folgende ENV Variablen:
@ -147,7 +218,7 @@ noch nicht existiert. In diesem Fall wird die Beziehung erst beim
nächsten Sync erstellt. Falls eine `memberOf` Gruppe nicht gefunden wird,
wird dies in der Konsole geloggt.
Für jede Synchronisation wird ein Log in der `ldap_sync_logs` Tabelle
Für jede Synchronisation wird ein Log-in der `ldap_sync_logs` Tabelle
erstellt.
Sowohl für die ldap_users als auch für die ldap_groups Tabelle wird

13
go.mod
View File

@ -1,9 +1,14 @@
module gitlab.uni-ulm.de/stuve-it/it-tools/backend
module git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend
go 1.21
require (
github.com/fatih/color v1.15.0
github.com/go-ldap/ldap/v3 v3.4.6
github.com/google/uuid v1.3.1
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
github.com/pocketbase/dbx v1.10.1
github.com/pocketbase/pocketbase v0.19.0
)
@ -34,7 +39,6 @@ require (
github.com/disintegration/imaging v1.6.2 // indirect
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/ganigeorgiev/fexpr v0.3.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
@ -43,23 +47,20 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/pocketbase/dbx v1.10.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect

4
go.sum
View File

@ -207,8 +207,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=

View File

@ -2,13 +2,13 @@ package ldapLogin
import (
"fmt"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapSync"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"github.com/go-ldap/ldap/v3"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapSync"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
"os"
)

View File

@ -6,11 +6,11 @@ this file contains functions to sync ldap users and groups to the database
import (
"fmt"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/forms"
"github.com/pocketbase/pocketbase/models"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
)
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
@ -27,8 +27,8 @@ func upsertLDAPGroup(app *pocketbase.PocketBase, ldapGroup *LDAPGroup) error {
// if record exists, update it
if res, _ := app.Dao().FindFirstRecordByFilter(
ldapGroupsTableName,
"gidNumber = {:gidNumber}",
dbx.Params{"gidNumber": ldapGroup.gidNumber},
"id = {:id}",
dbx.Params{"id": ldapGroup.id},
); res != nil {
record = res
} else { // if record does not exist, create it
@ -51,7 +51,7 @@ func upsertLDAPGroup(app *pocketbase.PocketBase, ldapGroup *LDAPGroup) error {
// load data
err = form.LoadData(map[string]any{
"gidNumber": ldapGroup.gidNumber,
"id": ldapGroup.id,
"cn": ldapGroup.cn,
"dn": ldapGroup.dn,
"description": ldapGroup.description,
@ -83,8 +83,8 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
// if record exists, update it
if res, _ := app.Dao().FindFirstRecordByFilter(
ldapUsersTableName,
"uidNumber = {:uidNumber}",
dbx.Params{"uidNumber": ldapUser.uidNumber},
"id = {:id}",
dbx.Params{"id": ldapUser.id},
); res != nil {
record = res
} else { // if record does not exist, create it
@ -111,7 +111,7 @@ func upsertLDAPUser(app *pocketbase.PocketBase, ldapUser *LDAPUser) error {
}
}
record.Set("uidNumber", ldapUser.uidNumber)
record.Set("id", ldapUser.id)
record.Set("givenName", ldapUser.givenName)
record.Set("sn", ldapUser.sn)
record.Set("username", ldapUser.cn)

View File

@ -3,12 +3,13 @@ package ldapSync
import (
"encoding/json"
"fmt"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"github.com/go-ldap/ldap/v3"
"github.com/google/uuid"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/forms"
"github.com/pocketbase/pocketbase/models"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
"os"
"time"
)
@ -20,6 +21,21 @@ type SyncResult struct {
Errors []error
}
// getObjectGUID This function gets the objectGUID from an ldap entry
//
// since the objectGUID is a binary value, it is returned as a string (uuid)
func getObjectGUID(entry *ldap.Entry) (string, error) {
// get the objectGUID
var bytes = entry.GetRawAttributeValue("objectGUID")
// convert to uuid
u, err := uuid.FromBytes(bytes)
if err != nil {
return "", err
}
return u.String()[:15], nil // only use the first 15 characters of the uuid
}
// upsertLDAPGroup This function creates / updates a record in the ldap groups table
//
// this function expects that the table ldapGroups already exists
@ -34,7 +50,7 @@ func syncLdapGroups(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResul
os.Getenv("LDAP_GROUP_BASE_DN"), // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
os.Getenv("LDAP_GROUP_FILTER"), // The filter to apply
[]string{"gidNumber", "description", "dn", "cn", "msSFU30NisDomain", "memberOf"},
[]string{"description", "dn", "cn", "msSFU30NisDomain", "memberOf", "objectGUID"},
nil,
)
@ -46,8 +62,15 @@ func syncLdapGroups(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResul
}
for _, entry := range groupsFoundInLdap.Entries {
var id, e = getObjectGUID(entry)
if e != nil {
errors = append(errors, fmt.Errorf("unable to get objectGUID for group with dn: %s - %s", entry.DN, e))
}
err := upsertLDAPGroup(app, &LDAPGroup{
gidNumber: entry.GetAttributeValue("gidNumber"),
id: id,
description: entry.GetAttributeValue("description"),
dn: entry.DN,
cn: entry.GetAttributeValue("cn"),
@ -115,7 +138,7 @@ func syncLdapUsers(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResult
os.Getenv("LDAP_BASE_DN"), // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
os.Getenv("LDAP_USER_FILTER"), // The filter to apply
[]string{"givenName", "sn", "accountExpires", "uidNumber", "mail", "dn", "cn", "msSFU30NisDomain", "memberOf"},
[]string{"givenName", "sn", "accountExpires", "mail", "dn", "cn", "msSFU30NisDomain", "memberOf", "objectGUID"},
nil,
)
@ -127,11 +150,18 @@ func syncLdapUsers(app *pocketbase.PocketBase, ldapClient *ldap.Conn) SyncResult
}
for _, entry := range usersFoundInLdap.Entries {
var id, e = getObjectGUID(entry)
if e != nil {
errors = append(errors, fmt.Errorf("unable to get objectGUID for user with dn: %s - %s", entry.DN, e))
}
err := upsertLDAPUser(app, &LDAPUser{
givenName: entry.GetAttributeValue("givenName"),
sn: entry.GetAttributeValue("sn"),
accountExpires: entry.GetAttributeValue("accountExpires"),
uidNumber: entry.GetAttributeValue("uidNumber"),
id: id,
mail: entry.GetAttributeValue("mail"),
dn: entry.DN,
cn: entry.GetAttributeValue("cn"),

View File

@ -5,9 +5,9 @@ package ldapSync
import (
"fmt"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/tools/cron"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/logger"
"os"
"strconv"
"time"
@ -88,7 +88,7 @@ func InitLdapSync(app *pocketbase.PocketBase) error {
ldapSyncSchedule := os.Getenv("LDAP_SYNC_SCHEDULE")
// syncs ldap every 2 minutes
// syncs ldap - interval specified in the LDAP_SYNC_SCHEDULE env variable
scheduler.MustAdd("ldapSync", ldapSyncSchedule, func() {
logger.LogInfoF("syncing LDAP ...")
syncLdap(app)

View File

@ -1,10 +1,10 @@
package ldapSync
type LDAPUser struct {
id string
givenName string
sn string
accountExpires string
uidNumber string
mail string
dn string
@ -14,7 +14,7 @@ type LDAPUser struct {
}
type LDAPGroup struct {
gidNumber string
id string
description string
dn string

View File

@ -33,13 +33,6 @@ func createLDAPGroupsTable(app *pocketbase.PocketBase) error {
form.UpdateRule = nil
form.DeleteRule = nil
// add group ID field
form.Schema.AddField(&schema.SchemaField{
Name: "gidNumber",
Type: schema.FieldTypeText,
Required: true,
})
// add description field
form.Schema.AddField(&schema.SchemaField{
Name: "description",
@ -64,7 +57,7 @@ func createLDAPGroupsTable(app *pocketbase.PocketBase) error {
// create index on cn
form.Indexes = types.JsonArray[string]{
"CREATE UNIQUE INDEX idx_ldapGroups ON " + ldapGroupsTableName + " (cn, gidNumber, dn)",
"CREATE UNIQUE INDEX idx_ldapGroups ON " + ldapGroupsTableName + " (cn, dn)",
}
// validate and submit (internally it calls app.Dao().SaveCollection(collection) in a transaction)
@ -144,13 +137,6 @@ func createLDAPUsersTable(app *pocketbase.PocketBase) error {
Required: true,
})
// add uidNumber field
form.Schema.AddField(&schema.SchemaField{
Name: "uidNumber",
Type: schema.FieldTypeText,
Required: true,
})
// add surname field
form.Schema.AddField(&schema.SchemaField{
Name: "sn",
@ -185,7 +171,7 @@ func createLDAPUsersTable(app *pocketbase.PocketBase) error {
// create index on username
form.Indexes = types.JsonArray[string]{
"CREATE UNIQUE INDEX idx_ldapUsers ON " + ldapGroupsTableName + " (cn, uidNumber, dn)",
"CREATE UNIQUE INDEX idx_ldapUsers ON " + ldapGroupsTableName + " (cn, dn)",
}
return form.Submit()

View File

@ -1,19 +1,27 @@
package main
import (
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapLogin"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/ldapSync"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/qrApi"
"github.com/joho/godotenv"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapLogin"
"gitlab.uni-ulm.de/stuve-it/it-tools/backend/ldapSync"
"log"
)
func main() {
// load env
godotenv.Load(".env.local")
godotenv.Load(".env")
err := godotenv.Load(".env.local")
if err != nil {
logger.LogInfoF("The file '.env.local' could not be loaded.")
}
err = godotenv.Load(".env")
if err != nil {
logger.LogInfoF("The file '.env' could not be loaded.")
}
// create app
app := pocketbase.New()
@ -28,6 +36,11 @@ func main() {
return ldapLogin.InitLDAPLogin(app, e)
})
// setup qr api
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
return qrApi.InitQRApi(app, e)
})
// start app
if err := app.Start(); err != nil {
log.Fatal(err)

111
qrApi/main.go Normal file
View File

@ -0,0 +1,111 @@
package qrApi
import (
"bytes"
goqr "git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/go-qr"
"git.stuve.uni-ulm.de/StuVe-IT/stuve-it-backend/logger"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"io"
)
// InitQRApi initializes ar code api endpoint
//
// this endpoint can be used to create qr codes
// GET /api/qr/v1
//
// the endpoint expects the following request data:
//
// {
// "data": "what to be encoded",
// "ecc": "error correction level ('L', 'M' (default), 'Q', 'H')",
// "scale": "scale of qr code (default 1), must be greater than 0",
// "border": "border of qr code (default 1), must be equal or greater than 0, one is equal to one qr-code 'pixel'"
// "color": "color of qr code (default #000000)"
// "colorBackground": "background color of qr code (default #FFFFFF)",
// }
//
// if the user is authenticated successfully the endpoint returns and apis.RecordAuthResponse
func InitQRApi(app *pocketbase.PocketBase, e *core.ServeEvent) error {
// add endpoint to app
logger.LogInfoF("Adding QR API Endpoint")
e.Router.GET("/api/qr/v1", func(c echo.Context) error {
// get data from request
data := struct {
Data string `json:"data"`
ECC string `json:"ecc"`
Scale int `json:"scale"`
Border int `json:"border"`
ColorDark string `json:"color"`
ColorLight string `json:"colorBackground"`
}{}
if err := c.Bind(&data); err != nil {
return apis.NewBadRequestError("Failed to read request data", err)
}
// get correct error correction level
var errCorLvl goqr.Ecc
switch data.ECC {
case "L":
errCorLvl = goqr.Low
case "M":
errCorLvl = goqr.Medium
case "Q":
errCorLvl = goqr.Quartile
case "H":
errCorLvl = goqr.High
default:
errCorLvl = goqr.Medium
}
// encode data to qr code
qrCode, err := goqr.EncodeText(data.Data, errCorLvl)
if err != nil {
return apis.NewBadRequestError("Failed to encode data to qr code", err)
}
// set default scale and border
scale := data.Scale
border := data.Border
if scale == 0 {
scale = 1
}
if border == 0 {
border = 1
}
config := goqr.NewQrCodeImgConfig(scale, border)
// create new buffer and writer
var buffer bytes.Buffer
writer := io.Writer(&buffer)
// set default colors
colorLight := data.ColorLight
colorDark := data.ColorDark
if colorLight == "" {
colorLight = "#FFFFFF"
}
if colorDark == "" {
colorDark = "#000000"
}
// write qr code to buffer
err = qrCode.WriteAsSVG(config, writer, colorLight, colorDark)
// return error if writing qr code to buffer fails
if err != nil {
logger.LogErrorF("Failed to write QR Code: %e", err)
return apis.NewBadRequestError("Failed to write QR Code", err)
}
// return qr code as svg
return c.Stream(200, "image/svg+xml", io.Reader(&buffer))
}, apis.ActivityLogger(app))
return nil
}