Skip to content

Commit

Permalink
upload project
Browse files Browse the repository at this point in the history
  • Loading branch information
kr-ilya committed Oct 10, 2023
1 parent 41d4984 commit 84d2509
Show file tree
Hide file tree
Showing 197 changed files with 14,365 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
pc
data
32 changes: 32 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Start from golang base image
FROM golang:alpine as builder

# Install git. Git is required for fetching the dependencies.
RUN apk update && apk add --no-cache git

# Set the current working directory inside the container
WORKDIR /app

# Copy go mod and sum files
COPY go.mod go.sum ./

# Download all dependencies. Dependencies will be cached if the go.mod and the go.sum files are not changed
RUN go mod download

COPY . .

# Build the Go app
RUN go build ./cmd/main.go

# Start a new stage from scratch
FROM alpine:latest
RUN apk --no-cache add ca-certificates

WORKDIR /opt/gofiber_server

# Copy the Pre-built binary file from the previous stage + .env file
COPY --from=builder /app/main .


#Command to run the executable
CMD ["./main"]
49 changes: 49 additions & 0 deletions backend/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
a "currency-telegram-webapp-backend/internal/app"
"currency-telegram-webapp-backend/internal/config"
"currency-telegram-webapp-backend/pkg/logger"
"fmt"
"os"
"os/signal"
"syscall"
)

func main() {
c, err := config.GetConfig()
if err != nil {
fmt.Printf("Get config: %v\n", err)
return
}

log, err := logger.NewLogger(logger.Config{
Type: c.LoggerType,
})
if err != nil {
fmt.Printf("Create logger: %v\n", err)
return
}

app := a.CreateApp(c, log)

done := make(chan struct{}, 1)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

go func() {
<-sigs
err = app.Shutdown()
if err != nil {
log.Fatalw("Shutdown", "error", err)
}

done <- struct{}{}
}()

app.Run()

fmt.Println("Started")

<-done
}
28 changes: 28 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module currency-telegram-webapp-backend

go 1.20

require (
github.com/goccy/go-json v0.10.2
github.com/gofiber/fiber/v2 v2.49.2
github.com/redis/go-redis/v9 v9.2.1
github.com/sethvargo/go-envconfig v0.9.0
github.com/valyala/fasthttp v1.50.0
go.uber.org/zap v1.26.0
)

require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/sys v0.12.0 // indirect
)
49 changes: 49 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofiber/fiber/v2 v2.49.2 h1:ONEN3/Vc+dUCxxDgZZwpqvhISgHqb+bu+isBiEyKEQs=
github.com/gofiber/fiber/v2 v2.49.2/go.mod h1:gNsKnyrmfEWFpJxQAV0qvW6l70K1dZGno12oLtukcts=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE=
github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
124 changes: 124 additions & 0 deletions backend/internal/app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package app

import (
"context"
"errors"

"github.com/goccy/go-json"
"github.com/valyala/fasthttp"
"go.uber.org/zap"

"fmt"

resp "currency-telegram-webapp-backend/pkg/api_response"

"currency-telegram-webapp-backend/internal/client"
"currency-telegram-webapp-backend/internal/config"
"currency-telegram-webapp-backend/internal/provider"
"currency-telegram-webapp-backend/internal/store"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/utils"
)

type App struct {
server *fiber.App
conf *config.ServiceConfig
log *zap.SugaredLogger
store store.Store
baseCurrency provider.Currency
p provider.Provider
api *client.Caller
}

func CreateApp(c *config.ServiceConfig, logger *zap.SugaredLogger) *App {
clientApi := &client.Caller{
Client: &fasthttp.Client{},
}

app := &App{
conf: c,
log: logger,
server: fiber.New(fiber.Config{
AppName: "backend currency telegram webapp",
DisableStartupMessage: false,
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
ErrorHandler: errorHandler(logger),
EnableSplittingOnParsers: true,
}),
store: store.NewRedisStore(&c.Redis),
baseCurrency: "",
p: provider.NewFixedProvider(clientApi, c.FixerAccessToken),
}

// auth middleware
app.server.Use(app.AuthMw)

app.server.Get("/api/rates", app.getRate)

// panic recover
app.server.Use(recover.New())

// custom 404 handler
app.server.Use(notFoundHandler)

if err := app.initBaseCurrency(); err != nil {
app.log.Fatalw("Init base currency", "error", err)
}

return app
}

func (app *App) Run() {
go func() {
if err := app.server.Listen(app.conf.ListenAddress); err != nil {
app.log.Fatalw("Start server", "error", err)
}
}()
}

func (app *App) Shutdown() error {
return app.server.ShutdownWithTimeout(config.ShutdownTimeout)
}

func errorHandler(log *zap.SugaredLogger) func(ctx *fiber.Ctx, err error) error {
return func(ctx *fiber.Ctx, err error) error {
// Status code defaults to 500
code := fiber.StatusInternalServerError
errData := utils.StatusMessage(code)

// Retrieve the custom status code if it's a *fiber.Error
var fiberErr *fiber.Error
if errors.As(err, &fiberErr) {
code = fiberErr.Code
errData = fiberErr.Message
}

log.Errorf("API panic recovered: %v", err)

return ctx.Status(code).JSON(resp.New(false, errData))
}
}

func (app *App) initBaseCurrency() error {
base, err := app.store.GetBase()
if err != nil {
if !errors.Is(err, store.ErrNotFound) {
return fmt.Errorf("failed get base currency %w", err)
}

data, err := app.p.Latest(context.Background())
if err != nil {
return err
}

if err := app.store.SetRates(data.Base, data.Timestamp, &data.Rates); err != nil {
return err
}
}

app.baseCurrency = base
return nil
}
65 changes: 65 additions & 0 deletions backend/internal/app/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package app

import (
resp "currency-telegram-webapp-backend/pkg/api_response"
"encoding/hex"
"io"
"net/url"
"sort"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"

"crypto/hmac"
"crypto/sha256"
)

// Auth middleware
func (a *App) AuthMw(ctx *fiber.Ctx) error {
q := ctx.Query("auth")
if q == "" {
return ctx.Status(fiber.StatusUnauthorized).JSON(resp.New(false, utils.StatusMessage(fiber.StatusUnauthorized)))
}

params, err := url.ParseQuery(q)
if err != nil {
a.log.Errorw("failed ParseQuery auth", "error", err)
return ctx.Status(fiber.StatusInternalServerError).JSON(resp.New(false, utils.StatusMessage(fiber.StatusInternalServerError)))
}

data := make([]string, 0, len(params)-1)
for k, v := range params {
if k != "hash" {
data = append(data, k+"="+v[0])
}
}

sort.Strings(data)

res := ""
for _, v := range data {
if res != "" {
res += "\n"
}

res += v
}

secretKey := hmac.New(sha256.New, []byte("WebAppData"))
if _, err = io.WriteString(secretKey, a.conf.BotToken); err != nil {
a.log.Errorw("failed create hmac", "error", err)
return ctx.Status(fiber.StatusInternalServerError).JSON(resp.New(false, utils.StatusMessage(fiber.StatusInternalServerError)))
}

hash := hmac.New(sha256.New, secretKey.Sum(nil))
if _, err = io.WriteString(hash, res); err != nil {
a.log.Errorw("failed create hmac", "error", err)
return ctx.Status(fiber.StatusInternalServerError).JSON(resp.New(false, utils.StatusMessage(fiber.StatusInternalServerError)))
}

if hex.EncodeToString(hash.Sum(nil)) != params.Get("hash") {
return ctx.Status(fiber.StatusUnauthorized).JSON(resp.New(false, utils.StatusMessage(fiber.StatusUnauthorized)))
}

return ctx.Next()
}

0 comments on commit 84d2509

Please sign in to comment.