basic structure

This commit is contained in:
kolaente 2018-12-06 15:22:00 +01:00
parent 52fcb8f157
commit c1d1a59a61
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
12 changed files with 918 additions and 4 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
.idea/
server
sofaraum-server
sofaraum-server
config.yml
config.yaml

26
go.mod
View File

@ -1 +1,25 @@
module git.kolaente.de/sofaraum/server
module code.sofaraum.de/server
require (
code.vikunja.io/web v0.0.0-20181130231148-b061c20192fb
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/garyburd/redigo v1.6.0 // indirect
github.com/go-openapi/jsonreference v0.17.2 // indirect
github.com/go-openapi/spec v0.17.2 // indirect
github.com/go-sql-driver/mysql v1.4.1
github.com/go-xorm/core v0.6.0
github.com/go-xorm/xorm v0.7.1
github.com/go-xorm/xorm-redis-cache v0.0.0-20180727005610-859b313566b2
github.com/labstack/echo v3.3.5+incompatible
github.com/mattn/go-sqlite3 v1.10.0
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/spf13/viper v1.3.0
github.com/swaggo/echo-swagger v0.0.0-20180315045949-97f46bb9e5a5
github.com/swaggo/files v0.0.0-20180215091130-49c8a91ea3fa // indirect
github.com/swaggo/swag v1.4.0
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc // indirect
golang.org/x/tools v0.0.0-20181205224935-3576414c54a4 // indirect
)

62
main.go
View File

@ -17,8 +17,66 @@
package main
import "fmt"
import (
"code.sofaraum.de/server/docs"
"code.sofaraum.de/server/pkg/config"
"code.sofaraum.de/server/pkg/log"
"code.sofaraum.de/server/pkg/models"
"code.sofaraum.de/server/pkg/routes"
"context"
"github.com/spf13/viper"
"os"
"os/signal"
"time"
)
// Version sets the version to be printed to the user. Gets overwritten by "make release" or "make build" with last git commit or tag.
var Version = "0.1"
func main() {
fmt.Println("schinken")
// Init logging
log.InitLogger()
// Init Config
err := config.InitConfig()
if err != nil {
log.Log.Error(err.Error())
os.Exit(1)
}
// Set Engine
err = models.SetEngine()
if err != nil {
log.Log.Error(err.Error())
os.Exit(1)
}
// Version notification
log.Log.Infof("Sofaraum version %s", Version)
// Additional swagger information
docs.SwaggerInfo.Version = Version
// Start the webserver
e := routes.NewEcho()
routes.RegisterRoutes(e)
// Start server
go func() {
if err := e.Start(viper.GetString("service.interface")); err != nil {
e.Logger.Info("shutting down...")
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 10 seconds.
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
log.Log.Infof("Shutting down...")
if err := e.Shutdown(ctx); err != nil {
e.Logger.Fatal(err)
}
}

90
pkg/config/config.go Normal file
View File

@ -0,0 +1,90 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package config
import (
"crypto/rand"
"fmt"
"github.com/spf13/viper"
"os"
"path/filepath"
"strings"
)
// InitConfig initializes the config, sets defaults etc.
func InitConfig() (err error) {
// Set defaults
// Service config
random, err := random(32)
if err != nil {
return err
}
// Service
viper.SetDefault("service.JWTSecret", random)
viper.SetDefault("service.interface", ":1073")
viper.SetDefault("service.frontendurl", "")
ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath := filepath.Dir(ex)
viper.SetDefault("service.rootpath", exPath)
viper.SetDefault("service.pagecount", 50)
// Database
viper.SetDefault("database.type", "sqlite")
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.user", "sofaraum")
viper.SetDefault("database.password", "")
viper.SetDefault("database.database", "sofaraum")
viper.SetDefault("database.path", "./sofaraum.db")
viper.SetDefault("database.showqueries", false)
viper.SetDefault("database.openconnections", 100)
// Cacher
viper.SetDefault("cache.enabled", false)
viper.SetDefault("cache.type", "memory")
viper.SetDefault("cache.maxelementsize", 1000)
viper.SetDefault("cache.redishost", "localhost:6379")
viper.SetDefault("cache.redispassword", "")
// Init checking for environment variables
viper.SetEnvPrefix("sofaraum")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
// Load the config file
viper.AddConfigPath(".")
viper.SetConfigName("config")
err = viper.ReadInConfig()
if err != nil {
fmt.Println(err)
fmt.Println("Using defaults.")
}
return nil
}
func random(length int) (string, error) {
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
return "", err
}
return fmt.Sprintf("%X", b), nil
}

37
pkg/log/logging.go Normal file
View File

@ -0,0 +1,37 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package log
import (
"github.com/op/go-logging"
"os"
)
// Log is the handler for the logger
var Log = logging.MustGetLogger("sofaraum-server")
var format = logging.MustStringFormatter(
`%{color}%{time:2006-01-02 15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
)
// InitLogger initializes the global log handler
func InitLogger() {
backend := logging.NewLogBackend(os.Stderr, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, format)
logging.SetBackend(backendFormatter)
}

152
pkg/models/error.go Normal file
View File

@ -0,0 +1,152 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.vikunja.io/web"
"fmt"
"net/http"
)
// ErrInvalidData represents a "ErrInvalidData" kind of error. Used when a struct is invalid -> validation failed.
type ErrInvalidData struct {
Message string
}
// IsErrInvalidData checks if an error is a ErrIDCannotBeZero.
func IsErrInvalidData(err error) bool {
_, ok := err.(ErrInvalidData)
return ok
}
func (err ErrInvalidData) Error() string {
return fmt.Sprintf("Struct is invalid. %s", err.Message)
}
// ErrCodeInvalidData holds the unique world-error code of this error
const ErrCodeInvalidData = 1000
// HTTPError holds the http error description
func (err ErrInvalidData) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidData, Message: err.Message}
}
// ValidationHTTPError is the http error when a validation fails
type ValidationHTTPError struct {
web.HTTPError
InvalidFields []string `json:"invalid_fields"`
}
// Error implements the Error type (so we can return it as type error)
func (err ValidationHTTPError) Error() string {
theErr := ErrInvalidData{
Message: err.Message,
}
return theErr.Error()
}
// =============
// User Errors
// =============
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
type ErrUserDoesNotExist struct {
UserID int64
}
// IsErrUserDoesNotExist checks if an error is a ErrUserDoesNotExist.
func IsErrUserDoesNotExist(err error) bool {
_, ok := err.(ErrUserDoesNotExist)
return ok
}
func (err ErrUserDoesNotExist) Error() string {
return fmt.Sprintf("User does not exist [user id: %d]", err.UserID)
}
// ErrCodeUserDoesNotExist holds the unique world-error code of this error
const ErrCodeUserDoesNotExist = 2000
// HTTPError holds the http error description
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
}
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
type ErrNoUsernamePassword struct{}
// IsErrNoUsernamePassword checks if an error is a ErrNoUsernamePassword.
func IsErrNoUsernamePassword(err error) bool {
_, ok := err.(ErrNoUsernamePassword)
return ok
}
func (err ErrNoUsernamePassword) Error() string {
return fmt.Sprintf("No username and password provided")
}
// ErrCodeNoUsernamePassword holds the unique world-error code of this error
const ErrCodeNoUsernamePassword = 2001
// HTTPError holds the http error description
func (err ErrNoUsernamePassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
}
// ErrWrongUsernameOrPassword is an error where the email was not confirmed
type ErrWrongUsernameOrPassword struct {
}
func (err ErrWrongUsernameOrPassword) Error() string {
return fmt.Sprintf("Wrong username or password")
}
// ErrCodeWrongUsernameOrPassword holds the unique world-error code of this error
const ErrCodeWrongUsernameOrPassword = 2002
// HTTPError holds the http error description
func (err ErrWrongUsernameOrPassword) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
}
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
func IsErrWrongUsernameOrPassword(err error) bool {
_, ok := err.(ErrWrongUsernameOrPassword)
return ok
}
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
type ErrCouldNotGetUserID struct{}
// IsErrCouldNotGetUserID checks if an error is a ErrCouldNotGetUserID.
func IsErrCouldNotGetUserID(err error) bool {
_, ok := err.(ErrCouldNotGetUserID)
return ok
}
func (err ErrCouldNotGetUserID) Error() string {
return fmt.Sprintf("Could not get user ID")
}
// ErrCodeCouldNotGetUserID holds the unique world-error code of this error
const ErrCodeCouldNotGetUserID = 2003
// HTTPError holds the http error description
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
}

110
pkg/models/models.go Normal file
View File

@ -0,0 +1,110 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"fmt"
_ "github.com/go-sql-driver/mysql" // Because.
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
xrc "github.com/go-xorm/xorm-redis-cache"
_ "github.com/mattn/go-sqlite3" // Because.
"encoding/gob"
"github.com/spf13/viper"
)
var (
x *xorm.Engine
tables []interface{}
)
func getEngine() (*xorm.Engine, error) {
// Use Mysql if set
if viper.GetString("database.type") == "mysql" {
connStr := fmt.Sprintf(
"%s:%s@tcp(%s)/%s?charset=utf8&parseTime=true",
viper.GetString("database.user"),
viper.GetString("database.password"),
viper.GetString("database.host"),
viper.GetString("database.database"))
e, err := xorm.NewEngine("mysql", connStr)
e.SetMaxOpenConns(viper.GetInt("database.openconnections"))
return e, err
}
// Otherwise use sqlite
path := viper.GetString("database.path")
if path == "" {
path = "./db.db"
}
return xorm.NewEngine("sqlite3", path)
}
func init() {
tables = append(tables)
}
// SetEngine sets the xorm.Engine
func SetEngine() (err error) {
x, err = getEngine()
if err != nil {
return fmt.Errorf("Failed to connect to database: %v", err)
}
// Cache
if viper.GetBool("cache.enabled") {
switch viper.GetString("cache.type") {
case "memory":
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), viper.GetInt("cache.maxelementsize"))
x.SetDefaultCacher(cacher)
break
case "redis":
cacher := xrc.NewRedisCacher(viper.GetString("cache.redishost"), viper.GetString("cache.redispassword"), xrc.DEFAULT_EXPIRATION, x.Logger())
x.SetDefaultCacher(cacher)
gob.Register(tables)
break
default:
fmt.Println("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
}
}
x.SetMapper(core.GonicMapper{})
// Sync dat shit
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
return fmt.Errorf("sync database struct error: %v", err)
}
x.ShowSQL(viper.GetBool("database.showqueries"))
return nil
}
func getLimitFromPageIndex(page int) (limit, start int) {
// Get everything when page index is -1
if page < 0 {
return 0, 0
}
limit = viper.GetInt("service.pagecount")
start = limit * (page - 1)
return
}

157
pkg/models/user.go Normal file
View File

@ -0,0 +1,157 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import (
"code.sofaraum.de/server/pkg/log"
"code.vikunja.io/web"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"golang.org/x/crypto/bcrypt"
"reflect"
)
// UserLogin Object to recive user credentials in JSON format
type UserLogin struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
}
// User holds information about an user
type User struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
Username string `xorm:"varchar(250) not null unique" json:"username" valid:"length(3|250)"`
Password string `xorm:"varchar(250) not null" json:"-"`
Email string `xorm:"varchar(250)" json:"email" valid:"email,length(0|250)"`
IsActive bool `json:"-"`
PasswordResetToken string `xorm:"varchar(450)" json:"-"`
EmailConfirmToken string `xorm:"varchar(450)" json:"-"`
Created int64 `xorm:"created" json:"created"`
Updated int64 `xorm:"updated" json:"updated"`
web.Auth `xorm:"-" json:"-"`
}
// AuthDummy implements the auth of the crud handler
func (User) AuthDummy() {}
// TableName returns the table name for users
func (User) TableName() string {
return "users"
}
func getUserForRights(a web.Auth) *User {
u, err := getUserWithError(a)
if err != nil {
log.Log.Error(err.Error())
}
return u
}
func getUserWithError(a web.Auth) (*User, error) {
u, is := a.(*User)
if !is {
return &User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(a))
}
return u, nil
}
// APIUserPassword represents a user object without timestamps and a json password field.
type APIUserPassword struct {
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}
// APIFormat formats an API User into a normal user struct
func (apiUser *APIUserPassword) APIFormat() User {
return User{
ID: apiUser.ID,
Username: apiUser.Username,
Password: apiUser.Password,
Email: apiUser.Email,
}
}
// GetUserByID gets informations about a user by its ID
func GetUserByID(id int64) (user *User, err error) {
// Apparently xorm does otherwise look for all users but return only one, which leads to returing one even if the ID is 0
if id < 1 {
return &User{}, ErrUserDoesNotExist{}
}
return GetUser(&User{ID: id})
}
// GetUser gets a user object
func GetUser(user *User) (userOut *User, err error) {
userOut = user
exists, err := x.Get(&userOut)
if !exists {
return &User{}, ErrUserDoesNotExist{UserID: user.ID}
}
return userOut, err
}
// CheckUserCredentials checks user credentials
func CheckUserCredentials(u *UserLogin) (*User, error) {
// Check if we have any credentials
if u.Password == "" || u.Username == "" {
return &User{}, ErrNoUsernamePassword{}
}
// Check if the user exists
user, err := GetUser(&User{Username: u.Username})
if err != nil {
return &User{}, err
}
// Check the users password
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(u.Password))
if err != nil {
if err == bcrypt.ErrMismatchedHashAndPassword {
return &User{}, ErrWrongUsernameOrPassword{}
}
return &User{}, err
}
return user, nil
}
// GetCurrentUser returns the current user based on its jwt token
func GetCurrentUser(c echo.Context) (user *User, err error) {
jwtinf := c.Get("user").(*jwt.Token)
claims := jwtinf.Claims.(jwt.MapClaims)
userID, ok := claims["id"].(float64)
if !ok {
return user, ErrCouldNotGetUserID{}
}
user = &User{
ID: int64(userID),
Email: claims["email"].(string),
Username: claims["username"].(string),
}
return
}

View File

@ -0,0 +1,80 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"code.sofaraum.de/server/pkg/models"
"code.vikunja.io/web/handler"
"crypto/md5"
"encoding/hex"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"github.com/spf13/viper"
"net/http"
"time"
)
// Token represents an authentification token
type Token struct {
Token string `json:"token"`
}
// Login is the login handler
// @Summary Login
// @Description Logs a user in. Returns a JWT-Token to authenticate further requests.
// @tags user
// @Accept json
// @Produce json
// @Param credentials body models.UserLogin true "The login credentials"
// @Success 200 {object} v1.Token
// @Failure 400 {object} models.Message "Invalid user password model."
// @Failure 403 {object} models.Message "Invalid username or password."
// @Router /login [post]
func Login(c echo.Context) error {
u := models.UserLogin{}
if err := c.Bind(&u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Please provide a username and password.")
}
// Check user
user, err := models.CheckUserCredentials(&u)
if err != nil {
return handler.HandleHTTPError(err, c)
}
// Create token
token := jwt.New(jwt.SigningMethodHS256)
// Set claims
claims := token.Claims.(jwt.MapClaims)
claims["username"] = user.Username
claims["email"] = user.Email
claims["id"] = user.ID
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
avatar := md5.Sum([]byte(user.Email))
claims["avatar"] = hex.EncodeToString(avatar[:])
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte(viper.GetString("service.JWTSecret")))
if err != nil {
return err
}
return c.JSON(http.StatusOK, Token{Token: t})
}

View File

@ -0,0 +1,34 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
)
// CheckToken checks prints a message if the token is valid or not. Currently only used for testing pourposes.
func CheckToken(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
fmt.Println(user.Valid)
return c.String(418, "🍵")
}

View File

@ -0,0 +1,50 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package v1
import (
"code.sofaraum.de/server/pkg/models"
"code.vikunja.io/web/handler"
"github.com/labstack/echo"
"net/http"
)
// UserShow gets all informations about the current user
// @Summary Get user information
// @Description Returns the current user object.
// @tags user
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} models.User
// @Failure 404 {object} code.vikunja.io/web.HTTPError "User does not exist."
// @Failure 500 {object} models.Message "Internal server error."
// @Router /user [get]
func UserShow(c echo.Context) error {
userInfos, err := models.GetCurrentUser(c)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
}
user, err := models.GetUserByID(userInfos.ID)
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, user)
}

120
pkg/routes/routes.go Normal file
View File

@ -0,0 +1,120 @@
// Sofaraum server is the server which collects the statistics
// for the sofaraum-heatmap application.
// Copyright 2018 K.Langenberg and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// @title Sofaraum Server API
// @license.name GPLv3
// @BasePath /api/v1
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
package routes
import (
_ "code.sofaraum.de/server/docs" // To generate swagger docs
"code.sofaraum.de/server/pkg/log"
"code.sofaraum.de/server/pkg/models"
apiv1 "code.sofaraum.de/server/pkg/routes/api/v1"
"code.vikunja.io/web"
"github.com/asaskevich/govalidator"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/spf13/viper"
"github.com/swaggo/echo-swagger"
)
// CustomValidator is a dummy struct to use govalidator with echo
type CustomValidator struct{}
// Validate validates stuff
func (cv *CustomValidator) Validate(i interface{}) error {
if _, err := govalidator.ValidateStruct(i); err != nil {
var errs []string
for field, e := range govalidator.ErrorsByField(err) {
errs = append(errs, field+": "+e)
}
httperr := models.ValidationHTTPError{
web.HTTPError{
Code: models.ErrCodeInvalidData,
Message: "Invalid Data",
},
errs,
}
return httperr
}
return nil
}
// NewEcho registers a new Echo instance
func NewEcho() *echo.Echo {
e := echo.New()
e.HideBanner = true
// Logger
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "${time_rfc3339_nano}: ${remote_ip} ${method} ${status} ${uri} ${latency_human} - ${user_agent}\n",
}))
// Validation
e.Validator = &CustomValidator{}
return e
}
// RegisterRoutes registers all routes for the application
func RegisterRoutes(e *echo.Echo) {
// CORS_SHIT
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
}))
// API Routes
a := e.Group("/api/v1")
// Swagger UI
a.GET("/swagger/*", echoSwagger.WrapHandler)
a.POST("/login", apiv1.Login)
// ===== Routes with Authetification =====
// Authetification
a.Use(middleware.JWT([]byte(viper.GetString("service.JWTSecret"))))
// Put the authprovider in the context to be able to use it later
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("AuthProvider", &web.Auths{
AuthObject: func(echo.Context) (web.Auth, error) {
return models.GetCurrentUser(c)
},
})
c.Set("LoggingProvider", log.Log)
return next(c)
}
})
a.POST("/tokenTest", apiv1.CheckToken)
// User stuff
a.GET("/user", apiv1.UserShow)
}