WIP: major/v2 #1
15 changed files with 349 additions and 173 deletions
|
@ -4,15 +4,16 @@ LABEL author="vlbeaudoin"
|
|||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY LICENSE cmd.go config.go db.go entity.go flag.go go.mod go.sum handler.go main.go server.go ./
|
||||
COPY LICENSE cmd.go config.go db.go entity.go flag.go go.mod go.sum handler.go server.go x509.go ./
|
||||
|
||||
ADD cmd/ cmd/
|
||||
ADD queries/ queries/
|
||||
ADD ui/ ui/
|
||||
|
||||
RUN CGO_ENABLED=0 go build \
|
||||
-a \
|
||||
-o presences \
|
||||
./
|
||||
./cmd/presences/
|
||||
|
||||
# Alpine
|
||||
|
||||
|
|
35
Makefile
35
Makefile
|
@ -4,13 +4,40 @@
|
|||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
# splits current git annotated tag into 'MAJOR MINOR PATCH EXTRAS'.
|
||||
# intended to be used as arguments for certain scripts requiring a semver as args
|
||||
current_semver := $(shell git describe | tr -d 'v' | tr '.' ' ' | tr '-' ' ')
|
||||
|
||||
.PHONY: help
|
||||
help: ## Show this help
|
||||
@egrep -h '\s##\s' $(MAKEFILE_LIST) | \
|
||||
@grep -E -h '\s##\s' $(MAKEFILE_LIST) | \
|
||||
sort | \
|
||||
awk \
|
||||
'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
.PHONY: build
|
||||
build: ## Use `docker build` to build the container image
|
||||
docker build . -f Dockerfile -t git.agecem.com/bottin/presences:latest
|
||||
.PHONY: go-install
|
||||
go-install: ## Use `go install` to build and link the executable
|
||||
go install -a ./cmd/presences/
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Build container image
|
||||
#docker-compose build \
|
||||
#--build-arg bottin_version=`git describe`
|
||||
docker-compose build
|
||||
|
||||
.PHONY: docker-deploy
|
||||
docker-deploy: ## Using `docker-compose up`, deploy the database and containers
|
||||
docker-compose up -d
|
||||
|
||||
.PHONY: docker-tag-from-git
|
||||
docker-tag-from-git: ## Tag latest image according to annotated tag from `git describe`
|
||||
./scripts/docker-tag.sh $(current_semver)
|
||||
|
||||
.PHONY: docker-push-from-git
|
||||
docker-push-from-git: ## Push images to git.agecem.com according to annotated tag from `git describe`
|
||||
./scripts/docker-push.sh $(current_semver)
|
||||
|
||||
## pipelines
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: docker-build docker-deploy ## Build and deploy container image
|
||||
|
|
50
cmd.go
50
cmd.go
|
@ -1,11 +1,15 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
"git.agecem.com/bottin/bottin/v11"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -49,3 +53,47 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
func run(ctx context.Context, cfg Config) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
bottinClient := bottin.APIClient{Caller: voki.New(
|
||||
http.DefaultClient,
|
||||
cfg.Bottin.Host,
|
||||
cfg.Bottin.Key,
|
||||
cfg.Bottin.Port,
|
||||
func() string {
|
||||
if cfg.Bottin.TLS.Enabled {
|
||||
return "https"
|
||||
} else {
|
||||
return "http"
|
||||
}
|
||||
}(),
|
||||
)}
|
||||
|
||||
// connect to db
|
||||
dbPool, err := pgxpool.New(ctx,
|
||||
fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
|
||||
cfg.DB.Username,
|
||||
cfg.DB.Password,
|
||||
cfg.DB.Host,
|
||||
cfg.DB.Port,
|
||||
cfg.DB.Database,
|
||||
cfg.DB.SSLMode,
|
||||
))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dbPool.Close()
|
||||
|
||||
dbClient := DBClient{Pool: dbPool}
|
||||
|
||||
if err := RunUIServer(ctx, cfg, &bottinClient, &dbClient); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
11
cmd/presences/main.go
Normal file
11
cmd/presences/main.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.agecem.com/bottin/presences"
|
||||
)
|
||||
|
||||
// Entry
|
||||
func main() {
|
||||
// Start commandline parsing then call `run`
|
||||
presences.Execute()
|
||||
}
|
|
@ -39,7 +39,7 @@ services:
|
|||
volumes:
|
||||
- 'presences-config:/etc/presences/'
|
||||
restart: 'unless-stopped'
|
||||
command: ['presences', '--config', '/etc/presences/config.yaml', 'server']
|
||||
command: ['presences', '--config', '/etc/presences/config.yaml']
|
||||
|
||||
adminer:
|
||||
depends_on:
|
||||
|
|
15
config.go
15
config.go
|
@ -1,11 +1,11 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.agecem.com/bottin/bottin/v10/pkg/bottin"
|
||||
"git.agecem.com/bottin/bottin/v11"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
@ -33,13 +33,12 @@ type Config struct {
|
|||
Key string
|
||||
}
|
||||
|
||||
// Credentials holds username-password pairs for basic authentication
|
||||
// not part of minimum viable product
|
||||
//Credentials map[string]string
|
||||
|
||||
UI struct {
|
||||
Username string
|
||||
Password string
|
||||
//Username string
|
||||
//Password string
|
||||
|
||||
// Credentials holds username-password pairs for basic authentication
|
||||
Credentials map[string]string
|
||||
}
|
||||
|
||||
// Port holds the port on which to expose the user interface
|
||||
|
|
2
db.go
2
db.go
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import "time"
|
||||
|
||||
|
|
101
flag.go
101
flag.go
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
@ -6,37 +6,106 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
configPort = "Port"
|
||||
defaultPort = 8080
|
||||
flagPort = "port"
|
||||
flagPortConfig = "Port"
|
||||
flagPortDefault = 8080
|
||||
flagPort = "port"
|
||||
|
||||
configTLSCert = "TLS.Cert"
|
||||
defaultTLSCert = ""
|
||||
flagTLSCert = "tls-cert"
|
||||
flagTLSCertConfig = "TLS.Cert"
|
||||
flagTLSCertDefault = ""
|
||||
flagTLSCert = "tls-cert"
|
||||
|
||||
configTLSKey = "TLS.Key"
|
||||
defaultTLSKey = ""
|
||||
flagTLSKey = "tls-key"
|
||||
flagTLSKeyConfig = "TLS.Key"
|
||||
flagTLSKeyDefault = ""
|
||||
flagTLSKey = "tls-key"
|
||||
|
||||
flagDBDatabaseConfig = "DB.Database"
|
||||
flagDBDatabaseDefault = "presences"
|
||||
flagDBDatabase = "db-database"
|
||||
|
||||
flagDBHostConfig = "DB.Host"
|
||||
flagDBHostDefault = "presences-db"
|
||||
flagDBHost = "db-host"
|
||||
|
||||
flagDBPasswordConfig = "DB.Password"
|
||||
flagDBPasswordDefault = "presences"
|
||||
flagDBPassword = "db-password"
|
||||
|
||||
flagDBPortConfig = "DB.Port"
|
||||
flagDBPortDefault = 5432
|
||||
flagDBPort = "db-port"
|
||||
|
||||
flagDBSSLModeConfig = "DB.SSLMode"
|
||||
flagDBSSLModeDefault = "prefer"
|
||||
flagDBSSLMode = "db-sslmode"
|
||||
|
||||
flagDBUsernameConfig = "DB.Username"
|
||||
flagDBUsernameDefault = "presences"
|
||||
flagDBUsername = "db-username"
|
||||
|
||||
/*
|
||||
flagUICredentialsConfig = "UI.Credentials"
|
||||
//flagUICredentialsDefault = make(map[string]string)
|
||||
flagUICredentials = "ui-credentials"
|
||||
*/
|
||||
|
||||
//TODO check if any necessary flags are missing
|
||||
)
|
||||
|
||||
// BindClientFlags declares client-related flags and config options in the specified *pflag.FlagSet
|
||||
func BindFlags(set *pflag.FlagSet) error {
|
||||
// Credentials -> seulement par config
|
||||
|
||||
set.Int(flagPort, defaultPort, "User interface port")
|
||||
if err := viper.BindPFlag(configPort, set.Lookup(flagPort)); err != nil {
|
||||
set.Int(flagPort, flagPortDefault, "User interface port")
|
||||
if err := viper.BindPFlag(flagPortConfig, set.Lookup(flagPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagTLSKey, defaultTLSKey, "User interface TLS private key (or path to file)")
|
||||
if err := viper.BindPFlag(configTLSKey, set.Lookup(flagTLSKey)); err != nil {
|
||||
set.String(flagTLSKey, flagTLSKeyDefault, "User interface TLS private key (or path to file)")
|
||||
if err := viper.BindPFlag(flagTLSKeyConfig, set.Lookup(flagTLSKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagTLSCert, defaultTLSCert, "User interface TLS certificate (or path to file)")
|
||||
if err := viper.BindPFlag(configTLSCert, set.Lookup(flagTLSCert)); err != nil {
|
||||
set.String(flagTLSCert, flagTLSCertDefault, "User interface TLS certificate (or path to file)")
|
||||
if err := viper.BindPFlag(flagTLSCertConfig, set.Lookup(flagTLSCert)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagDBDatabase, flagDBDatabaseDefault, "PostgreSQL database")
|
||||
if err := viper.BindPFlag(flagDBDatabaseConfig, set.Lookup(flagDBDatabase)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagDBHost, flagDBHostDefault, "PostgreSQL host")
|
||||
if err := viper.BindPFlag(flagDBHostConfig, set.Lookup(flagDBHost)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagDBPassword, flagDBPasswordDefault, "PostgreSQL password")
|
||||
if err := viper.BindPFlag(flagDBPasswordConfig, set.Lookup(flagDBPassword)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.Int(flagDBPort, flagDBPortDefault, "PostgreSQL port")
|
||||
if err := viper.BindPFlag(flagDBPortConfig, set.Lookup(flagDBPort)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagDBSSLMode, flagDBSSLModeDefault, "PostgreSQL ssl mode")
|
||||
if err := viper.BindPFlag(flagDBSSLModeConfig, set.Lookup(flagDBSSLMode)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
set.String(flagDBUsername, flagDBUsernameDefault, "PostgreSQL username")
|
||||
if err := viper.BindPFlag(flagDBUsernameConfig, set.Lookup(flagDBUsername)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
set.StringToString(flagUICredentials, nil, "Sets of credentials for the UI console")
|
||||
if err := viper.BindPFlag(flagUICredentialsConfig, set.Lookup(flagUICredentials)); err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
35
go.mod
35
go.mod
|
@ -4,44 +4,39 @@ go 1.23.6
|
|||
|
||||
require (
|
||||
codeberg.org/vlbeaudoin/voki/v3 v3.0.1
|
||||
git.agecem.com/bottin/bottin/v10 v10.6.0
|
||||
github.com/jackc/pgx/v5 v5.7.2
|
||||
git.agecem.com/bottin/bottin/v11 v11.0.2
|
||||
github.com/jackc/pgx/v5 v5.7.4
|
||||
github.com/labstack/echo/v4 v4.13.3
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/spf13/viper v1.20.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.8.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
76
go.sum
76
go.sum
|
@ -1,30 +1,29 @@
|
|||
codeberg.org/vlbeaudoin/voki/v3 v3.0.1 h1:pFjd/ZKsu4eOzRJYViE9F1S3RglSkAuIiqCo9IH9tUI=
|
||||
codeberg.org/vlbeaudoin/voki/v3 v3.0.1/go.mod h1:+6LMXosAu2ijNKV04sMwkeujpH+cghZU1fydqj2y95g=
|
||||
git.agecem.com/bottin/bottin/v10 v10.6.0 h1:I9xQMizlqfWHrJ1ZL2PlX/EeTpRm+B7bsheaBOVChFM=
|
||||
git.agecem.com/bottin/bottin/v10 v10.6.0/go.mod h1:KTwlqY5XdVi9F7cpwy3hxYN1DQm+74tBv7Wc9rfKXuM=
|
||||
git.agecem.com/bottin/bottin/v11 v11.0.2 h1:QtbjTDln3jcd4Vkc66uil+YoLisOPaDy2SerZQ95weo=
|
||||
git.agecem.com/bottin/bottin/v11 v11.0.2/go.mod h1:c0sV8VOTLtnwel3oCo8xyBTW9zdmrVRwiGEF5NyYjjY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1 h1:FWNFq4fM1wPfcK40yHE5UO3RUdSNPaBC+j3PokzA6OQ=
|
||||
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
@ -35,38 +34,31 @@ github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaa
|
|||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
||||
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk=
|
||||
github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
@ -80,26 +72,22 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
|
|||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,7 +6,7 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.agecem.com/bottin/bottin/v10/pkg/bottin"
|
||||
"git.agecem.com/bottin/bottin/v11"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
|
|
61
main.go
61
main.go
|
@ -1,61 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"codeberg.org/vlbeaudoin/voki/v3"
|
||||
"git.agecem.com/bottin/bottin/v10/pkg/bottin"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// Entry
|
||||
func main() {
|
||||
// Start commandline parsing then call `run`
|
||||
Execute()
|
||||
}
|
||||
|
||||
func run(ctx context.Context, cfg Config) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
bottinClient := bottin.APIClient{Caller: voki.New(
|
||||
http.DefaultClient,
|
||||
cfg.Bottin.Host,
|
||||
cfg.Bottin.Key,
|
||||
cfg.Bottin.Port,
|
||||
func() string {
|
||||
if cfg.Bottin.TLS.Enabled {
|
||||
return "https"
|
||||
} else {
|
||||
return "http"
|
||||
}
|
||||
}(),
|
||||
)}
|
||||
|
||||
// connect to db
|
||||
dbPool, err := pgxpool.New(ctx,
|
||||
fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
|
||||
cfg.DB.Username,
|
||||
cfg.DB.Password,
|
||||
cfg.DB.Host,
|
||||
cfg.DB.Port,
|
||||
cfg.DB.Database,
|
||||
cfg.DB.SSLMode,
|
||||
))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dbPool.Close()
|
||||
|
||||
dbClient := DBClient{Pool: dbPool}
|
||||
|
||||
if err := RunUIServer(ctx, cfg, &bottinClient, &dbClient); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
49
server.go
49
server.go
|
@ -1,12 +1,14 @@
|
|||
package main
|
||||
package presences
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.agecem.com/bottin/bottin/v10/pkg/bottin"
|
||||
"git.agecem.com/bottin/bottin/v11"
|
||||
"git.agecem.com/bottin/presences/ui"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
|
@ -41,19 +43,19 @@ func RunUIServer(ctx context.Context, cfg Config, bottinClient *bottin.APIClient
|
|||
|
||||
e.Pre(middleware.AddTrailingSlash())
|
||||
|
||||
// basic auth
|
||||
if cfg.UI.Username == "" || cfg.UI.Password == "" {
|
||||
return fmt.Errorf("UI username and password cannot be empty, please set UI.Password and UI.Username (PRESENCES_UI_PASSWORD and PRESENCES_UI_USERNAME")
|
||||
if len(cfg.UI.Credentials) == 0 {
|
||||
return fmt.Errorf("UI.Credentials config file option of type map[string]string must contain at least one username-password key-value pair. Note that there is no flag or ENV counterpart, this must be done through a config file.")
|
||||
}
|
||||
|
||||
e.Pre(middleware.BasicAuth(
|
||||
func(username, password string, c echo.Context) (bool, error) {
|
||||
userOK := subtle.ConstantTimeCompare([]byte(username), []byte(cfg.UI.Username)) == 1
|
||||
passOK := subtle.ConstantTimeCompare([]byte(password), []byte(cfg.UI.Password)) == 1
|
||||
if userOK && passOK {
|
||||
return true, nil
|
||||
rightPassword, userExists := cfg.UI.Credentials[username]
|
||||
if !userExists {
|
||||
return false, nil
|
||||
}
|
||||
return false, nil
|
||||
|
||||
passwordOK := subtle.ConstantTimeCompare([]byte(password), []byte(rightPassword)) == 1
|
||||
return passwordOK, nil
|
||||
}),
|
||||
)
|
||||
|
||||
|
@ -69,7 +71,30 @@ func RunUIServer(ctx context.Context, cfg Config, bottinClient *bottin.APIClient
|
|||
|
||||
address := fmt.Sprintf(":%d", cfg.Port)
|
||||
|
||||
return e.StartTLS(address, cfg.TLS.Cert, cfg.TLS.Key)
|
||||
}
|
||||
switch {
|
||||
case cfg.TLS.Cert != "" && cfg.TLS.Key != "":
|
||||
return e.StartTLS(address, cfg.TLS.Cert, cfg.TLS.Key)
|
||||
case cfg.TLS.Cert != "" && cfg.TLS.Key == "":
|
||||
return fmt.Errorf("found TLS certificate but missing associated TLS private key")
|
||||
case cfg.TLS.Cert == "" && cfg.TLS.Key != "":
|
||||
return fmt.Errorf("found TLS private key but missing associated TLS certificate")
|
||||
default:
|
||||
log.Println("No TLS pair was provided. Generating self-signed pair.")
|
||||
|
||||
tlsPair, err := newTLSPair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
Handler: e,
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsPair},
|
||||
},
|
||||
}
|
||||
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
74
x509.go
Normal file
74
x509.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package presences
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTLSPair() (tls.Certificate, error) {
|
||||
//TODO revise this code to make sure it is satisfying
|
||||
|
||||
// Generate an ECDSA private key using P256.
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
// Generate a random serial number.
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("Failed to generate serial number: %v", err)
|
||||
}
|
||||
|
||||
// Create a certificate template.
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"AGECEM-bottin-presences"},
|
||||
CommonName: "localhost", // common name localhost for local development
|
||||
},
|
||||
NotBefore: time.Now().Add(-time.Hour), // valid from 1 hour ago
|
||||
NotAfter: time.Now().Add(365 * 24 * time.Hour), // valid for 1 year
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true, // self-signed, so we can mark it as CA (optional)
|
||||
}
|
||||
|
||||
// Self-sign the certificate.
|
||||
derCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("Failed to create certificate: %v", err)
|
||||
}
|
||||
|
||||
// Encode the certificate PEM block.
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: derCert,
|
||||
})
|
||||
// Encode the private key PEM block.
|
||||
keyBytes, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("Unable to marshal ECDSA private key: %v", err)
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: keyBytes,
|
||||
})
|
||||
|
||||
// Load the generated certificate into a tls.Certificate.
|
||||
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, fmt.Errorf("failed to load X509 key pair: %v", err)
|
||||
}
|
||||
|
||||
return tlsCert, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue