Merge remote-tracking branch 'origin/main' into alertmanager-interface

This commit is contained in:
cubicroot 2022-04-18 12:53:09 +02:00
commit d1c62e24ed
26 changed files with 301 additions and 107 deletions

View file

@ -5,17 +5,10 @@ on:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test_build_publish:
name: Test, build, and publish
test:
name: Test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v2
@ -36,6 +29,22 @@ jobs:
source $(poetry env info --path)/bin/activate
make test
publish_docker_image:
name: Publish Docker image
needs: test
runs-on: ubuntu-latest
permissions:
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
@ -49,12 +58,41 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
build-args: PB_BUILD_VERSION=${{ github.ref_name }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
publish_github_release:
name: Publish GitHub Release
needs: test
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Export GOBIN
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,4 +1,4 @@
name: Main
name: Test
on:
push:
@ -32,3 +32,6 @@ jobs:
run: |
source $(poetry env info --path)/bin/activate
make test
- name: Build
run: make build

19
.goreleaser.yml Normal file
View file

@ -0,0 +1,19 @@
builds:
- id: pushbits
main: ./cmd/pushbits
goos:
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w -X main.version=v{{.Version}}
checksum:
algorithm: sha256
archives:
- id: pushbits
builds:
- pushbits
format: tar.gz

16
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/pushbits",
"cwd": "${workspaceFolder}"
}
]
}

View file

@ -1,5 +1,7 @@
FROM docker.io/library/golang:alpine as builder
ARG PB_BUILD_VERSION
WORKDIR /build
COPY . .
@ -8,7 +10,7 @@ RUN set -ex \
&& apk add --no-cache build-base \
&& go mod download \
&& go mod verify \
&& make build \
&& PB_BUILD_VERSION="$PB_BUILD_VERSION" make build \
&& chmod +x /build/out/pushbits
FROM docker.io/library/alpine

View file

@ -5,12 +5,17 @@ DOCS_DIR := ./docs
OUT_DIR := ./out
TESTS_DIR := ./tests
PB_BUILD_VERSION ?= $(shell git describe --tags)
ifeq ($(PB_BUILD_VERSION),)
_ := $(error Cannot determine build version)
endif
SEMGREP_MODFILE := $(TESTS_DIR)/semgrep-rules/go.mod
.PHONY: build
build:
mkdir -p $(OUT_DIR)
go build -ldflags="-w -s" -o $(OUT_DIR)/pushbits ./cmd/pushbits
go build -ldflags="-w -s -X main.version=$(PB_BUILD_VERSION)" -o $(OUT_DIR)/pushbits ./cmd/pushbits
.PHONY: clean
clean:
@ -37,7 +42,7 @@ setup:
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
go install github.com/swaggo/swag/cmd/swag@latest
go install honnef.co/go/tools/cmd/staticcheck@latest
go install honnef.co/go/tools/cmd/staticcheck@v0.2.2
poetry install
.PHONY: swag

View file

@ -72,7 +72,7 @@ This project totally would've used Signal if it would offer a proper API.
Sadly, neither [Signal](https://signal.org/) nor [WhatsApp](https://www.whatsapp.com/) come with an API (at the time of writing) through which PushBits could interact.
In [Telegram](https://telegram.org/) there is an API to run bots, but these are limited in that they cannot create chats by themselves.
If you insist on going with Telegram, have a look at [webhook2telegram](https://github.com/muety/webhook2telegram).
If you insist on going with Telegram, have a look at [telepush](https://github.com/muety/telepush).
The idea of a federated, synchronized but yet end-to-end encrypted protocol is awesome, but its clients simply aren't really there yet.
Still, if you haven't tried it yet, we'd encourage you to check it out.

View file

@ -1,7 +1,6 @@
package main
import (
"log"
"os"
"os/signal"
"syscall"
@ -10,10 +9,13 @@ import (
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/database"
"github.com/pushbits/server/internal/dispatcher"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/router"
"github.com/pushbits/server/internal/runner"
)
var version string
func setupCleanup(db *database.Database, dp *dispatcher.Dispatcher) {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
@ -30,41 +32,46 @@ func setupCleanup(db *database.Database, dp *dispatcher.Dispatcher) {
// @version 0.7.2
// @description Documentation for the PushBits server API.
// @contact.name PushBits
// @contact.name The PushBits Developers
// @contact.url https://www.pushbits.io
// @license.name ISC
// @license.url https://github.com/pushbits/server/blob/master/LICENSE
// @host your-domain.net
// @BasePath /
// @query.collection.format multi
// @schemes http https
// @securityDefinitions.basic BasicAuth
func main() {
log.Println("Starting PushBits.")
if len(version) == 0 {
log.L.Panic("Version not set")
} else {
log.L.Printf("Starting PushBits %s", version)
}
c := configuration.Get()
if c.Debug {
log.Printf("%+v", c)
log.SetDebug()
log.L.Printf("%+v", c)
}
cm := credentials.CreateManager(c.Security.CheckHIBP, c.Crypto)
db, err := database.Create(cm, c.Database.Dialect, c.Database.Connection)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
defer db.Close()
if err := db.Populate(c.Admin.Name, c.Admin.Password, c.Admin.MatrixID); err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
dp, err := dispatcher.Create(c.Matrix.Homeserver, c.Matrix.Username, c.Matrix.Password, c.Formatting)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
defer dp.Close()
@ -72,13 +79,13 @@ func main() {
err = db.RepairChannels(dp)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
engine := router.Create(c.Debug, cm, db, dp, &c.Alertmanager)
err = runner.Run(engine, c.HTTP.ListenAddress, c.HTTP.Port)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
}

1
go.mod
View file

@ -14,6 +14,7 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.4 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect

3
go.sum
View file

@ -95,7 +95,10 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

View file

@ -2,10 +2,10 @@ package api
import (
"errors"
"log"
"net/http"
"github.com/pushbits/server/internal/authentication"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"github.com/gin-gonic/gin"
@ -27,7 +27,7 @@ func (h *ApplicationHandler) generateToken(compat bool) string {
}
func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
log.Printf("Registering application %s.", a.Name)
log.L.Printf("Registering application %s.", a.Name)
channelID, err := h.DP.RegisterApplication(a.ID, a.Name, a.Token, u.MatrixID)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
@ -45,7 +45,7 @@ func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Appl
}
func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User, name string, compat bool) (*model.Application, error) {
log.Printf("Creating application %s.", name)
log.L.Printf("Creating application %s.", name)
application := model.Application{}
application.Name = name
@ -60,7 +60,7 @@ func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User,
if err := h.registerApplication(ctx, &application, u); err != nil {
err := h.DB.DeleteApplication(&application)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
log.Printf("Cannot delete application with ID %d.", application.ID)
log.L.Printf("Cannot delete application with ID %d.", application.ID)
}
return nil, err
@ -70,7 +70,7 @@ func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User,
}
func (h *ApplicationHandler) deleteApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
log.Printf("Deleting application %s (ID %d).", a.Name, a.ID)
log.L.Printf("Deleting application %s (ID %d).", a.Name, a.ID)
err := h.DP.DeregisterApplication(a, u)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
@ -86,15 +86,15 @@ func (h *ApplicationHandler) deleteApplication(ctx *gin.Context, a *model.Applic
}
func (h *ApplicationHandler) updateApplication(ctx *gin.Context, a *model.Application, updateApplication *model.UpdateApplication) error {
log.Printf("Updating application %s (ID %d).", a.Name, a.ID)
log.L.Printf("Updating application %s (ID %d).", a.Name, a.ID)
if updateApplication.Name != nil {
log.Printf("Updating application name to '%s'.", *updateApplication.Name)
log.L.Printf("Updating application name to '%s'.", *updateApplication.Name)
a.Name = *updateApplication.Name
}
if updateApplication.RefreshToken != nil && (*updateApplication.RefreshToken) {
log.Print("Updating application token.")
log.L.Print("Updating application token.")
compat := updateApplication.StrictCompatibility != nil && (*updateApplication.StrictCompatibility)
a.Token = h.generateToken(compat)
}
@ -129,7 +129,7 @@ func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) {
var createApplication model.CreateApplication
if err := ctx.Bind(&createApplication); err != nil {
log.Println(err)
log.L.Println(err)
return
}

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"testing"
@ -12,6 +11,7 @@ import (
"github.com/pushbits/server/internal/authentication/credentials"
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/database"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"github.com/pushbits/server/tests"
"github.com/pushbits/server/tests/mockups"
@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
db, err := mockups.GetEmptyDatabase(config.Crypto)
if err != nil {
cleanUp()
log.Println("Can not set up database: ", err)
log.L.Println("Can not set up database: ", err)
os.Exit(1)
}
TestDatabase = db
@ -58,7 +58,7 @@ func TestMain(m *testing.M) {
appHandler, err := getApplicationHandler(config)
if err != nil {
cleanUp()
log.Println("Can not set up application handler: ", err)
log.L.Println("Can not set up application handler: ", err)
os.Exit(1)
}
@ -80,7 +80,7 @@ func TestMain(m *testing.M) {
// Run
m.Run()
log.Println("Clean up after Test")
log.L.Println("Clean up after Test")
cleanUp()
}

View file

@ -1,12 +1,12 @@
package api
import (
"log"
"net/http"
"net/url"
"time"
"github.com/pushbits/server/internal/authentication"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"github.com/gin-gonic/gin"
@ -45,7 +45,7 @@ type NotificationHandler struct {
// @Router /message [post]
func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
application := authentication.GetApplication(ctx)
log.Printf("Sending notification for application %s.", application.Name)
log.L.Printf("Sending notification for application %s.", application.Name)
var notification model.Notification
if err := ctx.Bind(&notification); err != nil {
@ -79,7 +79,7 @@ func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
// @Router /message/{message_id} [DELETE]
func (h *NotificationHandler) DeleteNotification(ctx *gin.Context) {
application := authentication.GetApplication(ctx)
log.Printf("Deleting notification for application %s.", application.Name)
log.L.Printf("Deleting notification for application %s.", application.Name)
id, err := getMessageID(ctx)
if success := successOrAbort(ctx, http.StatusUnprocessableEntity, err); !success {

View file

@ -2,10 +2,10 @@ package api
import (
"errors"
"log"
"net/http"
"github.com/pushbits/server/internal/authentication"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"github.com/gin-gonic/gin"
@ -90,7 +90,7 @@ func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser mod
}
}
log.Printf("Updating user %s.", u.Name)
log.L.Printf("Updating user %s.", u.Name)
if updateUser.Name != nil {
u.Name = *updateUser.Name
@ -146,7 +146,7 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
return
}
log.Printf("Creating user %s.", createUser.Name)
log.L.Printf("Creating user %s.", createUser.Name)
user, err := h.DB.CreateUser(createUser)
@ -232,7 +232,7 @@ func (h *UserHandler) DeleteUser(ctx *gin.Context) {
}
}
log.Printf("Deleting user %s.", user.Name)
log.L.Printf("Deleting user %s.", user.Name)
if err := h.deleteApplications(ctx, user); err != nil {
return

View file

@ -1,9 +1,8 @@
package credentials
import (
"log"
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/log"
"github.com/alexedwards/argon2id"
)
@ -16,7 +15,7 @@ type Manager struct {
// CreateManager instanciates a credential manager.
func CreateManager(checkHIBP bool, c configuration.CryptoConfig) *Manager {
log.Println("Setting up credential manager.")
log.L.Println("Setting up credential manager.")
argon2Params := &argon2id.Params{
Memory: c.Argon2.Memory,

View file

@ -4,9 +4,10 @@ import (
"crypto/sha1" //#nosec G505 -- False positive, see the use below.
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"github.com/pushbits/server/internal/log"
)
const (
@ -27,7 +28,7 @@ func IsPasswordPwned(password string) (bool, error) {
lookup := hashStr[0:5]
match := hashStr[5:]
log.Printf("Checking HIBP for hashes starting with '%s'.", lookup)
log.L.Printf("Checking HIBP for hashes starting with '%s'.", lookup)
resp, err := http.Get(pwnedHashesURL + lookup)
if err != nil {
@ -35,13 +36,13 @@ func IsPasswordPwned(password string) (bool, error) {
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("Request failed with HTTP %s.", resp.Status)
log.L.Fatalf("Request failed with HTTP %s.", resp.Status)
}
defer resp.Body.Close()
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
}
bodyStr := string(bodyText)

View file

@ -2,9 +2,10 @@ package credentials
import (
"errors"
"log"
"github.com/alexedwards/argon2id"
"github.com/pushbits/server/internal/log"
)
// CreatePasswordHash returns a hashed version of the given password.
@ -21,7 +22,7 @@ func (m *Manager) CreatePasswordHash(password string) ([]byte, error) {
hash, err := argon2id.CreateHash(password, m.argon2Params)
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
panic(err)
}
@ -33,7 +34,7 @@ func ComparePassword(hash, password []byte) bool {
match, err := argon2id.ComparePasswordAndHash(string(password), string(hash))
if err != nil {
log.Fatal(err)
log.L.Fatal(err)
return false
}

View file

@ -3,12 +3,12 @@ package database
import (
"database/sql"
"errors"
"log"
"os"
"path/filepath"
"time"
"github.com/pushbits/server/internal/authentication/credentials"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"gorm.io/driver/mysql"
@ -36,7 +36,7 @@ func createFileDir(file string) {
// Create instanciates a database connection.
func Create(cm *credentials.Manager, dialect, connection string) (*Database, error) {
log.Println("Setting up database connection.")
log.L.Println("Setting up database connection.")
maxOpenConns := 5
@ -82,13 +82,13 @@ func Create(cm *credentials.Manager, dialect, connection string) (*Database, err
func (d *Database) Close() {
err := d.sqldb.Close()
if err != nil {
log.Printf("Error while closing database: %s", err)
log.L.Printf("Error while closing database: %s", err)
}
}
// Populate fills the database with initial information like the admin user.
func (d *Database) Populate(name, password, matrixID string) error {
log.Print("Populating database.")
log.L.Print("Populating database.")
var user model.User
@ -104,7 +104,7 @@ func (d *Database) Populate(name, password, matrixID string) error {
return errors.New("user cannot be created")
}
} else {
log.Printf("Priviledged user %s already exists.", name)
log.L.Printf("Priviledged user %s already exists.", name)
}
return nil
@ -112,7 +112,7 @@ func (d *Database) Populate(name, password, matrixID string) error {
// RepairChannels resets channels that have been modified by a user.
func (d *Database) RepairChannels(dp Dispatcher) error {
log.Print("Repairing application channels.")
log.L.Print("Repairing application channels.")
users, err := d.GetUsers()
if err != nil {
@ -140,11 +140,11 @@ func (d *Database) RepairChannels(dp Dispatcher) error {
}
if orphan {
log.Printf("Found orphan channel for application %s (ID %d)", application.Name, application.ID)
log.L.Printf("Found orphan channel for application %s (ID %d)", application.Name, application.ID)
if err = dp.RepairApplication(&application, &user); err != nil {
log.Printf("Unable to repair application %s (ID %d).", application.Name, application.ID)
log.Println(err)
log.L.Printf("Unable to repair application %s (ID %d).", application.Name, application.ID)
log.L.Println(err)
}
}
}

View file

@ -2,11 +2,11 @@ package dispatcher
import (
"fmt"
"log"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
mId "maunium.net/go/mautrix/id"
)
@ -17,7 +17,7 @@ func buildRoomTopic(id uint) string {
// RegisterApplication creates a channel for an application.
func (d *Dispatcher) RegisterApplication(id uint, name, token, user string) (string, error) {
log.Printf("Registering application %s, notifications will be relayed to user %s.\n", name, user)
log.L.Printf("Registering application %s, notifications will be relayed to user %s.\n", name, user)
resp, err := d.mautrixClient.CreateRoom(&mautrix.ReqCreateRoom{
Visibility: "private",
@ -28,18 +28,18 @@ func (d *Dispatcher) RegisterApplication(id uint, name, token, user string) (str
Topic: buildRoomTopic(id),
})
if err != nil {
log.Print(err)
log.L.Print(err)
return "", err
}
log.Printf("Application %s is now relayed to room with ID %s.\n", name, resp.RoomID.String())
log.L.Printf("Application %s is now relayed to room with ID %s.\n", name, resp.RoomID.String())
return resp.RoomID.String(), err
}
// DeregisterApplication deletes a channel for an application.
func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User) error {
log.Printf("Deregistering application %s (ID %d) with Matrix ID %s.\n", a.Name, a.ID, a.MatrixID)
log.L.Printf("Deregistering application %s (ID %d) with Matrix ID %s.\n", a.Name, a.ID, a.MatrixID)
// The user might have left the channel, but we can still try to remove them.
@ -47,17 +47,17 @@ func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User)
Reason: "This application was deleted",
UserID: mId.UserID(u.MatrixID),
}); err != nil {
log.Print(err)
log.L.Print(err)
return err
}
if _, err := d.mautrixClient.LeaveRoom(mId.RoomID(a.MatrixID)); err != nil {
log.Print(err)
log.L.Print(err)
return err
}
if _, err := d.mautrixClient.ForgetRoom(mId.RoomID(a.MatrixID)); err != nil {
log.Print(err)
log.L.Print(err)
return err
}
@ -66,7 +66,7 @@ func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User)
func (d *Dispatcher) sendRoomEvent(roomID, eventType string, content interface{}) error {
if _, err := d.mautrixClient.SendStateEvent(mId.RoomID(roomID), event.NewEventType(eventType), "", content); err != nil {
log.Print(err)
log.L.Print(err)
return err
}
@ -75,7 +75,7 @@ func (d *Dispatcher) sendRoomEvent(roomID, eventType string, content interface{}
// UpdateApplication updates a channel for an application.
func (d *Dispatcher) UpdateApplication(a *model.Application) error {
log.Printf("Updating application %s (ID %d) with Matrix ID %s.\n", a.Name, a.ID, a.MatrixID)
log.L.Printf("Updating application %s (ID %d) with Matrix ID %s.\n", a.Name, a.ID, a.MatrixID)
content := map[string]interface{}{
"name": a.Name,

View file

@ -1,11 +1,11 @@
package dispatcher
import (
"log"
"github.com/pushbits/server/internal/configuration"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/log"
)
// Dispatcher holds information for sending notifications to clients.
@ -16,7 +16,7 @@ type Dispatcher struct {
// Create instanciates a dispatcher connection.
func Create(homeserver, username, password string, formatting configuration.Formatting) (*Dispatcher, error) {
log.Println("Setting up dispatcher.")
log.L.Println("Setting up dispatcher.")
matrixClient, err := mautrix.NewClient(homeserver, "", "")
if err != nil {
@ -39,14 +39,14 @@ func Create(homeserver, username, password string, formatting configuration.Form
// Close closes the dispatcher connection.
func (d *Dispatcher) Close() {
log.Printf("Logging out.")
log.L.Printf("Logging out.")
_, err := d.mautrixClient.Logout()
if err != nil {
log.Printf("Error while logging out: %s", err)
log.L.Printf("Error while logging out: %s", err)
}
d.mautrixClient.ClearCredentials()
log.Printf("Successfully logged out.")
log.L.Printf("Successfully logged out.")
}

View file

@ -3,15 +3,16 @@ package dispatcher
import (
"fmt"
"html"
"log"
"strings"
"github.com/gomarkdown/markdown"
"github.com/pushbits/server/internal/model"
"github.com/pushbits/server/internal/pberrors"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
mId "maunium.net/go/mautrix/id"
"github.com/pushbits/server/internal/log"
"github.com/pushbits/server/internal/model"
"github.com/pushbits/server/internal/pberrors"
)
// MessageFormat is a matrix message format
@ -31,9 +32,9 @@ type MessageEvent struct {
Body string `json:"body"`
FormattedBody string `json:"formatted_body"`
MsgType MsgType `json:"msgtype"`
RelatesTo RelatesTo `json:"m.relates_to,omitempty"`
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
Format MessageFormat `json:"format"`
NewContent NewContent `json:"m.new_content,omitempty"`
NewContent *NewContent `json:"m.new_content,omitempty"`
}
// RelatesTo holds information about relations to other message events
@ -53,7 +54,7 @@ type NewContent struct {
// SendNotification sends a notification to the specified user.
func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notification) (eventId string, err error) {
log.Printf("Sending notification to room %s.", a.MatrixID)
log.L.Printf("Sending notification to room %s.", a.MatrixID)
plainMessage := strings.TrimSpace(n.Message)
plainTitle := strings.TrimSpace(n.Title)
@ -77,7 +78,7 @@ func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notificatio
// DeleteNotification sends a notification to the specified user that another notificaion is deleted
func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNotification) error {
log.Printf("Sending delete notification to room %s", a.MatrixID)
log.L.Printf("Sending delete notification to room %s", a.MatrixID)
var oldFormattedBody string
var oldBody string
@ -85,7 +86,7 @@ func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNot
deleteMessage, err := d.getMessage(a, n.ID)
if err != nil {
log.Println(err)
log.L.Println(err)
return pberrors.ErrorMessageNotFound
}
@ -133,7 +134,7 @@ func (d *Dispatcher) getFormattedMessage(n *model.Notification) string {
if ok {
if contentTypeRaw, ok := optionsDisplay["contentType"]; ok {
contentType := fmt.Sprintf("%v", contentTypeRaw)
log.Printf("Message content type: %s", contentType)
log.L.Printf("Message content type: %s", contentType)
switch contentType {
case "html", "text/html":
@ -212,15 +213,15 @@ func (d *Dispatcher) replaceMessage(a *model.Application, newBody, newFormattedB
Body: oldBody,
FormattedBody: oldFormattedBody,
MsgType: MsgTypeText,
NewContent: newMessage,
RelatesTo: replaceRelation,
NewContent: &newMessage,
RelatesTo: &replaceRelation,
Format: MessageFormatHTML,
}
sendEvent, err := d.mautrixClient.SendMessageEvent(mId.RoomID(a.MatrixID), event.EventMessage, &replaceEvent)
if err != nil {
log.Println(err)
log.L.Println(err)
return nil, err
}
@ -252,7 +253,7 @@ func (d *Dispatcher) respondToMessage(a *model.Application, body, formattedBody
notificationRelation := RelatesTo{
InReplyTo: notificationReply,
}
notificationEvent.RelatesTo = notificationRelation
notificationEvent.RelatesTo = &notificationRelation
return d.mautrixClient.SendMessageEvent(mId.RoomID(a.MatrixID), event.EventMessage, &notificationEvent)
}

77
internal/log/ginlogrus.go Normal file
View file

@ -0,0 +1,77 @@
// Source: https://github.com/toorop/gin-logrus
package log
import (
"fmt"
"math"
"net/http"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// GinLogger integrates logrus with gin
func GinLogger(logger logrus.FieldLogger, notLogged ...string) gin.HandlerFunc {
hostname, err := os.Hostname()
if err != nil {
hostname = "unknow"
}
var skip map[string]struct{}
if length := len(notLogged); length > 0 {
skip = make(map[string]struct{}, length)
for _, p := range notLogged {
skip[p] = struct{}{}
}
}
return func(c *gin.Context) {
path := c.Request.URL.Path
start := time.Now()
c.Next()
stop := time.Since(start)
latency := int(math.Ceil(float64(stop.Nanoseconds()) / 1000000.0))
statusCode := c.Writer.Status()
clientIP := c.ClientIP()
clientUserAgent := c.Request.UserAgent()
referer := c.Request.Referer()
dataLength := c.Writer.Size()
if dataLength < 0 {
dataLength = 0
}
if _, ok := skip[path]; ok {
return
}
entry := logger.WithFields(logrus.Fields{
"hostname": hostname,
"statusCode": statusCode,
"latency": latency,
"clientIP": clientIP,
"method": c.Request.Method,
"path": path,
"referer": referer,
"dataLength": dataLength,
"userAgent": clientUserAgent,
})
if len(c.Errors) > 0 {
entry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
} else {
msg := fmt.Sprintf("%s [%d] %s %s", clientIP, statusCode, c.Request.Method, path)
if statusCode >= http.StatusInternalServerError {
entry.Error(msg)
} else if statusCode >= http.StatusBadRequest {
entry.Warn(msg)
} else {
entry.Info(msg)
}
}
}
}

22
internal/log/log.go Normal file
View file

@ -0,0 +1,22 @@
package log
import (
"os"
log "github.com/sirupsen/logrus"
)
var L *log.Logger
func init() {
L = log.New()
L.SetOutput(os.Stderr)
L.SetLevel(log.InfoLevel)
L.SetFormatter(&log.TextFormatter{
DisableTimestamp: true,
})
}
func SetDebug() {
L.SetLevel(log.DebugLevel)
}

View file

@ -1,9 +1,8 @@
package model
import (
"log"
"github.com/pushbits/server/internal/authentication/credentials"
"github.com/pushbits/server/internal/log"
)
// User holds information like the name, the secret, and the applications of a user.
@ -37,7 +36,7 @@ type CreateUser struct {
// NewUser creates a new user.
func NewUser(cm *credentials.Manager, name, password string, isAdmin bool, matrixID string) (*User, error) {
log.Printf("Creating user %s.", name)
log.L.Printf("Creating user %s.", name)
passwordHash, err := cm.CreatePasswordHash(password)
if err != nil {

View file

@ -1,7 +1,8 @@
package router
import (
"log"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
"github.com/pushbits/server/internal/api"
"github.com/pushbits/server/internal/api/alertmanager"
@ -10,14 +11,12 @@ import (
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/database"
"github.com/pushbits/server/internal/dispatcher"
"github.com/gin-contrib/location"
"github.com/gin-gonic/gin"
"github.com/pushbits/server/internal/log"
)
// Create a Gin engine and setup all routes.
func Create(debug bool, cm *credentials.Manager, db *database.Database, dp *dispatcher.Dispatcher, alertmanagerConfig *configuration.Alertmanager) *gin.Engine {
log.Println("Setting up HTTP routes.")
log.L.Println("Setting up HTTP routes.")
if !debug {
gin.SetMode(gin.ReleaseMode)
@ -34,7 +33,8 @@ func Create(debug bool, cm *credentials.Manager, db *database.Database, dp *disp
MessageAnnotation: alertmanagerConfig.AnnotationMessage,
}}
r := gin.Default()
r := gin.New()
r.Use(log.GinLogger(log.L), gin.Recovery())
r.Use(location.Default())

View file

@ -3,17 +3,17 @@ package mockups
import (
"errors"
"io/ioutil"
"log"
"os"
"github.com/pushbits/server/internal/configuration"
"github.com/pushbits/server/internal/log"
)
// ReadConfig copies the given filename to the current folder and parses it as a config file. RemoveFile indicates whether to remove the copied file or not
func ReadConfig(filename string, removeFile bool) (config *configuration.Configuration, err error) {
defer func() {
if r := recover(); r != nil {
log.Println(r)
log.L.Println(r)
err = errors.New("paniced while reading config")
}
}()