mirror of
https://github.com/mainnika/a-quest.git
synced 2026-05-24 00:33:36 +00:00
task1 backend
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
"github.com/google/uuid"
|
||||
routing "github.com/jackwhelpton/fasthttp-routing/v2"
|
||||
"github.com/jackwhelpton/fasthttp-routing/v2/access"
|
||||
"github.com/jackwhelpton/fasthttp-routing/v2/cors"
|
||||
"github.com/jackwhelpton/fasthttp-routing/v2/fault"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
URL = "/check"
|
||||
URLHealthz = "/healthz"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
Base string
|
||||
Alg string
|
||||
Pub *ecdsa.PublicKey
|
||||
Priv *ecdsa.PrivateKey
|
||||
Answer []byte
|
||||
}
|
||||
|
||||
type Answer struct {
|
||||
Answer json.RawMessage `json:"answer"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (a *Api) GetHandler() fasthttp.RequestHandler {
|
||||
|
||||
crs := cors.Options{
|
||||
AllowOrigins: "*",
|
||||
AllowHeaders: "*",
|
||||
AllowMethods: "*",
|
||||
AllowCredentials: true,
|
||||
}
|
||||
|
||||
router := routing.New()
|
||||
router.Use(
|
||||
access.Logger(log.Debugf),
|
||||
cors.Handler(crs),
|
||||
fault.PanicHandler(log.Warnf),
|
||||
)
|
||||
|
||||
base := strings.TrimSuffix(a.Base, "/")
|
||||
api := router.Group(base)
|
||||
|
||||
api.Post(URL, a.checkAnswer)
|
||||
api.Get(URLHealthz, a.healthCheck)
|
||||
|
||||
return router.HandleRequest
|
||||
}
|
||||
|
||||
func (a *Api) healthCheck(ctx *routing.Context) (err error) {
|
||||
ctx.Response.Header.Set("Content-Type", "application/json")
|
||||
_, err = ctx.WriteString("{\"health\":\"ok\"}")
|
||||
if err != nil {
|
||||
return routing.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *Api) checkAnswer(ctx *routing.Context) (err error) {
|
||||
|
||||
now := time.Now()
|
||||
id := uuid.New()
|
||||
answer := &Answer{}
|
||||
|
||||
err = json.Unmarshal(ctx.Request.Body(), answer)
|
||||
if err != nil {
|
||||
return routing.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write(answer.Answer)
|
||||
|
||||
hexed := make([]byte, hex.EncodedLen(sha256.Size))
|
||||
|
||||
hex.Encode(hexed, hash.Sum(nil))
|
||||
|
||||
log.Debugf("req answer, %v", answer)
|
||||
log.Debugf("req hashed, %s", hexed)
|
||||
|
||||
valid := subtle.ConstantTimeCompare(a.Answer, hexed)
|
||||
if valid == 0 {
|
||||
return routing.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
|
||||
claims := &jwtgo.StandardClaims{
|
||||
Id: id.String(),
|
||||
Subject: answer.Name,
|
||||
Issuer: "Anonymous Moroz Grandfather",
|
||||
IssuedAt: now.Unix(),
|
||||
}
|
||||
|
||||
method := jwtgo.GetSigningMethod(a.Alg)
|
||||
token := jwtgo.NewWithClaims(method, claims)
|
||||
|
||||
jwt, err := token.SignedString(a.Priv)
|
||||
if err != nil {
|
||||
return routing.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
ctx.Response.Header.Set("Content-Type", "application/json")
|
||||
_, err = ctx.WriteString(fmt.Sprintf(`
|
||||
['переход на следующий уровень ищи в альтер имени безопасности узла',
|
||||
'используй ключ чтобы открыть следующий уровень',
|
||||
'%s']
|
||||
`, jwt))
|
||||
if err != nil {
|
||||
return routing.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *Api) getPrivKey(t *jwtgo.Token) (interface{}, error) {
|
||||
return a.Priv, nil
|
||||
}
|
||||
|
||||
func (a *Api) getKey(t *jwtgo.Token) (interface{}, error) {
|
||||
return a.Pub, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package lib
|
||||
|
||||
var (
|
||||
ConfPath = "config"
|
||||
ConfName = "task1"
|
||||
)
|
||||
|
||||
type AppConfig struct {
|
||||
HttpAPI struct {
|
||||
Base string `mapstructure:"base"`
|
||||
Addr string `mapstructure:"addr"`
|
||||
} `mapstructure:"httpApi"`
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package configure
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/mainnika/a-quest/task1-backend/lib"
|
||||
"github.com/mainnika/a-quest/task1-backend/lib/env"
|
||||
)
|
||||
|
||||
var Config lib.AppConfig
|
||||
|
||||
func init() {
|
||||
|
||||
path := env.ConfigPath
|
||||
name := env.ConfigName
|
||||
|
||||
if len(path) == 0 {
|
||||
path = lib.ConfPath
|
||||
}
|
||||
|
||||
if len(name) == 0 {
|
||||
name = lib.ConfName
|
||||
}
|
||||
|
||||
viper.AddConfigPath(path)
|
||||
viper.SetConfigName(name)
|
||||
viper.SetEnvPrefix(env.Prefix)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
err, _ := viper.ReadInConfig(), viper.Unmarshal(&Config)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
Prefix = "CFG"
|
||||
IsDevelopment = len(os.Getenv("DEBUG")) > 0
|
||||
ConfigPath = os.Getenv(fmt.Sprintf("%s_PATH", Prefix))
|
||||
ConfigName = os.Getenv(fmt.Sprintf("%s_NAME", Prefix))
|
||||
)
|
||||
Reference in New Issue
Block a user