ci\cd setup

This commit is contained in:
Skeris 2023-03-01 20:38:03 +03:00
parent a36cc6eeac
commit 9f8ff5d2df
8 changed files with 157 additions and 184 deletions

31
.gitlab-ci.yml Normal file

@ -0,0 +1,31 @@
include:
- project: "devops/pena-continuous-integration"
file: "/templates/docker/build-template.gitlab-ci.yml"
- project: "devops/pena-continuous-integration"
file: "/templates/docker/clean-template.gitlab-ci.yml"
- project: "devops/pena-continuous-integration"
file: "/templates/docker/deploy-template.gitlab-ci.yml"
stages:
- clean
- build
- deploy
clear-old-images:
extends: .clean_template
variables:
STAGING_BRANCH: "main"
PRODUCTION_BRANCH: "main"
build-app:
extends: .build_template
variables:
DOCKER_BUILD_PATH: "./Dockerfile"
STAGING_BRANCH: "main"
PRODUCTION_BRANCH: "main"
deploy-to-staging:
extends: .deploy_template
variables:
DEPLOY_TO: "staging"
BRANCH: "main"

@ -1,5 +1,13 @@
FROM alpine
ADD heruvym /
FROM golang:latest as builder
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o heruvym
FROM scratch
COPY --from=builder /app/heruvym .
ENV BB_PORT=1488
ENV BB_IS_PROD=false
ENV BB_MONGO_URI=mongodb://mongo:27017

@ -13,7 +13,6 @@ import (
"net/http"
"time"
rAL "bitbucket.org/skeris/profile/dal"
"github.com/skeris/appInit"
tb "gopkg.in/tucnak/telebot.v2"
@ -113,12 +112,6 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
}
fmt.Println("mongoconnnnnnn")
connRoles, err := rAL.ConnectToDb(ctx, logger, rAL.MongoDbOptions{
DalName: "MongoDB",
URI: options.MongoURI,
DbTable: options.MongoDbTable,
Collections: options.MongoCollections,
})
fmt.Println("mongod")
blobStore, err := minio.New(
ctx,
@ -150,7 +143,7 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
}
}
heruvym := service.New(blobStore, database, connRoles, logger, newBot)
heruvym := service.New(blobStore, database, logger, newBot)
mux := router.NewRouter(map[string]http.HandlerFunc{
"/support/create": heruvym.CreateTicket,
@ -170,7 +163,6 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
mw := middleware.NewMiddleware(
logger,
nil,
"*",
nil,
)
@ -189,7 +181,7 @@ func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.Co
})
},
mw.MiddlewareLogger,
mw.MiddlewareOriginAccess,
//mw.MiddlewareOriginAccess,
mw.MiddlewareRecovery,
mw.MiddlewareJwt,
mw.MiddlewareGetJwt,

@ -0,0 +1,15 @@
version: "3.3"
services:
heruvym:
container_name: heruvym
restart: unless-stopped
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
networks:
- backend_external
hostname: heruvym
tty: true
networks:
backend_external:
driver: bridge
attachable: true
internal: true

@ -7,17 +7,16 @@ import (
"time"
"github.com/dgrijalva/jwt-go"
"github.com/skeris/identity/cookie"
)
const (
DefaultAccessSecret = "awesomeAC"
DefaultHeaderKey = "Authorization"
RoleKey = "role"
)
var (
_ cookie.Cookie = new(JwtAdapter)
accessSecret = DefaultAccessSecret
accessSecret = DefaultAccessSecret
)
type JwtAdapter struct {
@ -85,10 +84,18 @@ func Decode(tokenString string) (*JwtAdapter, error) {
if err := claims.Validate(); err != nil {
return nil, err
}
return claims, nil
}
func GetRole(ctx context.Context) string {
role := ctx.Value(RoleKey)
if role == nil {
return ""
}
return role.(string)
}
func Timestamp() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}

@ -1,35 +1,34 @@
package middleware
import (
"bitbucket.org/skeris/profile/dal"
"bitbucket.org/skeris/profile/errors"
"context"
"fmt"
errors2 "github.com/pkg/errors"
"github.com/themakers/hlog"
"heruvym/jwt_adapter"
"net/http"
"strings"
errors2 "github.com/pkg/errors"
"github.com/themakers/hlog"
)
// My MiddleWare with gorilla/mux
type Middleware struct {
logger hlog.Logger
mongo dal.LayerMongoDb
logger hlog.Logger
// mongo dal.LayerMongoDb
allowedOrigins string
allowedRoles map[string]string // key - path, value - roles
}
func NewMiddleware(
logger hlog.Logger,
mongo dal.LayerMongoDb,
//mongo dal.LayerMongoDb,
allowedOrigins string,
allowedRoles map[string]string,
) *Middleware {
return &Middleware{
logger: logger,
mongo: mongo,
logger: logger,
// mongo: mongo,
allowedOrigins: allowedOrigins,
allowedRoles: allowedRoles,
}
@ -61,16 +60,8 @@ func recFn(rec interface{}) (int, string) {
)
if err, ok := rec.(error); ok {
if v, ok := errors.IsForbidden(err); ok {
code = http.StatusForbidden
message = v.Error()
} else if v, ok := errors.IsUnauthenticated(err); ok {
code = http.StatusUnauthorized
message = v.Error()
} else {
code = http.StatusInternalServerError
message = err.Error()
}
code = http.StatusInternalServerError
message = err.Error()
} else {
code = http.StatusInternalServerError
message = fmt.Sprintf("%v", rec)
@ -102,7 +93,15 @@ func (mw *Middleware) MiddlewareRecovery(next http.Handler) http.Handler {
func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var token string
var token, role string
switch r.Host {
case "admin.pena.digital":
role = "admin"
default:
role = "user"
}
tokenCookie, err := r.Cookie(jwt_adapter.DefaultHeaderKey)
if err != nil {
// Escape GET requests
@ -111,9 +110,9 @@ func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
return
}
if len(r.Header[jwt_adapter.DefaultHeaderKey]) <= 0 {
mw.logger.Emit(ErrorJwtAccess{Err: errors2.New(jwt_adapter.DefaultHeaderKey + "header missing")})
w.WriteHeader(http.StatusUnauthorized)
return
mw.logger.Emit(ErrorJwtAccess{Err: errors2.New(jwt_adapter.DefaultHeaderKey + "header missing")})
w.WriteHeader(http.StatusUnauthorized)
return
}
token = r.Header[jwt_adapter.DefaultHeaderKey][0]
token = strings.Replace(token, "Bearer ", "", -1)
@ -136,14 +135,14 @@ func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
return
}
ctx := context.WithValue(r.Context(), jwt_adapter.DefaultHeaderKey, adapter)
ctx := context.WithValue(context.WithValue(r.Context(), jwt_adapter.DefaultHeaderKey, adapter), jwt_adapter.RoleKey, role)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getJwtUserId(r *http.Request) (string, error) {
if jwtAdapter, ok := r.Context().Value(jwt_adapter.DefaultHeaderKey).(*jwt_adapter.JwtAdapter); ok {
return jwtAdapter.User, nil
return jwtAdapter.Id, nil
}
return "", errors2.New("no token in context")
@ -159,38 +158,38 @@ func (mw *Middleware) MiddlewareRoleAccess(next http.Handler) http.Handler {
next.ServeHTTP(w, r)
return
}
/*
id, err := getJwtUserId(r)
id, err := getJwtUserId(r)
if err != nil {
mw.logger.Emit(ErrorRoleAccess{Err: err})
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
role, err := mw.mongo.GetProfileRole(r.Context(), id)
if err != nil {
mw.logger.Emit(ErrorRoleAccess{Err: err})
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
if err != nil {
mw.logger.Emit(ErrorRoleAccess{Err: err})
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}*/
/*
role, err := mw.mongo.GetProfileRole(r.Context(), id)
if err != nil {
mw.logger.Emit(ErrorRoleAccess{Err: err})
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
*/
// Если у пользователя не задана роль - блокируем доступ
if role == "" {
err = errors.UserHaveNoRole("User have no role")
mw.logger.Emit(ErrorRoleAccess{err})
http.Error(w, err.Error(), http.StatusForbidden)
return
}
/* if role == "" {
err = errors.UserHaveNoRole("User have no role")
mw.logger.Emit(ErrorRoleAccess{err})
http.Error(w, err.Error(), http.StatusForbidden)
return
}
// Если указан астериск - доступ имеет любая роль
if !(allowedRoles == "*" || strings.Contains(allowedRoles, role)) {
err = errors.UserHaveNoRole("User role not allowed")
mw.logger.Emit(ErrorRoleAccess{err})
http.Error(w, err.Error(), http.StatusForbidden)
return
}
// Если указан астериск - доступ имеет любая роль
if !(allowedRoles == "*" || strings.Contains(allowedRoles, role)) {
err = errors.UserHaveNoRole("User role not allowed")
mw.logger.Emit(ErrorRoleAccess{err})
http.Error(w, err.Error(), http.StatusForbidden)
return
}*/
}
next.ServeHTTP(w, r)
@ -198,6 +197,7 @@ func (mw *Middleware) MiddlewareRoleAccess(next http.Handler) http.Handler {
}
// MiddlewareJwtPlug jwt заглушка для отладки кода, удалить в релизе
/*
func (mw *Middleware) MiddlewareJwtPlug(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
adapter := jwt_adapter.JwtAdapter{ID: "604b79aced1d431b9e911f56"}
@ -208,3 +208,4 @@ func (mw *Middleware) MiddlewareJwtPlug(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}
*/

@ -1,35 +1,12 @@
package middleware
import (
"github.com/themakers/hlog"
"heruvym/jwt_adapter"
"net/http"
"time"
"github.com/themakers/hlog"
)
func setJwtHeader(adapter *jwt_adapter.JwtAdapter, w http.ResponseWriter, logger hlog.Logger) error {
adapter.LastSeen = jwt_adapter.Timestamp()
token, err := adapter.Encode()
if err != nil {
logger.Emit(ErrorJwtEncode{Err: err})
return err
}
w.Header().Set(jwt_adapter.DefaultHeaderKey, token)
outToken, err := adapter.Encode()
const domain = ".fynrods.com"
http.SetCookie(w, &http.Cookie{
Name: jwt_adapter.DefaultHeaderKey,
Value: outToken,
Expires: time.Now().Add(time.Hour * 24 * 3),
Secure: true,
Domain: domain,
HttpOnly: true,
Path: "/",
})
return nil
}

@ -14,7 +14,6 @@ import (
"strings"
"sync"
rAL "bitbucket.org/skeris/profile/dal"
"github.com/rs/xid"
"github.com/themakers/hlog"
tb "gopkg.in/tucnak/telebot.v2"
@ -23,17 +22,19 @@ import (
type Heruvym struct {
logger hlog.Logger
dal *mongo.DAL
ral rAL.LayerMongoDb
bs *minio.BlobStore
notifier *tb.Bot
}
func New(blobs *minio.BlobStore, dataAccessLayer *mongo.DAL, ral rAL.LayerMongoDb, log hlog.Logger, notifier *tb.Bot) *Heruvym {
func New(
blobs *minio.BlobStore,
dataAccessLayer *mongo.DAL,
log hlog.Logger,
notifier *tb.Bot) *Heruvym {
return &Heruvym{
logger: log.Module("Service"),
dal: dataAccessLayer,
ral: ral,
bs: blobs,
notifier: notifier,
}
@ -105,17 +106,18 @@ func (h *Heruvym) CreateTicket(w http.ResponseWriter, r *http.Request) {
var (
ticketID string
tickets []model.Ticket
role = jwt_adapter.GetRole(ctx)
)
if session.User == "" {
tickets, _, err = h.dal.GetTickets4Sess(ctx, session.Session)
if role == "" {
tickets, _, err = h.dal.GetTickets4Sess(ctx, session.Id)
}
if err != nil || len(tickets) == 0 {
ticketID, err = h.dal.CreateTicket(
ctx,
session.User,
session.Session,
session.Id,
session.Id,
request.Title,
request.Message,
[]string{},
@ -127,44 +129,14 @@ func (h *Heruvym) CreateTicket(w http.ResponseWriter, r *http.Request) {
if _, err := h.dal.PutMessage(ctx,
request.Message,
session.User,
session.Session,
session.Id,
session.Id,
ticketID,
[]string{},
); err != nil {
http.Error(w, "CannotCreateMessage", http.StatusInternalServerError)
return
}
go func() {
if h.ral == nil {
return
}
role, _ := h.ral.GetProfileRole(context.TODO(), session.User)
if role != "user" && role != "" {
return
}
if session.User != "" {
additional, err := h.dal.GetAdditionalData(context.TODO(), session.User)
fmt.Println("CAN NOT NOTIFY", err)
if err == nil && h.notifier != nil {
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf("Поступило новое сообщение от пользователя %d с почтой %s",
additional.Uid, additional.Email)); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
return
}
}
if h.notifier != nil {
if _, err := h.notifier.Send(tb.ChatID(-1001344671794),
fmt.Sprintf("Поступило новое сообщение от незарегистриованного пользователя")); err != nil {
fmt.Println("CAN NOT NOTIFY", err)
}
}
}()
} else {
ticketID = tickets[0].ID
}
@ -202,28 +174,16 @@ func (h *Heruvym) GetList(ctx context.Context) chan interface{} {
output := make(chan interface{})
if sess.User == "" {
go h.unauthorizedTickets(ctx, sess.Session, output)
if sess.Id == "" {
go h.unauthorizedTickets(ctx, sess.Id, output)
} else {
var (
role string
err error
)
if h.ral != nil {
role, err = h.ral.GetProfileRole(ctx, sess.User)
role := jwt_adapter.GetRole(ctx)
if err != nil {
fmt.Println("HER ERR", err)
go h.hasNoRole(output)
}
} else {
role = "admin"
}
fmt.Println("ALL TICKETS Sess ", sess.User, role)
fmt.Println("ALL TICKETS Sess ", sess.Id, role)
if role == "admin" || role == "manager" {
go h.allTickets(ctx, output)
} else {
go h.userTickets(ctx, sess.User, output)
go h.userTickets(ctx, sess.Id, output)
}
}
@ -321,8 +281,8 @@ func (h *Heruvym) PutMessage(
message, err := h.dal.PutMessage(
ctx,
request.Message,
sess.User,
sess.Session,
sess.Id,
sess.Id,
request.TicketID,
[]string{},
)
@ -331,15 +291,8 @@ func (h *Heruvym) PutMessage(
}
go func() {
if h.ral == nil {
return
}
role, _ := h.ral.GetProfileRole(context.TODO(), sess.User)
if role != "user" && role != "" {
return
}
if sess.User != "" {
additional, err := h.dal.GetAdditionalData(context.TODO(), sess.User)
if sess.Id != "" {
additional, err := h.dal.GetAdditionalData(context.TODO(), sess.Id)
fmt.Println("CAN NOT NOTIFY", err)
if err == nil && h.notifier != nil {
@ -380,8 +333,8 @@ func (h *Heruvym) RequestScreenshot(
_, err := h.dal.PutSCRequest(
ctx,
sess.User,
sess.Session,
sess.Id,
sess.Id,
request.TicketID,
)
if err != nil {
@ -400,9 +353,9 @@ func (h *Heruvym) Subscribe(ctx context.Context) chan interface{} {
ticketID := ctx.Value(tools.ContextURLKey).(string)
output := make(chan interface{})
if sess.User == "" {
if sess.Id == "" {
go func() {
ticket, err := h.dal.GetTicket4Sess(ctx, ticketID, sess.Session)
ticket, err := h.dal.GetTicket4Sess(ctx, ticketID, sess.Id)
if err != nil || ticket == nil {
output <- errors.New("no tickets 4 session")
return
@ -440,18 +393,7 @@ func (h *Heruvym) Subscribe(ctx context.Context) chan interface{} {
}
}()
} else {
var (
role string
err error
)
if h.ral != nil {
role, err = h.ral.GetProfileRole(ctx, sess.User)
if err != nil {
go h.hasNoRole(output)
}
} else {
role = "admin"
}
role := jwt_adapter.GetRole(ctx)
if role == "admin" || role == "manager" {
go func() {
@ -494,7 +436,7 @@ func (h *Heruvym) Subscribe(ctx context.Context) chan interface{} {
fmt.Println("heryvym panic", v)
}
}()
ticket, err := h.dal.GetTicket4User(ctx, ticketID, sess.User)
ticket, err := h.dal.GetTicket4User(ctx, ticketID, sess.Id)
if err != nil || ticket == nil {
output <- errors.New("no tickets 4 user")
return
@ -634,7 +576,7 @@ type PickReq struct {
func (h *Heruvym) Pick(ctx context.Context, req PickReq) (error, int) {
sess := jwt_adapter.Get(ctx)
if err := h.dal.SetAnswerer(ctx, req.TicketID, sess.User); err != nil {
if err := h.dal.SetAnswerer(ctx, req.TicketID, sess.Id); err != nil {
return err, http.StatusBadRequest
}
@ -771,8 +713,8 @@ func (h *Heruvym) PutFile(w http.ResponseWriter, r *http.Request) {
message, err := h.dal.PutMessage(
r.Context(),
strings.Join(filenames, ", "),
sess.User,
sess.Session,
sess.Id,
sess.Id,
req.Ticket,
fileIDs,
)
@ -918,8 +860,8 @@ func (h *Heruvym) PutSC(w http.ResponseWriter, r *http.Request) {
message, err := h.dal.PutSCResponse(
r.Context(),
strings.Join(filenames, ", "),
sess.User,
sess.Session,
sess.Id,
sess.Id,
req.Ticket,
fileIDs,
)