Удален dal user; Изменение моделей с user на pena;

This commit is contained in:
Mikhail 2023-08-15 21:33:53 +00:00
parent 11c503fbe8
commit dba9890cc7
74 changed files with 3998 additions and 3540 deletions

@ -5,11 +5,27 @@ include:
file: "/templates/docker/clean-template.gitlab-ci.yml"
- project: "devops/pena-continuous-integration"
file: "/templates/docker/deploy-template.gitlab-ci.yml"
stages:
- lint
- clean
- build
- deploy
lint:
image: golangci/golangci-lint:v1.53.3-alpine
stage: lint
before_script:
- echo GITLAB_TOKEN = $GITLAB_TOKEN
- git config --global url."https://forgomod:${GITLAB_TOKEN}@penahub.gitlab.yandexcloud.net/".insteadOf "https://penahub.gitlab.yandexcloud.net/"
- export GOPRIVATE=penahub.gitlab.yandexcloud.net/backend/penahub_common/
- go install github.com/vektra/mockery/v2@v2.26.0
- go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4
script:
- go generate ./...
- golangci-lint version
- golangci-lint run ./...
clear-old-images:
extends: .clean_template
variables:

167
.golangci.yaml Normal file

@ -0,0 +1,167 @@
run:
timeout: 30m
skip-files:
- \.pb\.go$
- \.pb\.validate\.go$
- \.pb\.gw\.go$
- \.gen\.go$
skip-dirs:
- mocks
- proto
linters:
disable-all: true
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
# - depguard
- dogsled
- dupword
- durationcheck
- errcheck
- errchkjson
- exportloopref
# - goconst Временно отключен, ругается на dal\postgres\user.go [159, 18]
- gocritic
- godot
- gofmt
- gci
- goprintffuncname
- gosec
- gosimple
- govet
- importas
- ineffassign
- misspell
- nakedret
- nilerr
- noctx
- nolintlint
- nosprintfhostport
- prealloc
- predeclared
- revive
- rowserrcheck
- staticcheck
- stylecheck
- thelper
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
linters-settings:
errcheck:
exclude-functions:
- (io.Closer).Close
govet:
check-shadowing: true
gci:
custom-order: false
section-separators:
- newLine
sections:
- standard # Standard section: captures all standard packages.
- default # Default section: contains all imports that could not be matched to another section type.
- blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled.
- dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled.
importas:
no-unaliased: true
alias:
# Foundation libraries
- pkg: git.sbercloud.tech/products/paas/shared/foundation/management-server
alias: mgmtserver
maligned:
suggest-new: true
goconst:
min-len: 2
min-occurrences: 2
lll:
line-length: 140
revive:
rules:
# The following rules are recommended https://github.com/mgechev/revive#recommended-configuration
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
# - name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
# - name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id
#
# Rules in addition to the recommended configuration above.
#
- name: bool-literal-in-expr
- name: constant-logical-expr
gosec:
excludes:
- G304 # Potential file inclusion via variable
- G307 # Deferring unsafe method "Close" on type "\*os.File"
- G107 # Potential HTTP request made with variable url
- G108 # Profiling endpoint is automatically exposed on /debug/pprof
gocritic:
enabled-tags:
- diagnostic
- experimental
- performance
disabled-checks:
- appendAssign
- dupImport # https://github.com/go-critic/go-critic/issues/845
- evalOrder
- ifElseChain
- octalLiteral
- regexpSimplify
- sloppyReassign
- truncateCmp
- typeDefFirst
- unnamedResult
- unnecessaryDefer
- whyNoLint
- wrapperFunc
- rangeValCopy
- hugeParam
issues:
fix: true
exclude-rules:
- text: "at least one file in a package should have a package comment"
linters:
- stylecheck
- text: "should have a package comment, unless it's in another file for this package"
linters:
- golint
- text: "should have comment or be unexported"
linters:
- golint
- path: _test\.go
linters:
- gosec
- dupl
exclude-use-default: false
output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: colored-line-number
print-linter-name: true

@ -3,19 +3,21 @@ package amo
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"log"
"net/http"
"net/url"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
)
const (
OAUTH_URL = "https://www.amocrm.ru/oauth"
OauthURL = "https://www.amocrm.ru/oauth"
)
type Client struct {
@ -26,30 +28,50 @@ type Client struct {
Token *oauth2.Token
}
type transport struct {
underlyingTransport http.RoundTripper
}
func (t *transport) RoundTrip(request *http.Request) (*http.Response, error) {
request.Header.Set("User-Agent", "amoCRM-oAuth-client/1.0")
request.Header.Set("Content-Type", "application/json")
return t.underlyingTransport.RoundTrip(request)
}
type ClientApp struct {
Config *oauth2.Config
}
func NewClientApp(clientID, clientSecret string, redirectUri string) *ClientApp {
/*
TODO: Вероятно стоит вынести аргументы в отдельную структуру типа Deps.
Аргументация Кирилла:
Я думаю лучше все аргументы для инициализации модуля выносить в структуру,
потому что количество аргументов может в дальнейшем увеличиваться.
И чтобы избежать большого количества аргументов в количестве +3 штук, лучше выносить в структуру.
*/
func NewClientApp(clientID, clientSecret string, redirectURI string) *ClientApp {
return &ClientApp{
Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: OAUTH_URL,
TokenURL: OAUTH_URL + "2/access_token",
AuthURL: OauthURL,
TokenURL: OauthURL + "2/access_token",
},
RedirectURL: redirectUri,
RedirectURL: redirectURI,
Scopes: nil,
},
}
}
func (ca *ClientApp) GenerateOAuthUrl(userId, redirectUrl string) (string, error) {
state, err := tools.EncryptTokenRC4(tools.StateToken{
UserID: userId,
func (ca *ClientApp) GenerateOAuthURL(penaID, redirectURL string) (string, error) {
state, err := tools.EncryptTokenAES(tools.StateToken{
PenaID: penaID,
Service: "amo",
RedirectUrl: redirectUrl,
RedirectURL: redirectURL,
})
if err != nil {
@ -92,8 +114,6 @@ func (ca *ClientApp) NewClient(ctx context.Context, referer string, token *oauth
error) {
var err error
referer = "https://" + referer
client := &Client{
App: ca,
Config: &oauth2.Config{
@ -101,7 +121,7 @@ func (ca *ClientApp) NewClient(ctx context.Context, referer string, token *oauth
ClientSecret: ca.Config.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: ca.Config.Endpoint.AuthURL,
TokenURL: referer + "/oauth2/access_token",
TokenURL: "https://" + referer + "/oauth2/access_token",
},
RedirectURL: ca.Config.RedirectURL,
Scopes: ca.Config.Scopes,
@ -121,6 +141,7 @@ func (ca *ClientApp) NewClient(ctx context.Context, referer string, token *oauth
client.Token = token
client.HTTPClient = client.Config.Client(ctx, token)
client.HTTPClient.Transport = &transport{underlyingTransport: http.DefaultTransport}
return client, nil
}
@ -148,164 +169,208 @@ func (ca *ClientApp) RefreshToken(ctx context.Context, oldToken *oauth2.Token, r
return token, nil
}
func (c *Client) GetAccount() (*Account, error) {
req, err := http.NewRequest("GET", c.Subdomain+"/api/v4/account", nil)
func (c *Client) GetAccount(ctx context.Context) (*Account, error) {
requestURL := url.URL{
Scheme: "https",
Host: c.Subdomain,
Path: "api/v4/account",
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
if err != nil {
return nil, errors.Wrap(err, "GetAccount.NewRequest")
}
response, err := c.HTTPClient.Do(request)
if err != nil {
return nil, errors.Wrap(err, "GetAccount.DoRequest")
}
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetAccount.BodyClose"))
}
}()
var result Account
// TODO: Проверить в ssa сокращенную запись if err = operation(); err != nil {...}
err = json.NewDecoder(response.Body).Decode(&result)
if err != nil {
fmt.Println("1")
return nil, err
}
c.setHeaders(req)
return &result, nil
}
resp, err := c.HTTPClient.Do(req)
func (c *Client) GetLeadByID(ctx context.Context, id string) (*Lead, error) {
requestURLQueries := make(url.Values)
requestURLQueries.Add("with", "contacts,catalog_elements,is_price_modified_by_robot,loss_reason")
requestURL := url.URL{
Scheme: "https",
Host: c.Subdomain,
Path: fmt.Sprintf("api/v4/leads/%v", id),
RawQuery: requestURLQueries.Encode(),
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
fmt.Println("URL:", requestURL.String())
if err != nil {
fmt.Println("2")
return nil, err
return nil, errors.Wrap(err, "GetLeadByID.NewRequest")
}
var response Account
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
fmt.Println("3")
return nil, err
}
return &response, nil
}
func (c *Client) GetLeadById(id string) (*Lead, error) {
req, err := http.NewRequest("GET",
fmt.Sprintf("%v/api/v4/leads/%v?with=contacts,catalog_elements,is_price_modified_by_robot,loss_reason",
c.Subdomain, id),
nil,
)
fmt.Println("URL:", fmt.Sprintf("%v/api/v4/leads/%v?with=contacts,catalog_elements,is_price_modified_by_robot,"+
"loss_reason",
c.Subdomain, id))
response, err := c.HTTPClient.Do(request)
if err != nil {
fmt.Println("1", err)
return nil, err
return nil, errors.Wrap(err, "GetLeadByID.DoRequest")
}
c.setHeaders(req)
resp, err := c.HTTPClient.Do(req)
if err != nil {
fmt.Println("2", err)
return nil, err
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetLeadByID.BodyClose"))
}
}()
for resp.StatusCode == 429 {
for response.StatusCode == http.StatusTooManyRequests {
time.Sleep(time.Second)
resp, err = c.HTTPClient.Do(req)
response, err = c.HTTPClient.Do(request)
if err != nil {
fmt.Println("5", err)
return nil, err
}
return nil, errors.Wrap(err, "GetLeadByID.DoRequest")
}
var response Lead
str, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("4", err)
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetLeadByID.BodyClose"))
}
err = json.Unmarshal(str, &response)
if err != nil {
fmt.Println("3", err, string(str))
return nil, err
}()
}()
}
return &response, nil
}
func (c *Client) GetContactById(id string) (*Contact, error) {
req, err := http.NewRequest("GET",
fmt.Sprintf("%v/api/v4/contacts/%v?with=catalog_elements,leads,customers",
c.Subdomain, id),
nil,
)
if err != nil {
fmt.Println("1")
return nil, err
}
c.setHeaders(req)
resp, err := c.HTTPClient.Do(req)
var result Lead
responseData, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println("2")
return nil, err
return nil, errors.Wrap(err, "GetLeadByID.ReadAll")
}
for resp.StatusCode == 429 {
err = json.Unmarshal(responseData, &result)
if err != nil {
return nil, errors.Wrap(err, "GetLeadByID.Unmarshal")
}
return &result, nil
}
func (c *Client) GetContactByID(ctx context.Context, id string) (*Contact, error) {
requestURLQueries := make(url.Values)
requestURLQueries.Add("with", "catalog_elements,leads,customers")
requestURL := url.URL{
Scheme: "https",
Host: c.Subdomain,
Path: fmt.Sprintf("api/v4/contacts/%v", id),
RawQuery: requestURLQueries.Encode(),
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
if err != nil {
return nil, errors.Wrap(err, "GetContactByID.NewRequest")
}
response, err := c.HTTPClient.Do(request)
if err != nil {
return nil, errors.Wrap(err, "GetContactByID.DoRequest")
}
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetContactByID.BodyClose"))
}
}()
// TODO: переписать на отдельную сущность-клиент, которая сама внутри себя будет мониторить вопрос rate-limit
for response.StatusCode == http.StatusTooManyRequests {
time.Sleep(time.Second)
resp, err = c.HTTPClient.Do(req)
response, err = c.HTTPClient.Do(request)
if err != nil {
fmt.Println("5", err)
return nil, err
}
return nil, errors.Wrap(err, "GetContactByID.ForDoRequest")
}
var response Contact
err = json.NewDecoder(resp.Body).Decode(&response)
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetContactByID.BodyClose"))
}
}()
}()
}
var result Contact
err = json.NewDecoder(response.Body).Decode(&result)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetContactByID.Decode")
}
return &response, nil
return &result, nil
}
func (c *Client) GetCompanyById(id string) (*Company, error) {
req, err := http.NewRequest("GET",
fmt.Sprintf("%v/api/v4/companies/%v?with=contacts,leads,catalog_elements,customers",
c.Subdomain, id),
nil,
)
if err != nil {
fmt.Println("1")
return nil, err
func (c *Client) GetCompanyByID(ctx context.Context, id string) (*Company, error) {
requestURLQueries := make(url.Values)
requestURLQueries.Add("with", "contacts,leads,catalog_elements,customers")
requestURL := url.URL{
Scheme: "https",
Host: c.Subdomain,
Path: fmt.Sprintf("api/v4/companies/%v", id),
RawQuery: requestURLQueries.Encode(),
}
c.setHeaders(req)
resp, err := c.HTTPClient.Do(req)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
if err != nil {
fmt.Println("2")
return nil, err
return nil, errors.Wrap(err, "GetCompanyByID.NewRequest")
}
for resp.StatusCode == 429 {
response, err := c.HTTPClient.Do(request)
if err != nil {
return nil, errors.Wrap(err, "GetCompanyByID.DoRequest")
}
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetCompanyByID.BodyClose"))
}
}()
for response.StatusCode == http.StatusTooManyRequests {
time.Sleep(time.Second)
resp, err = c.HTTPClient.Do(req)
response, err = c.HTTPClient.Do(request)
if err != nil {
fmt.Println("5", err)
return nil, err
}
return nil, errors.Wrap(err, "GetCompanyByID.ForDoRequest")
}
var response Company
err = json.NewDecoder(resp.Body).Decode(&response)
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetCompanyByID.BodyClose"))
}
}()
}()
}
var result Company
err = json.NewDecoder(response.Body).Decode(&result)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetCompanyByID.Decode")
}
return &response, nil
}
func (c *Client) setHeaders(req *http.Request) {
req.Header.Set("User-Agent", "amoCRM-oAuth-client/1.0")
req.Header.Set("Content-Type", "application/json")
//if c.Token != nil {
// c.Token.SetAuthHeader(req)
//}
return &result, nil
}

@ -2,8 +2,9 @@ package amo
import (
"errors"
"github.com/dgrijalva/jwt-go"
"time"
"github.com/dgrijalva/jwt-go"
)
type RespAuthCode struct {
@ -14,15 +15,15 @@ type RespAuthCode struct {
}
type Lead struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"price"`
ResponsibleUserId int `json:"responsible_user_id"`
GroupId int `json:"group_id"`
StatusId int `json:"status_id"`
PipelineId int `json:"pipeline_id"`
LossReasonId int `json:"loss_reason_id"`
SourceId interface{} `json:"source_id"`
ResponsibleUserID int `json:"responseonsible_user_id"`
GroupID int `json:"group_id"`
StatusID int `json:"status_id"`
PipelineID int `json:"pipeline_id"`
LossReasonID int `json:"loss_reason_id"`
SourceID interface{} `json:"source_id"`
CreatedBy int `json:"created_by"`
UpdatedBy int `json:"updated_by"`
CreatedAt int `json:"created_at"`
@ -32,7 +33,7 @@ type Lead struct {
IsDeleted bool `json:"is_deleted"`
CustomFieldsValues []CustomField `json:"custom_fields_values"`
Score interface{} `json:"score"`
AccountId int `json:"account_id"`
AccountID int `json:"account_id"`
IsPriceModifiedByRobot bool `json:"is_price_modified_by_robot"`
Links Links `json:"_links"`
Embedded struct {
@ -45,7 +46,7 @@ type Lead struct {
}
type CustomField struct {
FieldId int `json:"field_id"`
FieldID int `json:"field_id"`
FieldName string `json:"field_name"`
FieldCode interface{} `json:"field_code"`
FieldType string `json:"field_type"`
@ -58,25 +59,25 @@ type CustomField struct {
}
type Tags struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
Color interface{} `json:"color"`
}
type Contact struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
ResponsibleUserId int `json:"responsible_user_id"`
GroupId int `json:"group_id"`
ResponsibleUserID int `json:"responseonsible_user_id"`
GroupID int `json:"group_id"`
CreatedBy int `json:"created_by"`
UpdatedBy int `json:"updated_by"`
CreatedAt int `json:"created_at"`
UpdatedAt int `json:"updated_at"`
ClosestTaskAt interface{} `json:"closest_task_at"`
CustomFieldsValues []CustomField `json:"custom_fields_values"`
AccountId int `json:"account_id"`
AccountID int `json:"account_id"`
Links Links `json:"_links"`
Embedded struct {
Tags []Tags `json:"tags"`
@ -88,17 +89,17 @@ type Contact struct {
}
type Company struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
ResponsibleUserId int `json:"responsible_user_id"`
GroupId int `json:"group_id"`
ResponsibleUserID int `json:"responseonsible_user_id"`
GroupID int `json:"group_id"`
CreatedBy int `json:"created_by"`
UpdatedBy int `json:"updated_by"`
CreatedAt int `json:"created_at"`
UpdatedAt int `json:"updated_at"`
ClosestTaskAt interface{} `json:"closest_task_at"`
CustomFieldsValues []CustomField `json:"custom_fields_values"`
AccountId int `json:"account_id"`
AccountID int `json:"account_id"`
Links Links `json:"_links"`
Embedded struct {
Tags []Tags `json:"tags"`
@ -106,12 +107,12 @@ type Company struct {
}
type Customer struct {
Id int `json:"id"`
ID int `json:"id"`
Links Links `json:"_links"`
}
type LossReason struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
Sort int `json:"sort"`
CreatedAt int `json:"created_at"`
@ -120,10 +121,10 @@ type LossReason struct {
}
type CatalogElement struct {
Id int `json:"id"`
ID int `json:"id"`
Metadata struct {
Quantity int `json:"quantity"`
CatalogId int `json:"catalog_id"`
CatalogID int `json:"catalog_id"`
} `json:"metadata"`
}
@ -134,14 +135,14 @@ type Links struct {
}
type Account struct {
Id int64 `json:"id"`
ID int64 `json:"id"`
Name string `json:"name"`
Subdomain string `json:"subdomain"`
CreatedAt int `json:"created_at"`
CreatedBy int `json:"created_by"`
UpdatedAt int `json:"updated_at"`
UpdatedBy int `json:"updated_by"`
CurrentUserId int `json:"current_user_id"`
CurrentUserID int `json:"current_user_id"`
Country string `json:"country"`
CustomersMode string `json:"customers_mode"`
IsUnsortedOn bool `json:"is_unsorted_on"`
@ -149,8 +150,8 @@ type Account struct {
IsHelpbotEnabled bool `json:"is_helpbot_enabled"`
IsTechnicalAccount bool `json:"is_technical_account"`
ContactNameDisplayOrder int `json:"contact_name_display_order"`
AmojoId string `json:"amojo_id"`
Uuid string `json:"uuid"`
AmojoID string `json:"amojo_id"`
UUID string `json:"uuid"`
Version int `json:"version"`
Links Links `json:"_links"`
Embedded struct {
@ -159,15 +160,15 @@ type Account struct {
CanCreateGroups bool `json:"can_create_groups"`
} `json:"amojo_rights"`
UsersGroups []struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
Uuid interface{} `json:"uuid"`
UUID interface{} `json:"uuid"`
} `json:"users_groups"`
TaskTypes []struct {
Id int `json:"id"`
ID int `json:"id"`
Name string `json:"name"`
Color interface{} `json:"color"`
IconId interface{} `json:"icon_id"`
IconID interface{} `json:"icon_id"`
Code string `json:"code"`
} `json:"task_types"`
EntityNames struct {
@ -227,30 +228,47 @@ type Account struct {
//
// Заголовок запроса: x-auth-token
//
// Алгоритм подписи токена: HS256, в качестве ключа используется secret_key интеграции
// Алгоритм подписи токена: HS256, в качестве ключа используется secret_key интеграции.
type XAuthToken struct {
Iss string `json:"iss"` // Адрес аккаунта
Aud string `json:"aud"` // Базовый адрес, сформированный исходя из значения redirect_uri в интеграции
Jti string `json:"jti"` // UUID токена
Iat int64 `json:"iat"` // Timestamp, когда был выдан токен
Nbf int64 `json:"nbf"` // Timestamp, когда токен начинает действовать
Exp int64 `json:"exp"` // Timestamp, когда токен будет просрочен
AccountId int64 `json:"account_id"` // ID аккаунта, из которого был сделан запрос
Subdomain string `json:"subdomain"` // Субдомен, из которого был сделан запрос
ClientUuid string `json:"client_uuid"` // UUID интеграции,
// которая сделала запрос
UserId int64 `json:"user_id"` // ID пользователя, из под которого был сделан запрос
IsAdmin bool `json:"is_admin"` // Является ли пользователь админом
/* Адрес аккаунта */
Iss string `json:"iss"`
/* Базовый адрес, сформированный исходя из значения redirect_uri в интеграции */
Aud string `json:"aud"`
/* UUID токена */
Jti string `json:"jti"`
/* Timestamp, когда был выдан токен */
Iat int64 `json:"iat"`
/* Timestamp, когда токен начинает действовать */
Nbf int64 `json:"nbf"`
/* Timestamp, когда токен будет просрочен */
Exp int64 `json:"exp"`
/* ID аккаунта, из которого был сделан запрос */
AccountID int64 `json:"account_id"`
/* Субдомен, из которого был сделан запрос */
Subdomain string `json:"subdomain"`
/* UUID интеграции, которая сделала запрос */
ClientUUID string `json:"client_uuid"`
/* ID пользователя, из под которого был сделан запрос */
UserID int64 `json:"user_id"`
/* Является ли пользователь админом */
IsAdmin bool `json:"is_admin"`
jwt.Claims
}
func (t *XAuthToken) Valid() error {
now := time.Now().Unix()
//if now >= t.Nbf && now <= t.Exp {
// return nil
//}
if now <= t.Exp {
return nil
}

51
broker/tariff/consumer.go Normal file

@ -0,0 +1,51 @@
package tariff
import (
"context"
"fmt"
"github.com/twmb/franz-go/pkg/kgo"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff/models"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff/utils/transfer"
"penahub.gitlab.yandexcloud.net/backend/templategen/proto/tariff"
)
type ConsumerDeps struct {
Logger *zap.Logger
Client *kgo.Client
}
type Consumer struct {
logger *zap.Logger
client *kgo.Client
}
func NewConsumer(deps ConsumerDeps) *Consumer {
return &Consumer{
logger: deps.Logger,
client: deps.Client,
}
}
func (receiver *Consumer) FetchTariffs(ctx context.Context) []models.Tariff {
fetches := receiver.client.PollFetches(ctx)
iter := fetches.RecordIter()
tariffs := make([]models.Tariff, 0)
for !iter.Done() {
record := iter.Next()
tariff := tariff.TariffMessage{}
if err := proto.Unmarshal(record.Value, &tariff); err != nil {
receiver.logger.Error(fmt.Sprintf("error decoding message: %v\n", err))
continue
}
tariffs = append(tariffs, *transfer.TariffProtoToModel(&tariff))
}
return tariffs
}

@ -0,0 +1,41 @@
package models
import "time"
const (
ServiceKey = "templategen"
PrivilegeTemplateCount = "templateCnt"
PrivilegeTemplateUnlimTime = "templateUnlimTime"
PrivilegeTemplateStorage = "templateStorage"
BasicAmountPrivilegeTemplateCount = 15
BasicAmountPrivilegeTemplateStorage = 100
)
type Tariff struct {
ID string `json:"_id"`
Name string `json:"name"`
UserID string `json:"user_id"`
Price int64 `json:"price"`
IsCustom bool `json:"isCustom"`
Privileges []Privilege `json:"privileges"`
Deleted bool `json:"isDeleted"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty"`
}
type Privilege struct {
ID string `json:"_id"`
Amount int64 `json:"amount"`
PrivilegeID string `json:"privilegeId"`
Name string `json:"name"`
ServiceKey string `json:"serviceKey"`
Description string `json:"description"`
Type string `json:"type"`
Value string `json:"value"`
Price float64 `json:"price"`
IsDeleted bool `json:"isDeleted"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
DeletedAt time.Time `json:"deletedAt"`
}

@ -0,0 +1,30 @@
package transfer
import (
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff/models"
"penahub.gitlab.yandexcloud.net/backend/templategen/proto/tariff"
)
func PrivilegeProtoToModel(privilege *tariff.PrivilegeMessage) *models.Privilege {
if privilege == nil {
return &models.Privilege{}
}
return &models.Privilege{
ID: privilege.GetPrivilegeID(),
ServiceKey: privilege.GetServiceKey(),
Type: privilege.GetType().String(),
Value: privilege.GetValue(),
Amount: int64(privilege.GetAmount()),
}
}
func PrivilegeArrayProtoToModel(privileges []*tariff.PrivilegeMessage) []models.Privilege {
privilegesModel := make([]models.Privilege, len(privileges))
for index, privilege := range privileges {
privilegesModel[index] = *PrivilegeProtoToModel(privilege)
}
return privilegesModel
}

@ -0,0 +1,27 @@
package transfer
import (
"time"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff/models"
"penahub.gitlab.yandexcloud.net/backend/templategen/proto/tariff"
)
func TariffProtoToModel(tariff *tariff.TariffMessage) *models.Tariff {
if tariff == nil {
return &models.Tariff{}
}
return &models.Tariff{
ID: "",
UserID: tariff.GetUserID(),
Name: "",
Price: 0,
IsCustom: false,
Privileges: PrivilegeArrayProtoToModel(tariff.Privileges),
Deleted: false,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
DeletedAt: &time.Time{},
}
}

@ -2,55 +2,18 @@ package dal
import (
"context"
"github.com/jackc/pgx/v4/pgxpool"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/mongos"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/postgres"
"time"
)
type PostgresDAL struct {
conn *pgxpool.Pool
User *postgres.User
YaDisk *postgres.YaDisk
}
func InitPostgresDAL(ctx context.Context, dbURL string) (*PostgresDAL, error) {
conn, err := pgxpool.Connect(ctx, dbURL)
if err != nil {
return nil, err
}
dal := PostgresDAL{
conn: conn,
User: postgres.InitUser(ctx, conn),
YaDisk: postgres.InitYaDisk(ctx, conn),
}
// Init all tables in data access layer
//val := reflect.ValueOf(dal)
//for i := 0; i < val.NumField(); i++ {
// result := val.Field(i).MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(ctx)})
//
// if !result[0].IsNil() {
// log.Println(result[0])
// }
//}
return &dal, nil
}
func (dal *PostgresDAL) Disconnect() {
}
type MongoDAL struct {
conn *mongo.Client
logger *zap.Logger
User *mongos.User
YaDisk *mongos.YaDisk
GDisk *mongos.GDisk
PenaDisk *mongos.PenaDisk
@ -62,16 +25,10 @@ type MongoDAL struct {
}
func InitMongoDAL(ctx context.Context, dbURL, dbTable string, logger *zap.Logger) (*MongoDAL, error) {
conn, err := mongo.NewClient(options.Client().ApplyURI(dbURL))
if err != nil {
return nil, err
}
ctxTO, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
err = conn.Connect(ctxTO)
conn, err := mongo.Connect(ctxTO, options.Client().ApplyURI(dbURL))
if err != nil {
return nil, err
@ -80,7 +37,6 @@ func InitMongoDAL(ctx context.Context, dbURL, dbTable string, logger *zap.Logger
dal := &MongoDAL{
conn: conn,
logger: logger,
User: mongos.InitUser(conn.Database(dbTable), logger),
YaDisk: mongos.InitYaDisk(conn.Database(dbTable), logger),
GDisk: mongos.InitGDisk(conn.Database(dbTable), logger),
PenaDisk: mongos.InitPenaDisk(conn.Database(dbTable), logger),

@ -6,25 +6,62 @@ import (
"golang.org/x/oauth2"
)
const (
ServiceKey = "templategen"
PrivilegeTemplateCount = "templateCnt"
PrivilegeTemplateUnlimTime = "templateUnlimTime"
PrivilegeTemplateStorage = "templateStorage"
BasicAmountPrivilegeTemplateCount = 15
BasicAmountPrivilegeTemplateStorage = 100
)
type Amo struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"` //Id пользователя Амо
AccountID string `bson:"account_id" json:"account_id"` // Account ID in AMO CRM
/* ID интеграции */
ClientID string `bson:"client_id" json:"client_id"`
/* Account ID in AMO CRM */
AccountID string `bson:"account_id" json:"account_id"`
/* Identifier Pena User */
PenaID string `bson:"pena_id" json:"pena_id"`
Subdomain string `bson:"subdomain" json:"subdomain"` // Subdomain in AMO CRM. Example: [subdomain].amocrm.ru/
Referer string `bson:"referer" json:"referer"` // Referer in AMO CRM. Example: subdomain.amocrm.ru
/* Subdomain in AMO CRM. Example: [subdomain].amocrm.ru/ */
Subdomain string `bson:"subdomain" json:"subdomain"`
/* Referer in AMO CRM. Example: subdomain.amocrm.ru */
Referer string `bson:"referer" json:"referer"`
FromWidget string `bson:"from_widget" json:"from_widget"`
AccessToken string `bson:"access_token" json:"-"`
RefreshToken string `bson:"refresh_token" json:"-"`
ExpiresIn time.Time `bson:"expires_in" json:"-"`
TokenType string `bson:"token_type" json:"-"`
AccessRules AmoAccessRules `bson:"access_rules" json:"access_rules"`
/* Ключ privileges.Privilege.PrivilegeID */
Privileges map[string]ShortPrivilige `bson:"privileges" json:"privileges"`
IsDeleted bool `bson:"is_deleted" json:"is_deleted"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}
type ShortPrivilige struct {
Amount int64 `bson:"amount" json:"amount"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
}
// AmoAccessRules - права доступа. Значением является UserID из XAuthToken.
type AmoAccessRules struct {
Visibility []int64 `bson:"visibility"`
Creation []int64 `bson:"creation"`

@ -1,32 +1,54 @@
package model
import (
"golang.org/x/oauth2"
"time"
"golang.org/x/oauth2"
)
// GDisk (StorageType) - хранит информацию о пользователе Google и данных необходимых для хранилища.
// Пользователь может иметь несколько хранилищ GDisk.
// Email - уникальное поле и закрепляется только за одним пользователем
type GDisk struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
Email string `bson:"email" json:"email"` // Google email. Unique ?
DisplayName string `bson:"display_name" json:"display_name"` // Отображаемое имя пользователя в Google
PhotoLink string `bson:"photo_link" json:"photo_link"` // Фото пользователя в Google
PenaID string `bson:"pena_id" json:"pena_id"`
/* Google email. Уникальное поле и закрепляется только за одним пользователем. */
Email string `bson:"email" json:"email"`
/* Отображаемое имя пользователя в Google */
DisplayName string `bson:"display_name" json:"display_name"`
/* Фото пользователя в Google */
PhotoLink string `bson:"photo_link" json:"photo_link"`
AccessToken string `bson:"access_token" json:"-"`
RefreshToken string `bson:"refresh_token" json:"-"`
ExpiresIn time.Time `bson:"expires_in" json:"-"`
TokenType string `bson:"token_type" json:"-"`
Name string `bson:"name" json:"name"` // Пользовательское название хранилища
/* Пользовательское название хранилища */
Name string `bson:"name" json:"name"`
DefaultFolder string `bson:"default_folder" json:"default_folder"`
DefaultFolderID string `bson:"default_folder_id" json:"default_folder_id"`
TemplateFolder string `bson:"template_folder" json:"template_folder"`
TemplateFolderID string `bson:"template_folder_id" json:"template_folder_id"`
SaveFolder string `bson:"save_folder" json:"save_folder"`
SaveFolderID string `bson:"save_folder_id" json:"save_folder_id"`
IsDeleted bool `bson:"is_deleted" json:"is_deleted"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}

@ -6,25 +6,40 @@ import (
type History struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
PenaID string `bson:"pena_id" json:"pena_id"`
AmoID string `bson:"amo_id" json:"amo_id"`
AmoUserID int64 `bson:"amo_user_id,omitempty" json:"amo_user_id"`
LeadID int64 `bson:"lead_id,omitempty" json:"lead_id"`
Errors []string `bson:"errors" json:"errors"`
Trigger HistoryTrigger `bson:"trigger" json:"trigger"`
TemplateID string `bson:"template_id" json:"template_id"`
Source HistorySource `bson:"source" json:"source"`
Target HistoryTarget `bson:"target" json:"target"`
DownloadUrl string `json:"download_url" bson:"download_url"` // Ссылка для скачивания результата
PublicUrl string `bson:"public_url" json:"public_url"`
/* Ссылка для скачивания результата */
DownloadURL string `json:"download_url" bson:"download_url"` //
PublicURL string `bson:"public_url" json:"public_url"`
WorkerTaskID string `bson:"worker_task_id" json:"worker_task_id"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
}
func NewHistory(userID, amoID string, amoUserID int64, trigger HistoryTrigger) *History {
func NewHistory(penaID, amoID string, amoUserID int64, trigger HistoryTrigger) *History {
return &History{
UserID: userID,
PenaID: penaID,
AmoID: amoID,
AmoUserID: amoUserID,
Errors: []string{},

@ -4,11 +4,19 @@ import "time"
type PenaDisk struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
Name string `bson:"name" json:"name"` // Пользовательское название хранилища
PenaID string `bson:"pena_id" json:"pena_id"`
/* Пользовательское название хранилища */
Name string `bson:"name" json:"name"`
TemplateFolder string `bson:"template_folder" json:"template_folder"`
SaveFolder string `bson:"save_folder" json:"save_folder"`
IsDeleted bool `bson:"is_deleted" json:"is_deleted"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}

@ -6,14 +6,28 @@ import (
type Template struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
LeadId int64 `bson:"lead_id" json:"lead_id"` // AMO lead
Name string `bson:"name" json:"name"` // Название шаблона
File string `bson:"file" json:"file"` // Имя файла-шаблона for Yandex Disk OR File id for Google Drive
StorageID string `bson:"storage_id" json:"storage_id"` // ID of GDisk or YaDisk
PenaID string `bson:"pena_id" json:"pena_id"`
/* AMO lead */
LeadID int64 `bson:"lead_id" json:"lead_id"`
/* Название шаблона */
Name string `bson:"name" json:"name"`
/* Имя файла-шаблона for Yandex Disk OR File id for Google Drive */
File string `bson:"file" json:"file"`
/* ID of GDisk or YaDisk */
StorageID string `bson:"storage_id" json:"storage_id"`
StorageType string `bson:"storage_type" json:"storage_type"`
GroupIDs []string `bson:"group_ids" json:"group_ids"`
IsDeleted bool `bson:"is_deleted" json:"is_deleted"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}

@ -6,7 +6,7 @@ import (
type TemplateGroup struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
PenaID string `bson:"pena_id" json:"pena_id"`
Name string `bson:"name" json:"name"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`

@ -1,18 +0,0 @@
package model
import (
"time"
)
type User struct {
ID string `bson:"_id,omitempty"`
FullName string `bson:"full_name"`
Email string `bson:"email"`
Password string `bson:"password"`
IsActivated bool `bson:"is_activated"`
RoleID int `bson:"role_id"`
JwtToken string `bson:"jwt_token"` // not in use
IsDeleted bool `bson:"is_deleted"`
CreatedAt time.Time `bson:"created_at"`
UpdatedAt time.Time `bson:"updated_at"`
}

@ -5,33 +5,44 @@ import (
)
type WorkerTask struct {
ID string `json:"id" bson:"_id,omitempty"` // Идентификатор задачи
UserID string `json:"user_id" bson:"user_id"` // Пользователь пены
AmoID string `json:"amo_id" bson:"amo_id"` // Амо аккаунт вызвавший генерацию
AmoUserID int64 `json:"amo_user_id" bson:"amo_user_id"` // Пользователь амо вызвавший генерацию
Status WorkerTaskStatus `json:"status" bson:"status"` // Статус генерации
LeadId int64 `json:"lead_id" bson:"lead_id"` // Сделка по которой происходит генерация
TemplateID string `json:"template_id" bson:"template_id"` // Шаблон для генерации
Source WorkerSource `json:"source" bson:"source"` // Исходный файл-шаблон
Target WorkerTarget `json:"target" bson:"target"` // Куда отправить результат
DownloadUrl string `json:"download_url" bson:"download_url"` // Ссылка для скачивания результата
PublicUrl string `json:"public_url" bson:"public_url"` // Публичная ссылка для просмотра\редактирования\скачивания
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` // Время последнего обновления
CreatedAt time.Time `json:"created_at" bson:"created_at"` // Время создания
}
/* Идентификатор задачи */
ID string `json:"id" bson:"_id,omitempty"`
func NewWorkerTask(userID string, amoID string, amoUserID int64, status WorkerTaskStatus, leadId int64,
templateID string, source WorkerSource, target WorkerTarget) *WorkerTask {
return &WorkerTask{
UserID: userID,
AmoID: amoID,
AmoUserID: amoUserID,
Status: status,
LeadId: leadId,
TemplateID: templateID,
Source: source,
Target: target,
}
/* Пользователь пены */
PenaID string `json:"pena_id" bson:"pena_id"`
/* Амо аккаунт вызвавший генерацию */
AmoID string `json:"amo_id" bson:"amo_id"`
/* Пользователь амо вызвавший генерацию */
AmoUserID int64 `json:"amo_user_id" bson:"amo_user_id"`
/* Статус генерации */
Status WorkerTaskStatus `json:"status" bson:"status"`
/* Сделка по которой происходит генерация */
LeadID int64 `json:"lead_id" bson:"lead_id"`
/* Шаблон для генерации */
TemplateID string `json:"template_id" bson:"template_id"`
/* Исходный файл-шаблон */
Source WorkerSource `json:"source" bson:"source"`
/* Куда отправить результат */
Target WorkerTarget `json:"target" bson:"target"`
/* Ссылка для скачивания результата */
DownloadURL string `json:"download_url" bson:"download_url"`
/* Публичная ссылка для просмотра\редактирования\скачивания */
PublicURL string `json:"public_url" bson:"public_url"`
/* Время последнего обновления */
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
/* Время создания */
CreatedAt time.Time `json:"created_at" bson:"created_at"`
}
type WorkerTaskStatus string

@ -1,28 +1,49 @@
package model
import (
"golang.org/x/oauth2"
"time"
"golang.org/x/oauth2"
)
// YaDisk (StorageType) - хранит информацию о пользователе Yandex и данных необходимых для хранилища.
// Пользователь может иметь несколько хранилищ YaDisk.
// Login - уникальное поле и закрепляется только за одним пользователем
/*
YaDisk (StorageType) - хранит информацию о пользователе Yandex и данных необходимых для хранилища.
Пользователь может иметь несколько хранилищ YaDisk.
*/
type YaDisk struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_id"`
UID string `bson:"uid" json:"uid"` // Yandex UID. Unique ?
Login string `bson:"login" json:"login"` // Yandex login. Unique ?
DisplayName string `bson:"display_name" json:"display_name"` // Отображаемое имя пользователя в Yandex
PenaID string `bson:"pena_id" json:"pena_id"`
/* Yandex UID. уникальное поле и закрепляется только за одним пользователем */
UID string `bson:"uid" json:"uid"`
/* Yandex login. уникальное поле и закрепляется только за одним пользователем */
Login string `bson:"login" json:"login"`
/* Отображаемое имя пользователя в Yandex */
DisplayName string `bson:"display_name" json:"display_name"`
AccessToken string `bson:"access_token" json:"-"`
RefreshToken string `bson:"refresh_token" json:"-"`
ExpiresIn time.Time `bson:"expires_in" json:"-"`
TokenType string `bson:"token_type" json:"-"`
Name string `bson:"name" json:"name"` // Пользовательское название хранилища
/* Пользовательское название хранилища */
Name string `bson:"name" json:"name"`
TemplateFolder string `bson:"template_folder" json:"template_folder"`
SaveFolder string `bson:"save_folder" json:"save_folder"`
IsDeleted bool `bson:"is_deleted" json:"is_deleted"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at"`
}

@ -2,13 +2,15 @@ package mongos
import (
"context"
"errors"
"fmt"
"time"
"github.com/pkg/errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type Amo struct {
@ -23,6 +25,7 @@ func InitAmo(db *mongo.Database, logger *zap.Logger) *Amo {
func (d *Amo) InsertOrUpdate(ctx context.Context, record *model.Amo) (string, error) {
if record.AccountID == "" {
err := errors.New("got empty account_id")
d.logger.Error("ErrorInsertOrUpdateAmo", zap.Error(err))
return "", err
}
@ -32,15 +35,18 @@ func (d *Amo) InsertOrUpdate(ctx context.Context, record *model.Amo) (string, er
found, err := d.GetByAccountID(ctx, record.AccountID)
if err != nil {
d.logger.Error("ErrorInsertOrUpdateAmo", zap.Error(err))
return "", err
}
if found == nil {
result, err := d.coll.InsertOne(ctx, record)
var result *mongo.InsertOneResult
result, err = d.coll.InsertOne(ctx, record)
if err != nil {
d.logger.Error("ErrorInsertOrUpdateAmo", zap.Error(err))
return "", err
}
id := result.InsertedID.(primitive.ObjectID).Hex()
d.logger.Info("ErrorInsertOrUpdateAmo", zap.String("id", id))
@ -71,34 +77,36 @@ func (d *Amo) GetByID(ctx context.Context, id string) (*model.Amo, error) {
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetAmo", zap.Error(err))
return nil, err
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetAmo", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetAmo", zap.Error(err))
return nil, nil
}
d.logger.Info("InfoGetAmo", zap.String("id", result.ID))
return &result, nil
}
func (d *Amo) GetListByUserID(ctx context.Context, userID string) ([]model.Amo, error) {
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *Amo) GetListByPenaID(ctx context.Context, penaID string) ([]model.Amo, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
var result []model.Amo
cur, err := d.coll.Find(ctx, filter)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetListAmo", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetListAmo", zap.Error(err))
return nil, err
}
}
err = cur.All(ctx, &result)
if err != nil {
@ -110,27 +118,77 @@ func (d *Amo) GetListByUserID(ctx context.Context, userID string) ([]model.Amo,
return result, nil
}
func (d *Amo) GetByAccountID(ctx context.Context, accId string) (*model.Amo, error) {
filter := bson.M{"account_id": accId, "is_deleted": false}
func (d *Amo) GetByAccountID(ctx context.Context, accountID string) (*model.Amo, error) {
filter := bson.M{"account_id": accountID, "is_deleted": false}
var result model.Amo
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetAmo", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetAmo", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetAmo", zap.String("id", result.ID))
return &result, nil
}
// Update - обновляет запись по id или accountID
func (d *Amo) GetByPenaID(ctx context.Context, penaID string) (*model.Amo, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
var result model.Amo
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetAmo", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetAmo", zap.Error(err))
return nil, err
}
d.logger.Info("InfoGetAmo", zap.String("id", result.ID))
return &result, nil
}
func (d *Amo) GetAll(ctx context.Context) ([]model.Amo, error) {
filter := bson.M{"is_deleted": false}
var result []model.Amo
cur, err := d.coll.Find(ctx, filter)
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetAllAmo", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetAllAmo", zap.Error(err))
return nil, err
}
err = cur.All(ctx, &result)
if err != nil {
d.logger.Error("ErrorGetAllAmo", zap.Error(err))
return nil, err
}
d.logger.Info("InfoGetAllAmo")
return result, nil
}
// Update - обновляет запись по id или accountID.
func (d *Amo) Update(ctx context.Context, record *model.Amo) error {
filter := bson.M{"is_deleted": false}
@ -152,6 +210,14 @@ func (d *Amo) Update(ctx context.Context, record *model.Amo) error {
update := bson.M{"updated_at": time.Now()}
if record.AccountID != "" {
update["account_id"] = record.AccountID
}
if record.PenaID != "" {
update["pena_id"] = record.PenaID
}
// Token
if record.AccessToken != "" {
update["access_token"] = record.AccessToken
@ -182,7 +248,7 @@ func (d *Amo) Update(ctx context.Context, record *model.Amo) error {
update["access_rules.delete"] = record.AccessRules.Delete
}
updated, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
updated, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateAmo", zap.Error(err))
@ -199,18 +265,12 @@ func (d *Amo) Update(ctx context.Context, record *model.Amo) error {
return nil
}
func (d *Amo) DeleteByUserID(ctx context.Context, userID string) error {
if userID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeleteAmo", zap.Error(err))
return err
}
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *Amo) DeleteByPenaID(ctx context.Context, penaID string) error {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err := d.coll.UpdateMany(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateMany(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorDeleteAmo", zap.Error(err))
return err
@ -243,7 +303,7 @@ func (d *Amo) UpdateAccessRules(ctx context.Context, id string, record *model.Am
update["access_rules.delete"] = record.Delete
}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateAmoAccessRules", zap.Error(err))
@ -252,3 +312,178 @@ func (d *Amo) UpdateAccessRules(ctx context.Context, id string, record *model.Am
return nil
}
func (d *Amo) AddPrivilege(ctx context.Context, id, privilegeID string, privilegeAmount int64) error {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorAmoAddPrivilege", zap.Error(err))
return err
}
filter := bson.M{"_id": objID, "is_deleted": false}
privilegeKey := fmt.Sprintf("privileges.%v", privilegeID)
now := time.Now()
updateData := bson.M{
privilegeKey: bson.M{
"created_at": now,
"amount": privilegeAmount,
},
"updated_at": now,
}
update := bson.D{
{Key: "$set", Value: updateData},
}
_, err = d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoAddPrivilege", zap.Error(err))
return err
}
return nil
}
func (d *Amo) AddPrivilegeByPenaID(ctx context.Context, penaID, privilegeID string, privilegeAmount int64) error {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
privilegeKey := fmt.Sprintf("privileges.%v", privilegeID)
now := time.Now()
updateData := bson.M{
privilegeKey: bson.M{
"created_at": now,
"amount": privilegeAmount,
},
"updated_at": now,
}
update := bson.D{
{Key: "$set", Value: updateData},
}
_, err := d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoAddPrivilege", zap.Error(err))
return err
}
return nil
}
func (d *Amo) UpdateAmountPrivilege(ctx context.Context, id, privilegeID string, privilegeAmount int64) error {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorAmoUpdateAmountPrivilege", zap.Error(err))
return err
}
privilegeAmountKey := fmt.Sprintf("privileges.%v.amount", privilegeID)
filter := bson.M{
"_id": objID,
"is_deleted": false,
privilegeAmountKey: bson.M{"$exists": true},
}
updateData := bson.M{
"updated_at": time.Now(),
privilegeAmountKey: privilegeAmount,
}
update := bson.D{
{Key: "$set", Value: updateData},
}
_, err = d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoUpdateAmountPrivilege", zap.Error(err))
return err
}
return nil
}
func (d *Amo) UpdateAmountPrivilegeByPenaID(ctx context.Context, penaID, privilegeID string, privilegeAmount int64) error {
privilegeAmountKey := fmt.Sprintf("privileges.%v.amount", privilegeID)
filter := bson.M{
"pena_id": penaID,
"is_deleted": false,
privilegeAmountKey: bson.M{"$exists": true},
}
updateData := bson.M{
"updated_at": time.Now(),
privilegeAmountKey: privilegeAmount,
}
update := bson.D{
{Key: "$set", Value: updateData},
}
_, err := d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoUpdateAmountPrivilege", zap.Error(err))
return err
}
return nil
}
func (d *Amo) DeletePrivilege(ctx context.Context, id, privilegeID string) error {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorAmoDeletetPrivilege", zap.Error(err))
return err
}
filter := bson.M{"_id": objID, "is_deleted": false}
privilegeKey := fmt.Sprintf("privileges.%v", privilegeID)
update := bson.D{
{Key: "$unset", Value: bson.M{privilegeKey: ""}},
{Key: "$set", Value: bson.M{"updated_at": time.Now()}},
}
_, err = d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoDeletePrivilege", zap.Error(err))
return err
}
return nil
}
func (d *Amo) DeletePrivilegeByPenaID(ctx context.Context, penaID, privilegeID string) error {
filter := bson.M{"_id": penaID, "is_deleted": false}
privilegeKey := fmt.Sprintf("privileges.%v", privilegeID)
update := bson.D{
{Key: "$unset", Value: bson.M{privilegeKey: ""}},
{Key: "$set", Value: bson.M{"updated_at": time.Now()}},
}
_, err := d.coll.UpdateOne(ctx, filter, update)
if err != nil {
d.logger.Error("ErrorAmoDeletePrivilege", zap.Error(err))
return err
}
return nil
}

@ -3,12 +3,13 @@ package mongos
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type GDisk struct {
@ -45,11 +46,14 @@ func (d *GDisk) InsertOrUpdate(ctx context.Context, record *model.GDisk) (string
if record.TemplateFolder == "" {
record.TemplateFolder = "templategen/templates"
}
if record.SaveFolder == "" {
record.SaveFolder = "templategen/saved"
}
result, err := d.coll.InsertOne(ctx, record)
var result *mongo.InsertOneResult
result, err = d.coll.InsertOne(ctx, record)
if err != nil {
d.logger.Error("ErrorInsertOrUpdateGDisk", zap.Error(err))
return "", err
@ -82,14 +86,15 @@ func (d *GDisk) GetByID(ctx context.Context, id string) (*model.GDisk, error) {
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetGDisk", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetGDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetGDisk", zap.String("id", result.ID))
return &result, nil
@ -102,34 +107,36 @@ func (d *GDisk) GetByEmail(ctx context.Context, email string) (*model.GDisk, err
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetGDisk", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetGDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetGDisk", zap.String("id", result.ID))
return &result, nil
}
func (d *GDisk) GetListByUserID(ctx context.Context, userID string) ([]model.GDisk, error) {
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *GDisk) GetListByPenaID(ctx context.Context, penaID string) ([]model.GDisk, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
var result []model.GDisk
cur, err := d.coll.Find(ctx, filter)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetListGDisk", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetListGDisk", zap.Error(err))
return nil, err
}
}
err = cur.All(ctx, &result)
if err != nil {
@ -222,7 +229,7 @@ func (d *GDisk) Update(ctx context.Context, record *model.GDisk) error {
update["save_folder_id"] = record.SaveFolderID
}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateGDisk", zap.Error(err))
@ -232,18 +239,12 @@ func (d *GDisk) Update(ctx context.Context, record *model.GDisk) error {
return nil
}
func (d *GDisk) DeleteByUserID(ctx context.Context, userID string) error {
if userID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeleteGDisk", zap.Error(err))
return err
}
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *GDisk) DeleteByPenaID(ctx context.Context, penaID string) error {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorDeleteGDisk", zap.Error(err))
return err

@ -3,12 +3,12 @@ package mongos
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
)
@ -59,14 +59,15 @@ func (d *History) GetByID(ctx context.Context, id string) (*model.History, error
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetHistory", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetHistory", zap.String("id", result.ID))
return &result, nil
@ -76,11 +77,6 @@ type HistoryFilter struct {
AmoID string `bson:"amo_id,omitempty" json:"amo_id"`
WorkerTaskID string `bson:"worker_task_id,omitempty" json:"worker_task_id"`
AmoUserID int64 `bson:"amo_user_id,omitempty" json:"amo_user_id"`
// LeadID int64 `bson:"lead_id,omitempty" json:"lead_id"`
// Trigger model.HistoryTrigger `bson:"trigger,omitempty" json:"trigger"`
// TemplateID string `bson:"template_id,omitempty" json:"template_id"`
// Source *model.HistorySource `bson:"source,omitempty" json:"source"`
// Target *model.HistoryTarget `bson:"target,omitempty" json:"target"`
}
func (d *History) GetByFilter(ctx context.Context, filter *HistoryFilter) ([]model.History, error) {
@ -88,15 +84,13 @@ func (d *History) GetByFilter(ctx context.Context, filter *HistoryFilter) ([]mod
filter = &HistoryFilter{}
}
cur, err := d.coll.Find(ctx, bson.M{})
cur, err := d.coll.Find(ctx, filter)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
}
}
var result []model.History
err = cur.All(ctx, &result)
@ -150,14 +144,15 @@ func (d *History) GetByWorkerTaskID(ctx context.Context, workerTaskID string) (*
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetHistory", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetHistory", zap.String("id", result.ID))
return &result, nil
@ -192,15 +187,15 @@ func (d *History) UpdateByID(ctx context.Context, record *model.History) error {
update["worker_task_id"] = record.WorkerTaskID
}
if record.PublicUrl != "" {
update["public_url"] = record.PublicUrl
if record.PublicURL != "" {
update["public_url"] = record.PublicURL
}
if record.DownloadUrl != "" {
update["download_url"] = record.DownloadUrl
if record.DownloadURL != "" {
update["download_url"] = record.DownloadURL
}
_, err = d.coll.UpdateByID(ctx, objID, bson.D{{"$set", update}})
_, err = d.coll.UpdateByID(ctx, objID, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateHistory", zap.Error(err))
return err

@ -3,13 +3,14 @@ package mongos
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"time"
)
type PenaDisk struct {
@ -25,13 +26,9 @@ func (d *PenaDisk) InsertOrUpdate(ctx context.Context, record *model.PenaDisk) (
record.CreatedAt = time.Now()
record.UpdatedAt = record.CreatedAt
var found *model.PenaDisk
var err error
found, err := d.GetByPenaID(ctx, record.PenaID)
if record.UserID != "" {
found, err = d.GetByUserID(ctx, record.UserID)
} else {
err = errors.New("user_id required")
if err != nil {
d.logger.Error("ErrorInsertOrUpdatePenaDisk", zap.Error(err))
return "", err
}
@ -39,10 +36,10 @@ func (d *PenaDisk) InsertOrUpdate(ctx context.Context, record *model.PenaDisk) (
if found == nil {
// Set default TemplateFolder && SaveFolder
if record.TemplateFolder == "" {
record.TemplateFolder = penadisk.DEFAULT_TEMPLATE_FOLDER
record.TemplateFolder = penadisk.DefaultTemplateFolder
}
if record.SaveFolder == "" {
record.SaveFolder = penadisk.DEFAULT_SAVE_FOLDER
record.SaveFolder = penadisk.DefaultSaveFolder
}
result, err := d.coll.InsertOne(ctx, record)
@ -70,40 +67,42 @@ func (d *PenaDisk) GetByID(ctx context.Context, id string) (*model.PenaDisk, err
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetPenaDisk", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetPenaDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetPenaDisk", zap.String("id", result.ID))
return &result, nil
}
func (d *PenaDisk) GetByUserID(ctx context.Context, userID string) (*model.PenaDisk, error) {
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *PenaDisk) GetByPenaID(ctx context.Context, penaID string) (*model.PenaDisk, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
var result model.PenaDisk
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetPenaDisk", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetPenaDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetPenaDisk", zap.String("id", result.ID))
return &result, nil
}
// Update - обновляет запись по её id или UserID
// Update - обновляет запись по её id или PenaID.
func (d *PenaDisk) Update(ctx context.Context, record *model.PenaDisk) error {
filter := bson.M{"is_deleted": false}
@ -114,10 +113,10 @@ func (d *PenaDisk) Update(ctx context.Context, record *model.PenaDisk) error {
return err
}
filter["_id"] = objID
} else if record.UserID != "" {
filter["user_id"] = record.UserID
} else if record.PenaID != "" {
filter["pena_id"] = record.PenaID
} else {
err := errors.New("got empty id and user_id")
err := errors.New("got empty id and pena_id")
d.logger.Error("ErrorUpdatePenaDisk", zap.Error(err))
return err
}
@ -136,7 +135,7 @@ func (d *PenaDisk) Update(ctx context.Context, record *model.PenaDisk) error {
update["save_folder"] = record.SaveFolder
}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdatePenaDisk", zap.Error(err))
@ -146,18 +145,12 @@ func (d *PenaDisk) Update(ctx context.Context, record *model.PenaDisk) error {
return nil
}
func (d *PenaDisk) DeleteByUserID(ctx context.Context, userID string) error {
if userID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeletePenaDisk", zap.Error(err))
return err
}
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *PenaDisk) DeleteByPenaID(ctx context.Context, penaID string) error {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorDeletePenaDisk", zap.Error(err))
return err

@ -4,12 +4,13 @@ import (
"context"
"errors"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type Template struct {
@ -27,24 +28,12 @@ func (d *Template) Insert(ctx context.Context, record *model.Template) (string,
record.UpdatedAt = now
record.IsDeleted = false
if record.UserID == "" {
if record.PenaID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeleteTemplate", zap.Error(err))
return "", err
}
//found, err := d.GetByNameAndUserID(ctx, record.Name, record.UserID)
//if err != nil {
// d.logger.Error("ErrorInsertTemplate", zap.Error(err))
// return "", err
//}
//if found != nil {
// err = errors.New("template with this name already exists")
// d.logger.Error("ErrorInsertTemplate", zap.Error(err))
// return "", err
//}
if record.GroupIDs == nil {
record.GroupIDs = []string{}
}
@ -75,43 +64,45 @@ func (d *Template) GetByID(ctx context.Context, id string) (*model.Template, err
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetTemplate", zap.String("id", result.ID))
return &result, nil
}
func (d *Template) GetByLeadId(ctx context.Context, id int64) (*model.Template, error) {
func (d *Template) GetByLeadID(ctx context.Context, id int64) (*model.Template, error) {
filter := bson.M{"lead_id": id, "is_deleted": false}
var result model.Template
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetTemplate", zap.String("id", result.ID))
return &result, nil
}
func (d *Template) GetByNameAndUserID(ctx context.Context, name, userID string) (*model.Template, error) {
filter := bson.M{"name": name, "user_id": userID, "is_deleted": false}
func (d *Template) GetByNameAndPenaID(ctx context.Context, name, penaID string) (*model.Template, error) {
filter := bson.M{"name": name, "pena_id": penaID, "is_deleted": false}
fmt.Println("-----------------------------------")
fmt.Println(filter)
@ -119,43 +110,45 @@ func (d *Template) GetByNameAndUserID(ctx context.Context, name, userID string)
var result model.Template
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetTemplate", zap.String("id", result.ID))
return &result, nil
}
func (d *Template) GetByFilenameAndUserID(ctx context.Context, name, userID string) (*model.Template, error) {
filter := bson.M{"filename": name, "user_id": userID, "is_deleted": false}
func (d *Template) GetByFilenameAndPenaID(ctx context.Context, name, penaID string) (*model.Template, error) {
filter := bson.M{"filename": name, "pena_id": penaID, "is_deleted": false}
var result model.Template
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetTemplate", zap.String("id", result.ID))
return &result, nil
}
func (d *Template) GetListByUserID(ctx context.Context, userID string) ([]model.Template, error) {
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *Template) GetListByPenaID(ctx context.Context, penaID string) ([]model.Template, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
var result []model.Template
@ -163,29 +156,29 @@ func (d *Template) GetListByUserID(ctx context.Context, userID string) ([]model.
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
err = cursor.All(ctx, &result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
return result, nil
}
func (d *Template) GetListByUserIDAndStorageType(ctx context.Context, userID, storageType string) ([]model.Template,
func (d *Template) GetListByPenaIDAndStorageType(ctx context.Context, penaID, storageType string) ([]model.Template,
error) {
filter := bson.M{"user_id": userID, "storage_type": storageType, "is_deleted": false}
filter := bson.M{"pena_id": penaID, "storage_type": storageType, "is_deleted": false}
var result []model.Template
@ -193,28 +186,27 @@ func (d *Template) GetListByUserIDAndStorageType(ctx context.Context, userID, st
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
err = cursor.All(ctx, &result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplate", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
return result, nil
}
func (d *Template) GetListByGroupID(ctx context.Context, groupID, userID string) ([]model.Template, error) {
filter := bson.M{"user_id": userID}
func (d *Template) GetListByGroupID(ctx context.Context, groupID, penaID string) ([]model.Template, error) {
filter := bson.M{"pena_id": penaID}
if groupID == "" {
filter["group_ids"] = []string{}
@ -240,7 +232,7 @@ func (d *Template) GetListByGroupID(ctx context.Context, groupID, userID string)
return result, nil
}
func (d *Template) PushGroup(ctx context.Context, id, groupID, userID string) error {
func (d *Template) PushGroup(ctx context.Context, id, groupID, penaID string) error {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
@ -256,8 +248,8 @@ func (d *Template) PushGroup(ctx context.Context, id, groupID, userID string) er
filter := bson.M{"_id": objID}
if userID != "" {
filter["user_id"] = userID
if penaID != "" {
filter["pena_id"] = penaID
}
update := bson.M{
@ -277,7 +269,7 @@ func (d *Template) PushGroup(ctx context.Context, id, groupID, userID string) er
return nil
}
func (d *Template) PullGroup(ctx context.Context, id, groupID, userID string) error {
func (d *Template) PullGroup(ctx context.Context, id, groupID, penaID string) error {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
@ -293,8 +285,8 @@ func (d *Template) PullGroup(ctx context.Context, id, groupID, userID string) er
filter := bson.M{"_id": objID}
if userID != "" {
filter["user_id"] = userID
if penaID != "" {
filter["pena_id"] = penaID
}
update := bson.M{
@ -362,8 +354,8 @@ func (d *Template) UpdateByID(ctx context.Context, record *model.Template) error
update["file"] = record.File
}
if record.LeadId > 0 {
update["lead_id"] = record.LeadId
if record.LeadID > 0 {
update["lead_id"] = record.LeadID
}
if record.StorageID != "" {
@ -374,7 +366,7 @@ func (d *Template) UpdateByID(ctx context.Context, record *model.Template) error
update["storage_type"] = record.StorageType
}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateTemplate", zap.Error(err))
@ -385,13 +377,13 @@ func (d *Template) UpdateByID(ctx context.Context, record *model.Template) error
}
func (d *Template) UpdateByLeadID(ctx context.Context, record *model.Template) error {
if record.LeadId <= 0 {
if record.LeadID < 1 {
err := errors.New("got empty id")
d.logger.Error("ErrorUpdateTemplate", zap.Error(err))
return err
}
filter := bson.M{"lead_id": record.LeadId, "is_deleted": false}
filter := bson.M{"lead_id": record.LeadID, "is_deleted": false}
update := bson.M{"updated_at": time.Now()}
@ -403,8 +395,8 @@ func (d *Template) UpdateByLeadID(ctx context.Context, record *model.Template) e
update["file"] = record.File
}
if record.LeadId > 0 {
update["lead_id"] = record.LeadId
if record.LeadID > 0 {
update["lead_id"] = record.LeadID
}
if record.StorageID != "" {
@ -415,7 +407,7 @@ func (d *Template) UpdateByLeadID(ctx context.Context, record *model.Template) e
update["storage_type"] = record.StorageType
}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateTemplate", zap.Error(err))
@ -443,7 +435,7 @@ func (d *Template) DeleteByID(ctx context.Context, id string) error {
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorDeleteTemplate", zap.Error(err))
return err

@ -3,12 +3,13 @@ package mongos
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type TemplateGroup struct {
@ -25,8 +26,8 @@ func (d *TemplateGroup) Insert(ctx context.Context, record *model.TemplateGroup)
record.CreatedAt = now
record.UpdatedAt = now
if record.UserID == "" {
err := errors.New("user_id required")
if record.PenaID == "" {
err := errors.New("pena_id required")
d.logger.Error("ErrorInsertTemplateGroup", zap.Error(err))
return "", err
}
@ -64,41 +65,43 @@ func (d *TemplateGroup) GetByID(ctx context.Context, id string) (*model.Template
var result model.TemplateGroup
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplateGroup", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplateGroup", zap.Error(err))
return nil, err
}
}
d.logger.Info("ErrorGetTemplateGroup", zap.String("id", result.ID))
return &result, nil
}
func (d *TemplateGroup) GetListByUserID(ctx context.Context, userID string) ([]model.TemplateGroup, error) {
if userID == "" {
err := errors.New("user_id required")
func (d *TemplateGroup) GetListByPenaID(ctx context.Context, penaID string) ([]model.TemplateGroup, error) {
if penaID == "" {
err := errors.New("pena_id required")
d.logger.Error("ErrorGetListTemplateGroup")
return nil, err
}
filter := bson.M{"user_id": userID}
filter := bson.M{"pena_id": penaID}
var result []model.TemplateGroup
cur, err := d.coll.Find(ctx, filter)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
if err == mongo.ErrNoDocuments {
d.logger.Info("InfoGetTemplateGroup", zap.String("id", "not found"))
return nil, nil
}
d.logger.Error("ErrorGetTemplateGroup", zap.Error(err))
return nil, err
}
}
err = cur.All(ctx, &result)
@ -122,8 +125,8 @@ func (d *TemplateGroup) Update(ctx context.Context, record *model.TemplateGroup)
filter := bson.M{"_id": objID}
if record.UserID != "" {
filter["user_id"] = record.UserID
if record.PenaID != "" {
filter["pena_id"] = record.PenaID
}
update := bson.M{"updated_at": time.Now()}
@ -132,7 +135,7 @@ func (d *TemplateGroup) Update(ctx context.Context, record *model.TemplateGroup)
update["name"] = record.Name
}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateTemplateGroup")
@ -163,14 +166,8 @@ func (d *TemplateGroup) Delete(ctx context.Context, id string) error {
return nil
}
func (d *TemplateGroup) DeleteByUserID(ctx context.Context, userID string) error {
if userID == "" {
err := errors.New("user_id required")
d.logger.Error("ErrorDeleteTemplateGroup", zap.Error(err))
return err
}
filter := bson.M{"user_id": userID}
func (d *TemplateGroup) DeleteByPenaID(ctx context.Context, penaID string) error {
filter := bson.M{"pena_id": penaID}
_, err := d.coll.DeleteMany(ctx, filter)

@ -1,203 +0,0 @@
package mongos
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type User struct {
coll *mongo.Collection
logger *zap.Logger
}
func InitUser(db *mongo.Database, logger *zap.Logger) *User {
return &User{coll: db.Collection("user"), logger: logger}
}
func (d *User) Insert(ctx context.Context, record *model.User) (string, error) {
now := time.Now()
record.CreatedAt = now
record.UpdatedAt = now
record.IsDeleted = false
// Find user by email
found, err := d.GetByEmail(ctx, record.Email)
if found != nil {
err = errors.New("user already exists")
}
if err != nil {
d.logger.Error("ErrorInsertUser", zap.Error(err))
return "", err
}
gpass, err := bcrypt.GenerateFromPassword([]byte(record.Password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
record.Password = string(gpass)
result, err := d.coll.InsertOne(ctx, record)
if err != nil {
d.logger.Error("ErrorInsertUser", zap.Error(err))
return "", err
}
return result.InsertedID.(primitive.ObjectID).Hex(), nil
}
func (d *User) GetByID(ctx context.Context, id string) (*model.User, error) {
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorGetUser", zap.Error(err))
return nil, err
}
filter := bson.M{"_id": objID, "is_deleted": false}
var result model.User
err = d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetUser", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetUser", zap.String("id", result.ID))
return &result, nil
}
func (d *User) GetByEmail(ctx context.Context, email string) (*model.User, error) {
filter := bson.M{"email": email, "is_deleted": false}
var result model.User
err := d.coll.FindOne(ctx, filter).Decode(&result)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetUser", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetUser", zap.String("id", result.ID))
return &result, nil
}
//func (d *User) GetByFilter(ctx context.Context, ) ([]model.User, error) {
//
//}
func (d *User) Update(ctx context.Context, record *model.User) error {
if record.ID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
objID, err := primitive.ObjectIDFromHex(record.ID)
if err != nil {
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
filter := bson.M{"_id": objID, "is_deleted": false}
update := bson.M{"updated_at": time.Now()}
if record.FullName != "" {
update["username"] = record.FullName
}
if record.Email != "" {
update["email"] = record.Email
}
if record.RoleID > 0 {
update["role_id"] = record.RoleID
}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
if err != nil {
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
return nil
}
func (d *User) UpdateIsActivated(ctx context.Context, id string, isActivated bool) error {
if id == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
filter := bson.M{"_id": objID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_activated": isActivated}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
if err != nil {
d.logger.Error("ErrorUpdateUser", zap.Error(err))
return err
}
return nil
}
func (d *User) Delete(ctx context.Context, id string) error {
if id == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeleteUser", zap.Error(err))
return err
}
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
d.logger.Error("ErrorDeleteUser", zap.Error(err))
return err
}
filter := bson.M{"_id": objID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
if err != nil {
d.logger.Error("ErrorDeleteUser", zap.Error(err))
return err
}
return nil
}

@ -3,13 +3,14 @@ package mongos
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"time"
)
type WorkerTask struct {
@ -27,7 +28,7 @@ func (d *WorkerTask) Insert(ctx context.Context, record *model.WorkerTask) (stri
}
record.CreatedAt = time.Now()
record.UpdatedAt = time.Now()
record.UpdatedAt = record.CreatedAt
result, err := d.coll.InsertOne(ctx, record)
@ -59,24 +60,22 @@ func (d *WorkerTask) GetByID(ctx context.Context, id string) (*model.WorkerTask,
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetWorkerTask", zap.String("id", result.ID))
return &result, nil
}
type WorkerStatusFilter struct {
UserID string `json:"user_id" bson:"user_id,omitempty"` // Пользователь пены
PenaID string `json:"pena_id" bson:"pena_id,omitempty"` // Пользователь пены
AmoID string `json:"amo_id" bson:"amo_id,omitempty"` // Амо аккаунт вызвавший генерацию
AmoUserID int64 `json:"amo_user_id" bson:"amo_user_id,omitempty"` // Пользователь амо вызвавший генерацию
Status model.WorkerTaskStatus `json:"status" bson:"status,omitempty"` // Статус генерации
Data map[string]any `json:"data" bson:"data,omitempty"` // Данные по которым происходит генерация
LeadId int64 `json:"lead_id" bson:"lead_id,omitempty"` // Сделка по которой происходит генерация
LeadID int64 `json:"lead_id" bson:"lead_id,omitempty"` // Сделка по которой происходит генерация
TemplateID string `json:"template_id" bson:"template_id,omitempty"` // Шаблон для генерации
Source *model.WorkerSource `json:"source" bson:"source,omitempty"` // Исходный файл-шаблон
Target *model.WorkerTarget `json:"target" bson:"target,omitempty"`
@ -91,12 +90,10 @@ func (d *WorkerTask) GetByFilter(ctx context.Context, filter *WorkerStatusFilter
cur, err := d.coll.Find(ctx, filter, opts)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
}
var result []model.WorkerTask
err = cur.All(ctx, &result)
@ -123,9 +120,9 @@ func (d *WorkerTask) UpdateStatus(ctx context.Context, id string, status model.W
return err
}
update := bson.M{"status": status}
update := bson.M{"status": status, "updated_at": time.Now()}
_, err = d.coll.UpdateByID(ctx, objID, bson.D{{"$set", update}})
_, err = d.coll.UpdateByID(ctx, objID, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateWorkerTaskStatus", zap.Error(err))
@ -155,8 +152,8 @@ func (d *WorkerTask) UpdateByID(ctx context.Context, record *model.WorkerTask) e
update := bson.M{"updated_at": time.Now()}
if record.UserID != "" {
update["user_id"] = record.UserID
if record.PenaID != "" {
update["pena_id"] = record.PenaID
}
if record.AmoID != "" {
@ -176,8 +173,9 @@ func (d *WorkerTask) UpdateByID(ctx context.Context, record *model.WorkerTask) e
update["status"] = record.Status
}
if record.LeadId > 0 {
update["lead_id"] = record.LeadId
// TODO: Изменить логику работы, когда Михаил примет решение по этому моменту.
if record.LeadID > 0 {
update["lead_id"] = record.LeadID
}
if record.TemplateID != "" {
@ -208,15 +206,15 @@ func (d *WorkerTask) UpdateByID(ctx context.Context, record *model.WorkerTask) e
update["target.storage_type"] = record.Target.StorageType
}
if record.DownloadUrl != "" {
update["download_url"] = record.DownloadUrl
if record.DownloadURL != "" {
update["download_url"] = record.DownloadURL
}
if record.PublicUrl != "" {
update["public_url"] = record.PublicUrl
if record.PublicURL != "" {
update["public_url"] = record.PublicURL
}
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err = d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateWorkerTask", zap.Error(err))
@ -248,14 +246,20 @@ func (d *WorkerTask) Delete(ctx context.Context, id string) error {
return nil
}
// Listen - смотрит за добавлением и обновлением задач с выбранным статусом и возвращает их в выбранный канал
// Listen - смотрит за добавлением и обновлением задач с выбранным статусом и возвращает их в выбранный канал.
func (d *WorkerTask) Listen(ctx context.Context, status model.WorkerTaskStatus, workerTaskChan chan model.WorkerTask) {
operationTypes := []bson.D{{{"operationType", "insert"}},
{{"operationType", "update"}}}
operationTypes := []bson.D{
{{Key: "operationType", Value: "insert"}},
{{Key: "operationType", Value: "update"}},
}
matchStageOpTypes := bson.D{{"$match", bson.D{{"$or", operationTypes}}}}
matchStageOpTypes := bson.D{
{Key: "$match", Value: bson.D{{Key: "$or", Value: operationTypes}}},
}
matchStageStatus := bson.D{{"$match", bson.D{{"fullDocument.status", status}}}}
matchStageStatus := bson.D{
{Key: "$match", Value: bson.D{{Key: "fullDocument.status", Value: status}}},
}
opts := options.ChangeStream().SetFullDocument(options.UpdateLookup)
@ -299,7 +303,6 @@ func (d *WorkerTask) Listen(ctx context.Context, status model.WorkerTaskStatus,
d.logger.Error("ErrorWatchWorkerTask", zap.Error(err))
return
}
}
workerTaskChan <- task
}
@ -308,12 +311,10 @@ func (d *WorkerTask) Listen(ctx context.Context, status model.WorkerTaskStatus,
}
}()
select {
case <-ctx.Done():
err = changeStream.Close(context.TODO())
<-ctx.Done()
err = changeStream.Close(context.Background()) // TODO: проверить сокращенную запись в ssa
if err != nil {
d.logger.Error("ErrorWatchWorkerTask", zap.Error(err))
}
return
}
}

@ -3,13 +3,14 @@ package mongos
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
YaDisk2 "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
"time"
)
type YaDisk struct {
@ -44,13 +45,15 @@ func (d *YaDisk) InsertOrUpdate(ctx context.Context, record *model.YaDisk) (stri
if found == nil {
// Set default TemplateFolder && SaveFolder
if record.TemplateFolder == "" {
record.TemplateFolder = YaDisk2.DEFAULT_TEMPLATE_FOLDER
record.TemplateFolder = YaDisk2.DefaultTemplateFolder
}
if record.SaveFolder == "" {
record.SaveFolder = YaDisk2.DEFAULT_SAVE_FOLDER
record.SaveFolder = YaDisk2.DefaultSaveFolder
}
result, err := d.coll.InsertOne(ctx, record)
var result *mongo.InsertOneResult
result, err = d.coll.InsertOne(ctx, record)
if err != nil {
d.logger.Error("ErrorInsertOrUpdateYaDisk", zap.Error(err))
return "", err
@ -92,12 +95,10 @@ func (d *YaDisk) GetByID(ctx context.Context, id string) (*model.YaDisk, error)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetYaDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetYaDisk", zap.String("id", result.ID))
return &result, nil
@ -112,19 +113,17 @@ func (d *YaDisk) GetByLogin(ctx context.Context, login string) (*model.YaDisk, e
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetYaDisk", zap.Error(err))
return nil, err
}
}
d.logger.Info("InfoGetYaDisk", zap.String("id", result.ID))
return &result, nil
}
func (d *YaDisk) GetListByUserID(ctx context.Context, userID string) ([]model.YaDisk, error) {
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *YaDisk) GetListByPenaID(ctx context.Context, penaID string) ([]model.YaDisk, error) {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
result := []model.YaDisk{}
@ -132,12 +131,10 @@ func (d *YaDisk) GetListByUserID(ctx context.Context, userID string) ([]model.Ya
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
} else if err != nil {
d.logger.Error("ErrorGetListYaDisk", zap.Error(err))
return nil, err
}
}
err = cur.All(ctx, &result)
if err != nil {
@ -148,7 +145,7 @@ func (d *YaDisk) GetListByUserID(ctx context.Context, userID string) ([]model.Ya
return result, nil
}
// Update - обновляет запись по её id или логину Yandex
// Update - обновляет запись по её id или логину Yandex.
func (d *YaDisk) Update(ctx context.Context, record *model.YaDisk) error {
filter := bson.M{"is_deleted": false}
@ -206,7 +203,7 @@ func (d *YaDisk) Update(ctx context.Context, record *model.YaDisk) error {
update["save_folder"] = record.SaveFolder
}
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorUpdateYaDisk", zap.Error(err))
@ -216,18 +213,12 @@ func (d *YaDisk) Update(ctx context.Context, record *model.YaDisk) error {
return nil
}
func (d *YaDisk) DeleteByUserID(ctx context.Context, userID string) error {
if userID == "" {
err := errors.New("got empty user id")
d.logger.Error("ErrorDeleteYaDisk", zap.Error(err))
return err
}
filter := bson.M{"user_id": userID, "is_deleted": false}
func (d *YaDisk) DeleteByPenaID(ctx context.Context, penaID string) error {
filter := bson.M{"pena_id": penaID, "is_deleted": false}
update := bson.M{"updated_at": time.Now(), "is_deleted": true}
_, err := d.coll.UpdateMany(ctx, filter, bson.D{{"$set", update}})
_, err := d.coll.UpdateMany(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
d.logger.Error("ErrorDeleteYaDisk", zap.Error(err))
return err

@ -1,9 +0,0 @@
package postgres
import (
"errors"
)
var ErrorGotEmptyRow = errors.New("got empty row")
var ErrorNotFound = errors.New("no rows in result set")
var ErrorTimeout = errors.New("timeout")

@ -1,276 +0,0 @@
package postgres
import (
"context"
"errors"
"fmt"
rs "github.com/danilsolovyov/reflectgostructv1"
"github.com/jackc/pgx/v4/pgxpool"
"golang.org/x/crypto/bcrypt"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"strconv"
"strings"
"time"
)
type User struct {
conn *pgxpool.Pool
}
func InitUser(ctx context.Context, conn *pgxpool.Pool) *User {
d := &User{conn: conn}
err := d.init(ctx)
if err != nil {
//glg.Error("ErrInitUser:", err)
return nil
}
return d
}
func (d *User) init(ctx context.Context) error {
s := rs.PsqlTagToSql(&model.User{})
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS \"user\" (%v);", s)
_, err := d.conn.Exec(ctx, sql)
return err
}
func (d *User) Insert(ctx context.Context, record *model.User) (string, error) {
now := time.Now().UTC()
record.CreatedAt = now
record.UpdatedAt = now
record.IsDeleted = false
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return "", err
}
// Find users by email
sql := fmt.Sprintf("SELECT id FROM \"user\" WHERE email = '%v'", record.Email)
var foundID int
err = conn.QueryRow(ctx, sql).Scan(&foundID)
if err != nil {
if err.Error() != ErrorNotFound.Error() {
return "", err
}
}
if foundID > 0 {
fmt.Println("user already exists", foundID)
err = errors.New("user already exists")
return "", err
}
gpass, err := bcrypt.GenerateFromPassword([]byte(record.Password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
record.Password = string(gpass)
tags, values := rs.GetPsqlTagsAndValues(record)
sql = fmt.Sprintf("INSERT INTO \"user\" (%v) VALUES (%v) RETURNING id;", tags, values)
var id int
err = conn.QueryRow(ctx, sql).Scan(&id)
conn.Release()
if err != nil {
return "", err
}
return strconv.Itoa(id), nil
}
func (d *User) GetByID(ctx context.Context, id int) (*model.User, error) {
var result model.User
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return nil, err
}
sql := fmt.Sprintf("SELECT * FROM \"user\" WHERE id = %v", id)
rows, err := conn.Query(ctx, sql)
conn.Release()
if err != nil {
return nil, err
}
for rows.Next() {
err = rows.Scan(&result.ID, &result.FullName, &result.Email, &result.Password,
&result.IsActivated, &result.JwtToken, &result.IsDeleted, &result.CreatedAt, &result.UpdatedAt)
if err != nil {
return nil, err
}
}
return &result, err
}
func (d *User) GetByEmail(ctx context.Context, email string) (*model.User, error) {
var result model.User
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return nil, err
}
sql := fmt.Sprintf("SELECT * FROM \"user\" WHERE email = '%v'", email)
rows, err := conn.Query(ctx, sql)
conn.Release()
if err != nil {
return nil, err
}
id := 0
for rows.Next() {
err = rows.Scan(&id, &result.FullName, &result.Email, &result.Password,
&result.IsActivated, &result.RoleID, &result.JwtToken, &result.IsDeleted, &result.CreatedAt,
&result.UpdatedAt)
if err != nil {
return nil, err
}
}
result.ID = strconv.Itoa(id)
return &result, err
}
func (d *User) GetByFilter(ctx context.Context, start, count int, needle map[string]string) ([]model.User, error) {
var result []model.User
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return nil, err
}
needleToSql := ""
if len(needle) > 0 {
needleToSql += " AND "
i := 0
for k, v := range needle {
v = strings.ReplaceAll(v, "'", "''")
needleToSql += fmt.Sprintf("%v = %v", k, v)
if i < len(needle) {
needleToSql += " AND "
}
i++
}
}
sql := fmt.Sprintf("SELECT * FROM \"user\" WHERE (id >= %v %v) ORDER BY id LIMIT %v;",
start,
needleToSql,
count,
)
rows, err := conn.Query(ctx, sql)
conn.Release()
if err != nil {
return nil, err
}
if rows == nil {
err = ErrorGotEmptyRow
return nil, err
}
for rows.Next() {
var u model.User
err = rows.Scan(&u.ID, &u.FullName, &u.Email, &u.Password,
&u.IsActivated, &u.RoleID, &u.JwtToken, &u.IsDeleted, &u.CreatedAt, &u.UpdatedAt)
if err != nil {
return nil, err
}
result = append(result, u)
}
return result, nil
}
func (d *User) UpdateByID(ctx context.Context, record *model.User) error {
record.UpdatedAt = time.Now()
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
tags, values := rs.GetPsqlTagsAndValues(record)
sql := fmt.Sprintf("UPDATE \"user\" SET (%v) = (%v) WHERE id = %v;", tags, values, record.ID)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}
func (d *User) DeleteByID(ctx context.Context, id int) error {
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
sql := fmt.Sprintf("UPDATE \"user\" SET is_deleted = true WHERE id = %v", id)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}
func (d *User) SetJwtToken(ctx context.Context, id int, jwtToken string) error {
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
sql := fmt.Sprintf("UPDATE \"user\" SET jwt_token = '%v' WHERE id = %v", jwtToken, id)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}
func (d *User) ChangePassword(ctx context.Context, id int, password string) error {
gpass, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
sql := fmt.Sprintf("UPDATE \"user\" SET password = '%v' WHERE id = %v", gpass, id)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}

@ -1,242 +0,0 @@
package postgres
import (
"context"
"errors"
"fmt"
rs "github.com/danilsolovyov/reflectgostructv1"
"github.com/jackc/pgx/v4/pgxpool"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"strconv"
"strings"
"time"
)
type YaDisk struct {
conn *pgxpool.Pool
}
func InitYaDisk(ctx context.Context, conn *pgxpool.Pool) *YaDisk {
d := &YaDisk{conn: conn}
err := d.init(ctx)
if err != nil {
//glg.Error("ErrInitYaDisk", err)
return nil
}
return d
}
func (d *YaDisk) init(ctx context.Context) error {
s := rs.PsqlTagToSql(&model.YaDisk{})
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS \"YaDisk\" (%v);", s)
_, err := d.conn.Exec(ctx, sql)
return err
}
func (d *YaDisk) Insert(ctx context.Context, record *model.YaDisk) (int, error) {
now := time.Now().UTC()
record.CreatedAt = now
record.UpdatedAt = now
record.IsDeleted = false
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return 0, err
}
//Find YaDisk by user_id
sql := fmt.Sprintf("SELECT id FROM \"YaDisk\" WHERE user_id = '%v'", record.UserID)
var foundID int
err = conn.QueryRow(ctx, sql).Scan(&foundID)
if err != nil {
if err.Error() != ErrorNotFound.Error() {
return 0, err
}
}
if foundID > 0 {
fmt.Println("YaDisk already exists", foundID)
err = errors.New("YaDisk already exists")
return 0, err
}
tags, values := rs.GetPsqlTagsAndValues(record)
sql = fmt.Sprintf("INSERT INTO \"YaDisk\" (%v) VALUES (%v) RETURNING id;", tags, values)
var id int
err = conn.QueryRow(ctx, sql).Scan(&id)
conn.Release()
if err != nil {
return 0, err
}
return id, nil
}
func (d *YaDisk) InsertOrUpdate(ctx context.Context, record *model.YaDisk) (string, error) {
now := time.Now().UTC()
record.CreatedAt = now
record.UpdatedAt = now
record.IsDeleted = false
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return "", err
}
//Find YaDisk by user_id
sql := fmt.Sprintf("SELECT id FROM \"YaDisk\" WHERE user_id = '%v'", record.UserID)
var foundID int
err = conn.QueryRow(ctx, sql).Scan(&foundID)
if err != nil {
fmt.Println("errr", err)
if err.Error() != ErrorNotFound.Error() {
return "", err
}
}
tags, values := rs.GetPsqlTagsAndValues(record)
var id int
if foundID > 0 {
sql = fmt.Sprintf("UPDATE \"YaDisk\" SET (%v) = (%v) WHERE id = %v;", tags, values, record.ID)
id = foundID
} else {
sql = fmt.Sprintf("INSERT INTO \"YaDisk\" (%v) VALUES (%v) RETURNING id;", tags, values)
}
err = conn.QueryRow(ctx, sql).Scan(&id)
conn.Release()
if err != nil {
return "", err
}
return strconv.Itoa(id), nil
}
func (d *YaDisk) GetByID(ctx context.Context, id string) (*model.YaDisk, error) {
var result model.YaDisk
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return nil, err
}
sql := fmt.Sprintf("SELECT * FROM \"YaDisk\" WHERE id = %v", id)
rows, err := conn.Query(ctx, sql)
conn.Release()
if err != nil {
return nil, err
}
for rows.Next() {
err = rows.Scan(&result.ID, &result.UserID, &result.AccessToken, &result.TemplateFolder,
&result.SaveFolder, &result.ExpiresIn, &result.IsDeleted, &result.CreatedAt, &result.UpdatedAt)
if err != nil {
return nil, err
}
}
return &result, err
}
func (d *YaDisk) GetByFilter(ctx context.Context, start, count int, needle map[string]string) ([]model.YaDisk, error) {
var result []model.YaDisk
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return nil, err
}
needleToSql := ""
if len(needle) > 0 {
needleToSql += " AND "
i := 0
for k, v := range needle {
v = strings.ReplaceAll(v, "'", "''")
needleToSql += fmt.Sprintf("%v = %v", k, v)
if i < len(needle) {
needleToSql += " AND "
}
i++
}
}
sql := fmt.Sprintf("SELECT * FROM \"YaDisk\" WHERE (id >= %v %v) ORDER BY id LIMIT %v;",
start,
needleToSql,
count,
)
rows, err := conn.Query(ctx, sql)
conn.Release()
if err != nil {
return nil, err
}
if rows == nil {
err = ErrorGotEmptyRow
return nil, err
}
for rows.Next() {
var u model.YaDisk
err = rows.Scan(&u.ID, &u.UserID, &u.AccessToken, &u.TemplateFolder,
&u.SaveFolder, &u.ExpiresIn, &u.IsDeleted, &u.CreatedAt, &u.UpdatedAt)
if err != nil {
return nil, err
}
result = append(result, u)
}
return result, nil
}
func (d *YaDisk) UpdateByID(ctx context.Context, record *model.YaDisk) error {
record.UpdatedAt = time.Now().UTC()
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
tags, values := rs.GetPsqlTagsAndValues(record)
sql := fmt.Sprintf("UPDATE \"YaDisk\" SET (%v) = (%v) WHERE id = %v;", tags, values, record.ID)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}
func (d *YaDisk) DeleteByID(ctx context.Context, id string) error {
conn, err := d.conn.Acquire(ctx)
if err != nil {
conn.Release()
return err
}
sql := fmt.Sprintf("UPDATE \"YaDisk\" SET is_deleted = true WHERE id = %v", id)
err = conn.QueryRow(ctx, sql).Scan()
conn.Release()
if err != nil {
return err
}
return nil
}

@ -1,19 +1,21 @@
package GDisk
package gdisk
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/drive/v3"
"google.golang.org/api/option"
"io"
"net/http"
"os"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
"strings"
)
const (
@ -37,7 +39,7 @@ type ClientApp struct {
func NewClientApp(credentials string) (*ClientApp, error) {
b, err := os.ReadFile(credentials)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "NewClientApp.ReadFile")
}
config, err := google.ConfigFromJSON(b,
@ -45,21 +47,21 @@ func NewClientApp(credentials string) (*ClientApp, error) {
drive.DriveMetadataScope,
)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "NewClientApp.ConfigFromJSON")
}
return &ClientApp{Config: config}, nil
}
func (ca *ClientApp) GenerateOAuthUrl(userId, redirectUrl string) (string, error) {
state, err := tools.EncryptTokenRC4(tools.StateToken{
UserID: userId,
func (ca *ClientApp) GenerateOAuthURL(penaID, redirectURL string) (string, error) {
state, err := tools.EncryptTokenAES(tools.StateToken{
PenaID: penaID,
Service: "google",
RedirectUrl: redirectUrl,
RedirectURL: redirectURL,
})
if err != nil {
return "", err
return "", errors.Wrap(err, "GenerateOAuthURL.Encrypt")
}
return ca.Config.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.ApprovalForce), nil
@ -68,7 +70,7 @@ func (ca *ClientApp) GenerateOAuthUrl(userId, redirectUrl string) (string, error
func (ca *ClientApp) RefreshToken(ctx context.Context, oldToken *oauth2.Token) (*oauth2.Token, error) {
token, err := ca.Config.TokenSource(ctx, oldToken).Token()
if err != nil {
return nil, err
return nil, errors.Wrap(err, "RefreshToken")
}
return token, nil
@ -76,15 +78,15 @@ func (ca *ClientApp) RefreshToken(ctx context.Context, oldToken *oauth2.Token) (
func (ca *ClientApp) NewClient(ctx context.Context, token *oauth2.Token) (*Client, error) {
client := ca.Config.Client(ctx, token)
srv, err := drive.NewService(ctx, option.WithHTTPClient(client))
service, err := drive.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return nil, err
return nil, errors.Wrap(err, "NewClient.NewService")
}
return &Client{
App: ca,
HTTPClient: client,
Service: srv,
Service: service,
Token: token,
}, nil
}
@ -97,24 +99,24 @@ func (c *Client) SetToken(token oauth2.Token) {
c.Token = &token
}
func (c *Client) GetDisk() {
srv, err := drive.NewService(context.Background(), option.WithHTTPClient(c.HTTPClient))
func (c *Client) GetDisk(ctx context.Context) {
service, err := drive.NewService(ctx, option.WithHTTPClient(c.HTTPClient))
if err != nil {
fmt.Println("err123", err)
log.Println(errors.Wrap(err, "GetDisk.NewService"))
return
}
about, err := srv.About.Get().Fields("storageQuota", "user", "exportFormats", "maxImportSizes", "maxUploadSize").Do()
about, err := service.About.Get().Fields("storageQuota", "user", "exportFormats", "maxImportSizes", "maxUploadSize").Do()
if err != nil {
fmt.Println("err1234", err)
log.Println(errors.Wrap(err, "GetDisk.ServiceAboutGet"))
return
}
fmt.Printf("srv.About %+v \r\n", about.User)
dl, err := srv.Drives.List().Do()
dl, err := service.Drives.List().Do()
if err != nil {
fmt.Println("err12345", err)
log.Println(errors.Wrap(err, "GetDisk.ServiceDriveListDo"))
return
}
@ -124,32 +126,32 @@ func (c *Client) GetDisk() {
}
}
// GetUserInfo - получить информацию о пользователе Google
func (c *Client) GetUserInfo() (*drive.User, error) {
srv, err := drive.NewService(context.Background(), option.WithHTTPClient(c.HTTPClient))
// GetUserInfo - получить информацию о пользователе Google.
func (c *Client) GetUserInfo(ctx context.Context) (*drive.User, error) {
service, err := drive.NewService(ctx, option.WithHTTPClient(c.HTTPClient))
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetUserInfo.NewService")
}
about, err := srv.About.Get().Fields("user").Do()
about, err := service.About.Get().Fields("user").Do()
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetUserInfo.ServiceDo")
}
if about == nil {
return nil, errors.New("got empty about")
return nil, errors.Wrap(errors.New("got empty about"), "GetUserInfo")
}
if about.User == nil {
return nil, errors.New("got empty about.User")
return nil, errors.Wrap(errors.New("got empty about.User"), "GetUserInfo")
}
return about.User, nil
}
// GetResourcesByName - получить информацию о ресурсе
// GetResourcesByName - получить информацию о ресурсе.
func (c *Client) GetResourcesByName(name, parentID string) (*drive.FileList, error) {
query := "name = '" + name + "' and trashed = false"
@ -164,14 +166,14 @@ func (c *Client) GetResourcesByName(name, parentID string) (*drive.FileList, err
if strings.Contains(err.Error(), "404") {
return nil, nil
}
return nil, err
return nil, errors.Wrap(err, "GetResourcesByName.ServiceFileListDo")
}
return fl, nil
}
// GetResources - получить список файлов и папок ресурса по его id
// Если id не указан, то принимается значение 'root'
// Если id не указан, то принимается значение 'root'.
func (c *Client) GetResources(id string) (*drive.FileList, error) {
if id == "" {
id = "root"
@ -185,78 +187,81 @@ func (c *Client) GetResources(id string) (*drive.FileList, error) {
if strings.Contains(err.Error(), "404") {
return nil, nil
}
return nil, err
return nil, errors.Wrap(err, "GetResources.ServiceFileListDo")
}
return fl, nil
}
// PutResources - создание папки
// PutResources - создание папки.
func (c *Client) PutResources(name, parentID string) (*drive.File, error) {
q := &drive.File{Name: name, MimeType: "application/vnd.google-apps.folder", IsAppAuthorized: true}
queryFile := &drive.File{Name: name, MimeType: "application/vnd.google-apps.folder", IsAppAuthorized: true}
if parentID != "" {
q.Parents = []string{parentID}
queryFile.Parents = []string{parentID}
}
f, err := c.Service.Files.Create(q).Do()
f, err := c.Service.Files.Create(queryFile).Do()
if err != nil {
fmt.Println("3", err.Error())
fmt.Println("")
return nil, err
return nil, errors.Wrap(err, "PutResources.ServiceFileListDo")
}
return f, nil
}
func (c *Client) DeleteResources(id string) error {
return c.Service.Files.Delete(id).Do()
err := c.Service.Files.Delete(id).Do()
return errors.Wrap(err, "DeleteResources")
}
// UploadFile - отправить файл в диск
// UploadFile - отправить файл в диск.
func (c *Client) UploadFile(filepath, mimetype, parentID string) (string, string, error) {
file, err := os.Open(filepath)
defer file.Close()
defer func() {
if err = file.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "UploadFile.FileClose"))
}
}()
if err != nil {
return "", "", err
return "", "", errors.Wrap(err, "UploadFile.OsOpen")
}
filename := strings.Split(file.Name(), "/")
q := &drive.File{
queryFile := &drive.File{
Name: filename[len(filename)-1],
MimeType: mimetype,
IsAppAuthorized: true,
}
if parentID != "" {
q.Parents = []string{parentID}
queryFile.Parents = []string{parentID}
}
fileData, err := c.Service.Files.Create(q).Media(file).Do()
fileData, err := c.Service.Files.Create(queryFile).Media(file).Do()
if err != nil {
return "", "", err
return "", "", errors.Wrap(err, "UploadFile.ServiceFilesCreate")
}
return fileData.Id, fileData.ExportLinks[mimetype], nil
}
func (c *Client) UploadFileBytes(file []byte, filename, mimetype, parentID string) (string, string, error) {
q := &drive.File{
queryFile := &drive.File{
Name: filename,
MimeType: mimetype,
IsAppAuthorized: true,
}
if parentID != "" {
q.Parents = []string{parentID}
queryFile.Parents = []string{parentID}
}
fileData, err := c.Service.Files.Create(q).Media(bytes.NewReader(file)).Do()
fileData, err := c.Service.Files.Create(queryFile).Media(bytes.NewReader(file)).Do()
if err != nil {
return "", "", err
return "", "", errors.Wrap(err, "UploadFileBytes.ServiceFilesCreateDo")
}
return fileData.Id, fileData.ExportLinks[mimetype], nil
@ -265,7 +270,7 @@ func (c *Client) UploadFileBytes(file []byte, filename, mimetype, parentID strin
func (c *Client) DownloadFile(filepath, fileID string) error {
file, err := c.Service.Files.Get(fileID).Do()
if err != nil {
return err
return errors.Wrap(err, "DownloadFile.ServiceFilesGetDo")
}
var resp *http.Response
@ -274,26 +279,37 @@ func (c *Client) DownloadFile(filepath, fileID string) error {
resp, err = c.Service.Files.Export(fileID, MimeTypeDocx).
Fields("exportFormat", "docx").
Download()
err = errors.Wrap(err, "DownloadFile.Export")
} else {
resp, err = c.Service.Files.Get(fileID).Download()
err = errors.Wrap(err, "DownloadFile.Download")
}
if err != nil {
return err
}
defer resp.Body.Close()
defer func() {
if err = resp.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "DownloadFile.BodyClose"))
}
}()
out, err := os.Create(filepath)
defer out.Close()
defer func() {
if err = out.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "DownloadFile.OutFileClose"))
}
}()
if err != nil {
return err
return errors.Wrap(err, "DownloadFile.OsCreate")
}
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
return errors.Wrap(err, "DownloadFile.IoCopy")
}
return nil
@ -302,31 +318,45 @@ func (c *Client) DownloadFile(filepath, fileID string) error {
func (c *Client) DownloadFileBytes(fileID string) ([]byte, error) {
file, err := c.Service.Files.Get(fileID).Do()
if err != nil {
return nil, err
return nil, errors.Wrap(err, "DownloadFileBytes.ServiceFilesGetDo")
}
var resp *http.Response
if file.MimeType == "application/vnd.google-apps.document" {
resp, err = c.Service.Files.Export(fileID, MimeTypeDocx).
Fields("exportFormat", "docx").
Download()
resp, err = c.Service.Files.Export(fileID, MimeTypeDocx).Fields("exportFormat", "docx").Download()
err = errors.Wrap(err, "DownloadFileBytes.Export")
defer func() {
if err = resp.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "DownloadFileBytes.BodyClose"))
}
}()
} else {
resp, err = c.Service.Files.Get(fileID).Download()
err = errors.Wrap(err, "DownloadFileBytes.Download")
defer func() {
if err = resp.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "DownloadFileBytes.BodyClose"))
}
}()
}
if err != nil {
return nil, err
}
defer resp.Body.Close()
defer func() {
if err = resp.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "DownloadFile.BodyClose"))
}
}()
return io.ReadAll(resp.Body)
}
// MakeDefaultDirs - проверяет диск на наличие папок DefaultDir, TemplateDir и SaveDir.
// Если папки не существуют, то создает их
func (c *Client) MakeDefaultDirs(defaultId, defaultName, templateId, templateName, saveId,
// Если папки не существуют, то создает их.
func (c *Client) MakeDefaultDirs(defaultID, defaultName, templateID, templateName, saveID,
saveName string) (*drive.File, *drive.File, *drive.File, error) {
var defaultDir, templateDir, saveDir *drive.File
@ -345,88 +375,91 @@ func (c *Client) MakeDefaultDirs(defaultId, defaultName, templateId, templateNam
var err error
// Check Default directory
if defaultId == "" {
fl, err := c.GetResourcesByName(defaultName, "")
if defaultID == "" {
var fl *drive.FileList
fl, err = c.GetResourcesByName(defaultName, "")
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.DefaultDir.GetResourceByName")
}
if len(fl.Files) == 0 {
defaultDir, err = c.PutResources(defaultName, "")
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.DefaultDir.PutResource")
}
} else {
defaultDir = fl.Files[0]
}
} else {
defaultDir, err = c.Service.Files.Get(defaultId).Do()
defaultDir, err = c.Service.Files.Get(defaultID).Do()
if err != nil {
if strings.Contains(err.Error(), "404") {
defaultDir, err = c.PutResources(defaultName, "")
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.DefaultDir.PutResource")
}
} else {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.DefaultDir.ServiceFilesGetDo")
}
}
}
// Check Template Directory
if templateId == "" {
fl, err := c.GetResourcesByName(templateName, defaultDir.Id)
if templateID == "" {
var fl *drive.FileList
fl, err = c.GetResourcesByName(templateName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.TemplateDir.GetResourcesByName")
}
if len(fl.Files) == 0 {
templateDir, err = c.PutResources(templateName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.TemplateDir.PutResources")
}
} else {
templateDir = fl.Files[0]
}
} else {
templateDir, err = c.Service.Files.Get(templateId).Do()
templateDir, err = c.Service.Files.Get(templateID).Do()
if err != nil {
if strings.Contains(err.Error(), "404") {
templateDir, err = c.PutResources(templateName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.TemplateDir.PutResources")
}
} else {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.TemplateDir.ServiceFilesGetDo")
}
}
}
// Check Save Directory
if saveId == "" {
fl, err := c.GetResourcesByName(saveName, defaultDir.Id)
if saveID == "" {
var fl *drive.FileList
fl, err = c.GetResourcesByName(saveName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.SaveDir.GetResourcesByName")
}
if len(fl.Files) == 0 {
saveDir, err = c.PutResources(saveName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.SaveDir.PutResources")
}
} else {
saveDir = fl.Files[0]
}
} else {
saveDir, err = c.Service.Files.Get(templateId).Do()
saveDir, err = c.Service.Files.Get(templateID).Do()
if err != nil {
if strings.Contains(err.Error(), "404") {
saveDir, err = c.PutResources(saveName, defaultDir.Id)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.SaveDir.PutResources")
}
} else {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, "MakeDefaultDirs.SaveDir.ServiceFilesGetDo")
}
}
}

@ -1 +1 @@
package GDisk
package gdisk

@ -11,8 +11,7 @@ import (
func getEnv(mask interface{}) Env {
r := reflect.ValueOf(mask)
var argTypeRV reflect.Value
argTypeRV = reflect.New(r.Type())
argTypeRV := reflect.New(r.Type())
for i := 0; i < r.NumField(); i++ {
v := r.Type().Field(i).Tag.Get("env")
@ -26,11 +25,15 @@ func getEnv(mask interface{}) Env {
val = d
}
switch t := r.Type().Field(i).Type.Name(); t {
switch t := r.Type().Field(i).Type.String(); t {
case "string":
argTypeRV.Elem().Field(i).SetString(val)
case "[]string":
divider := ","
arr := strings.Split(val, divider)
argTypeRV.Elem().Field(i).Set(reflect.ValueOf(arr))
case "bool":
if strings.ToLower(val) == "true" {
if strings.EqualFold(val, "true") {
argTypeRV.Elem().Field(i).SetBool(true)
} else {
argTypeRV.Elem().Field(i).SetBool(false)
@ -42,7 +45,7 @@ func getEnv(mask interface{}) Env {
}
argTypeRV.Elem().Field(i).SetUint(num)
default:
panic(errors.New("Something strange happend: " + t))
panic(errors.New("Something strange happened: " + t))
}
}

83
go.mod

@ -1,28 +1,28 @@
module penahub.gitlab.yandexcloud.net/backend/templategen
go 1.19
go 1.20
require (
github.com/danilsolovyov/doc-template v0.0.0-20230327151707-b8182a1ee9f4
github.com/danilsolovyov/reflectgostructv1 v0.0.11
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gofiber/fiber/v2 v2.45.0
github.com/gofiber/fiber/v2 v2.48.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/jackc/pgx/v4 v4.18.1
github.com/minio/minio-go/v7 v7.0.50
github.com/stretchr/testify v1.8.1
github.com/minio/minio-go/v7 v7.0.61
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.8.4
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33
go.mongodb.org/mongo-driver v1.11.3
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.7.0
golang.org/x/oauth2 v0.6.0
google.golang.org/api v0.114.0
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230222172022-007eb6fd0b1f
github.com/twmb/franz-go v1.14.3
go.mongodb.org/mongo-driver v1.12.1
go.uber.org/zap v1.25.0
golang.org/x/oauth2 v0.11.0
google.golang.org/api v0.137.0
google.golang.org/protobuf v1.31.0
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230712221540-d9932eb7254e
)
require (
cloud.google.com/go/compute v1.19.0 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@ -30,56 +30,45 @@ require (
github.com/fatih/color v1.10.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/s2a-go v0.1.5 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.0 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/twmb/franz-go/pkg/kmsg v1.6.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.47.0 // indirect
github.com/valyala/fasthttp v1.48.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
google.golang.org/grpc v1.57.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

338
go.sum

@ -1,27 +1,25 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/danilsolovyov/doc-template v0.0.0-20230327151707-b8182a1ee9f4 h1:GwGyqqRbG1MQi+GgPX9yX8FGNwZJPIpyxivb4OjKgnc=
github.com/danilsolovyov/doc-template v0.0.0-20230327151707-b8182a1ee9f4/go.mod h1:nR0IgQFyfxaKnzDMdxyNpNieoL4hW593trhr4q4j6dM=
github.com/danilsolovyov/reflectgostructv1 v0.0.11 h1:aAJo9mWTCt1sgL1bB3LkV2d/hQ07zU5Pj2zpiCaailI=
github.com/danilsolovyov/reflectgostructv1 v0.0.11/go.mod h1:CwTyBEg4Y22p1qZtHzEe07C5nsSVcc7vx3jdzphjmv4=
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=
@ -32,16 +30,14 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofiber/fiber/v2 v2.45.0 h1:p4RpkJT9GAW6parBSbcNFH2ApnAuW3OzaQzbOCoDu+s=
github.com/gofiber/fiber/v2 v2.45.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0=
github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
@ -50,19 +46,21 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -73,121 +71,52 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg=
github.com/google/s2a-go v0.1.5/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
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.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k=
github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/minio/minio-go/v7 v7.0.61 h1:87c+x8J3jxQ5VUGimV9oHdpjsAvy3fhneEBKuoKEVUI=
github.com/minio/minio-go/v7 v7.0.61/go.mod h1:BTu8FcrEw+HidY0zd/0eny43QnVNkXRPXrLXFuQBHXg=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -195,223 +124,155 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA=
github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/twmb/franz-go v1.14.3 h1:cq8rxAnVYU1uF3SRVn8eEaUf+AaXKWlB0Cl3Ca7JSa4=
github.com/twmb/franz-go v1.14.3/go.mod h1:nMAvTC2kHtK+ceaSHeHm4dlxC78389M/1DjpOswEgu4=
github.com/twmb/franz-go/pkg/kmsg v1.6.1 h1:tm6hXPv5antMHLasTfKv9R+X03AjHSkSkXhQo2c5ALM=
github.com/twmb/franz-go/pkg/kmsg v1.6.1/go.mod h1:se9Mjdt0Nwzc9lnjJ0HyDtLyBnaBDAd7pCje47OhSyw=
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.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc=
github.com/valyala/fasthttp v1.48.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=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE=
go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
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.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.137.0 h1:QrKX6uNvzJLr0Fd3vWVqcyrcmFoYi036VUAsZbiF4+s=
google.golang.org/api v0.137.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -423,21 +284,18 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230222172022-007eb6fd0b1f h1:/ACy953BoX+0T8nizgukSZe/+seDvLne0zJC2lAa4EU=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230222172022-007eb6fd0b1f/go.mod h1:SCqfOb+FHQayOQcxHQKd52Az4sur3hr3Lx1I4UiLf6g=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230712221540-d9932eb7254e h1:nZLUtqm+u3Tf339bioyyaUsxV10RSqKeukVa52H/+DQ=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20230712221540-d9932eb7254e/go.mod h1:SCqfOb+FHQayOQcxHQKd52Az4sur3hr3Lx1I4UiLf6g=

@ -12,9 +12,9 @@ import (
"go.uber.org/zap"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
)
type ReqAmoSaveToken struct {
@ -27,90 +27,103 @@ type ReqAmoSaveToken struct {
State string `json:"state" schema:"state"`
FromWidget string `json:"from_widget" schema:"from_widget"`
Referer string `json:"referer" schema:"referer"`
Platform string `json:"platform" schema:"platform"` // Вообще без понятия, что это
/* Указывает на какой платформе установлен виджет */
Platform string `json:"platform" schema:"platform"`
}
// TODO: обсудить сокращения w, r
func (h *Handlers) AmoSaveToken(w http.ResponseWriter, r *http.Request) {
var req ReqAmoSaveToken
var request ReqAmoSaveToken
err := r.ParseForm()
if err != nil {
fmt.Println("affi", err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
err = schema.NewDecoder().Decode(&req, r.Form)
err = schema.NewDecoder().Decode(&request, r.Form)
if err != nil {
fmt.Println("affi1", err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.AccessToken == "" && req.Code == "" {
if request.AccessToken == "" && request.Code == "" {
err = errors.New("token required")
fmt.Println("affi2", err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if request.State == "" {
err = errors.New("got empty state")
h.reportError(w, http.StatusBadRequest, err)
return
}
// get user
var state tools.StateToken
err = tools.DecryptTokenAES(request.State, &state)
if err != nil {
h.reportError(w, http.StatusUnauthorized, err)
return
}
token := &oauth2.Token{
AccessToken: req.AccessToken,
RefreshToken: req.RefreshToken,
TokenType: req.TokenType,
Expiry: time.Now().Add(time.Duration(req.ExpiresIn) * time.Second),
AccessToken: request.AccessToken,
RefreshToken: request.RefreshToken,
TokenType: request.TokenType,
Expiry: time.Now().Add(time.Duration(request.ExpiresIn) * time.Second),
}
amoClient, err := h.Amo.NewClient(r.Context(), req.Referer, token, req.Code)
amoClient, err := h.Amo.NewClient(r.Context(), request.Referer, token, request.Code)
if err != nil {
fmt.Println("affi3", err)
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
token = amoClient.Token
amoAcc, err := amoClient.GetAccount()
accountAmo, err := amoClient.GetAccount(r.Context())
if err != nil {
fmt.Println("affi4", err)
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
// Insert/Update token in DB
_, err = h.dal.Amo.InsertOrUpdate(r.Context(), &model.Amo{
UserID: req.ClientID,
AccountID: strconv.FormatInt(amoAcc.Id, 10),
ClientID: request.ClientID,
AccountID: strconv.FormatInt(accountAmo.ID, 10),
PenaID: state.PenaID,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
FromWidget: req.FromWidget,
FromWidget: request.FromWidget,
AccessRules: model.AmoAccessRules{
Visibility: []int64{},
Creation: []int64{},
Delete: []int64{},
},
Referer: req.Referer,
Subdomain: amoAcc.Subdomain,
Referer: request.Referer,
Subdomain: accountAmo.Subdomain,
ExpiresIn: token.Expiry,
TokenType: token.TokenType,
})
if err != nil {
fmt.Println("affi5", err)
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
http.Redirect(w, r, "", http.StatusPermanentRedirect)
http.Redirect(w, r, state.RedirectURL, http.StatusPermanentRedirect)
}
type RespAmoState struct {
GenCount int `json:"gen_count"`
AuthYandexUrl string `json:"auth_yandex_url"`
AuthGoogleUrl string `json:"auth_google_url"`
AuthAmoUrl string `json:"auth_amo_url"`
AuthYandexURL string `json:"auth_yandex_url"`
AuthGoogleURL string `json:"auth_google_url"`
AuthAmoURL string `json:"auth_amo_url"`
Storages map[string]interface{} `json:"storages"`
Examples any `json:"examples"`
InvalidTokens map[string]any `json:"invalid_tokens"` // Невалидные токены, которые не получилось обновить
@ -119,55 +132,55 @@ type RespAmoState struct {
Delete []int64 `json:"delete"`
}
// AmoState - получить актуальное состояние аккаунта пользователя в Amo
// AmoState - получить актуальное состояние аккаунта пользователя в Amo.
func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
account, err := h.dal.Amo.GetByAccountID(ctx, amoData.AccountID)
if err != nil {
h.reportError(w, fmt.Errorf("db unexpected error %s", err.Error()), http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, fmt.Errorf("db unexpected error %s", err.Error()))
return
}
if account == nil {
/*penaid, ok := ctx.Value("PenaUserID").(string)
if !ok || penaid == "" {
h.reportError(w, fmt.Errorf("no pena token"), http.StatusUnauthorized)
return
}*/
account = &model.Amo{
AccountID: amoData.AccountID,
//PenaID: penaid,
PenaID: middleware.GetPenaUserID(r),
}
id, err := h.dal.Amo.InsertOrUpdate(ctx, account)
var id string
id, err = h.dal.Amo.InsertOrUpdate(ctx, account)
if err != nil {
h.reportError(w, fmt.Errorf("db unexpected error %s", err.Error()), http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, fmt.Errorf("db unexpected error %s", err.Error()))
return
}
account.ID = id
}
yaStorages, err := h.dal.YaDisk.GetListByUserID(r.Context(), amoData.UserID)
yaStorages, err := h.dal.YaDisk.GetListByPenaID(r.Context(), account.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
gStorages, err := h.dal.GDisk.GetListByUserID(r.Context(), amoData.UserID)
gStorages, err := h.dal.GDisk.GetListByPenaID(r.Context(), account.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
penaStorage, err := h.dal.PenaDisk.GetByUserID(ctx, amoData.AccountID)
penaStorage, err := h.dal.PenaDisk.GetByPenaID(ctx, account.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -177,26 +190,26 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
"penadisk": penaStorage,
}
redirectUri := "https://" + amoData.Referer + h.opts.AmoRedirectUrn
authYandexUrl, err := h.YaDisk.GenerateOAuthUrl(amoData.UserID, redirectUri)
redirectURI := "https://" + amoData.Referer + h.opts.AmoRedirectUrn
authYandexURL, err := h.YaDisk.GenerateOAuthURL(account.PenaID, redirectURI)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
authGoogleUrl, err := h.GDisk.GenerateOAuthUrl(amoData.UserID, redirectUri)
authGoogleURL, err := h.GDisk.GenerateOAuthURL(account.PenaID, redirectURI)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
authAmoUrl, err := h.Amo.GenerateOAuthUrl(amoData.UserID, redirectUri)
authAmoURL, err := h.Amo.GenerateOAuthURL(account.PenaID, redirectURI)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
//Add examples
// TODO: Add examples
var examples any
pathExamples := "./static/examples/docx"
@ -208,21 +221,20 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
result := map[string]string{}
pathExamples = "static/examples/docx"
for _, file := range fsExamples {
downloadUrl := fmt.Sprintf("https://%v/%v/%v", h.opts.Domain, pathExamples, file.Name())
result[file.Name()] = downloadUrl
downloadURL := fmt.Sprintf("https://%v/%v/%v", h.opts.Domain, pathExamples, file.Name())
result[file.Name()] = downloadURL
}
examples = result
}
if penaStorage == nil {
if _, err := penadisk.NewClient(amoData.AccountID).GetDisk(); err != nil {
if _, err = penadisk.NewClient(amoData.AccountID).GetDisk(r.Context()); err != nil {
h.logger.Error("ErrorCheckPenaDisk", zap.Error(err))
}
// Check PenaDisk
_, err = h.dal.PenaDisk.InsertOrUpdate(r.Context(), &model.PenaDisk{
UserID: amoData.AccountID,
PenaID: amoData.PenaID,
Name: "PenaDisk",
})
@ -240,7 +252,8 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
continue
}
token, err := h.YaDisk.RefreshToken(r.Context(), storage.Token())
var token *oauth2.Token
token, err = h.YaDisk.RefreshToken(r.Context(), storage.Token())
if err == nil {
_, err = h.dal.YaDisk.InsertOrUpdate(r.Context(), &model.YaDisk{
@ -265,7 +278,8 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
continue
}
token, err := h.GDisk.RefreshToken(r.Context(), storage.Token())
var token *oauth2.Token
token, err = h.GDisk.RefreshToken(r.Context(), storage.Token())
if err == nil {
_, err = h.dal.GDisk.InsertOrUpdate(r.Context(), &model.GDisk{
@ -283,10 +297,10 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
}
// Amo tokens
amoList, err := h.dal.Amo.GetListByUserID(r.Context(), amoData.UserID)
amoList, err := h.dal.Amo.GetListByPenaID(r.Context(), amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -295,7 +309,8 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
continue
}
token, err := h.Amo.RefreshToken(r.Context(), amo.Token(), amo.Referer)
var token *oauth2.Token
token, err = h.Amo.RefreshToken(r.Context(), amo.Token(), amo.Referer)
if err == nil {
_, err = h.dal.Amo.InsertOrUpdate(r.Context(), &model.Amo{
@ -316,9 +331,9 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
resp := RespAmoState{
GenCount: 97,
AuthYandexUrl: authYandexUrl,
AuthGoogleUrl: authGoogleUrl,
AuthAmoUrl: authAmoUrl,
AuthYandexURL: authYandexURL,
AuthGoogleURL: authGoogleURL,
AuthAmoURL: authAmoURL,
Storages: storages,
Visibility: amoData.AccessRules.Visibility,
Creation: amoData.AccessRules.Creation,
@ -327,7 +342,7 @@ func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
InvalidTokens: invalidTokens,
}
sendResponse(w, 200, resp)
h.sendResponse(w, http.StatusOK, resp)
}
type ReqAmoAccessRules struct {
@ -336,47 +351,47 @@ type ReqAmoAccessRules struct {
Delete []int64 `json:"delete"`
}
// AmoAccessRules - задать правила для пользователя
// AmoAccessRules - задать правила для пользователя.
func (h *Handlers) AmoAccessRules(w http.ResponseWriter, r *http.Request) {
var req ReqAmoAccessRules
var request ReqAmoAccessRules
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
isAdmin := r.Context().Value("amoIsAdmin").(bool)
if !isAdmin {
h.reportError(w, errors.New("need admin access"), http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, errors.New("need admin access"))
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if (req.Visibility == nil || len(req.Visibility) == 0) &&
(req.Creation == nil || len(req.Creation) == 0) &&
(req.Delete == nil || len(req.Delete) == 0) {
h.reportError(w, errors.New("empty request"), http.StatusBadRequest)
if (request.Visibility == nil || len(request.Visibility) == 0) &&
(request.Creation == nil || len(request.Creation) == 0) &&
(request.Delete == nil || len(request.Delete) == 0) {
h.reportError(w, http.StatusBadRequest, errors.New("empty request"))
return
}
err = h.dal.Amo.UpdateAccessRules(r.Context(), amoData.ID, &model.AmoAccessRules{
Visibility: req.Visibility,
Creation: req.Creation,
Delete: req.Delete,
Visibility: request.Visibility,
Creation: request.Creation,
Delete: request.Delete,
})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
w.WriteHeader(200)
w.WriteHeader(http.StatusOK)
}

@ -3,14 +3,16 @@ package handlers
import (
"errors"
"fmt"
"github.com/gorilla/schema"
"go.uber.org/zap"
"google.golang.org/api/drive/v3"
"io"
"net/http"
"os"
"github.com/gorilla/schema"
"go.uber.org/zap"
"google.golang.org/api/drive/v3"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
GDisk "penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/templategen"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
)
@ -21,91 +23,91 @@ type ReqGDiskSaveToken struct {
Scope string `json:"scope" schema:"scope"`
}
// GDiskSaveToken - сохраняет токен авторизации
// GDiskSaveToken - сохраняет токен авторизации.
func (h *Handlers) GDiskSaveToken(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskSaveToken
var request ReqGDiskSaveToken
err := r.ParseForm()
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
err = schema.NewDecoder().Decode(&req, r.Form)
err = schema.NewDecoder().Decode(&request, r.Form)
if err != nil {
h.reportError(w, err, 500)
h.reportError(w, 500, err)
return
}
if req.State == "" {
if request.State == "" {
err = errors.New("GDiskErr: got empty state")
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Code == "" {
if request.Code == "" {
err = errors.New("GDiskErr: got empty code")
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
// get user
var state tools.StateToken
err = tools.DecryptTokenRC4(req.State, &state)
err = tools.DecryptTokenAES(request.State, &state)
if err != nil {
h.reportError(w, err, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, err)
return
}
// generate token
token, err := h.GDisk.GetToken(r.Context(), req.Code)
token, err := h.GDisk.GetToken(r.Context(), request.Code)
if err != nil {
h.reportError(w, err, 500)
h.reportError(w, http.StatusInternalServerError, err)
return
}
gDisk, err := h.GDisk.NewClient(r.Context(), token)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
token = gDisk.Token
gUser, err := gDisk.GetUserInfo()
gUser, err := gDisk.GetUserInfo(r.Context())
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
// Make default directories in Google Drive
gDiskData, err := h.dal.GDisk.GetByEmail(r.Context(), gUser.EmailAddress)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
var defaultId, defaultName, templateId, templateName, saveId, saveName string
var defaultID, defaultName, templateID, templateName, saveID, saveName string
if gDiskData != nil {
defaultId = gDiskData.DefaultFolderID
defaultID = gDiskData.DefaultFolderID
defaultName = gDiskData.DefaultFolder
templateId = gDiskData.TemplateFolderID
templateID = gDiskData.TemplateFolderID
templateName = gDiskData.TemplateFolder
saveId = gDiskData.SaveFolderID
saveID = gDiskData.SaveFolderID
saveName = gDiskData.SaveFolder
}
defaultFolder, templateFolder, saveFolder, err := gDisk.MakeDefaultDirs(defaultId, defaultName, templateId,
templateName, saveId, saveName)
defaultFolder, templateFolder, saveFolder, err := gDisk.MakeDefaultDirs(defaultID, defaultName, templateID,
templateName, saveID, saveName)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
// Insert/Update token in DB
_, err = h.dal.GDisk.InsertOrUpdate(r.Context(), &model.GDisk{
UserID: state.UserID,
PenaID: state.PenaID,
Name: fmt.Sprintf("Google Disk (%v)", gUser.EmailAddress),
DisplayName: gUser.DisplayName,
Email: gUser.EmailAddress,
@ -123,18 +125,18 @@ func (h *Handlers) GDiskSaveToken(w http.ResponseWriter, r *http.Request) {
})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
http.Redirect(w, r, state.RedirectUrl, http.StatusPermanentRedirect)
http.Redirect(w, r, state.RedirectURL, http.StatusPermanentRedirect)
}
type ReqGDiskSetSettings struct {
ID string `json:"id"`
Name string `json:"name"` // Пользовательское название хранилища
DefaultFolder string `json:"default_folder"`
DefaultFolderId string `json:"default_folder_id"`
DefaultFolderID string `json:"default_folder_id"`
TemplateFolder string `json:"template_folder"`
TemplateFolderID string `json:"template_folder_id"`
SaveFolder string `json:"save_folder"`
@ -142,46 +144,42 @@ type ReqGDiskSetSettings struct {
}
func (h *Handlers) GDiskSetSettings(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskSetSettings
var request ReqGDiskSetSettings
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
err = h.dal.GDisk.Update(r.Context(), &model.GDisk{
ID: req.ID,
Name: req.Name,
DefaultFolder: req.DefaultFolder,
DefaultFolderID: req.DefaultFolderId,
TemplateFolder: req.TemplateFolder,
TemplateFolderID: req.TemplateFolderID,
SaveFolder: req.SaveFolder,
SaveFolderID: req.SaveFolderID,
ID: request.ID,
Name: request.Name,
DefaultFolder: request.DefaultFolder,
DefaultFolderID: request.DefaultFolderID,
TemplateFolder: request.TemplateFolder,
TemplateFolderID: request.TemplateFolderID,
SaveFolder: request.SaveFolder,
SaveFolderID: request.SaveFolderID,
})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
}
func (h *Handlers) GDiskGetFile(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
type ReqGDiskGetResources struct {
@ -198,131 +196,131 @@ type ReqGDiskGetResources struct {
// - Если указан Name и ParentID ищет файл\папку только в родителе
// - ParentID игнорируется без Name
func (h *Handlers) GDiskGetResources(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskGetResources
var request ReqGDiskGetResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), req.ID)
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if gdiskInfo == nil {
h.reportError(w, errors.New("gdisk info not found"), http.StatusForbidden)
h.reportError(w, http.StatusForbidden, errors.New("gdisk info not found"))
return
}
client, err := h.GDisk.NewClient(r.Context(), gdiskInfo.Token())
if err != nil {
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
var res *drive.FileList
if req.Name != "" {
res, err = client.GetResourcesByName(req.Name, req.ParentID)
if request.Name != "" {
res, err = client.GetResourcesByName(request.Name, request.ParentID)
} else {
res, err = client.GetResources(req.FolderID)
res, err = client.GetResources(request.FolderID)
}
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, res)
h.sendResponse(w, http.StatusOK, res)
}
type ReqGDiskGetDirTemplate struct {
Email string `json:"email"` // Required. Google Email ?
}
// GDiskGetDirTemplate - возвращает данные по папке template, включая список её файлов и папок
// GDiskGetDirTemplate - возвращает данные по папке template, включая список её файлов и папок.
func (h *Handlers) GDiskGetDirTemplate(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskGetDirTemplate
var request ReqGDiskGetDirTemplate
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Email == "" {
h.reportError(w, errors.New("email required"), http.StatusBadRequest)
if request.Email == "" {
h.reportError(w, http.StatusBadRequest, errors.New("email required"))
}
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
gdiskInfo, err := h.dal.GDisk.GetByEmail(r.Context(), req.Email)
gdiskInfo, err := h.dal.GDisk.GetByEmail(r.Context(), request.Email)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if gdiskInfo == nil {
h.reportError(w, errors.New("gdisk info not found"), http.StatusForbidden)
h.reportError(w, http.StatusForbidden, errors.New("gdisk info not found"))
return
}
if gdiskInfo.Token() == nil {
h.reportError(w, errors.New("google token invalid"), http.StatusForbidden)
h.reportError(w, http.StatusForbidden, errors.New("google token invalid"))
return
}
client, err := h.GDisk.NewClient(r.Context(), gdiskInfo.Token())
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
dir, err := client.GetResources(gdiskInfo.TemplateFolderID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, dir)
h.sendResponse(w, http.StatusOK, dir)
}
// GDiskGetList - возвращает список хранилищ Google закрепленных за пользователем по его UserID из JWT
// GDiskGetList - возвращает список хранилищ Google закрепленных за пользователем по его UserID из JWT.
func (h *Handlers) GDiskGetList(w http.ResponseWriter, r *http.Request) {
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
gdiskInfo, err := h.dal.GDisk.GetListByUserID(r.Context(), amoData.UserID)
gdiskInfo, err := h.dal.GDisk.GetListByPenaID(r.Context(), amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, gdiskInfo)
h.sendResponse(w, http.StatusOK, gdiskInfo)
}
type ReqGDiskPutResources struct {
@ -331,57 +329,57 @@ type ReqGDiskPutResources struct {
ParentID string `json:"parent_id"`
}
// GDiskPutResources - создать папку в хранилище
// GDiskPutResources - создать папку в хранилище.
func (h *Handlers) GDiskPutResources(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskPutResources
var request ReqGDiskPutResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
if req.Name == "" {
h.reportError(w, errors.New("name required"), http.StatusBadRequest)
if request.Name == "" {
h.reportError(w, http.StatusBadRequest, errors.New("name required"))
return
}
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), req.ID)
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if gdiskInfo == nil {
h.reportError(w, errors.New("gdisk info not found"), http.StatusForbidden)
h.reportError(w, http.StatusForbidden, errors.New("gdisk info not found"))
return
}
client, err := h.GDisk.NewClient(r.Context(), gdiskInfo.Token())
if err != nil {
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
file, err := client.PutResources(req.Name, req.ParentID)
file, err := client.PutResources(request.Name, request.ParentID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, file)
h.sendResponse(w, http.StatusOK, file)
}
type ReqGDiskUploadResources struct {
@ -389,27 +387,33 @@ type ReqGDiskUploadResources struct {
ParentID string `json:"parent_id" schema:"parent_id"`
}
// GDiskUploadResources - загрузить файл в хранилище
// GDiskUploadResources - загрузить файл в хранилище.
func (h *Handlers) GDiskUploadResources(w http.ResponseWriter, r *http.Request) {
// Check form
fileData, fileHeader, err := r.FormFile("file")
defer fileData.Close()
defer fileData.Close() //nolint
if err != nil {
h.reportError(w, http.StatusInternalServerError, err)
return
}
var req ReqGDiskUploadResources
err = schema.NewDecoder().Decode(&req, r.Form)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
var maxSize int64 = 10 << 20 // mb
if fileHeader.Size > maxSize {
h.reportError(w, errors.New("max size 10 mb"), http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, errors.New("max size 10 mb"))
return
}
@ -418,30 +422,30 @@ func (h *Handlers) GDiskUploadResources(w http.ResponseWriter, r *http.Request)
downloadPath := fmt.Sprintf("%v/%v", templategen.TempDownloaded, fileHeader.Filename)
out, err := os.Create(downloadPath)
defer out.Close()
defer out.Close() //nolint
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
_, err = io.Copy(out, fileData)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
// Upload file to the storage
storage, err := h.dal.GDisk.GetByID(r.Context(), req.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
client, err := h.GDisk.NewClient(r.Context(), storage.Token())
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -449,7 +453,7 @@ func (h *Handlers) GDiskUploadResources(w http.ResponseWriter, r *http.Request)
_, _, err = client.UploadFile(downloadPath, GDisk.MimeTypeDocx, req.ParentID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -461,53 +465,53 @@ type ReqGDiskDeleteResources struct {
FolderID string `json:"folder_id"`
}
// GDiskDeleteResources - удалить папку\файл
// GDiskDeleteResources - удалить папку\файл.
func (h *Handlers) GDiskDeleteResources(w http.ResponseWriter, r *http.Request) {
var req ReqGDiskDeleteResources
var request ReqGDiskDeleteResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
if req.FolderID == "" {
h.reportError(w, errors.New("folder_id required"), http.StatusBadRequest)
if request.FolderID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("folder_id required"))
return
}
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), req.ID)
gdiskInfo, err := h.dal.GDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if gdiskInfo == nil {
h.reportError(w, errors.New("gdisk info not found"), http.StatusForbidden)
h.reportError(w, http.StatusForbidden, errors.New("gdisk info not found"))
return
}
client, err := h.GDisk.NewClient(r.Context(), gdiskInfo.Token())
if err != nil {
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
err = client.DeleteResources(req.FolderID)
err = client.DeleteResources(request.FolderID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}

@ -3,43 +3,48 @@ package handlers
import (
"errors"
"fmt"
"go.uber.org/zap"
"io"
"net/http"
"net/url"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/templategen"
"strconv"
"strings"
"time"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/templategen"
"penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
)
type ReqGeneratorByAmoLead struct {
File string `json:"file"` // Путь до файла в Yandex Disk, либо ID файла в Google Disk
StorageID string `json:"storage_id"`
StorageType string `json:"storage_type"`
LeadId int64 `json:"lead_id"` // Required.
LeadID int64 `json:"lead_id"` // Required.
}
type RespGenerated struct {
DownloadUrl string `json:"download_url"`
DownloadURL string `json:"download_url"`
}
// GeneratorByAmoLead - сгенерировать файл по lead_id и указанному файлу если он установлен,
// или по шаблону если файл не указан
// или по шаблону если файл не указан.
func (h *Handlers) GeneratorByAmoLead(w http.ResponseWriter, r *http.Request) {
var req ReqGeneratorByAmoLead
var request ReqGeneratorByAmoLead
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
amoUserID := getAmoUserID(r)
amoUserID := middleware.GetAmoUserID(r)
history := model.NewHistory(amoData.UserID, amoData.ID, amoUserID, model.HistoryTriggerAmoLead)
history := model.NewHistory(amoData.PenaID, amoData.ID, amoUserID, model.HistoryTriggerAmoLead)
defer func() {
_, err := h.dal.History.Insert(r.Context(), history)
if err != nil {
@ -47,56 +52,57 @@ func (h *Handlers) GeneratorByAmoLead(w http.ResponseWriter, r *http.Request) {
}
}()
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
history.LeadID = req.LeadId
history.LeadID = request.LeadID
if req.LeadId <= 0 {
if request.LeadID <= 0 {
err = errors.New("lead_id required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
// Отправляем задачу в воркер
var file, storageType, storageID, templateID string
if req.File != "" {
history.Source.File = req.File
history.Source.StorageID = req.StorageID
history.Source.StorageType = req.StorageType
if request.File != "" {
history.Source.File = request.File
history.Source.StorageID = request.StorageID
history.Source.StorageType = request.StorageType
if req.StorageType == "" {
if request.StorageType == "" {
err = errors.New("storage required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.StorageID == "" {
if request.StorageID == "" {
err = errors.New("storage_id required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
file = req.File
storageType = req.StorageType
storageID = req.StorageID
file = request.File
storageType = request.StorageType
storageID = request.StorageID
} else {
template, err := h.dal.Template.GetByLeadId(r.Context(), req.LeadId)
var template *model.Template
template, err = h.dal.Template.GetByLeadID(r.Context(), request.LeadID)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if template == nil {
history.AddError(err)
h.reportError(w, err, http.StatusNotFound)
h.reportError(w, http.StatusNotFound, err)
return
}
@ -106,26 +112,34 @@ func (h *Handlers) GeneratorByAmoLead(w http.ResponseWriter, r *http.Request) {
templateID = template.ID
}
task := model.NewWorkerTask(amoData.UserID, amoData.ID, amoUserID, model.WorkerTaskStatusNew, req.LeadId,
templateID, model.WorkerSource{
task := &model.WorkerTask{
PenaID: amoData.PenaID,
AmoID: amoData.ID,
AmoUserID: amoUserID,
Status: model.WorkerTaskStatusNew,
LeadID: request.LeadID,
TemplateID: templateID,
Source: model.WorkerSource{
File: file,
StorageID: storageID,
StorageType: storageType,
}, model.WorkerTarget{
},
Target: model.WorkerTarget{
File: "",
StorageID: storageID,
StorageType: storageType,
})
},
}
taskId, err := h.dal.WorkerTask.Insert(r.Context(), task)
taskID, err := h.dal.WorkerTask.Insert(r.Context(), task)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
history.WorkerTaskID = taskId
history.WorkerTaskID = taskID
w.WriteHeader(http.StatusOK)
}
@ -139,20 +153,20 @@ type ReqGeneratorByData struct {
Data map[string]any `json:"data"`
}
// GeneratorByData - сгенерировать файл по данным и указанному файлу или шаблону
// GeneratorByData - сгенерировать файл по данным и указанному файлу или шаблону.
func (h *Handlers) GeneratorByData(w http.ResponseWriter, r *http.Request) {
var req ReqGeneratorByData
var request ReqGeneratorByData
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
amoUserID := getAmoUserID(r)
amoUserID := middleware.GetAmoUserID(r)
history := model.NewHistory(amoData.UserID, amoData.ID, amoUserID, model.HistoryTriggerData)
history := model.NewHistory(amoData.PenaID, amoData.ID, amoUserID, model.HistoryTriggerData)
defer func() {
_, err := h.dal.History.Insert(r.Context(), history)
if err != nil {
@ -160,19 +174,20 @@ func (h *Handlers) GeneratorByData(w http.ResponseWriter, r *http.Request) {
}
}()
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
var file, name, storageID, storageType string
if req.TemplateID != "" {
template, err := h.dal.Template.GetByID(r.Context(), req.TemplateID)
if request.TemplateID != "" {
var template *model.Template
template, err = h.dal.Template.GetByID(r.Context(), request.TemplateID)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
file = template.File
@ -180,53 +195,53 @@ func (h *Handlers) GeneratorByData(w http.ResponseWriter, r *http.Request) {
storageID = template.StorageID
name = template.Name
} else {
if req.File == "" {
if request.File == "" {
err = errors.New("file required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.StorageType == "" {
if request.StorageType == "" {
err = errors.New("storage required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.StorageID == "" {
if request.StorageID == "" {
err = errors.New("storage_id required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Name == "" {
if request.Name == "" {
err = errors.New("name required")
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
file = req.File
storageType = req.StorageType
storageID = req.StorageID
name = req.Name
file = request.File
storageType = request.StorageType
storageID = request.StorageID
name = request.Name
}
exportUrl, err := h.generate(w, r, file, name, storageID, storageType, amoData.UserID, history, req.Data)
exportURL, err := h.generate(r, file, name, storageID, storageType, amoData.PenaID, history, request.Data)
if err != nil {
history.AddError(err)
sendResponse(w, http.StatusInternalServerError, err)
h.sendResponse(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, RespGenerated{DownloadUrl: exportUrl})
h.sendResponse(w, http.StatusOK, RespGenerated{DownloadURL: exportURL})
}
// GeneratorByAmoWebhook - эндпоинт для вебхука amo. Генерирует файл по ранее заданному шаблону
// GeneratorByAmoWebhook - эндпоинт для вебхука amo. Генерирует файл по ранее заданному шаблону.
func (h *Handlers) GeneratorByAmoWebhook(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
@ -239,7 +254,7 @@ func (h *Handlers) GeneratorByAmoWebhook(w http.ResponseWriter, r *http.Request)
history := model.NewHistory("", "", 0, model.HistoryTriggerWebhook)
defer func() {
_, err := h.dal.History.Insert(r.Context(), history)
_, err = h.dal.History.Insert(r.Context(), history)
if err != nil {
h.logger.Error("GeneratorByData", zap.Error(err))
}
@ -248,49 +263,47 @@ func (h *Handlers) GeneratorByAmoWebhook(w http.ResponseWriter, r *http.Request)
p, err := url.ParseQuery(body)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
leadId := p.Get("event[data][id]")
//subdomain := p.Get("subdomain")
accId := p.Get("account_id")
templateId := p.Get("action[settings][widget][settings][template_id]")
// pipelineId := p.Get("event[data][pipeline_id]")
leadID := p.Get("event[data][id]")
accID := p.Get("account_id")
templateID := p.Get("action[settings][widget][settings][template_id]")
history.TemplateID = templateId
history.TemplateID = templateID
convertedLeadId, err := strconv.ParseInt(leadId, 10, 64)
convertedLeadID, err := strconv.ParseInt(leadID, 10, 64)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
history.LeadID = convertedLeadId
history.LeadID = convertedLeadID
// Запрашиваем данные по аккаунту
amoData, err := h.dal.Amo.GetByAccountID(r.Context(), accId)
amoData, err := h.dal.Amo.GetByAccountID(r.Context(), accID)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if amoData == nil {
err = errors.New("amo account not found")
history.AddError(err)
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
history.AmoID = amoData.ID
history.UserID = amoData.UserID
history.PenaID = amoData.PenaID
template, err := h.dal.Template.GetByID(r.Context(), templateId)
template, err := h.dal.Template.GetByID(r.Context(), templateID)
if template == nil {
history.AddError(err)
h.reportError(w, err, http.StatusNotFound)
h.reportError(w, http.StatusNotFound, err)
return
}
@ -306,34 +319,72 @@ func (h *Handlers) GeneratorByAmoWebhook(w http.ResponseWriter, r *http.Request)
}
// Отправляем задачу в воркер
task := model.NewWorkerTask(amoData.UserID, amoData.ID, 0, model.WorkerTaskStatusNew, convertedLeadId,
templateId, model.WorkerSource{
task := &model.WorkerTask{
PenaID: amoData.PenaID,
AmoID: amoData.ID,
AmoUserID: 0,
Status: model.WorkerTaskStatusNew,
LeadID: convertedLeadID,
TemplateID: templateID,
Source: model.WorkerSource{
File: template.File,
StorageID: template.StorageID,
StorageType: template.StorageType,
}, model.WorkerTarget{
},
Target: model.WorkerTarget{
File: "",
StorageID: template.StorageID,
StorageType: template.StorageType,
})
},
}
taskId, err := h.dal.WorkerTask.Insert(r.Context(), task)
taskID, err := h.dal.WorkerTask.Insert(r.Context(), task)
if err != nil {
history.AddError(err)
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
history.WorkerTaskID = taskId
history.WorkerTaskID = taskID
w.WriteHeader(http.StatusOK)
}
// generate - локальная функция, для генерации файла, возвращает ссылку для скачивания сгенерированного файла
func (h *Handlers) generate(w http.ResponseWriter, r *http.Request, file, name, storageID, storageType,
userID string, history *model.History, data any) (string, error) {
var exportUrl string
// generate - локальная функция, для генерации файла, возвращает ссылку для скачивания сгенерированного файла.
func (h *Handlers) generate(r *http.Request, file, name, storageID, storageType,
penaID string, history *model.History, data any) (string, error) {
amoData, err := h.dal.Amo.GetByPenaID(r.Context(), penaID)
if err != nil {
return "", err
}
// Check privileges
if len(amoData.Privileges) == 0 {
return "", errors.New("user has no tariff")
}
var privilegeAmount int64
// По количеству генераций
privilege, privilegeCountExists := amoData.Privileges[model.PrivilegeTemplateCount]
if privilegeCountExists {
privilegeAmount = privilege.Amount
if privilegeAmount < 1 {
return "", errors.New("tariff ended - not enough count")
}
}
// По дате
privilege, privilegeUnlimTimeExists := amoData.Privileges[model.PrivilegeTemplateUnlimTime]
if privilegeUnlimTimeExists {
if privilege.CreatedAt.AddDate(0, 0, int(privilege.Amount)).After(time.Now()) {
return "", errors.New("tariff ended - by time")
}
}
var exportURL string
history.Source.File = file
history.Source.StorageID = storageID
history.Source.StorageType = storageType
@ -344,77 +395,83 @@ func (h *Handlers) generate(w http.ResponseWriter, r *http.Request, file, name,
// Генерируем файл и загружаем в хранилище
switch storageType {
case "gdisk":
gdiskData, err := h.dal.GDisk.GetByID(r.Context(), storageID)
var gdiskData *model.GDisk
gdiskData, err = h.dal.GDisk.GetByID(r.Context(), storageID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", err
}
if gdiskData == nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", errors.New("google disk data not found")
}
client, err := h.GDisk.NewClient(r.Context(), gdiskData.Token())
//history.Target.File, exportUrl, err = templategen.GDiskGenerateDoc(file, name, userID, gdiskData.SaveFolderID, client,
// data)
//
history.Target.File, exportUrl, err = templategen.GDiskGenerateDocBytes(file, name, userID, gdiskData.SaveFolderID, client, data)
var client *gdisk.Client
client, err = h.GDisk.NewClient(r.Context(), gdiskData.Token())
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", err
}
history.Target.File, exportURL, err = templategen.GDiskGenerateDocBytes(file, name, gdiskData.SaveFolderID, client, data)
if err != nil {
return "", err
}
case "yadisk":
yaDiskData, err := h.dal.YaDisk.GetByID(r.Context(), storageID)
var yaDiskData *model.YaDisk
yaDiskData, err = h.dal.YaDisk.GetByID(r.Context(), storageID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", err
}
client, err := h.YaDisk.NewClient(r.Context(), yaDiskData.Token(), "")
if yaDiskData == nil {
return "", errors.New("yandex disk data not found")
}
//history.Target.File, exportUrl, err = templategen.YaDiskGenerateDoc(file, name, userID, yaDiskData.SaveFolder,
// client,
// data)
history.Target.File, exportUrl, err = templategen.YaDiskGenerateDocBytes(file, name, userID, yaDiskData.SaveFolder,
client,
data)
var client *yadisk.Client
client, err = h.YaDisk.NewClient(r.Context(), yaDiskData.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", err
}
history.Target.File, exportURL, err = templategen.YaDiskGenerateDocBytes(r.Context(), file, name, yaDiskData.SaveFolder, client, data)
if err != nil {
return "", err
}
case "example":
generatedFile, err := templategen.ExampleGenerate(file, data)
var generatedFile string
generatedFile, err = templategen.ExampleGenerate(file, data)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", err
}
history.Target.File = generatedFile
generatedFile = strings.TrimPrefix(generatedFile, "./")
exportUrl = fmt.Sprintf("https://%v/%v", h.opts.Domain, generatedFile)
exportURL = fmt.Sprintf("https://%v/%v", h.opts.Domain, generatedFile)
case "penadisk":
client := penadisk.NewClient(userID)
var err error
client := penadisk.NewClient(penaID)
saveFolder := "templategen/saved"
history.Target.File, exportUrl, err = templategen.PenaDiskGenerateDocBytes(file, name, userID, saveFolder,
history.Target.File, exportURL, err = templategen.PenaDiskGenerateDocBytes(r.Context(), file, name, saveFolder,
client, data)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
return "", nil
return "", err
}
default:
h.reportError(w, errors.New("got unknown storage"), http.StatusInternalServerError)
return "", nil
err = errors.New("got unknown storage")
return "", err
}
// Обновляем счетчик генераций
return exportUrl, nil
if privilegeCountExists {
err = h.dal.Amo.UpdateAmountPrivilege(r.Context(), amoData.ID, model.PrivilegeTemplateCount, privilegeAmount-1)
if err != nil {
return "", err
}
}
return exportURL, nil
}

@ -3,14 +3,12 @@ package handlers
import (
"encoding/json"
"errors"
"html/template"
"io/ioutil"
"io"
"net/http"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/amo"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
GDisk "penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
YaDisk "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
)
@ -36,57 +34,26 @@ func NewHandlers(dal *dal.MongoDAL, yaDisk *YaDisk.ClientApp, gDisk *GDisk.Clien
// LOCAL FUNCTIONS
func execTemplate(tplFile string, data interface{}, w http.ResponseWriter) error {
tpl := template.Must(template.ParseFiles("./template/"+tplFile, "./template/header.gohtml",
"./template/footer.gohtml"))
return tpl.Execute(w, data)
}
func sendResponse(w http.ResponseWriter, status int, response interface{}) error {
func (h *Handlers) sendResponse(w http.ResponseWriter, status int, response interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
e := json.NewEncoder(w)
e.SetEscapeHTML(false)
return e.Encode(response)
err := e.Encode(response)
if err != nil {
h.logger.Error("ErrorHandler - JSON ENCODER", zap.Error(err))
}
}
func (h *Handlers) reportError(w http.ResponseWriter, err error, status int) {
func (h *Handlers) reportError(w http.ResponseWriter, status int, err error) {
h.logger.Error("ErrorHandler", zap.Error(err))
sendResponse(w, status, err.Error())
}
func getAmoByJwt(r *http.Request) *model.Amo {
amoData, ok := r.Context().Value("amoData").(*model.Amo)
if ok {
return amoData
}
return nil
}
func getAmoUserID(r *http.Request) int64 {
result, ok := r.Context().Value("amoUserID").(int64)
if ok {
return result
}
return 0
}
func getJwtUser(r *http.Request) string {
user, ok := r.Context().Value("UserID").(string)
if ok {
return user
}
return ""
h.sendResponse(w, status, err.Error())
}
func decodePost(s interface{}, r *http.Request) error {
reqBody, err := ioutil.ReadAll(r.Body)
reqBody, err := io.ReadAll(r.Body)
if err != nil {
return err

@ -5,6 +5,7 @@ import (
"net/http"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/mongos"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
)
type ReqGetHistoryByID struct {
@ -12,76 +13,76 @@ type ReqGetHistoryByID struct {
}
func (h *Handlers) GetHistoryByID(w http.ResponseWriter, r *http.Request) {
var req ReqGetHistoryByID
var request ReqGetHistoryByID
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
}
result, err := h.dal.History.GetByID(r.Context(), req.ID)
result, err := h.dal.History.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if result == nil {
sendResponse(w, http.StatusNotFound, "not found")
h.sendResponse(w, http.StatusNotFound, "not found") // TODO: проверить на фронте, используется ли тело. Если нет, то поменять на w.WriteHeader(http.StatusNotFound)
return
}
sendResponse(w, http.StatusOK, result)
h.sendResponse(w, http.StatusOK, result)
}
func (h *Handlers) GetHistoryListByFilter(w http.ResponseWriter, r *http.Request) {
var req mongos.HistoryFilter
var request mongos.HistoryFilter
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
amoUserID := getAmoUserID(r)
amoUserID := middleware.GetAmoUserID(r)
if amoUserID > 0 {
req.AmoUserID = amoUserID
request.AmoUserID = amoUserID
}
req.AmoID = amoData.AccountID
request.AmoID = amoData.AccountID
result, err := h.dal.History.GetByFilter(r.Context(), &req)
result, err := h.dal.History.GetByFilter(r.Context(), &request)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if len(result) == 0 {
sendResponse(w, http.StatusNotFound, "not found")
h.sendResponse(w, http.StatusNotFound, "not found") // TODO: проверить на фронте, используется ли тело. Если нет, то поменять на w.WriteHeader(http.StatusNotFound)
return
}
sendResponse(w, http.StatusOK, result)
h.sendResponse(w, http.StatusOK, result)
}
type ReqGetHistoryList struct {
@ -90,32 +91,32 @@ type ReqGetHistoryList struct {
}
func (h *Handlers) GetHistoryList(w http.ResponseWriter, r *http.Request) {
var req ReqGetHistoryList
var request ReqGetHistoryList
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
result, err := h.dal.History.GetListPageByAmoID(r.Context(), amoData.ID, req.Limit, req.Page)
result, err := h.dal.History.GetListPageByAmoID(r.Context(), amoData.ID, request.Limit, request.Page)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if len(result) == 0 {
sendResponse(w, http.StatusNotFound, "not found")
h.sendResponse(w, http.StatusNotFound, "not found") // TODO: проверить на фронте, используется ли тело. Если нет, то поменять на w.WriteHeader(http.StatusNotFound)
return
}
sendResponse(w, http.StatusOK, result)
h.sendResponse(w, http.StatusOK, result)
}

@ -4,6 +4,6 @@ import (
"net/http"
)
func (h *Handlers) PageNotFound(w http.ResponseWriter, r *http.Request) {
sendResponse(w, 404, "404 - Page not found / Страница не найдена")
func (h *Handlers) PageNotFound(w http.ResponseWriter, _ *http.Request) {
h.sendResponse(w, 404, "404 - Page not found / Страница не найдена")
}

@ -2,10 +2,11 @@ package handlers
import (
"errors"
"github.com/gorilla/schema"
"net/http"
"fmt"
"github.com/gorilla/schema"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
)
@ -16,32 +17,32 @@ type ReqPenaDiskSetSettings struct {
}
func (h *Handlers) PenaDiskSetSettings(w http.ResponseWriter, r *http.Request) {
var req ReqPenaDiskSetSettings
var request ReqPenaDiskSetSettings
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
err = h.dal.PenaDisk.Update(r.Context(), &model.PenaDisk{
UserID: amoData.AccountID,
Name: req.Name,
TemplateFolder: req.TemplateFolder,
SaveFolder: req.SaveFolder})
PenaID: amoData.PenaID,
Name: request.Name,
TemplateFolder: request.TemplateFolder,
SaveFolder: request.SaveFolder})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
type ReqPenaDiskGetResources struct {
@ -51,31 +52,30 @@ type ReqPenaDiskGetResources struct {
}
func (h *Handlers) PenaDiskGetResources(w http.ResponseWriter, r *http.Request) {
var req ReqPenaDiskGetResources
var request ReqPenaDiskGetResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
fmt.Println("pdclient1", amoData.AccountID, req.Path)
client := penadisk.NewClient(amoData.AccountID)
client := penadisk.NewClient(amoData.PenaID)
resource, err := client.GetResources(req.Path, req.Recursive, req.WIthVersions)
resource, err := client.GetResources(r.Context(), request.Path, request.Recursive, request.WIthVersions)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, resource)
h.sendResponse(w, http.StatusOK, resource)
}
type ReqPenaDiskPutResources struct {
@ -83,33 +83,33 @@ type ReqPenaDiskPutResources struct {
}
func (h *Handlers) PenaDiskPutResources(w http.ResponseWriter, r *http.Request) {
var req ReqPenaDiskPutResources
amoData := getAmoByJwt(r)
var request ReqPenaDiskPutResources
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Path == "" {
h.reportError(w, errors.New("path required"), http.StatusBadRequest)
if request.Path == "" {
h.reportError(w, http.StatusBadRequest, errors.New("path required"))
}
client := penadisk.NewClient(amoData.AccountID)
client := penadisk.NewClient(amoData.PenaID)
err = client.PutResources(req.Path)
err = client.PutResources(r.Context(), request.Path)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
type ReqPenaDiskDeleteResources struct {
@ -118,92 +118,87 @@ type ReqPenaDiskDeleteResources struct {
}
func (h *Handlers) PenaDiskDeleteResources(w http.ResponseWriter, r *http.Request) {
var req ReqPenaDiskDeleteResources
var request ReqPenaDiskDeleteResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Path == "" {
h.reportError(w, errors.New("path required"), http.StatusBadRequest)
if request.Path == "" {
h.reportError(w, http.StatusBadRequest, errors.New("path required"))
}
client := penadisk.NewClient(amoData.AccountID)
client := penadisk.NewClient(amoData.PenaID)
err = client.DeleteResources(req.Path, req.Permanently)
err = client.DeleteResources(r.Context(), request.Path, request.Permanently)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
type ReqPenaDiskUploadResources struct {
Id string `json:"id" schema:"id"`
ID string `json:"id" schema:"id"`
Path string `json:"path" schema:"path"`
Overwrite bool `json:"overwrite" schema:"overwrite"`
}
func (h *Handlers) PenaDiskUploadResources(w http.ResponseWriter, r *http.Request) {
/*amoData := getAmoByJwt(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
return
}
fmt.Println("PenaDiskUploadResources1", amoData)
*/
// Check form
fileData, fileHeader, err := r.FormFile("file")
if err != nil {
h.reportError(w, http.StatusBadRequest, err)
return
}
defer fileData.Close()
var req ReqPenaDiskUploadResources
err = schema.NewDecoder().Decode(&req, r.Form)
var request ReqPenaDiskUploadResources
err = schema.NewDecoder().Decode(&request, r.Form)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Id == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
if req.Path == "" {
req.Path = "/"
if request.Path == "" {
request.Path = "/"
}
var maxSize int64 = 10 << 20 // mb
if fileHeader.Size > maxSize {
h.reportError(w, errors.New("max size 10 mb"), http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, errors.New("max size 10 mb"))
return
}
pd, err := h.dal.PenaDisk.GetByID(r.Context(), req.Id)
pd, err := h.dal.PenaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
fmt.Println("pdclient2", pd.UserID, req.Path, fileHeader.Filename)
// Upload file to the storage
client := penadisk.NewClient(pd.UserID)
client := penadisk.NewClient(pd.PenaID)
//storagePath := fmt.Sprintf("%v/%v", req.Path, fileHeader.Filename)
_, _, err = client.UploadResources(req.Path, fileHeader.Filename, req.Overwrite, fileData)
_, _, err = client.UploadResources(r.Context(), request.Path, fileHeader.Filename, false, fileData)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -215,32 +210,32 @@ type ReqPenaDiskDownloadLink struct {
}
func (h *Handlers) PenaDiskDownloadLink(w http.ResponseWriter, r *http.Request) {
var req ReqPenaDiskDownloadLink
var request ReqPenaDiskDownloadLink
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Path == "" {
h.reportError(w, errors.New("path required"), http.StatusBadRequest)
if request.Path == "" {
h.reportError(w, http.StatusBadRequest, errors.New("path required"))
}
client := penadisk.NewClient(amoData.AccountID)
client := penadisk.NewClient(amoData.PenaID)
href, err := client.GetPublicDownloadURL(req.Path)
href, err := client.GetPublicDownloadURL(r.Context(), request.Path)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, href)
h.sendResponse(w, http.StatusOK, href)
}

@ -3,13 +3,15 @@ package handlers
import (
"errors"
"net/http"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"strconv"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
)
type ReqTemplateSet struct {
LeadId int64 `json:"lead_id"` // Required for insert/update
TemplateId string `json:"template_id"` // Required for update - может потом удалить ?
LeadID int64 `json:"lead_id"` // Required for insert/update
TemplateID string `json:"template_id"` // Required for update - может потом удалить ?
Name string `json:"name"`
File string `json:"file"` // Required.
StorageID string `json:"storage_id"` // Required. id gdisk or yadisk
@ -20,84 +22,84 @@ type RespTemplateSet struct {
ID string `json:"id"`
}
// TemplateSet - устанавливает/обновляет шаблон для сделки
// TemplateSet - устанавливает/обновляет шаблон для сделки.
func (h *Handlers) TemplateSet(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateSet
var request ReqTemplateSet
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.StorageType == "" || req.StorageID == "" {
h.reportError(w, errors.New("storage_type required "), http.StatusBadRequest)
if request.StorageType == "" || request.StorageID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("storage_type required "))
return
}
if req.StorageID == "" {
h.reportError(w, errors.New("storage_id required"), http.StatusBadRequest)
if request.StorageID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("storage_id required"))
return
}
if req.File == "" {
h.reportError(w, errors.New("file required"), http.StatusBadRequest)
if request.File == "" {
h.reportError(w, http.StatusBadRequest, errors.New("file required"))
return
}
// Search/update template
update := &model.Template{
ID: req.TemplateId,
UserID: amoData.UserID,
LeadId: req.LeadId,
Name: req.Name,
File: req.File,
StorageID: req.StorageID,
StorageType: req.StorageType,
ID: request.TemplateID,
PenaID: amoData.PenaID,
LeadID: request.LeadID,
Name: request.Name,
File: request.File,
StorageID: request.StorageID,
StorageType: request.StorageType,
IsDeleted: false,
}
templateId := ""
if req.TemplateId == "" {
if req.LeadId > 0 {
templateID := ""
if request.TemplateID == "" {
if request.LeadID > 0 {
err = h.dal.Template.UpdateByLeadID(r.Context(), update)
} else {
templateId, err = h.dal.Template.Insert(r.Context(), update)
templateID, err = h.dal.Template.Insert(r.Context(), update)
}
} else {
err = h.dal.Template.UpdateByID(r.Context(), update)
}
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, RespTemplateSet{templateId})
h.sendResponse(w, http.StatusOK, RespTemplateSet{templateID})
}
func (h *Handlers) TemplateGetListByUser(w http.ResponseWriter, r *http.Request) {
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
result, err := h.dal.Template.GetListByUserID(r.Context(), amoData.UserID)
result, err := h.dal.Template.GetListByPenaID(r.Context(), amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, result)
h.sendResponse(w, http.StatusOK, result)
}
type ReqTemplateGetListByGroup struct {
@ -105,96 +107,91 @@ type ReqTemplateGetListByGroup struct {
}
func (h *Handlers) TemplateGetListByGroup(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGetListByGroup
var request ReqTemplateGetListByGroup
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
//if req.GroupID == "" {
// h.reportError(w, errors.New("group_id required"), http.StatusBadRequest)
// return
//}
resp, err := h.dal.Template.GetListByGroupID(r.Context(), req.GroupID, amoData.UserID)
resp, err := h.dal.Template.GetListByGroupID(r.Context(), request.GroupID, amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, resp)
h.sendResponse(w, http.StatusOK, resp)
}
type ReqTemplateGet struct {
LeadId int64 `json:"lead_id"`
TemplateId string `json:"template_id"`
LeadID int64 `json:"lead_id"`
TemplateID string `json:"template_id"`
}
func (h *Handlers) TemplateGet(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGet
var request ReqTemplateGet
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.TemplateId == "" && strconv.FormatInt(req.LeadId, 10) == "" {
h.reportError(w, err, http.StatusBadRequest)
if request.TemplateID == "" && strconv.FormatInt(request.LeadID, 10) == "" {
h.reportError(w, http.StatusBadRequest, err)
return
}
var template *model.Template
if req.TemplateId != "" {
template, err = h.dal.Template.GetByID(r.Context(), req.TemplateId)
if request.TemplateID != "" {
template, err = h.dal.Template.GetByID(r.Context(), request.TemplateID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
}
if req.LeadId > 0 {
template, err = h.dal.Template.GetByLeadId(r.Context(), req.LeadId)
if request.LeadID > 0 {
template, err = h.dal.Template.GetByLeadID(r.Context(), request.LeadID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
}
if template == nil {
templateId, err := h.dal.Template.Insert(r.Context(), &model.Template{
UserID: amoData.UserID,
LeadId: req.LeadId,
var templateID string
templateID, err = h.dal.Template.Insert(r.Context(), &model.Template{
PenaID: amoData.PenaID,
LeadID: request.LeadID,
})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
template = &model.Template{ID: templateId}
template = &model.Template{ID: templateID}
}
sendResponse(w, 200, template)
h.sendResponse(w, http.StatusOK, template)
}
type ReqTemplateUpdate struct {
@ -204,41 +201,41 @@ type ReqTemplateUpdate struct {
}
func (h *Handlers) TemplateUpdate(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateUpdate
var request ReqTemplateUpdate
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
record := &model.Template{
ID: req.ID,
UserID: amoData.UserID,
Name: req.Name,
File: req.File,
ID: request.ID,
PenaID: amoData.PenaID,
Name: request.Name,
File: request.File,
}
err = h.dal.Template.UpdateByID(r.Context(), record)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
type ReqTemplateDelete struct {
@ -246,134 +243,36 @@ type ReqTemplateDelete struct {
}
func (h *Handlers) TemplateDelete(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateDelete
var request ReqTemplateDelete
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
err = h.dal.Template.DeleteByID(r.Context(), req.ID)
err = h.dal.Template.DeleteByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
type ReqTemplateInit struct {
EventName string `json:"event_name"`
Data map[string]interface{} `json:"data"`
}
func (h *Handlers) TemplateInit(w http.ResponseWriter, r *http.Request) {
//var req ReqTemplateInit
//
//user := getJwtUser(r)
//
//if user == nil {
// h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
// return
//}
//
//err := decodePost(&resp, r)
//
//if err != nil {
// h.reportError(w, err, http.StatusBadRequest)
// return
//}
//
//// Ищем ивент
//t, err := h.dal.Template.GetByNameAndUserID(r.Context(), resp.EventName, amoData.UserID)
//
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//if t == nil {
// h.reportError(w, errors.New("template not found"), http.StatusNotFound)
// return
//}
//
//YaDiskData, err := h.dal.YaDisk.GetListByUserID(r.Context(), amoData.UserID)
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//if YaDiskData == nil {
// h.reportError(w, errors.New("YaDisk not found"), http.StatusNotFound)
// return
//}
//
//// download file
//token := &oauth2.Token{
// AccessToken: YaDiskData.AccessToken,
// TokenType: YaDiskData.TokenType,
// RefreshToken: YaDiskData.RefreshToken,
// Expiry: YaDiskData.ExpiresIn,
//}
//
//if !token.Valid() {
// h.reportError(w, errors.New("invalid yandex token"), http.StatusForbidden)
// return
//}
//
//client := h.YaDisk.NewClient(token)
//
//res, err := client.GetResources(YaDiskData.TemplateFolder + "/" + t.Filename)
//
//if res == nil {
// h.reportError(w, errors.New("resource in yandex disk not found"), http.StatusNotFound)
// return
//}
//downloadFileName := fmt.Sprintf("./tmp/downloaded/%v_%v", t.UserID, t.Filename)
//
//err = templategen.DownloadDocument(downloadFileName, res.File)
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//// parsing file
//dc, err := docTemp.GetTemplate(downloadFileName)
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//dc.Parse()
//
//saveFilename := fmt.Sprintf("%v_%v_%v", t.UserID, time.Now().Unix(), t.Filename)
//err = dc.Execute("./tmp/parsed/"+saveFilename, resp.HistoryTriggerData)
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//// Send to Yandex Disk
//
//err = client.UploadResourcesUrl(YaDiskData.SaveFolder+"/"+saveFilename, "https://solweb.site/tmp/parsed/"+saveFilename)
//if err != nil {
// h.reportError(w, err, http.StatusInternalServerError)
// return
//}
//
//fmt.Println("https://solweb.site/tmp/parsed/" + saveFilename)
}

@ -2,9 +2,11 @@ package handlers
import (
"errors"
"github.com/gorilla/mux"
"net/http"
"github.com/gorilla/mux"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
)
type ReqTemplateGroupCreate struct {
@ -16,68 +18,73 @@ type RespTemplateGroupCreate struct {
}
func (h *Handlers) TemplateGroupCreate(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGroupCreate
var request ReqTemplateGroupCreate
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Name == "" {
h.reportError(w, errors.New("name required"), http.StatusBadRequest)
if request.Name == "" {
h.reportError(w, http.StatusBadRequest, errors.New("name required"))
return
}
groupID, err := h.dal.TemplateGroup.Insert(r.Context(), &model.TemplateGroup{
UserID: amoData.AccountID,
Name: req.Name,
PenaID: amoData.PenaID,
Name: request.Name,
})
sendResponse(w, http.StatusOK, &RespTemplateGroupCreate{ID: groupID})
if err != nil {
h.reportError(w, http.StatusInternalServerError, err)
return
}
h.sendResponse(w, http.StatusOK, &RespTemplateGroupCreate{ID: groupID})
}
func (h *Handlers) TemplateGroupGet(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
if id == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
resp, err := h.dal.TemplateGroup.GetByID(r.Context(), id)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, &resp)
h.sendResponse(w, http.StatusOK, &resp)
}
func (h *Handlers) TemplateGroupGetList(w http.ResponseWriter, r *http.Request) {
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
resp, err := h.dal.TemplateGroup.GetListByUserID(r.Context(), amoData.AccountID)
resp, err := h.dal.TemplateGroup.GetListByPenaID(r.Context(), amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, &resp)
h.sendResponse(w, http.StatusOK, &resp)
}
type ReqTemplateGroupEdit struct {
@ -86,39 +93,39 @@ type ReqTemplateGroupEdit struct {
}
func (h *Handlers) TemplateGroupEdit(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGroupEdit
var request ReqTemplateGroupEdit
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
if req.Name == "" {
h.reportError(w, errors.New("name required"), http.StatusBadRequest)
if request.Name == "" {
h.reportError(w, http.StatusBadRequest, errors.New("name required"))
return
}
err = h.dal.TemplateGroup.Update(r.Context(), &model.TemplateGroup{
ID: req.ID,
UserID: amoData.AccountID,
Name: req.Name,
ID: request.ID,
PenaID: amoData.PenaID,
Name: request.Name,
})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -130,38 +137,38 @@ type ReqTemplateGroupDelete struct {
}
func (h *Handlers) TemplateGroupDelete(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGroupDelete
var request ReqTemplateGroupDelete
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
//TODO: Крайне желательно переписать на транзакции!
err = h.dal.Template.DeleteGroupFromAll(r.Context(), req.ID)
err = h.dal.Template.DeleteGroupFromAll(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
err = h.dal.TemplateGroup.Delete(r.Context(), req.ID)
err = h.dal.TemplateGroup.Delete(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -174,35 +181,35 @@ type ReqTemplateGroupPut struct {
}
func (h *Handlers) TemplateGroupPut(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGroupPut
var request ReqTemplateGroupPut
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.TemplateID == "" {
h.reportError(w, errors.New("template_id required"), http.StatusBadRequest)
if request.TemplateID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("template_id required"))
return
}
if req.GroupID == "" {
h.reportError(w, errors.New("group_id required"), http.StatusBadRequest)
if request.GroupID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("group_id required"))
return
}
err = h.dal.Template.PushGroup(r.Context(), req.TemplateID, req.GroupID, amoData.AccountID)
err = h.dal.Template.PushGroup(r.Context(), request.TemplateID, request.GroupID, amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -215,35 +222,35 @@ type ReqTemplateGroupRemove struct {
}
func (h *Handlers) TemplateGroupRemove(w http.ResponseWriter, r *http.Request) {
var req ReqTemplateGroupRemove
var request ReqTemplateGroupRemove
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.TemplateID == "" {
h.reportError(w, errors.New("template_id required"), http.StatusBadRequest)
if request.TemplateID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("template_id required"))
return
}
if req.GroupID == "" {
h.reportError(w, errors.New("group_id required"), http.StatusBadRequest)
if request.GroupID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("group_id required"))
return
}
err = h.dal.Template.PullGroup(r.Context(), req.TemplateID, req.GroupID, amoData.AccountID)
err = h.dal.Template.PullGroup(r.Context(), request.TemplateID, request.GroupID, amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}

@ -11,8 +11,9 @@ import (
"go.uber.org/zap"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal/model"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
YaDisk "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
)
type ReqYaDiskSaveToken struct {
@ -27,104 +28,105 @@ type ReqYaDiskSaveToken struct {
}
func (h *Handlers) YaDiskSaveToken(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskSaveToken
var request ReqYaDiskSaveToken
err := r.ParseForm()
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
err = schema.NewDecoder().Decode(&req, r.Form)
err = schema.NewDecoder().Decode(&request, r.Form)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.Error != "" {
err = errors.New("YaDiskErr:" + req.Error)
h.reportError(w, err, http.StatusBadRequest)
if request.Error != "" {
err = errors.New("YaDiskErr:" + request.Error)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.AccessToken == "" && req.Code == "" {
if request.AccessToken == "" && request.Code == "" {
err = errors.New("got empty token")
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.State == "" {
if request.State == "" {
err = errors.New("got empty state")
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
var state tools.StateToken
err = tools.DecryptTokenRC4(req.State, &state)
err = tools.DecryptTokenAES(request.State, &state)
if err != nil {
h.reportError(w, err, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, err)
return
}
token := &oauth2.Token{
AccessToken: req.AccessToken,
TokenType: req.TokenType,
RefreshToken: req.RefreshToken,
Expiry: time.Now().Add(time.Duration(req.ExpiresIn) * time.Second),
AccessToken: request.AccessToken,
TokenType: request.TokenType,
RefreshToken: request.RefreshToken,
Expiry: time.Now().Add(time.Duration(request.ExpiresIn) * time.Second),
}
yaDiskClient, err := h.YaDisk.NewClient(r.Context(), token, req.Code)
yaDiskClient, err := h.YaDisk.NewClient(r.Context(), token, request.Code)
if err != nil {
h.reportError(w, err, http.StatusForbidden)
h.reportError(w, http.StatusForbidden, err)
return
}
token = yaDiskClient.Token
yaUser, err := yaDiskClient.GetUser()
yaUser, err := yaDiskClient.GetUser(r.Context())
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
// Insert/Update token in DB
_, err = h.dal.YaDisk.InsertOrUpdate(r.Context(), &model.YaDisk{
UserID: state.UserID,
PenaID: state.PenaID,
Name: fmt.Sprintf("Yandex Disk (%v)", yaUser.Login),
UID: yaUser.Uid,
UID: yaUser.UID,
Login: yaUser.Login,
DisplayName: yaUser.DisplayName,
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
ExpiresIn: token.Expiry,
TokenType: token.TokenType,
TemplateFolder: YaDisk.DEFAULT_TEMPLATE_FOLDER,
SaveFolder: YaDisk.DEFAULT_SAVE_FOLDER,
TemplateFolder: yadisk.DefaultTemplateFolder,
SaveFolder: yadisk.DefaultSaveFolder,
})
if err != nil {
h.reportError(w, err, 500)
h.reportError(w, 500, err)
return
}
// Make default directories in YandexDisk
_, err = yaDiskClient.PutResources(YaDisk.DEFAULT_FOLDER)
_, err = yaDiskClient.PutResources(yadisk.DefaultFolder)
if err != nil {
h.logger.Error("ErrorHandler", zap.Error(err))
}
_, err = yaDiskClient.PutResources(YaDisk.DEFAULT_TEMPLATE_FOLDER)
_, err = yaDiskClient.PutResources(yadisk.DefaultTemplateFolder)
if err != nil {
h.logger.Error("ErrorHandler", zap.Error(err))
}
_, err = yaDiskClient.PutResources(YaDisk.DEFAULT_SAVE_FOLDER)
_, err = yaDiskClient.PutResources(yadisk.DefaultSaveFolder)
if err != nil {
h.logger.Error("ErrorHandler", zap.Error(err))
}
http.Redirect(w, r, state.RedirectUrl, http.StatusPermanentRedirect)
http.Redirect(w, r, state.RedirectURL, http.StatusPermanentRedirect)
}
type ReqYaDiskSetSettings struct {
@ -135,55 +137,55 @@ type ReqYaDiskSetSettings struct {
}
func (h *Handlers) YaDiskSetSettings(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskSetSettings
var request ReqYaDiskSetSettings
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
err = h.dal.YaDisk.Update(r.Context(), &model.YaDisk{
ID: req.ID,
Name: req.Name,
TemplateFolder: req.TemplateFolder,
SaveFolder: req.SaveFolder})
ID: request.ID,
Name: request.Name,
TemplateFolder: request.TemplateFolder,
SaveFolder: request.SaveFolder})
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, nil)
w.WriteHeader(http.StatusOK)
}
// YaDiskGetList - возвращает список хранилищ Yandex закрепленных за пользователем по его UserID из JWT
// YaDiskGetList - возвращает список хранилищ Yandex закрепленных за пользователем по его UserID из JWT.
func (h *Handlers) YaDiskGetList(w http.ResponseWriter, r *http.Request) {
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
yadiskInfo, err := h.dal.YaDisk.GetListByUserID(r.Context(), amoData.UserID)
yadiskInfo, err := h.dal.YaDisk.GetListByPenaID(r.Context(), amoData.PenaID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, yadiskInfo)
h.sendResponse(w, http.StatusOK, yadiskInfo)
}
type ReqYaDiskGetResources struct {
@ -194,45 +196,45 @@ type ReqYaDiskGetResources struct {
}
func (h *Handlers) YaDiskGetResources(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskGetResources
var request ReqYaDiskGetResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), req.ID)
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
client, err := h.YaDisk.NewClient(r.Context(), yadisk.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
resource, err := client.GetResources(req.Path, req.Limit, req.Offset)
resource, err := client.GetResources(r.Context(), request.Path, request.Limit, request.Offset)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, resource)
h.sendResponse(w, http.StatusOK, resource)
}
type ReqYaDiskPutResources struct {
@ -241,45 +243,45 @@ type ReqYaDiskPutResources struct {
}
func (h *Handlers) YaDiskPutResources(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskPutResources
var request ReqYaDiskPutResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), req.ID)
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
client, err := h.YaDisk.NewClient(r.Context(), yadisk.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
res, err := client.PutResources(req.Path)
res, err := client.PutResources(request.Path)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, http.StatusOK, res)
h.sendResponse(w, http.StatusOK, res)
}
type ReqYaDiskDownload struct {
@ -288,66 +290,73 @@ type ReqYaDiskDownload struct {
}
func (h *Handlers) YaDiskDownloadLink(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskDownload
amoData := getAmoByJwt(r)
var request ReqYaDiskDownload
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), req.ID)
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
if yadisk == nil {
h.reportError(w, errors.New("yandex disk not found"), http.StatusUnprocessableEntity)
h.reportError(w, http.StatusUnprocessableEntity, errors.New("yandex disk not found"))
return
}
client, err := h.YaDisk.NewClient(r.Context(), yadisk.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
url, err := client.DownloadFile(req.Path)
url, err := client.DownloadFile(request.Path)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
sendResponse(w, 200, url)
h.sendResponse(w, http.StatusOK, url)
}
func (h *Handlers) YaDiskDownload(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskDownload
err := decodePost(&req, r)
var request ReqYaDiskDownload
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
resp, err := http.Get(req.Path)
reqGet, err := http.NewRequestWithContext(r.Context(), http.MethodGet, request.Path, http.NoBody)
if err != nil {
sendResponse(w, http.StatusBadRequest, err.Error())
h.reportError(w, http.StatusBadRequest, err)
return
}
resp, err := http.DefaultClient.Do(reqGet)
if err != nil {
h.reportError(w, http.StatusInternalServerError, err)
return
}
defer resp.Body.Close()
if _, err := io.Copy(w, resp.Body); err != nil {
sendResponse(w, http.StatusBadRequest, err.Error())
h.reportError(w, http.StatusBadRequest, err)
}
}
@ -359,48 +368,53 @@ type ReqYaDiskUploadResources struct {
func (h *Handlers) YaDiskUploadResources(w http.ResponseWriter, r *http.Request) {
// Check form
fileData, fileHeader, err := r.FormFile("file")
if err != nil {
h.reportError(w, http.StatusBadRequest, err)
return
}
defer fileData.Close()
var req ReqYaDiskUploadResources
err = schema.NewDecoder().Decode(&req, r.Form)
var request ReqYaDiskUploadResources
err = schema.NewDecoder().Decode(&request, r.Form)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
if req.Path == "" {
req.Path = "disk:"
if request.Path == "" {
request.Path = "disk:"
}
var maxSize int64 = 10 << 20 // mb
if fileHeader.Size > maxSize {
h.reportError(w, errors.New("max size 10 mb"), http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, errors.New("max size 10 mb"))
return
}
// Upload file to the storage
storage, err := h.dal.YaDisk.GetByID(r.Context(), req.ID)
storage, err := h.dal.YaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
client, err := h.YaDisk.NewClient(r.Context(), storage.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
storagePath := fmt.Sprintf("%v/%v", req.Path, fileHeader.Filename)
storagePath := fmt.Sprintf("%v/%v", request.Path, fileHeader.Filename)
_, _, err = client.UploadResources(storagePath, fileData)
_, _, err = client.UploadResources(r.Context(), storagePath, fileData)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
@ -413,41 +427,41 @@ type ReqYaDiskDeleteResources struct {
}
func (h *Handlers) YaDiskDeleteResources(w http.ResponseWriter, r *http.Request) {
var req ReqYaDiskPutResources
var request ReqYaDiskPutResources
amoData := getAmoByJwt(r)
amoData := middleware.GetAmoData(r)
if amoData == nil {
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
h.reportError(w, http.StatusUnauthorized, ErrorUnauthorized)
return
}
err := decodePost(&req, r)
err := decodePost(&request, r)
if err != nil {
h.reportError(w, err, http.StatusBadRequest)
h.reportError(w, http.StatusBadRequest, err)
return
}
if req.ID == "" {
h.reportError(w, errors.New("id required"), http.StatusBadRequest)
if request.ID == "" {
h.reportError(w, http.StatusBadRequest, errors.New("id required"))
return
}
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), req.ID)
yadisk, err := h.dal.YaDisk.GetByID(r.Context(), request.ID)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
client, err := h.YaDisk.NewClient(r.Context(), yadisk.Token(), "")
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}
err = client.DeleteResources(req.Path)
err = client.DeleteResources(r.Context(), request.Path)
if err != nil {
h.reportError(w, err, http.StatusInternalServerError)
h.reportError(w, http.StatusInternalServerError, err)
return
}

96
main.go

@ -3,41 +3,54 @@ package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gorilla/mux"
"github.com/twmb/franz-go/pkg/kgo"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"penahub.gitlab.yandexcloud.net/backend/templategen/amo"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal"
GDisk "penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/handlers"
"penahub.gitlab.yandexcloud.net/backend/templategen/middleware"
"penahub.gitlab.yandexcloud.net/backend/templategen/privileges"
YaDisk "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/tools"
"penahub.gitlab.yandexcloud.net/backend/templategen/worker"
"penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
)
type Env struct {
Domain string `env:"DOMAIN" default:"tempgen.pena.digital"`
LogFile string `env:"LOG_FILE" default:"./tmp/logs.log"`
MongoUrl string `env:"MONGO_URL" default:"mongodb://mongo:30000/?replicaSet=penahub-rs"`
DbName string `env:"DB_NAME" default:"templategen"`
MongoURL string `env:"MONGO_URL" default:"mongodb://mongo:30000/?replicaSet=penahub-rs"`
DBName string `env:"DB_NAME" default:"templategen"`
YaDiskClientID string `env:"YADISK_CLIENT_ID" default:"94482c181e5148c096ae6ad3b2a981ea"`
YaDiskClientSecret string `env:"YADISK_CLIENT_SECRET" default:"7dc4f541c3f64f4a9078e59d7494d222"`
GDiskCredentials string `env:"GDISK_CREDENTIALS" default:"./static/gdisk-credentials.json"`
AmoClientID string `env:"AMO_CLIENT_ID" default:"6c7f3fdb-cce7-4fb0-a8a3-640b695c8d00"`
AmoClientSecret string `env:"AMO_CLIENT_SECRET" default:"5oJU1L8pScLKfEasShJ6KuDU32VrIs6BVKQ6BiY4vABIQycUWJziULytiUs5jaZU"`
AmoRedirectUrn string `env:"AMO_REDIRECT_URN" default:"/settings/widgets/penagen/"`
TokenKey string `env:"TOKEN_KEY" default:"z1eRU{fq*VtfLAszrz"`
KafkaTariff string `env:"KAFKA_TARIFF"`
KafkaBrokers []string `env:"KAFKA_BROKERS"`
KafkaTariffTopic string `env:"KAFKA_TARIFF_TOPIC"`
KafkaConsumerGroupID string `env:"KAFKA_CONSUMER_GROUP_ID"`
PrivilegesDomain string `env:"PRIVILEGES_DOMAIN"`
}
func main() {
opts := GetOpts()
ctx, cancel := context.WithCancel(context.Background())
// Set tools/token
tools.SetToken(opts.TokenKey)
// Logger
cfgLogger := zap.NewDevelopmentConfig()
cfgLogger.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
@ -51,20 +64,23 @@ func main() {
panic(err)
}
if err := privileges.PublishPrivileges(ctx, "https://admin.pena.digital/strator"); err != nil {
err = privileges.PublishPrivileges(ctx, "https://admin.pena.digital/strator")
if err != nil {
logger.Error("ErrorPublishPrivileges", zap.Error(err))
}
// Start Data Access Layer
mongoDal, err := dal.InitMongoDAL(ctx, opts.MongoUrl, opts.DbName, logger)
mongoDal, err := dal.InitMongoDAL(ctx, opts.MongoURL, opts.DBName, logger)
defer mongoDal.Disconnect()
if err != nil {
logger.Fatal("ErrorConnectToDAL", zap.Error(err))
logger.Error("ErrorConnectToDAL", zap.Error(err))
return
}
// Yandex Disk
yaDisk := YaDisk.NewClientApp(
yaDisk := yadisk.NewClientApp(
opts.YaDiskClientID,
opts.YaDiskClientSecret,
fmt.Sprintf("https://%v/yadisk", opts.Domain),
@ -72,16 +88,49 @@ func main() {
)
// Google Drive
gDisk, err := GDisk.NewClientApp(opts.GDiskCredentials)
gDisk, err := gdisk.NewClientApp(opts.GDiskCredentials)
if err != nil {
log.Fatal("ErrorCreateGoogleDriveClientApp:", err)
logger.Error("ErrorCreateGoogleDriveClientApp", zap.Error(err))
return
}
// Amo
amoApp := amo.NewClientApp(
opts.AmoClientID,
opts.AmoClientSecret,
fmt.Sprintf("https://%v/amo", opts.Domain))
fmt.Sprintf("https://%v/amo", opts.Domain),
)
// Tariff
kafkaTariffClient, err := kgo.NewClient(
kgo.SeedBrokers(opts.KafkaBrokers...),
kgo.ConsumerGroup(opts.KafkaConsumerGroupID),
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
kgo.DefaultProduceTopic(opts.KafkaTariffTopic),
kgo.ConsumeTopics(opts.KafkaTariffTopic),
)
if err != nil {
logger.Error("ErrorCreateKafkaTariffClient", zap.Error(err))
return
}
tariffConsumerDeps := tariff.ConsumerDeps{
Logger: logger,
Client: kafkaTariffClient,
}
tariffConsumer := tariff.NewConsumer(tariffConsumerDeps)
// Tariff worker
tariffWorkerDeps := worker.TariffWorkerDeps{
Logger: logger,
Dal: mongoDal,
TariffConsumer: tariffConsumer,
PrivilegesDomain: opts.PrivilegesDomain,
}
tariffWorker := worker.NewTariffWorker(tariffWorkerDeps)
go tariffWorker.Run(ctx)
// Handlers
h := handlers.NewHandlers(mongoDal, yaDisk, gDisk, amoApp, logger, &handlers.HandlerVars{
@ -90,10 +139,9 @@ func main() {
})
r := mux.NewRouter()
// Add Assets
//fs := http.FileServer(http.Dir("assets"))
//r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", fs))
// Add Assets
r.PathPrefix("/tmp/generated/").Handler(http.StripPrefix("/tmp/generated/",
http.FileServer(http.Dir("tmp/generated"))))
@ -154,7 +202,6 @@ func main() {
r.HandleFunc("/template/getListByGroup", h.TemplateGetListByGroup)
r.HandleFunc("/template/update", h.TemplateUpdate)
r.HandleFunc("/template/delete", h.TemplateDelete)
r.HandleFunc("/template/init", h.TemplateInit) // устарело
r.HandleFunc("/group/create", h.TemplateGroupCreate)
r.HandleFunc("/group/getList", h.TemplateGroupGetList)
@ -171,8 +218,6 @@ func main() {
r.HandleFunc("/generator/byamowebhook", h.GeneratorByAmoWebhook)
r.HandleFunc("/history/get", h.GetHistoryByID)
//r.HandleFunc("/history/getListByFilter", h.GetHistoryListByFilter)
//r.HandleFunc("/history/getlistbyfilter", h.GetHistoryListByFilter)
r.HandleFunc("/history/getList", h.GetHistoryList)
r.HandleFunc("/history/getlist", h.GetHistoryList)
@ -185,7 +230,7 @@ func main() {
mw.MiddlewareHeaders,
mw.MiddlewareCors,
mw.MiddlewareAmoJwt,
//mw.MiddlewareJwt,
mw.MiddlewareJwt,
// mw.MiddlewareAmoPlug,
mw.Logger,
)
@ -194,12 +239,14 @@ func main() {
srv := http.Server{
Handler: r,
ReadHeaderTimeout: time.Second * 3, // TODO: Обсудить со Skeris
}
go func() {
err := srv.ListenAndServe()
if err != nil {
logger.Fatal("CanNotServe", zap.Error(err))
logger.Error("CanNotServe", zap.Error(err))
return
}
}()
@ -211,7 +258,8 @@ func main() {
go func() {
err := srv.ListenAndServeTLS(fullCert, privCert)
if err != nil {
logger.Fatal("CanNotServe", zap.Error(err))
logger.Error("CanNotServe", zap.Error(err))
return
}
}()
@ -221,9 +269,11 @@ func main() {
killSignal := <-interrupt
switch killSignal {
case os.Interrupt:
logger.Fatal("AppInterrupted")
logger.Error("AppInterrupted")
return
case syscall.SIGTERM:
logger.Fatal("AppTerminated")
logger.Error("AppTerminated")
return
}
defer cancel()

@ -22,6 +22,15 @@ type Middleware struct {
AmoAccessMap map[string]string // key - endpoint; value - access (visibility, creation, delete)
}
type contextKey uint
const (
PenaUserIDContextKey contextKey = 1 // UserID from Pena JWT
AmoDataContextKey contextKey = 2 // AmoData
AmoIsAdminContextKey contextKey = 3 // Is Admin?
AmoUserIDContextKey contextKey = 4 // ID of user making the request from AMO CRM
)
func InitMiddleware(dal *dal.MongoDAL, amo *amo.ClientApp, logger *zap.Logger,
amoAccessMap map[string]string) *Middleware {
return &Middleware{dal: dal, amo: amo, logger: logger, AmoAccessMap: amoAccessMap}
@ -29,7 +38,7 @@ func InitMiddleware(dal *dal.MongoDAL, amo *amo.ClientApp, logger *zap.Logger,
func (mw *Middleware) MiddlewareCors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
} else {
next.ServeHTTP(w, r)
@ -43,16 +52,17 @@ func (mw *Middleware) Logger(next http.Handler) http.Handler {
next.ServeHTTP(w, r)
return
}
userId := getJwtUser(r)
amoData := getAmoByJwt(r)
var penaID string
amoData := GetAmoData(r)
if amoData != nil {
userId = amoData.UserID
penaID = amoData.PenaID
}
mw.logger.Info("HttpRequest",
zap.String("URL", r.URL.String()),
zap.String("UserID", userId),
zap.String("PenaID", penaID),
)
next.ServeHTTP(w, r)
})
@ -66,18 +76,36 @@ func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
}
ctx := r.Context()
if amoData := getAmoByJwt(r); amoData != nil {
ctx = context.WithValue(ctx, "UserID", amoData.UserID)
// Если уже прошла авторизация через сервис AMO, то пропускаем дальнейшие действия
amoData := GetAmoData(r)
if amoData != nil {
ctx = context.WithValue(ctx, PenaUserIDContextKey, amoData.PenaID)
next.ServeHTTP(w, r.WithContext(ctx))
return
}
// Check token in cookies
c := r.Header.Get(jwt_adapter.DefaultHeaderKey)
// Check token in headers
headerValue := r.Header.Get(jwt_adapter.DefaultHeaderKey)
jwt, err := jwt_adapter.Decode(c)
if err == nil {
ctx = context.WithValue(ctx, "PenaUserID", jwt.GetUserID())
jwt, err := jwt_adapter.Decode(headerValue)
if err != nil {
mw.reportError(w, http.StatusUnauthorized, err)
}
ctx = context.WithValue(ctx, PenaUserIDContextKey, jwt.GetUserID())
// Add AMO data to context
amoData, err = mw.dal.Amo.GetByPenaID(ctx, jwt.GetUserID())
if err != nil {
mw.reportError(w, http.StatusInternalServerError, err)
return
}
ctx = context.WithValue(ctx, AmoDataContextKey, amoData)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
@ -89,7 +117,6 @@ func (mw *Middleware) MiddlewareHeaders(next http.Handler) http.Handler {
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE")
//w.Header().Add()
next.ServeHTTP(w, r)
})
}
@ -114,22 +141,21 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
token, err := mw.amo.DecodeJwt(r)
if err != nil {
if required {
mw.reportError(w, err, http.StatusUnauthorized)
mw.reportError(w, http.StatusUnauthorized, err)
} else {
// mw.logger.Error("ErrAmoJwtAccess", zap.Error(err))
next.ServeHTTP(w, r)
}
return
}
data, err := mw.dal.Amo.GetByAccountID(r.Context(), strconv.FormatInt(token.AccountId, 10))
data, err := mw.dal.Amo.GetByAccountID(r.Context(), strconv.FormatInt(token.AccountID, 10))
if err != nil {
mw.reportError(w, err, http.StatusInternalServerError)
mw.reportError(w, http.StatusInternalServerError, err)
return
}
if data == nil && !strings.Contains(r.URL.String(), "/amo/state") {
mw.reportError(w, errors.New("amo account not found"), http.StatusUnauthorized)
mw.reportError(w, http.StatusUnauthorized, errors.New("amo account not found"))
return
}
@ -141,8 +167,8 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
if ok {
// Если не админ, то проверяем права пользователя
if rule == "visibility" && !isAcessGranted {
for _, userId := range data.AccessRules.Visibility {
if userId == token.UserId {
for _, userID := range data.AccessRules.Visibility {
if userID == token.UserID {
isAcessGranted = true
break
}
@ -150,8 +176,8 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
}
if rule == "creation" && !isAcessGranted {
for _, userId := range data.AccessRules.Creation {
if userId == token.UserId {
for _, userID := range data.AccessRules.Creation {
if userID == token.UserID {
isAcessGranted = true
break
}
@ -159,8 +185,8 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
}
if rule == "delete" && !isAcessGranted {
for _, userId := range data.AccessRules.Delete {
if userId == token.UserId {
for _, userID := range data.AccessRules.Delete {
if userID == token.UserID {
isAcessGranted = true
break
}
@ -172,13 +198,13 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
// Если прав нет, прерываемся
if !isAcessGranted {
mw.reportError(w, errors.New("amoUser forbidden"), http.StatusForbidden) // mb: http.StatusForbidden
mw.reportError(w, http.StatusForbidden, errors.New("amoUser forbidden")) // mb: http.StatusForbidden
return
}
ctx := context.WithValue(r.Context(), "amoData", data)
ctx = context.WithValue(ctx, "amoIsAdmin", token.IsAdmin)
ctx = context.WithValue(ctx, "amoUserID", token.UserId)
ctx := context.WithValue(r.Context(), AmoDataContextKey, data)
ctx = context.WithValue(ctx, AmoIsAdminContextKey, token.IsAdmin)
ctx = context.WithValue(ctx, AmoUserIDContextKey, token.UserID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
@ -196,24 +222,24 @@ func (mw *Middleware) MiddlewareAmoPlug(next http.Handler) http.Handler {
data, err := mw.dal.Amo.GetByAccountID(r.Context(), "30228997")
if err != nil {
mw.reportError(w, err, http.StatusInternalServerError)
mw.reportError(w, http.StatusInternalServerError, err)
return
}
if data == nil {
mw.reportError(w, errors.New("amo account not found"), http.StatusUnauthorized)
mw.reportError(w, http.StatusUnauthorized, errors.New("amo account not found"))
return
}
ctx = context.WithValue(ctx, "amoData", data)
ctx = context.WithValue(ctx, AmoDataContextKey, data)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getJwtUser(r *http.Request) string {
user, ok := r.Context().Value("UserID").(string)
func GetPenaUserID(r *http.Request) string {
user, ok := r.Context().Value(PenaUserIDContextKey).(string)
if ok {
return user
}
@ -221,8 +247,8 @@ func getJwtUser(r *http.Request) string {
return ""
}
func getAmoByJwt(r *http.Request) *model.Amo {
amoData, ok := r.Context().Value("amoData").(*model.Amo)
func GetAmoData(r *http.Request) *model.Amo {
amoData, ok := r.Context().Value(AmoDataContextKey).(*model.Amo)
if ok {
return amoData
}
@ -230,9 +256,24 @@ func getAmoByJwt(r *http.Request) *model.Amo {
return nil
}
func (mw *Middleware) reportError(w http.ResponseWriter, err error, status int) error {
func GetAmoUserID(r *http.Request) int64 {
result, ok := r.Context().Value(AmoUserIDContextKey).(int64)
if ok {
return result
}
return 0
}
func (mw *Middleware) reportError(w http.ResponseWriter, status int, err error) {
mw.logger.Error("ErrorMiddleware", zap.Error(err))
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
return json.NewEncoder(w).Encode(err)
e := json.NewEncoder(w)
e.SetEscapeHTML(false)
errJSON := e.Encode(err)
if errJSON != nil {
mw.logger.Error("ErrorMiddleware - JSON ENCODE", zap.Error(errJSON))
}
}

@ -210,8 +210,8 @@ components:
properties:
id:
$ref: "#/components/defaultSchemas/id"
user_id:
$ref: "#/components/defaultSchemas/user_id"
pena_id:
$ref: "#/components/defaultSchemas/pena_id"
account_id:
type: string
description: "Идентификатор аккаунта в AMO CRM"
@ -266,8 +266,8 @@ components:
properties:
id:
$ref: "#/components/defaultSchemas/id"
user_id:
$ref: "#/components/defaultSchemas/user_id"
pena_id:
$ref: "#/components/defaultSchemas/pena_id"
email:
type: string
description: "E-mail профиля Google"
@ -321,8 +321,8 @@ components:
properties:
id:
$ref: "#/components/defaultSchemas/id"
user_id:
$ref: "#/components/defaultSchemas/user_id"
pena_id:
$ref: "#/components/defaultSchemas/pena_id"
uid:
type: string
description: "Yandex UID"
@ -359,8 +359,8 @@ components:
properties:
id:
$ref: "#/components/defaultSchemas/id"
user_id:
$ref: "#/components/defaultSchemas/user_id"
pena_id:
$ref: "#/components/defaultSchemas/pena_id"
lead_id:
type: string
description: "Идентификатор сделки в AMO CRM"
@ -399,8 +399,8 @@ components:
properties:
id:
$ref: "#/components/defaultSchemas/id"
user_id:
$ref: "#/components/defaultSchemas/user_id"
pena_id:
$ref: "#/components/defaultSchemas/pena_id"
name:
type: string
description: "Название группы"
@ -561,7 +561,7 @@ components:
description: "Уникальный идентификатор"
example: "635520d143ebb05286f4fba6"
required: true
user_id:
pena_id:
type: string
description: "Уникальный идентификатор пользователя"
example: "62ac67a1471fd0f7892353bf"

@ -2,35 +2,38 @@ package penadisk
import (
"bytes"
"context"
"encoding/json"
"github.com/minio/minio-go/v7"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"os"
"fmt"
"strconv"
"strings"
"github.com/minio/minio-go/v7"
)
const (
DEFAULT_FOLDER = "templategen"
DEFAULT_TEMPLATE_FOLDER = DEFAULT_FOLDER + "/templates"
DEFAULT_SAVE_FOLDER = DEFAULT_FOLDER + "/saved"
DefaultFolder = "templategen"
DefaultTemplateFolder = DefaultFolder + "/templates"
DefaultSaveFolder = DefaultFolder + "/saved"
)
var (
URL = ""
V1_DISK_API = URL + "/api/v1/"
V1DiskAPI = URL + "/api/v1/"
)
func init() {
URL = os.Getenv("PENADISK_URL")
V1_DISK_API = URL + "api/v1/"
V1DiskAPI = URL + "api/v1/"
}
type Client struct {
/* ID пользователя пены */
userID string
}
@ -38,8 +41,8 @@ func NewClient(userID string) *Client {
return &Client{userID: userID}
}
func (c *Client) GetDisk() (*BucketWithStats, error) {
req, err := http.NewRequest("GET", V1_DISK_API+"bucket", nil)
func (c *Client) GetDisk(ctx context.Context) (*BucketWithStats, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"bucket", http.NoBody)
if err != nil {
return nil, err
}
@ -61,11 +64,11 @@ func (c *Client) GetDisk() (*BucketWithStats, error) {
return &r, err
}
func (c *Client) SetBucketQuota(size uint64) error {
func (c *Client) SetBucketQuota(ctx context.Context, size uint64) error {
data := url.Values{}
data.Set("size", strconv.FormatUint(size, 10))
req, err := http.NewRequest("PUT", V1_DISK_API+"bucket/quota/set?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/quota/set?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -80,8 +83,8 @@ func (c *Client) SetBucketQuota(size uint64) error {
return checkError(resp)
}
func (c *Client) UnsetBucketQuota() error {
req, err := http.NewRequest("PUT", V1_DISK_API+"bucket/quota/unset", nil)
func (c *Client) UnsetBucketQuota(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/quota/unset", http.NoBody)
if err != nil {
return err
}
@ -96,8 +99,8 @@ func (c *Client) UnsetBucketQuota() error {
return checkError(resp)
}
func (c *Client) EnableBucketVersioning() error {
req, err := http.NewRequest("PUT", V1_DISK_API+"bucket/versioning/enable", nil)
func (c *Client) EnableBucketVersioning(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/versioning/enable", http.NoBody)
if err != nil {
return err
}
@ -112,8 +115,8 @@ func (c *Client) EnableBucketVersioning() error {
return checkError(resp)
}
func (c *Client) SuspendBucketVersioning() error {
req, err := http.NewRequest("PUT", V1_DISK_API+"bucket/versioning/suspend", nil)
func (c *Client) SuspendBucketVersioning(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"bucket/versioning/suspend", http.NoBody)
if err != nil {
return err
}
@ -128,8 +131,8 @@ func (c *Client) SuspendBucketVersioning() error {
return checkError(resp)
}
// GetResources - получить информацию о ресурсах
func (c *Client) GetResources(path string, recursive, withVersion bool) ([]minio.ObjectInfo, error) {
// GetResources - получить информацию о ресурсах.
func (c *Client) GetResources(ctx context.Context, path string, recursive, withVersion bool) ([]minio.ObjectInfo, error) {
data := url.Values{}
data.Set("path", path)
@ -141,7 +144,7 @@ func (c *Client) GetResources(path string, recursive, withVersion bool) ([]minio
data.Set("with_versions", "true")
}
req, err := http.NewRequest("GET", V1_DISK_API+"resources?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
if err != nil {
return nil, err
}
@ -163,12 +166,12 @@ func (c *Client) GetResources(path string, recursive, withVersion bool) ([]minio
return r, err
}
// PutResources - создать папку
func (c *Client) PutResources(path string) error {
// PutResources - создать папку.
func (c *Client) PutResources(ctx context.Context, path string) error {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", V1_DISK_API+"resources?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -183,22 +186,23 @@ func (c *Client) PutResources(path string) error {
return checkError(resp)
}
func (c *Client) UploadResources(path, filename string, overwrite bool,
file io.Reader) (fullPath string, exportUrl string, err error) {
func (c *Client) UploadResources(ctx context.Context, path, filename string, overwrite bool,
file io.Reader) (fullPath string, exportURL string, err error) {
buf := new(bytes.Buffer)
bw := multipart.NewWriter(buf)
if !strings.HasSuffix(path, "/") {
path += "/"
}
if err := bw.WriteField("path", path); err != nil {
err = bw.WriteField("path", path)
if err != nil {
return "", "", err
}
if overwrite {
if err := bw.WriteField("overwrite", "true"); err != nil {
err = bw.WriteField("overwrite", "true")
if err != nil {
return "", "", err
}
}
@ -213,8 +217,16 @@ func (c *Client) UploadResources(path, filename string, overwrite bool,
return "", "", err
}
bw.Close()
req, err := http.NewRequest("POST", V1_DISK_API+"resources/upload", buf)
err = bw.Close()
if err != nil {
return "", "", err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, V1DiskAPI+"resources/upload", buf)
if err != nil {
return "", "", err
}
c.setHeaders(req)
req.Header.Set("Content-Type", bw.FormDataContentType())
@ -231,18 +243,18 @@ func (c *Client) UploadResources(path, filename string, overwrite bool,
fullPath = path + filename
exportUrl, err = c.GetPublicDownloadURL(fullPath)
exportURL, err = c.GetPublicDownloadURL(ctx, fullPath)
return fullPath, exportUrl, err
return fullPath, exportURL, err
}
// GetPublicDownloadURL - получить публичную ссылку для скачивания файла. Ссылка живет 30 минут
func (c *Client) GetPublicDownloadURL(path string) (href string, err error) {
// GetPublicDownloadURL - получить публичную ссылку для скачивания файла. Ссылка живет 30 минут.
func (c *Client) GetPublicDownloadURL(ctx context.Context, path string) (href string, err error) {
data := url.Values{}
data.Set("path", path)
// Получаем публичную ссылку на файл живущую 30 минут
req, err := http.NewRequest("GET", V1_DISK_API+"resources/download?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"resources/download?"+data.Encode(), http.NoBody)
if err != nil {
return "", err
}
@ -265,14 +277,23 @@ func (c *Client) GetPublicDownloadURL(path string) (href string, err error) {
return r.Href, err
}
func (c *Client) DownloadResource(path, downloadPath string) error {
href, err := c.GetPublicDownloadURL(path)
func (c *Client) DownloadResource(ctx context.Context, path, downloadPath string) error {
href, err := c.GetPublicDownloadURL(ctx, path)
if err != nil {
return err
}
resp, err := http.Get(href)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, http.NoBody)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err != nil {
@ -293,7 +314,7 @@ func (c *Client) DownloadResource(path, downloadPath string) error {
if err != nil {
return err
}
defer out.Close()
defer out.Close() //nolint
// Write the body to file
_, err = io.Copy(out, resp.Body)
@ -301,19 +322,26 @@ func (c *Client) DownloadResource(path, downloadPath string) error {
return err
}
func (c *Client) DownloadResourceBytes(path string) ([]byte, error) {
href, err := c.GetPublicDownloadURL(path)
func (c *Client) DownloadResourceBytes(ctx context.Context, path string) ([]byte, error) {
href, err := c.GetPublicDownloadURL(ctx, path)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, http.NoBody)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
resp, err := http.Get(href)
defer resp.Body.Close()
if err != nil {
return nil, err
}
if err = checkError(resp); err != nil {
return nil, err
}
@ -321,7 +349,7 @@ func (c *Client) DownloadResourceBytes(path string) ([]byte, error) {
return io.ReadAll(resp.Body)
}
func (c *Client) CopyResources(from, path string, overwrite bool) error {
func (c *Client) CopyResources(ctx context.Context, from, path string, overwrite bool) error {
data := url.Values{}
data.Set("from", from)
data.Set("path", path)
@ -329,7 +357,7 @@ func (c *Client) CopyResources(from, path string, overwrite bool) error {
data.Set("overwrite", "true")
}
req, err := http.NewRequest("PUT", V1_DISK_API+"resources/copy?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/copy?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -344,7 +372,7 @@ func (c *Client) CopyResources(from, path string, overwrite bool) error {
return checkError(resp)
}
func (c *Client) MoveResources(from, path string, overwrite bool) error {
func (c *Client) MoveResources(ctx context.Context, from, path string, overwrite bool) error {
data := url.Values{}
data.Set("from", from)
data.Set("path", path)
@ -352,7 +380,7 @@ func (c *Client) MoveResources(from, path string, overwrite bool) error {
data.Set("overwrite", "true")
}
req, err := http.NewRequest("PUT", V1_DISK_API+"resources/move?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/move?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -367,14 +395,14 @@ func (c *Client) MoveResources(from, path string, overwrite bool) error {
return checkError(resp)
}
func (c *Client) DeleteResources(path string, permanently bool) error {
func (c *Client) DeleteResources(ctx context.Context, path string, permanently bool) error {
data := url.Values{}
data.Set("path", path)
if permanently {
data.Set("permanently", "true")
}
req, err := http.NewRequest("DELETE", V1_DISK_API+"resources?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, V1DiskAPI+"resources?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -389,11 +417,11 @@ func (c *Client) DeleteResources(path string, permanently bool) error {
return checkError(resp)
}
func (c *Client) PublishResources(path string) (href string, err error) {
func (c *Client) PublishResources(ctx context.Context, path string) (href string, err error) {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", V1_DISK_API+"resources/publish?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/publish?"+data.Encode(), http.NoBody)
if err != nil {
return "", err
@ -416,11 +444,11 @@ func (c *Client) PublishResources(path string) (href string, err error) {
return r.Href, err
}
func (c *Client) UnpublishResources(path string) error {
func (c *Client) UnpublishResources(ctx context.Context, path string) error {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", V1_DISK_API+"resources/unpublish?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"resources/unpublish?"+data.Encode(), http.NoBody)
if err != nil {
return err
@ -443,7 +471,9 @@ func (c *Client) setHeaders(req *http.Request) {
func checkError(resp *http.Response) error {
switch resp.StatusCode {
case 200, 201, 202, 203, 204, 205, 206, 207, 208, 226:
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNonAuthoritativeInfo,
http.StatusNoContent, http.StatusResetContent, http.StatusPartialContent,
http.StatusMultiStatus, http.StatusAlreadyReported, http.StatusIMUsed:
return nil
}
@ -453,5 +483,5 @@ func checkError(resp *http.Response) error {
return err
}
return fmt.Errorf(fmt.Sprintf("api/penaDisk: %v | %v", resp.StatusCode, string(body)))
return fmt.Errorf("api/penaDisk: %v | %v", resp.StatusCode, string(body))
}

@ -11,9 +11,9 @@ type RespPublishResources struct {
}
type Bucket struct {
Id string `json:"id"`
ID string `json:"id"`
Name string `json:"name"`
FolderId string `json:"folderId"`
FolderID string `json:"folderId"`
AnonymousAccessFlags struct {
Read bool `json:"read"`
List bool `json:"list"`
@ -23,16 +23,16 @@ type Bucket struct {
Versioning string `json:"versioning"`
MaxSize string `json:"maxSize"`
Policy string `json:"policy"`
Acl struct {
ACL struct {
Grants []struct {
Permission string `json:"permission"`
GrantType string `json:"grantType"`
GranteeId string `json:"granteeId"`
GranteeID string `json:"granteeId"`
} `json:"grants"`
} `json:"acl"`
CreatedAt time.Time `json:"createdAt"`
Cors []struct {
Id string `json:"id"`
ID string `json:"id"`
AllowedMethods []string `json:"allowedMethods"`
AllowedHeaders []string `json:"allowedHeaders"`
AllowedOrigins []string `json:"allowedOrigins"`
@ -48,12 +48,12 @@ type Bucket struct {
} `json:"redirectAllRequests"`
RoutingRules []struct {
Condition struct {
HttpErrorCodeReturnedEquals string `json:"httpErrorCodeReturnedEquals"`
HTTPErrorCodeReturnedEquals string `json:"httpErrorCodeReturnedEquals"`
KeyPrefixEquals string `json:"keyPrefixEquals"`
} `json:"condition"`
Redirect struct {
Hostname string `json:"hostname"`
HttpRedirectCode string `json:"httpRedirectCode"`
HTTPRedirectCode string `json:"httpRedirectCode"`
Protocol string `json:"protocol"`
ReplaceKeyPrefixWith string `json:"replaceKeyPrefixWith"`
ReplaceKeyWith string `json:"replaceKeyWith"`
@ -61,7 +61,7 @@ type Bucket struct {
} `json:"routingRules"`
} `json:"websiteSettings"`
LifecycleRules []struct {
Id string `json:"id"`
ID string `json:"id"`
Enabled bool `json:"enabled"`
Filter struct {
Prefix string `json:"prefix"`

@ -1,9 +1,10 @@
package privileges;
package privileges
import (
"fmt"
"context"
"fmt"
"time"
"github.com/gofiber/fiber/v2"
)
@ -54,7 +55,7 @@ var (
)
func PublishPrivileges(ctx context.Context, domain string) error {
old, err := getActualPrivileges(ctx, domain)
old, err := GetActualPrivileges(ctx, domain)
if err != nil {
return err
}
@ -82,8 +83,8 @@ func PublishPrivileges(ctx context.Context, domain string) error {
return updatePrivileges(ctx, Privileges, domain)
}
func getActualPrivileges(
ctx context.Context,
func GetActualPrivileges(
_ context.Context,
domain string) ([]Privilege, error) {
res := []Privilege{}
_, _, err := fiber.Get(domain + "/privilege/service/templategen").Struct(&res)
@ -94,9 +95,9 @@ func getActualPrivileges(
return res, nil
}
func updatePrivileges(ctx context.Context, data []Privilege, domain string) error {
func updatePrivileges(_ context.Context, data []Privilege, domain string) error {
_, _, err := fiber.Put(domain + "/privilege/many").JSON(map[string][]Privilege{
"privilegies": data,
"privilege": data,
}).Bytes()
if err != nil {
return err[0]
@ -106,12 +107,12 @@ func updatePrivileges(ctx context.Context, data []Privilege, domain string) erro
}
func setupActualPrivileges(
ctx context.Context,
_ context.Context,
data []Privilege,
domain string) error {
res := []Privilege{}
_, _, err := fiber.Post(domain + "/privilege/many").JSON(map[string][]Privilege{
"privilegies": data,
"privilege": data,
}).Struct(&res)
if err != nil {
@ -121,7 +122,7 @@ func setupActualPrivileges(
return nil
}
func removePrivilege(ctx context.Context, domain, id string) error {
func removePrivilege(_ context.Context, domain, id string) error {
_, _, err := fiber.Delete(domain + "/privilege/").JSON(map[string]string{"privilegeId": id}).Bytes()
if err != nil {
return err[0]

@ -1,17 +1,19 @@
package privileges
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/themakers/bdd"
"context"
)
func TestPrivileges(t *testing.T) {
ctx := context.Background()
bdd.Scenario(t, "setPrivilegesLogic", func(t *testing.T, runID string) {
t.Helper()
bdd.Test(t, "getOldPrivileges", func() {
p, err := getActualPrivileges(ctx,"https://admin.pena.digital/strator")
p, err := GetActualPrivileges(ctx, "https://admin.pena.digital/strator")
assert.NoError(t, err)
assert.NotEqual(t, len(p), 0)
})

313
proto/tariff/models.pb.go Normal file

@ -0,0 +1,313 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v4.23.4
// source: models.proto
package tariff
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type PrivilegeType int32
const (
PrivilegeType_Full PrivilegeType = 0
PrivilegeType_Day PrivilegeType = 1
PrivilegeType_Count PrivilegeType = 2
)
// Enum value maps for PrivilegeType.
var (
PrivilegeType_name = map[int32]string{
0: "Full",
1: "Day",
2: "Count",
}
PrivilegeType_value = map[string]int32{
"Full": 0,
"Day": 1,
"Count": 2,
}
)
func (x PrivilegeType) Enum() *PrivilegeType {
p := new(PrivilegeType)
*p = x
return p
}
func (x PrivilegeType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (PrivilegeType) Descriptor() protoreflect.EnumDescriptor {
return file_models_proto_enumTypes[0].Descriptor()
}
func (PrivilegeType) Type() protoreflect.EnumType {
return &file_models_proto_enumTypes[0]
}
func (x PrivilegeType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use PrivilegeType.Descriptor instead.
func (PrivilegeType) EnumDescriptor() ([]byte, []int) {
return file_models_proto_rawDescGZIP(), []int{0}
}
type PrivilegeMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PrivilegeID string `protobuf:"bytes,1,opt,name=PrivilegeID,proto3" json:"PrivilegeID,omitempty"`
ServiceKey string `protobuf:"bytes,2,opt,name=ServiceKey,proto3" json:"ServiceKey,omitempty"`
Type PrivilegeType `protobuf:"varint,3,opt,name=Type,proto3,enum=tariff.PrivilegeType" json:"Type,omitempty"`
Value string `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
Amount uint64 `protobuf:"varint,5,opt,name=Amount,proto3" json:"Amount,omitempty"`
}
func (x *PrivilegeMessage) Reset() {
*x = PrivilegeMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_models_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PrivilegeMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PrivilegeMessage) ProtoMessage() {}
func (x *PrivilegeMessage) ProtoReflect() protoreflect.Message {
mi := &file_models_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PrivilegeMessage.ProtoReflect.Descriptor instead.
func (*PrivilegeMessage) Descriptor() ([]byte, []int) {
return file_models_proto_rawDescGZIP(), []int{0}
}
func (x *PrivilegeMessage) GetPrivilegeID() string {
if x != nil {
return x.PrivilegeID
}
return ""
}
func (x *PrivilegeMessage) GetServiceKey() string {
if x != nil {
return x.ServiceKey
}
return ""
}
func (x *PrivilegeMessage) GetType() PrivilegeType {
if x != nil {
return x.Type
}
return PrivilegeType_Full
}
func (x *PrivilegeMessage) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *PrivilegeMessage) GetAmount() uint64 {
if x != nil {
return x.Amount
}
return 0
}
type TariffMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Privileges []*PrivilegeMessage `protobuf:"bytes,1,rep,name=Privileges,proto3" json:"Privileges,omitempty"`
UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"`
}
func (x *TariffMessage) Reset() {
*x = TariffMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_models_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TariffMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TariffMessage) ProtoMessage() {}
func (x *TariffMessage) ProtoReflect() protoreflect.Message {
mi := &file_models_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TariffMessage.ProtoReflect.Descriptor instead.
func (*TariffMessage) Descriptor() ([]byte, []int) {
return file_models_proto_rawDescGZIP(), []int{1}
}
func (x *TariffMessage) GetPrivileges() []*PrivilegeMessage {
if x != nil {
return x.Privileges
}
return nil
}
func (x *TariffMessage) GetUserID() string {
if x != nil {
return x.UserID
}
return ""
}
var File_models_proto protoreflect.FileDescriptor
var file_models_proto_rawDesc = []byte{
0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x74, 0x61, 0x72, 0x69, 0x66, 0x66, 0x22, 0xad, 0x01, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x76, 0x69,
0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50,
0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x49, 0x44, 0x12, 0x1e, 0x0a,
0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a,
0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x61,
0x72, 0x69, 0x66, 0x66, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79,
0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06,
0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x61, 0x0a, 0x0d, 0x54, 0x61, 0x72, 0x69, 0x66, 0x66,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x69,
0x6c, 0x65, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x61,
0x72, 0x69, 0x66, 0x66, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x2a, 0x2d, 0x0a, 0x0d, 0x50, 0x72, 0x69,
0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75,
0x6c, 0x6c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x61, 0x79, 0x10, 0x01, 0x12, 0x09, 0x0a,
0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x02, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x74, 0x61,
0x72, 0x69, 0x66, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_models_proto_rawDescOnce sync.Once
file_models_proto_rawDescData = file_models_proto_rawDesc
)
func file_models_proto_rawDescGZIP() []byte {
file_models_proto_rawDescOnce.Do(func() {
file_models_proto_rawDescData = protoimpl.X.CompressGZIP(file_models_proto_rawDescData)
})
return file_models_proto_rawDescData
}
var file_models_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_models_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_models_proto_goTypes = []interface{}{
(PrivilegeType)(0), // 0: tariff.PrivilegeType
(*PrivilegeMessage)(nil), // 1: tariff.PrivilegeMessage
(*TariffMessage)(nil), // 2: tariff.TariffMessage
}
var file_models_proto_depIdxs = []int32{
0, // 0: tariff.PrivilegeMessage.Type:type_name -> tariff.PrivilegeType
1, // 1: tariff.TariffMessage.Privileges:type_name -> tariff.PrivilegeMessage
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_models_proto_init() }
func file_models_proto_init() {
if File_models_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_models_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PrivilegeMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_models_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TariffMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_models_proto_rawDesc,
NumEnums: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_models_proto_goTypes,
DependencyIndexes: file_models_proto_depIdxs,
EnumInfos: file_models_proto_enumTypes,
MessageInfos: file_models_proto_msgTypes,
}.Build()
File_models_proto = out.File
file_models_proto_rawDesc = nil
file_models_proto_goTypes = nil
file_models_proto_depIdxs = nil
}

24
proto/tariff/models.proto Normal file

@ -0,0 +1,24 @@
syntax = "proto3";
package tariff;
option go_package = "./tariff";
message PrivilegeMessage {
string PrivilegeID = 1;
string ServiceKey = 2;
PrivilegeType Type = 3;
string Value = 4;
uint64 Amount = 5;
}
message TariffMessage {
repeated PrivilegeMessage Privileges = 1;
string UserID = 2;
}
enum PrivilegeType {
Full = 0;
Day = 1;
Count = 2;
}

@ -8,7 +8,7 @@ import (
func AmoLeadFieldsToRuMap(data *amo.Lead) map[string]interface{} {
result := map[string]interface{}{
"ID": data.Id,
"ID": data.ID,
"Название": data.Name,
"Бюджет": data.Price,
"Создатель": data.CreatedBy,

@ -1,8 +1,9 @@
package templategen
import (
docTemp "github.com/danilsolovyov/doc-template"
"strings"
docTemp "github.com/danilsolovyov/doc-template"
)
func ExampleGenerate(filename string, data any) (string, error) {

@ -13,7 +13,7 @@ var (
)
// indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible.
func indexArg(index reflect.Value, cap int) (int, error) {
func indexArg(index reflect.Value, caps int) (int, error) {
var x int64
switch index.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@ -25,7 +25,7 @@ func indexArg(index reflect.Value, cap int) (int, error) {
default:
return 0, fmt.Errorf("cannot index slice/array with type %s", index.Type())
}
if x < 0 || int(x) < 0 || int(x) >= cap {
if x < 0 || int(x) < 0 || int(x) >= caps {
return 0, fmt.Errorf("index out of range: %d", x)
}
return int(x), nil
@ -80,11 +80,12 @@ func index(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error)
case reflect.Array, reflect.Slice, reflect.String:
x, err := indexArg(index, item.Len())
if err != nil {
return reflect.Value{}, nil
return reflect.Value{}, err
}
item = item.Index(x)
case reflect.Map:
index, err := prepareArg(index, item.Type().Key())
var err error
index, err = prepareArg(index, item.Type().Key())
if err != nil {
return reflect.Value{}, err
}

@ -1,49 +1,13 @@
package templategen
import (
"fmt"
"reflect"
"runtime"
"strings"
)
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem()
)
// maxExecDepth specifies the maximum stack depth of templates within
// templates. This limit is only practically reached by accidentally
// recursive template invocations. This limit allows us to return
// an error instead of triggering a stack overflow.
var maxExecDepth = initMaxExecDepth()
func initMaxExecDepth() int {
if runtime.GOARCH == "wasm" {
return 1000
}
return 100000
}
// variable holds the dynamic value of a variable such as $, $x etc.
type variable struct {
name string
value reflect.Value
}
var zero reflect.Value
type missingValType struct{}
var missingVal = reflect.ValueOf(missingValType{})
// doublePercent returns the string with %'s replaced by %%, if necessary,
// so it can be used safely inside a Printf format string.
func doublePercent(str string) string {
return strings.ReplaceAll(str, "%", "%%")
}
// TODO: It would be nice if ExecError was more broken down, but
// the way ErrorContext embeds the template name makes the
// processing too clumsy.
@ -64,42 +28,6 @@ func (e ExecError) Unwrap() error {
return e.Err
}
// writeError is the wrapper type used internally when Execute has an
// error writing to its output. We strip the wrapper in errRecover.
// Note that this is not an implementation of error, so it cannot escape
// from the package as an error value.
type writeError struct {
Err error // Original error.
}
func isTrue(val reflect.Value) (truth, ok bool) {
if !val.IsValid() {
// Something like var x interface{}, never set. It's a form of nil.
return false, true
}
switch val.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
truth = val.Len() > 0
case reflect.Bool:
truth = val.Bool()
case reflect.Complex64, reflect.Complex128:
truth = val.Complex() != 0
case reflect.Chan, reflect.Func, reflect.Pointer, reflect.Interface:
truth = !val.IsNil()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
truth = val.Int() != 0
case reflect.Float32, reflect.Float64:
truth = val.Float() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
truth = val.Uint() != 0
case reflect.Struct:
truth = true // Struct values are always true.
default:
return
}
return truth, true
}
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
func canBeNil(typ reflect.Type) bool {
switch typ.Kind() {

@ -4,7 +4,7 @@ import (
GDisk "penahub.gitlab.yandexcloud.net/backend/templategen/gdisk"
)
func GDiskGenerateDoc(file, name, userID, saveFolderID string, client *GDisk.Client, data interface{}) (string, string, error) {
func GDiskGenerateDoc(file, name, saveFolderID string, client *GDisk.Client, data interface{}) (string, string, error) {
filename := GenerateDocName(name, "gf")
// Download file
@ -22,23 +22,27 @@ func GDiskGenerateDoc(file, name, userID, saveFolderID string, client *GDisk.Cli
}
// Upload file
fileId, exportUrl, err := client.UploadFile(
fileID, exportURL, err := client.UploadFile(
TempGenerated+"/"+filename,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
saveFolderID)
if err != nil {
return "", "", err
}
// Delete temps
err = DeleteDocument(downloaded)
if err != nil {
return fileId, exportUrl, err
return fileID, exportURL, err
}
err = DeleteDocument(generated)
return fileId, exportUrl, err
return fileID, exportURL, err
}
func GDiskGenerateDocBytes(fileID, name, userID, saveFolderID string, client *GDisk.Client, data interface{}) (string, string, error) {
func GDiskGenerateDocBytes(fileID, name, saveFolderID string, client *GDisk.Client, data interface{}) (string, string, error) {
filename := GenerateDocName(name, "gf")
// Download fileID
@ -55,11 +59,11 @@ func GDiskGenerateDocBytes(fileID, name, userID, saveFolderID string, client *GD
}
// Upload fileID
fileId, exportUrl, err := client.UploadFileBytes(
resultFileID, exportURL, err := client.UploadFileBytes(
resultBytes,
filename,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
saveFolderID)
return fileId, exportUrl, err
return resultFileID, exportURL, err
}

@ -2,13 +2,15 @@ package templategen
import (
"bytes"
"context"
"fmt"
"os"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"time"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
)
func PenaDiskGenerateDoc(file, name, userID, saveFolder string, client *penadisk.Client, data interface{}) (string, string, error) {
func PenaDiskGenerateDoc(ctx context.Context, file, name, saveFolder string, client *penadisk.Client, data interface{}) (string, string, error) {
d, ok := data.(map[string]interface{})
fmt.Println("data", data)
var fio string
@ -30,7 +32,7 @@ func PenaDiskGenerateDoc(file, name, userID, saveFolder string, client *penadisk
// Download file
downloaded := TempDownloaded + "/" + filename
err := client.DownloadResource(file, downloaded)
err := client.DownloadResource(ctx, file, downloaded)
if err != nil {
return "", "", err
}
@ -43,12 +45,15 @@ func PenaDiskGenerateDoc(file, name, userID, saveFolder string, client *penadisk
}
out, err := os.Open(generated)
defer out.Close()
if err != nil {
return "", "", err
}
defer out.Close() //nolint
// Upload file
filePath, exportUrl, err := client.UploadResources(saveFolder+"/"+filename, filename, true, out)
filePath, exportURL, err := client.UploadResources(ctx, saveFolder+"/"+filename, filename, true, out)
if err != nil {
return "", "", err
}
@ -56,15 +61,15 @@ func PenaDiskGenerateDoc(file, name, userID, saveFolder string, client *penadisk
// Delete temps
err = DeleteDocument(downloaded)
if err != nil {
return filePath, exportUrl, err
return filePath, exportURL, err
}
err = DeleteDocument(generated)
return filePath, exportUrl, err
return filePath, exportURL, err
}
func PenaDiskGenerateDocBytes(file, name, userID, saveFolder string, client *penadisk.Client, data interface{}) (string, string, error) {
func PenaDiskGenerateDocBytes(ctx context.Context, file, name, saveFolder string, client *penadisk.Client, data interface{}) (string, string, error) {
d, ok := data.(map[string]interface{})
fmt.Println("data", data)
var fio string
@ -85,21 +90,25 @@ func PenaDiskGenerateDocBytes(file, name, userID, saveFolder string, client *pen
filename := GenerateDocName(name, fio)
// Download file
fileBytes, err := client.DownloadResourceBytes(file)
fileBytes, err := client.DownloadResourceBytes(ctx, file)
if err != nil {
return "", "", err
}
// Generate file
result, err := GenerateBytesFile(fileBytes, data)
fmt.Println("FILELEN", len(result), string(fileBytes))
out := bytes.NewReader(result)
// Upload file
filePath, exportUrl, err := client.UploadResources(saveFolder, filename, true, out)
if err != nil {
return "", "", err
}
return filePath, exportUrl, err
fmt.Println("FILELEN", len(result), string(fileBytes))
out := bytes.NewReader(result)
// Upload file
filePath, exportURL, err := client.UploadResources(ctx, saveFolder, filename, true, out)
if err != nil {
return "", "", err
}
return filePath, exportURL, err
}

@ -1,6 +1,7 @@
package templategen
import (
"context"
"fmt"
"io"
"net/http"
@ -49,9 +50,14 @@ func GenerateBytesFile(file []byte, data interface{}) ([]byte, error) {
return dc.ExecuteBytes(data)
}
// DownloadDocument - загружает документ в filepath из url
func DownloadDocument(filepath, url string) error {
resp, err := http.Get(url)
// DownloadDocument - загружает документ в filepath из url.
func DownloadDocument(ctx context.Context, filepath, url string) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
@ -63,7 +69,7 @@ func DownloadDocument(filepath, url string) error {
if err != nil {
return err
}
defer out.Close()
defer out.Close() //nolint
// Write the body to file
_, err = io.Copy(out, resp.Body)
@ -73,7 +79,3 @@ func DownloadDocument(filepath, url string) error {
func DeleteDocument(filepath string) error {
return os.Remove(filepath)
}
func ShareDocument(filepath string) (error, string) {
return nil, ""
}

@ -2,6 +2,7 @@ package templategen
import (
"bytes"
"context"
"fmt"
"os"
"time"
@ -9,7 +10,7 @@ import (
YaDisk "penahub.gitlab.yandexcloud.net/backend/templategen/yadisk"
)
func YaDiskGenerateDoc(file, name, userID, saveFolder string, client *YaDisk.Client, data interface{}) (string, string, error) {
func YaDiskGenerateDoc(ctx context.Context, file, name, saveFolder string, client *YaDisk.Client, data interface{}) (string, string, error) {
d, ok := data.(map[string]interface{})
fmt.Println("data", data)
var fio string
@ -31,7 +32,7 @@ func YaDiskGenerateDoc(file, name, userID, saveFolder string, client *YaDisk.Cli
// Download file
downloaded := TempDownloaded + "/" + filename
err := client.DownloadResource(file, downloaded)
err := client.DownloadResource(ctx, file, downloaded)
if err != nil {
return "", "", err
}
@ -44,12 +45,14 @@ func YaDiskGenerateDoc(file, name, userID, saveFolder string, client *YaDisk.Cli
}
out, err := os.Open(generated)
defer out.Close()
if err != nil {
return "", "", err
}
defer out.Close() //nolint
// Upload file
filePath, exportUrl, err := client.UploadResources(saveFolder+"/"+filename, out)
filePath, exportURL, err := client.UploadResources(ctx, saveFolder+"/"+filename, out)
if err != nil {
return "", "", err
}
@ -57,15 +60,15 @@ func YaDiskGenerateDoc(file, name, userID, saveFolder string, client *YaDisk.Cli
// Delete temps
err = DeleteDocument(downloaded)
if err != nil {
return filePath, exportUrl, err
return filePath, exportURL, err
}
err = DeleteDocument(generated)
return filePath, exportUrl, err
return filePath, exportURL, err
}
func YaDiskGenerateDocBytes(file, name, userID, saveFolder string, client *YaDisk.Client, data interface{}) (string, string, error) {
func YaDiskGenerateDocBytes(ctx context.Context, file, name, saveFolder string, client *YaDisk.Client, data interface{}) (string, string, error) {
d, ok := data.(map[string]interface{})
fmt.Println("data", data)
var fio string
@ -86,17 +89,24 @@ func YaDiskGenerateDocBytes(file, name, userID, saveFolder string, client *YaDis
filename := GenerateDocName(name, fio)
// Download file
fileBytes, err := client.DownloadResourcesBytes(file)
// Generate file
result, err := GenerateBytesFile(fileBytes, data)
resultReader := bytes.NewReader(result)
// Upload file
filePath, exportUrl, err := client.UploadResources(saveFolder+"/"+filename, resultReader)
fileBytes, err := client.DownloadResourcesBytes(ctx, file)
if err != nil {
return "", "", err
}
return filePath, exportUrl, err
// Generate file
result, err := GenerateBytesFile(fileBytes, data)
if err != nil {
return "", "", err
}
resultReader := bytes.NewReader(result)
// Upload file
filePath, exportURL, err := client.UploadResources(ctx, saveFolder+"/"+filename, resultReader)
if err != nil {
return "", "", err
}
return filePath, exportURL, err
}

@ -1,23 +1,27 @@
package tools
import (
"crypto/rc4"
"crypto/aes"
"encoding/hex"
"encoding/json"
)
const (
TokenKey = "z1eRU{fq*VtfLAszrz"
var (
TokenKey = "z1eRU{fq*VtfLAszrz" //nolint
)
type StateToken struct {
UserID string `json:"user_id"`
Service string `json:"service"` // yandex, google, etc
RedirectUrl string `json:"redirect_url"`
func SetToken(token string) {
TokenKey = token
}
func EncryptTokenRC4(data any) (string, error) {
cipher, err := rc4.NewCipher([]byte(TokenKey))
type StateToken struct {
PenaID string `json:"pena_id"`
Service string `json:"service"` // yandex, google, etc
RedirectURL string `json:"redirect_url"`
}
func EncryptTokenAES(data any) (string, error) {
cipher, err := aes.NewCipher([]byte(TokenKey))
if err != nil {
return "", err
@ -29,13 +33,13 @@ func EncryptTokenRC4(data any) (string, error) {
}
encrypted := make([]byte, len(msg))
cipher.XORKeyStream(encrypted, msg)
cipher.Encrypt(encrypted, msg)
return hex.EncodeToString(encrypted), nil
}
func DecryptTokenRC4(data string, result any) error {
cipher, err := rc4.NewCipher([]byte(TokenKey))
func DecryptTokenAES(data string, result any) error {
cipher, err := aes.NewCipher([]byte(TokenKey))
if err != nil {
return err
@ -49,7 +53,7 @@ func DecryptTokenRC4(data string, result any) error {
decrypted := make([]byte, len(msg))
cipher.XORKeyStream(decrypted, msg)
cipher.Decrypt(decrypted, msg)
err = json.Unmarshal(decrypted, &result)
if err != nil {

193
worker/tariff.go Normal file

@ -0,0 +1,193 @@
package worker
import (
"context"
"time"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff"
"penahub.gitlab.yandexcloud.net/backend/templategen/broker/tariff/models"
"penahub.gitlab.yandexcloud.net/backend/templategen/dal"
"penahub.gitlab.yandexcloud.net/backend/templategen/penadisk"
"penahub.gitlab.yandexcloud.net/backend/templategen/privileges"
)
/* TODO: Сделать рефакторинг кода!
Какой то колхоз с привилегиями получился.
Сейчас у меня 3 разных модели привилегий из разных пакетов: boker\tariff\models, proto, privileges.
*/
type TariffWorkerDeps struct {
Logger *zap.Logger
Dal *dal.MongoDAL
TariffConsumer *tariff.Consumer
PrivilegesDomain string
}
type TariffWorker struct {
logger *zap.Logger
dal *dal.MongoDAL
tariffConsumer *tariff.Consumer
privilegesDomain string
}
func NewTariffWorker(deps TariffWorkerDeps) *TariffWorker {
return &TariffWorker{
logger: deps.Logger,
dal: deps.Dal,
tariffConsumer: deps.TariffConsumer,
privilegesDomain: deps.PrivilegesDomain,
}
}
func (w *TariffWorker) Run(ctx context.Context) {
ticker := time.NewTicker(5 * time.Minute)
for {
select {
case <-ctx.Done():
w.logger.Info("tariff worker done")
return
case <-ticker.C:
w.Do(ctx)
}
}
}
func (w *TariffWorker) Do(ctx context.Context) {
// получаем актуальные привилегии
getActualPrivileges, err := privileges.GetActualPrivileges(ctx, w.privilegesDomain)
if err != nil {
w.logger.Error("cannot get actual privileges")
return
}
actualPrivileges := make(map[string]models.Privilege)
for _, privilege := range getActualPrivileges {
actualPrivileges[privilege.ID] = models.Privilege{
ID: privilege.ID,
Amount: 0,
PrivilegeID: privilege.PrivilegeID,
Name: privilege.Name,
ServiceKey: privilege.ServiceKey,
Description: privilege.Description,
Type: privilege.Type,
Value: privilege.Value,
Price: privilege.Price,
IsDeleted: privilege.IsDeleted,
CreatedAt: privilege.CreatedAt,
UpdatedAt: privilege.UpdatedAt,
DeletedAt: privilege.DeletedAt,
}
}
// проверяем новые тарифы
tariffs := w.tariffConsumer.FetchTariffs(ctx)
for _, tariff := range tariffs {
penadisk := penadisk.NewClient(tariff.UserID)
for _, newPrivilege := range tariff.Privileges {
if newPrivilege.ServiceKey != models.ServiceKey {
continue
}
privilege := actualPrivileges[newPrivilege.ID]
privilege.Amount = newPrivilege.Amount
if err = w.dal.Amo.AddPrivilege(ctx,
tariff.UserID,
privilege.PrivilegeID,
privilege.Amount); err != nil {
w.logger.Error("cannot add privilege",
zap.Error(err),
zap.String("user_id", tariff.UserID),
zap.String("privilege_id", privilege.PrivilegeID))
}
// передаем в penahub disk запрос на изменение размера диска пользователя
if privilege.PrivilegeID == models.PrivilegeTemplateStorage {
if err = penadisk.SetBucketQuota(ctx, uint64(privilege.Amount)<<20); err != nil {
w.logger.Error("cannot set bucket quota",
zap.Error(err),
zap.String("user_id", tariff.UserID),
zap.String("privilege_id", privilege.PrivilegeID))
}
}
}
}
// получаем пользователей
amos, err := w.dal.Amo.GetAll(ctx)
if err != nil {
w.logger.Error("cannot get all amos", zap.Error(err))
return
}
for _, amo := range amos {
penadisk := penadisk.NewClient(amo.PenaID)
for privilegeID, privilege := range amo.Privileges {
// проверяем истекшие привилегии
if privilegeID == models.PrivilegeTemplateUnlimTime &&
privilege.CreatedAt.AddDate(0, 0, int(privilege.Amount)).After(time.Now()) {
if err = w.dal.Amo.DeletePrivilege(ctx, amo.ID, privilegeID); err != nil {
w.logger.Error("cannot delete privilege",
zap.Error(err),
zap.String("amo_id", amo.ID),
zap.String("privilege_id", privilegeID))
}
}
// обновляем базовую привилегию на количество, если она закончилась и прошел месяц
if privilegeID == models.PrivilegeTemplateCount &&
privilege.Amount == 0 &&
privilege.CreatedAt.AddDate(0, 1, 0).Before(time.Now()) {
if err = w.dal.Amo.AddPrivilege(ctx,
amo.ID,
privilegeID,
models.BasicAmountPrivilegeTemplateCount); err != nil {
w.logger.Error("cannot add privilege",
zap.Error(err),
zap.String("amo_ID", amo.ID),
zap.String("privilege_id", privilegeID))
}
}
}
// добавляем базовые привилегии если у пользователя нет никаких
if len(amo.Privileges) == 0 {
// привилегия на количество генераций
if err = w.dal.Amo.AddPrivilege(ctx,
amo.ID,
models.PrivilegeTemplateCount,
models.BasicAmountPrivilegeTemplateCount); err != nil {
w.logger.Error("cannot add privilege",
zap.Error(err),
zap.String("amo_ID", amo.ID),
zap.String("privilege_id", models.PrivilegeTemplateCount))
}
// привилегия на объем диска
if err = w.dal.Amo.AddPrivilege(ctx,
amo.ID,
models.PrivilegeTemplateStorage,
models.BasicAmountPrivilegeTemplateStorage); err != nil {
w.logger.Error("cannot add privilege",
zap.Error(err),
zap.String("amo_ID", amo.ID),
zap.String("privilege_id", models.PrivilegeTemplateStorage))
}
if err := penadisk.SetBucketQuota(ctx, 100<<20); err != nil {
w.logger.Error("cannot set bucket quota",
zap.Error(err),
zap.String("user_id", amo.PenaID),
zap.String("privilege_id", models.PrivilegeTemplateStorage))
}
}
}
}

@ -1,4 +1,4 @@
package YaDisk
package yadisk
import (
"context"
@ -18,12 +18,12 @@ import (
)
const (
BASE_URL = "https://cloud-api.yandex.net"
OAUTH_URL = "https://oauth.yandex.ru"
V1_DISK_API = BASE_URL + "/v1/disk"
DEFAULT_FOLDER = "disk:/templategen"
DEFAULT_TEMPLATE_FOLDER = DEFAULT_FOLDER + "/templates"
DEFAULT_SAVE_FOLDER = DEFAULT_FOLDER + "/saved"
BaseURL = "https://cloud-api.yandex.net"
OauthURL = "https://oauth.yandex.ru"
V1DiskAPI = BaseURL + "/v1/disk"
DefaultFolder = "disk:/templategen"
DefaultTemplateFolder = DefaultFolder + "/templates"
DefaultSaveFolder = DefaultFolder + "/saved"
)
type Client struct {
@ -34,30 +34,30 @@ type Client struct {
type ClientApp struct {
Config *oauth2.Config
ServiceUrl string
ServiceURL string
}
func NewClientApp(clientID, clientSecret, redirectUri, serviceUrl string) *ClientApp {
func NewClientApp(clientID, clientSecret, redirectURI, serviceURL string) *ClientApp {
return &ClientApp{
Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: OAUTH_URL + "/authorize",
TokenURL: OAUTH_URL + "/token",
AuthURL: OauthURL + "/authorize",
TokenURL: OauthURL + "/token",
},
RedirectURL: redirectUri,
RedirectURL: redirectURI,
Scopes: nil,
},
ServiceUrl: serviceUrl,
ServiceURL: serviceURL,
}
}
func (ca *ClientApp) GenerateOAuthUrl(userId, redirectUrl string) (string, error) {
state, err := tools.EncryptTokenRC4(tools.StateToken{
UserID: userId,
func (ca *ClientApp) GenerateOAuthURL(penaID, redirectURL string) (string, error) {
state, err := tools.EncryptTokenAES(tools.StateToken{
PenaID: penaID,
Service: "yandex",
RedirectUrl: redirectUrl,
RedirectURL: redirectURL,
})
if err != nil {
@ -99,8 +99,8 @@ func (ca *ClientApp) RefreshToken(ctx context.Context, oldToken *oauth2.Token) (
return token, nil
}
func (c *Client) GetDisk() (*Disk, error) {
req, err := http.NewRequest("GET", V1_DISK_API, nil)
func (c *Client) GetDisk(ctx context.Context) (*Disk, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI, http.NoBody)
if err != nil {
return nil, err
}
@ -111,6 +111,8 @@ func (c *Client) GetDisk() (*Disk, error) {
return nil, err
}
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
return nil, err
@ -125,8 +127,8 @@ func (c *Client) GetDisk() (*Disk, error) {
return &r, nil
}
func (c *Client) GetUser() (*User, error) {
disk, err := c.GetDisk()
func (c *Client) GetUser(ctx context.Context) (*User, error) {
disk, err := c.GetDisk(ctx)
if err != nil {
return nil, err
}
@ -144,7 +146,7 @@ func (c *Client) DownloadFile(path string) (*DResp, error) {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("GET", V1_DISK_API+"/resources/download?"+data.Encode(), nil)
req, err := http.NewRequest(http.MethodGet, V1DiskAPI+"/resources/download?"+data.Encode(), http.NoBody)
if err != nil {
return nil, err
}
@ -154,6 +156,8 @@ func (c *Client) DownloadFile(path string) (*DResp, error) {
return nil, err
}
defer res.Body.Close()
if err := checkError(res); err != nil {
return nil, err
}
@ -167,7 +171,7 @@ func (c *Client) DownloadFile(path string) (*DResp, error) {
return &resp, nil
}
func (c *Client) GetResources(path string, limit, offset int) (*Resource, error) {
func (c *Client) GetResources(ctx context.Context, path string, limit, offset int) (*Resource, error) {
if path == "" {
path = "disk:/"
}
@ -184,7 +188,7 @@ func (c *Client) GetResources(path string, limit, offset int) (*Resource, error)
data.Encode()
req, err := http.NewRequest("GET", V1_DISK_API+"/resources?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"/resources?"+data.Encode(), http.NoBody)
if err != nil {
return nil, err
@ -195,7 +199,9 @@ func (c *Client) GetResources(path string, limit, offset int) (*Resource, error)
return nil, err
}
if resp.StatusCode == 404 {
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
@ -210,53 +216,60 @@ func (c *Client) GetResources(path string, limit, offset int) (*Resource, error)
return &r, err
}
func (c *Client) DownloadResource(path, downloadPath string) error {
res, err := c.GetResources(path, 0, 0)
func (c *Client) DownloadResource(ctx context.Context, path, downloadPath string) error {
res, err := c.GetResources(ctx, path, 0, 0)
if err != nil {
return err
}
if res == nil {
return errors.New("file not found")
}
if err != nil {
return err
}
resp, err := http.Get(res.File)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, res.File, http.NoBody)
if err != nil {
return err
}
defer resp.Body.Close()
resp, err := http.DefaultClient.Do(req)
if checkError(resp) != nil {
return err
}
defer resp.Body.Close()
// Create the file
out, err := os.Create(downloadPath)
if err != nil {
return err
}
defer out.Close()
defer out.Close() //nolint
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
}
func (c *Client) DownloadResourcesBytes(path string) ([]byte, error) {
res, err := c.GetResources(path, 0, 0)
if res == nil {
return nil, errors.New("file not found")
}
func (c *Client) DownloadResourcesBytes(ctx context.Context, path string) ([]byte, error) {
res, err := c.GetResources(ctx, path, 0, 0)
if err != nil {
return nil, err
}
resp, err := http.Get(res.File)
if res == nil {
return nil, errors.New("file not found")
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, res.File, http.NoBody)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
@ -271,11 +284,11 @@ func (c *Client) DownloadResourcesBytes(path string) ([]byte, error) {
return io.ReadAll(resp.Body)
}
// PutResources - создание папки
// PutResources - создание папки.
func (c *Client) PutResources(path string) (*RespPutResources, error) {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", V1_DISK_API+"/resources?"+data.Encode(), nil)
req, err := http.NewRequest(http.MethodPut, V1DiskAPI+"/resources?"+data.Encode(), http.NoBody)
if err != nil {
return nil, err
}
@ -285,7 +298,9 @@ func (c *Client) PutResources(path string) (*RespPutResources, error) {
return nil, err
}
if resp.StatusCode == 409 {
defer resp.Body.Close()
if resp.StatusCode == http.StatusConflict {
return nil, nil
}
@ -300,10 +315,10 @@ func (c *Client) PutResources(path string) (*RespPutResources, error) {
return &r, err
}
func (c *Client) DeleteResources(path string) error {
func (c *Client) DeleteResources(ctx context.Context, path string) error {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("DELETE", V1_DISK_API+"/resources?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, V1DiskAPI+"/resources?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
@ -313,24 +328,25 @@ func (c *Client) DeleteResources(path string) error {
return err
}
defer resp.Body.Close()
err = checkError(resp)
return err
}
// UploadResourcesUrl - Загрузить файл на Яндекс диск по URL. Нерекомендуемый
func (c *Client) UploadResourcesUrl(path, downloadPath string) (string, error) {
downloadPath = strings.TrimLeft(downloadPath, "..")
// UploadResourcesURL - Загрузить файл на Яндекс диск по URL. Нерекомендуемый.
func (c *Client) UploadResourcesURL(ctx context.Context, path, downloadPath string) (string, error) {
downloadPath = strings.TrimLeft(downloadPath, ".")
fmt.Println("dp:", c.App.ServiceUrl+downloadPath)
fmt.Println("dp:", c.App.ServiceURL+downloadPath)
fmt.Println("path:", path)
data := url.Values{}
data.Set("path", path)
data.Set("url", c.App.ServiceUrl+downloadPath)
data.Set("url", c.App.ServiceURL+downloadPath)
req, err := http.NewRequest("POST", V1_DISK_API+"/resources/upload?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, V1DiskAPI+"/resources/upload?"+data.Encode(), http.NoBody)
fmt.Println("req:", req.URL)
if err != nil {
@ -342,6 +358,8 @@ func (c *Client) UploadResourcesUrl(path, downloadPath string) (string, error) {
return "", err
}
defer resp.Body.Close()
if checkError(resp) != nil {
return "", err
}
@ -352,19 +370,21 @@ func (c *Client) UploadResourcesUrl(path, downloadPath string) (string, error) {
return "", err
}
status, err := c.GetOperationsByUrl(r.Href)
status, err := c.GetOperationsByURL(ctx, r.Href)
if err != nil {
return "", err
}
// Ожидаем пока загрузится файл на Я.Диск. Timeout: 5 sec; tick: 100 ms;
timer := time.NewTimer(5 * time.Second)
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for status == "in-progress" {
select {
case <-timer.C:
status = "timeout"
case <-time.Tick(100 * time.Millisecond):
status, err = c.GetOperationsByUrl(r.Href)
case <-ticker.C:
status, err = c.GetOperationsByURL(ctx, r.Href)
if err != nil {
timer.Stop()
return "", err
@ -374,29 +394,29 @@ func (c *Client) UploadResourcesUrl(path, downloadPath string) (string, error) {
timer.Stop()
if status != "success" {
return "", errors.New(fmt.Sprintf("bad upload status: %v", status))
return "", fmt.Errorf("bad upload status: %v", status)
}
resource, err := c.GetResources(path, 0, 0)
resource, err := c.GetResources(ctx, path, 0, 0)
if err != nil {
return "", err
}
var exportUrl string
var exportURL string
if resource != nil {
exportUrl = resource.File
exportURL = resource.File
} else {
return "", errors.New("resource not found")
}
return exportUrl, err
return exportURL, err
}
// UploadResources - Загрузить файл на Яндекс диск
func (c *Client) UploadResources(path string, file io.Reader) (string, string, error) {
// UploadResources - Загрузить файл на Яндекс диск.
func (c *Client) UploadResources(ctx context.Context, path string, file io.Reader) (string, string, error) {
// Get url for request
target, err := c.getUploadResourcesUrl(path)
target, err := c.getUploadResourcesURL(ctx, path)
if err != nil {
return "", "", err
}
@ -405,48 +425,59 @@ func (c *Client) UploadResources(path string, file io.Reader) (string, string, e
return "", "", errors.New("got empty url for upload")
}
req, err := http.NewRequest(target.Method, target.Href, file)
req, err := http.NewRequestWithContext(ctx, target.Method, target.Href, file)
if err != nil {
return "", "", err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return "", "", err
}
if err := checkError(resp); err != nil {
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
return "", "", err
}
// Get uploaded data
resource, err := c.GetResources(path, 0, 0)
resource, err := c.GetResources(ctx, path, 0, 0)
if err != nil {
return "", "", err
}
var exportUrl string
var exportURL string
if resource != nil {
exportUrl = resource.File
exportURL = resource.File
} else {
return "", "", errors.New("resource not found")
}
return resource.Path, exportUrl, err
return resource.Path, exportURL, err
}
// getUploadResourcesUrl - получить ссылку для отправки файла по TLS
func (c *Client) getUploadResourcesUrl(path string) (*RespUploadResources, error) {
// getUploadResourcesURL - получить ссылку для отправки файла по TLS.
func (c *Client) getUploadResourcesURL(ctx context.Context, path string) (*RespUploadResources, error) {
data := url.Values{}
data.Set("path", path)
data.Set("overwrite", "true")
req, err := http.NewRequest("GET", V1_DISK_API+"/resources/upload?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"/resources/upload?"+data.Encode(), http.NoBody)
if err != nil {
return nil, err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
@ -459,14 +490,19 @@ func (c *Client) getUploadResourcesUrl(path string) (*RespUploadResources, error
return &r, err
}
// GetOperations - возвращает статус асинхронной операции
func (c *Client) GetOperations(operationID string) (string, error) {
req, err := http.NewRequest("GET", V1_DISK_API+"/operations/"+operationID, nil)
// GetOperations - возвращает статус асинхронной операции.
func (c *Client) GetOperations(ctx context.Context, operationID string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, V1DiskAPI+"/operations/"+operationID, http.NoBody)
if err != nil {
return "", err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
@ -479,15 +515,20 @@ func (c *Client) GetOperations(operationID string) (string, error) {
return r.Status, err
}
// GetOperationsByUrl - возвращает статус асинхронной операции
func (c *Client) GetOperationsByUrl(url string) (string, error) {
req, err := http.NewRequest("GET", url, nil)
// GetOperationsByURL - возвращает статус асинхронной операции.
func (c *Client) GetOperationsByURL(ctx context.Context, url string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return "", err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
@ -500,17 +541,22 @@ func (c *Client) GetOperationsByUrl(url string) (string, error) {
return r.Status, err
}
// PublishResource - публикует ресурс
func (c *Client) PublishResource(path string) error {
// PublishResource - публикует ресурс.
func (c *Client) PublishResource(ctx context.Context, path string) error {
data := url.Values{}
data.Set("path", path)
req, err := http.NewRequest("PUT", V1_DISK_API+"/resources/publish?"+data.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodPut, V1DiskAPI+"/resources/publish?"+data.Encode(), http.NoBody)
if err != nil {
return err
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
err = checkError(resp)
if err != nil {
@ -520,10 +566,12 @@ func (c *Client) PublishResource(path string) error {
return nil
}
// checkError - если статус не в диапазоне 200-х вернет расшифровку ошибки
// checkError - если статус не в диапазоне 200-х вернет расшифровку ошибки.
func checkError(resp *http.Response) error {
switch resp.StatusCode {
case 200, 201, 202, 203, 204, 205, 206, 207, 208, 226:
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNonAuthoritativeInfo,
http.StatusNoContent, http.StatusResetContent, http.StatusPartialContent,
http.StatusMultiStatus, http.StatusAlreadyReported, http.StatusIMUsed:
return nil
}
r := Error{}
@ -532,6 +580,5 @@ func checkError(resp *http.Response) error {
return err
}
return errors.New(fmt.Sprintf("api/yaDisk (%v) err: %v | %v (%v)", resp.StatusCode, r.Message, r.Description,
r.Error))
return fmt.Errorf("api/yaDisk (%v) err: %v | %v (%v)", resp.StatusCode, r.Message, r.Description, r.Error)
}

@ -1,4 +1,4 @@
package YaDisk
package yadisk
import (
"time"
@ -39,7 +39,7 @@ type User struct {
Country string `json:"country"`
Login string `json:"login"`
DisplayName string `json:"display_name"`
Uid string `json:"uid"`
UID string `json:"uid"`
}
type Resource struct {
@ -52,11 +52,11 @@ type Resource struct {
Total int `json:"total"`
} `json:"_embedded"`
Name string `json:"name"`
PublicUrl string `json:"public_url"`
PublicURL string `json:"public_url"`
PublicKey string `json:"public_key"`
Exif struct {
} `json:"exif"`
ResourceId string `json:"resource_id"`
ResourceID string `json:"resource_id"`
Created time.Time `json:"created"`
Modified time.Time `json:"modified"`
Path string `json:"path"`