Апи начало

This commit is contained in:
Anatoly Prohacky 2023-04-17 12:00:34 +10:00
parent e6a62b4f2b
commit 2d08002194
15 changed files with 694 additions and 0 deletions

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
build:
go build -v ./cmd/apisirius
run:
go run ./cmd/apisirius
.DEFAULT_GOAL := build

28
cmd/apisirius/main.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/zhashkevych/s3-file-uploader/pkg/config"
"github.com/zhashkevych/s3-file-uploader/pkg/server"
)
func main() {
log.SetFormatter(&log.JSONFormatter{})
log.SetOutput(os.Stdout)
log.SetLevel(log.WarnLevel)
if err := config.Init(); err != nil {
log.Fatalf("%s", err.Error())
}
accessKey := os.Getenv("ACCESS_KEY")
secretKey := os.Getenv("SECRET_KEY")
app := server.NewApp(accessKey, secretKey)
if err := app.Run(viper.GetString("port")); err != nil {
log.Fatalf("%s", err.Error())
}
}

5
config/config.yml Normal file
View File

@ -0,0 +1,5 @@
port: 8000
storage:
endpoint: "localstack:4572"
bucket: "file-storage"

5
go.sum
View File

@ -1,7 +1,9 @@
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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
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/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
@ -11,6 +13,7 @@ github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
@ -23,6 +26,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@ -59,4 +63,5 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=

View File

@ -0,0 +1,78 @@
package http
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/zhashkevych/s3-file-uploader/pkg/uploader"
)
const (
MAX_UPLOAD_SIZE = 5 << 20 // 5 megabytes
)
var (
IMAGE_TYPES = map[string]interface{}{
"image/jpeg": nil,
"image/png": nil,
}
)
type Handler struct {
uploader uploader.Uploader
}
func NewHandler(uploader uploader.Uploader) *Handler {
return &Handler{
uploader: uploader,
}
}
type uploadResponse struct {
Status string `json:"status"`
Msg string `json:"message,omitempty"`
URL string `json:"url,omitempty"`
}
func (h *Handler) Upload(c *gin.Context) {
// Limit Upload File Size
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, MAX_UPLOAD_SIZE)
file, fileHeader, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, &uploadResponse{
Status: "error",
Msg: err.Error(),
})
return
}
defer file.Close()
buffer := make([]byte, fileHeader.Size)
file.Read(buffer)
fileType := http.DetectContentType(buffer)
// Validate File Type
if _, ex := IMAGE_TYPES[fileType]; !ex {
c.JSON(http.StatusBadRequest, &uploadResponse{
Status: "error",
Msg: "file type is not supported",
})
return
}
url, err := h.uploader.Upload(c.Request.Context(), file, fileHeader.Size, fileType)
if err != nil {
c.JSON(http.StatusBadRequest, &uploadResponse{
Status: "error",
Msg: err.Error(),
})
return
}
c.JSON(http.StatusOK, &uploadResponse{
Status: "ok",
URL: url,
})
}

View File

@ -0,0 +1,12 @@
package http
import (
"github.com/gin-gonic/gin"
"github.com/zhashkevych/s3-file-uploader/pkg/uploader"
)
func RegisterHTTPEndpoints(router *gin.RouterGroup, uploader uploader.Uploader) {
h := NewHandler(uploader)
router.POST("/upload", h.Upload)
}

View File

@ -0,0 +1,10 @@
package controller
import (
"context"
"io"
)
type Uploader interface {
Upload(ctx context.Context, file io.Reader, size int64, contentType string) (string, error)
}

View File

@ -0,0 +1,41 @@
package upload
import (
"context"
"io"
"math/rand"
"github.com/zhashkevych/s3-file-uploader/sidecar/filestorage"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
fileNameLength = 16
)
type Uploader struct {
fs *filestorage.FileStorage
}
func NewUploader(fs *filestorage.FileStorage) *Uploader {
return &Uploader{
fs: fs,
}
}
func (u *Uploader) Upload(ctx context.Context, file io.Reader, size int64, contentType string) (string, error) {
return u.fs.Upload(ctx, filestorage.UploadInput{
File: file,
Name: generateFileName(),
Size: size,
ContentType: contentType,
})
}
func generateFileName() string {
b := make([]byte, fileNameLength)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}

12
internal/model/config.go Normal file
View File

@ -0,0 +1,12 @@
package config
import (
"github.com/spf13/viper"
)
func Init() error {
viper.AddConfigPath("./config")
viper.SetConfigName("config")
return viper.ReadInConfig()
}

131
internal/model/model.go Normal file
View File

@ -0,0 +1,131 @@
package model
import "fmt"
type Bolid struct {
Devices []DevBolid
Zones []ZoneBolid
}
type DevBolid struct {
Name string
Type int64
Addr int64
Input []InputBolid
}
type InputBolid struct {
ZoneId int64
Name string
Addr int64
}
type ZoneBolid struct {
ZoneId int64
Name string
Number int64
}
type ZoneInfo struct {
ZoneNum int64
ZoneName string
Input []InputInfo
}
type InputInfo struct {
DevAdd int64
Address int64
}
func (z *ZoneInfo) InputString() string {
var zones string
i := 0
for _, inp := range z.Input {
if i != 0 {
zones = zones + ", "
}
zones = zones + fmt.Sprint(inp.DevAdd) + "." + fmt.Sprint(inp.Address)
i++
}
return zones
}
func (b *Bolid) ZoneInfo() []ZoneInfo {
var zonesInfo []ZoneInfo
for _, zon := range b.Zones {
var zi ZoneInfo
for _, dev := range b.Devices {
var i InputInfo
for _, inp := range dev.Input {
if zon.ZoneId == inp.ZoneId {
zi.ZoneNum = zon.Number
zi.ZoneName = zon.Name
i.DevAdd = dev.Addr
i.Address = inp.Addr
zi.Input = append(zi.Input, i)
}
}
}
zonesInfo = append(zonesInfo, zi)
}
return zonesInfo
}
func (s *Sirius) NewBolid() *Bolid {
return &Bolid{
Devices: s.NewDevices(),
Zones: s.NewZones(),
}
}
func (s *Sirius) NewDevices() []DevBolid {
var devices []DevBolid
for _, dev := range s.Device.Get() {
d := DevBolid{
Name: dev.Name,
Type: dev.Type,
Addr: dev.Addr,
Input: s.GetInput(dev.DevID),
}
devices = append(devices, d)
}
return devices
}
func (s *Sirius) GetInput(devId int64) []InputBolid {
var inputs []InputBolid
for _, inp := range s.Input.Get() {
if devId == inp.DevID {
i := InputBolid{
ZoneId: inp.ZoneID,
Name: inp.Name,
Addr: inp.AddrU,
}
inputs = append(inputs, i)
}
}
return inputs
}
func (s *Sirius) NewZones() []ZoneBolid {
var zones []ZoneBolid
for _, zon := range s.Zone.Get() {
z := ZoneBolid{
ZoneId: zon.ZoneID,
Name: zon.Name,
Number: zon.Number,
}
zones = append(zones, z)
}
return zones
}

257
internal/model/sirius.go Normal file
View File

@ -0,0 +1,257 @@
package model
// Sirius structure
type Sirius struct {
Device `json:"device"`
Zone `json:"zone"`
ZoneGroup `json:"zone_group"`
Input `json:"input"`
Output `json:"output"`
Reader `json:"reader"`
Channel `json:"channel"`
AccessGroup `json:"access_group"`
User `json:"user"`
Script `json:"script"`
Program `json:"program"`
Controller `json:"controller"`
}
// Device
type Device struct {
Keys []string `json:"keys"`
Rows [][]interface{} `json:"rows"`
}
func (z *Device) Get() []DeviceRows {
var zr []DeviceRows
for _, z := range z.Rows {
i, b, s, _ := InterToArray(z)
zz := DeviceRows{
DevID: i[0],
I2: i[1],
B1: b[0],
Addr: i[2],
I4: i[3],
Type: i[4],
I6: i[5],
I7: i[6],
I8: i[7],
I9: i[8],
Name: s[0],
}
zr = append(zr, zz)
}
return zr
}
// Type-40(КДЛС-С) Type-81(КДЛ-2И)
type DeviceRows struct {
DevID int64
I2 int64
B1 bool
Addr int64
I4 int64
Type int64
I6 int64
I7 int64
I8 int64
I9 int64
IntArr1 []int64
IntArr2 []int64
Name string
}
// Zone
type Zone struct {
Keys []string `json:"keys"`
Rows [][]interface{} `json:"rows"`
}
func (z *Zone) Get() []ZoneRows {
var zr []ZoneRows
for _, z := range z.Rows {
i, b, s, _ := InterToArray(z)
zz := ZoneRows{
ZoneID: i[0],
I1: i[1],
B1: b[0],
Number: i[2],
I3: i[3],
I4: i[4],
B2: b[1],
B3: b[2],
Name: s[0],
}
zr = append(zr, zz)
}
return zr
}
type ZoneRows struct {
ZoneID int64
I1 int64
B1 bool
Number int64
I3 int64
I4 int64
B2 bool
B3 bool
Name string
}
// ZoneGroup
type ZoneGroup struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// type ZoneGroupRows struct {
// Number int64
// I2 int64
// B1 bool
// I3 int64
// I4 int64
// IntArr []int64
// Name string
// }
// Input
type Input struct {
Keys []string `json:"keys"`
Rows [][]interface{} `json:"rows"`
}
func (z *Input) Get() []InputRows {
var zr []InputRows
for _, z := range z.Rows {
i, _, s, _ := InterToArray(z)
zz := InputRows{
InputID: i[0],
DevID: i[1],
AddrU: i[2],
I4: i[3],
I5: i[4],
I6: i[5],
ZoneID: i[6],
I8: i[7],
Name: s[0],
}
zr = append(zr, zz)
}
return zr
}
type InputRows struct {
InputID int64
DevID int64
AddrU int64
I4 int64
I5 int64
I6 int64
ZoneID int64
I8 int64
Name string
}
// Output
type Output struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// type OutputRows struct {
// Addr int64
// I2 int64
// I3 int64
// I4 int64
// I5 int64
// I6 int64
// I7 int64
// I8 int64
// I9 int64
// I10 int64
// I11 int64
// IntArr1 []int64
// IntArr2 []int64
// I12 int64
// I13 int64
// Name string
// }
// Reader
type Reader struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// type ReaderRows struct {
// I1 int64
// I2 int64
// I3 int64
// I4 int64
// I5 int64
// I6 int64
// IntArr1 []int64
// IntArr2 []int64
// Name string
// }
// Channel
type Channel struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// type ChannelRows struct {
// I1 int64
// I2 int64
// I3 int64
// I4 int64
// I5 int64
// Name string
// }
// AccessGroup
type AccessGroup struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// type AccessGroupRows struct {
// I1 int64
// I2 int64
// IntArr1 []AccessGroupIntArray
// Name string
// }
// type AccessGroupIntArray struct {
// I1 int64
// I2 int64
// }
// User
type User struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// Script
type Script struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// Program
type Program struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}
// Controller
type Controller struct {
Keys []string `json:"keys"`
Rows []interface{} `json:"rows"`
}

22
internal/model/utils.go Normal file
View File

@ -0,0 +1,22 @@
package model
func InterToArray(inp []interface{}) ([]int64, []bool, []string, []interface{}) {
var i []int64
var b []bool
var s []string
var a []interface{}
for _, v1 := range inp {
switch v2 := v1.(type) {
case float64:
i = append(i, int64(v2))
case bool:
b = append(b, bool(v2))
case string:
s = append(s, string(v2))
default:
a = append(a, v2)
}
}
return i, b, s, a
}

85
internal/server/app.go Normal file
View File

@ -0,0 +1,85 @@
package server
import (
"context"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go"
"github.com/spf13/viper"
"github.com/zhashkevych/s3-file-uploader/pkg/uploader"
uphttp "github.com/zhashkevych/s3-file-uploader/pkg/uploader/delivery/http"
"github.com/zhashkevych/s3-file-uploader/pkg/uploader/upload"
"github.com/zhashkevych/s3-file-uploader/sidecar/filestorage"
"log"
"net/http"
"os"
"os/signal"
"time"
)
type App struct {
httpServer *http.Server
fileStorage *filestorage.FileStorage
imageUploader uploader.Uploader
}
func NewApp(accessKey, secretKey string) *App {
// Initiate an S3 compatible client
client, err := minio.New(viper.GetString("storage.endpoint"), accessKey, secretKey, false)
if err != nil {
log.Fatal(err)
}
fileStorage := filestorage.NewFileStorage(
client,
viper.GetString("storage.bucket"),
viper.GetString("storage.endpoint"),
)
return &App{
fileStorage: fileStorage,
imageUploader: upload.NewUploader(fileStorage),
}
}
func (a *App) Run(port string) error {
// Init gin handler
router := gin.Default()
router.Use(
gin.Recovery(),
gin.Logger(),
)
// API endpoints
api := router.Group("/api")
uphttp.RegisterHTTPEndpoints(api, a.imageUploader)
// HTTP Server
a.httpServer = &http.Server{
Addr: ":" + port,
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go func() {
if err := a.httpServer.ListenAndServe(); err != nil {
log.Fatalf("Failed to listen and serve: %+v", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, os.Interrupt)
<-quit
ctx, shutdown := context.WithTimeout(context.Background(), 5*time.Second)
defer shutdown()
return a.httpServer.Shutdown(ctx)
}

BIN
main.exe Normal file

Binary file not shown.

Binary file not shown.