Удален 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)
resp, err := c.HTTPClient.Do(req)
if err != nil {
fmt.Println("2")
return nil, err
}
var response Account
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
fmt.Println("3")
return nil, err
}
return &response, nil
return &result, 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,
)
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")
fmt.Println("URL:", fmt.Sprintf("%v/api/v4/leads/%v?with=contacts,catalog_elements,is_price_modified_by_robot,"+
"loss_reason",
c.Subdomain, id))
if err != nil {
fmt.Println("1", err)
return nil, err
requestURL := url.URL{
Scheme: "https",
Host: c.Subdomain,
Path: fmt.Sprintf("api/v4/leads/%v", id),
RawQuery: requestURLQueries.Encode(),
}
c.setHeaders(req)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
resp, err := c.HTTPClient.Do(req)
fmt.Println("URL:", requestURL.String())
if err != nil {
fmt.Println("2", err)
return nil, err
return nil, errors.Wrap(err, "GetLeadByID.NewRequest")
}
for resp.StatusCode == 429 {
response, err := c.HTTPClient.Do(request)
if err != nil {
return nil, errors.Wrap(err, "GetLeadByID.DoRequest")
}
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetLeadByID.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, "GetLeadByID.DoRequest")
}
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetLeadByID.BodyClose"))
}
}()
}()
}
var response Lead
str, err := ioutil.ReadAll(resp.Body)
var result Lead
responseData, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println("4", err)
}
err = json.Unmarshal(str, &response)
if err != nil {
fmt.Println("3", err, string(str))
return nil, err
return nil, errors.Wrap(err, "GetLeadByID.ReadAll")
}
return &response, nil
err = json.Unmarshal(responseData, &result)
if err != nil {
return nil, errors.Wrap(err, "GetLeadByID.Unmarshal")
}
return &result, 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
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(),
}
c.setHeaders(req)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL.String(), http.NoBody)
if err != nil {
return nil, errors.Wrap(err, "GetContactByID.NewRequest")
}
resp, err := c.HTTPClient.Do(req)
response, err := c.HTTPClient.Do(request)
if err != nil {
fmt.Println("2")
return nil, err
return nil, errors.Wrap(err, "GetContactByID.DoRequest")
}
for resp.StatusCode == 429 {
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")
}
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetContactByID.BodyClose"))
}
}()
}()
}
var response Contact
err = json.NewDecoder(resp.Body).Decode(&response)
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")
}
func() {
defer func() {
if err = response.Body.Close(); err != nil {
log.Println("ERROR", errors.Wrap(err, "GetCompanyByID.BodyClose"))
}
}()
}()
}
var response Company
err = json.NewDecoder(resp.Body).Decode(&response)
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"
)
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
PenaID string `bson:"pena_id" json:"pena_id"`
const (
ServiceKey = "templategen"
PrivilegeTemplateCount = "templateCnt"
PrivilegeTemplateUnlimTime = "templateUnlimTime"
PrivilegeTemplateStorage = "templateStorage"
BasicAmountPrivilegeTemplateCount = 15
BasicAmountPrivilegeTemplateStorage = 100
)
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
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"`
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 Amo struct {
ID string `bson:"_id,omitempty" json:"id"`
/* 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 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,33 +1,55 @@
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
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"` // Пользовательское название хранилища
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"`
ID string `bson:"_id,omitempty" json:"id"`
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"`
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"`
}
func (m *GDisk) Token() *oauth2.Token {

@ -5,26 +5,41 @@ import (
)
type History struct {
ID string `bson:"_id,omitempty" json:"id"`
UserID string `bson:"user_id" json:"user_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"`
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"`
ID string `bson:"_id,omitempty" json:"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"`
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{},

@ -3,12 +3,20 @@ package model
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"` // Пользовательское название хранилища
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"`
ID string `bson:"_id,omitempty" json:"id"`
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"`
}

@ -5,15 +5,29 @@ 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
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"`
ID string `bson:"_id,omitempty" json:"id"`
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,29 +1,50 @@
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
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"` // Пользовательское название хранилища
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"`
ID string `bson:"_id,omitempty" json:"id"`
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"`
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"`
}
func (m *YaDisk) Token() *oauth2.Token {

@ -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,33 +77,35 @@ 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 != 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, 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 {
d.logger.Error("ErrorGetListAmo", zap.Error(err))
return nil, err
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)
@ -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 {
d.logger.Error("ErrorGetAmo", zap.Error(err))
return nil, err
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,13 +86,14 @@ 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 {
d.logger.Error("ErrorGetGDisk", zap.Error(err))
return nil, err
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))
@ -102,33 +107,35 @@ 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 {
d.logger.Error("ErrorGetGDisk", zap.Error(err))
return nil, err
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 {
d.logger.Error("ErrorGetListGDisk", zap.Error(err))
return nil, err
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)
@ -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,13 +59,14 @@ 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 {
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
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))
@ -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,14 +84,12 @@ 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 {
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
}
} else if err != nil {
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
}
var result []model.History
@ -150,13 +144,14 @@ 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 {
d.logger.Error("ErrorGetHistory", zap.Error(err))
return nil, err
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))
@ -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 {
d.logger.Error("ErrorGetPenaDisk", zap.Error(err))
return nil, err
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 {
d.logger.Error("ErrorGetPenaDisk", zap.Error(err))
return nil, err
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,13 +64,14 @@ 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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
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))
@ -89,20 +79,21 @@ func (d *Template) GetByID(ctx context.Context, id string) (*model.Template, err
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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
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))
@ -110,8 +101,8 @@ func (d *Template) GetByLeadId(ctx context.Context, id int64) (*model.Template,
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,13 +110,14 @@ 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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
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))
@ -133,20 +125,21 @@ func (d *Template) GetByNameAndUserID(ctx context.Context, name, userID string)
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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
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))
@ -154,8 +147,8 @@ func (d *Template) GetByFilenameAndUserID(ctx context.Context, name, userID stri
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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
} 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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
} 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 {
d.logger.Error("ErrorGetTemplate", zap.Error(err))
return nil, err
}
}
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,13 +65,14 @@ 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 {
d.logger.Error("ErrorGetTemplateGroup", zap.Error(err))
return nil, err
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))
@ -78,26 +80,27 @@ func (d *TemplateGroup) GetByID(ctx context.Context, id string) (*model.Template
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 {
d.logger.Error("ErrorGetTemplateGroup", zap.Error(err))
return nil, err
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,11 +60,9 @@ func (d *WorkerTask) GetByID(ctx context.Context, id string) (*model.WorkerTask,
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
} else if err != nil {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
d.logger.Info("InfoGetWorkerTask", zap.String("id", result.ID))
@ -71,12 +70,12 @@ func (d *WorkerTask) GetByID(ctx context.Context, id string) (*model.WorkerTask,
}
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,11 +90,9 @@ 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 {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
} else if err != nil {
d.logger.Error("ErrorGetWorkerTask", zap.Error(err))
return nil, err
}
var result []model.WorkerTask
@ -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())
if err != nil {
d.logger.Error("ErrorWatchWorkerTask", zap.Error(err))
}
return
<-ctx.Done()
err = changeStream.Close(context.Background()) // TODO: проверить сокращенную запись в ssa
if err != nil {
d.logger.Error("ErrorWatchWorkerTask", zap.Error(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"
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,11 +95,9 @@ func (d *YaDisk) GetByID(ctx context.Context, id string) (*model.YaDisk, error)
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetYaDisk", zap.Error(err))
return nil, err
}
} else if err != nil {
d.logger.Error("ErrorGetYaDisk", zap.Error(err))
return nil, err
}
d.logger.Info("InfoGetYaDisk", zap.String("id", result.ID))
@ -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 {
d.logger.Error("ErrorGetYaDisk", zap.Error(err))
return nil, err
}
} 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,11 +131,9 @@ func (d *YaDisk) GetListByUserID(ctx context.Context, userID string) ([]model.Ya
if err == mongo.ErrNoDocuments {
return nil, nil
} else {
if err != nil {
d.logger.Error("ErrorGetListYaDisk", zap.Error(err))
return nil, err
}
} else if err != nil {
d.logger.Error("ErrorGetListYaDisk", zap.Error(err))
return nil, err
}
err = cur.All(ctx, &result)
@ -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{},
Creation: []int64{},
Delete: []int64{},
},
Referer: req.Referer,
Subdomain: amoAcc.Subdomain,
ExpiresIn: token.Expiry,
TokenType: token.TokenType,
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
}
// Обновляем счетчик генераций
if privilegeCountExists {
err = h.dal.Amo.UpdateAmountPrivilege(r.Context(), amoData.ID, model.PrivilegeTemplateCount, privilegeAmount-1)
if err != nil {
return "", err
}
}
return exportUrl, nil
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
}

120
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"`
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/"`
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"`
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 {
logger.Error("ErrorPublishPrivileges", zap.Error(err))
}
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,21 +230,23 @@ func main() {
mw.MiddlewareHeaders,
mw.MiddlewareCors,
mw.MiddlewareAmoJwt,
//mw.MiddlewareJwt,
//mw.MiddlewareAmoPlug,
mw.MiddlewareJwt,
// mw.MiddlewareAmoPlug,
mw.Logger,
)
r.NotFoundHandler = r.NewRoute().HandlerFunc(h.PageNotFound).GetHandler()
srv := http.Server{
Handler: r,
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)
})
}
@ -102,7 +129,7 @@ func (mw *Middleware) MiddlewareAmoJwt(next http.Handler) http.Handler {
}
authMap := map[string]interface{}{
//"/amo": nil,
// "/amo": nil,
}
required := false
@ -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/"
URL = ""
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,14 +217,22 @@ 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())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", err
}
@ -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,131 +1,132 @@
package privileges;
package privileges
import (
"fmt"
"context"
"time"
"github.com/gofiber/fiber/v2"
"context"
"fmt"
"time"
"github.com/gofiber/fiber/v2"
)
type Privilege struct {
ID string `json:"_id"`
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"`
ID string `json:"_id"`
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"`
}
const skey = "templategen"
var (
Privileges = []Privilege{
{
PrivilegeID: "templateCnt",
Name: "Количество Шаблонов",
ServiceKey: skey,
Description: "Количество шаблонов, которые может сделать пользователь сервиса",
Type: "count",
Value: "шаблон",
},
{
PrivilegeID: "templateUnlimTime",
Name: "Безлимит",
ServiceKey: skey,
Description: "Количество дней, в течении которых пользование сервисом безлимитно",
Type: "day",
Value: "день",
},
{
PrivilegeID: "templateStorage",
Name: "Размер Диска",
ServiceKey: skey,
Description: "Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации",
Type: "count",
Value: "МБ",
},
}
Privileges = []Privilege{
{
PrivilegeID: "templateCnt",
Name: "Количество Шаблонов",
ServiceKey: skey,
Description: "Количество шаблонов, которые может сделать пользователь сервиса",
Type: "count",
Value: "шаблон",
},
{
PrivilegeID: "templateUnlimTime",
Name: "Безлимит",
ServiceKey: skey,
Description: "Количество дней, в течении которых пользование сервисом безлимитно",
Type: "day",
Value: "день",
},
{
PrivilegeID: "templateStorage",
Name: "Размер Диска",
ServiceKey: skey,
Description: "Обьём ПенаДиска для хранения шаблонов и результатов шаблонизации",
Type: "count",
Value: "МБ",
},
}
)
func PublishPrivileges(ctx context.Context, domain string) error {
old, err := getActualPrivileges(ctx, domain)
if err != nil {
return err
}
old, err := GetActualPrivileges(ctx, domain)
if err != nil {
return err
}
if len(old) == 0 {
return setupActualPrivileges(ctx, Privileges, domain)
}
if len(old) == 0 {
return setupActualPrivileges(ctx, Privileges, domain)
}
found := false
for _, oldPriv := range old {
for _, newPriv := range Privileges {
if newPriv.PrivilegeID == oldPriv.PrivilegeID {
found = true
}
}
if !found {
if err := removePrivilege(ctx, domain, oldPriv.PrivilegeID); err != nil {
return err
}
}
found = false
}
found := false
return updatePrivileges(ctx, Privileges, domain)
for _, oldPriv := range old {
for _, newPriv := range Privileges {
if newPriv.PrivilegeID == oldPriv.PrivilegeID {
found = true
}
}
if !found {
if err := removePrivilege(ctx, domain, oldPriv.PrivilegeID); err != nil {
return err
}
}
found = false
}
return updatePrivileges(ctx, Privileges, domain)
}
func getActualPrivileges(
ctx context.Context,
domain string) ([]Privilege, error) {
res := []Privilege{}
_, _, err := fiber.Get(domain+"/privilege/service/templategen").Struct(&res)
if err != nil {
return res, err[0]
}
fmt.Println("str", res)
return res, nil
func GetActualPrivileges(
_ context.Context,
domain string) ([]Privilege, error) {
res := []Privilege{}
_, _, err := fiber.Get(domain + "/privilege/service/templategen").Struct(&res)
if err != nil {
return res, err[0]
}
fmt.Println("str", res)
return res, nil
}
func updatePrivileges(ctx context.Context, data []Privilege, domain string) error {
_, _, err := fiber.Put(domain+"/privilege/many").JSON(map[string][]Privilege{
"privilegies": data,
}).Bytes()
if err != nil {
return err[0]
}
func updatePrivileges(_ context.Context, data []Privilege, domain string) error {
_, _, err := fiber.Put(domain + "/privilege/many").JSON(map[string][]Privilege{
"privilege": data,
}).Bytes()
if err != nil {
return err[0]
}
return nil
return nil
}
func setupActualPrivileges(
ctx context.Context,
data []Privilege,
domain string) error {
res := []Privilege{}
_, _, err := fiber.Post(domain+"/privilege/many").JSON(map[string][]Privilege{
"privilegies": data,
}).Struct(&res)
_ context.Context,
data []Privilege,
domain string) error {
res := []Privilege{}
_, _, err := fiber.Post(domain + "/privilege/many").JSON(map[string][]Privilege{
"privilege": data,
}).Struct(&res)
if err != nil {
return err[0]
}
if err != nil {
return err[0]
}
return nil
return nil
}
func removePrivilege(ctx context.Context, domain, id string) error {
_, _, err := fiber.Delete(domain+"/privilege/").JSON(map[string]string{"privilegeId":id}).Bytes()
if err != nil {
return err[0]
}
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]
}
return nil
return nil
}

@ -1,23 +1,25 @@
package privileges
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/themakers/bdd"
"context"
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/themakers/bdd"
)
func TestPrivileges(t *testing.T) {
ctx := context.Background()
bdd.Scenario(t, "setPrivilegesLogic", func(t *testing.T, runID string){
bdd.Test(t, "getOldPrivileges", func(){
p, err := getActualPrivileges(ctx,"https://admin.pena.digital/strator")
assert.NoError(t,err)
assert.NotEqual(t, len(p), 0)
})
bdd.Test(t, "setOldPrivileges", func(){
err := setupActualPrivileges(ctx,Privileges,"https://admin.pena.digital/strator")
assert.NoError(t,err)
})
})
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")
assert.NoError(t, err)
assert.NotEqual(t, len(p), 0)
})
bdd.Test(t, "setOldPrivileges", func() {
err := setupActualPrivileges(ctx, Privileges, "https://admin.pena.digital/strator")
assert.NoError(t, err)
})
})
}

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;
}

@ -1,30 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFHDCCBASgAwIBAgISBLd688uTwblyd0xdFr3L6daQMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMjA4MDYwMjI1MDhaFw0yMjExMDQwMjI1MDdaMBYxFDASBgNVBAMT
C3NvbHdlYi5zaXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyX6Y
zAOtmLRKbxckleGYjS5+Z7DUxUyprRqUDr6x1WXNk04y8xil68tiDjYSduQ1KYu0
lv0hcauKHurhrVpuXOrUI9ysEIyx/xwA5cV6n96gZMv9cO6L4D2V/Nz/8n+aOJZf
i9LHaxHbXK4TRuN0uqUCRhpzVzSIHz5SYamwelr8UowzjCIubCT5tTL5t3g4KF9G
kYvY18pcUFHWk6O1N4CqvEyFWVRjOxEN9NKtEzfScov4dP/X5dU9Hgyuld2OPv6S
RAa/OxhYbO0xwcDV+ARRUGJXffwhA0vn5NTimRb49cUx3PAt+LP4sq3ed/utJPSz
O+BzwglB/oKAzwm5uQIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBTyUZZlKNRuajOyxJrw9tMlJNd4BDAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDm
H6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5v
LmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAW
BgNVHREEDzANggtzb2x3ZWIuc2l0ZTBMBgNVHSAERTBDMAgGBmeBDAECATA3Bgsr
BgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0
Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB3AN+lXqtogk8fbK3uuF9OPlrq
zaISpGpejjsSwCBEXCpzAAABgnEvaaAAAAQDAEgwRgIhAKK+fkTyJFn/HHbYj9BV
LMbGKDIRFjCoLZCAV2cTkvoYAiEA3pc6LREny7ExvkO+0lN38AC6k7jJJa9qdyoS
nucCz6YAdQBGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiFq/L8cP5tRwAAAYJxL2nI
AAAEAwBGMEQCIAuhH4/x7DCwUIu7enewxdn9roNY83x0J9Rja3TZo6W/AiAu/pDM
RweFdZcz21HyqUTVQfjBlabgH8UtNX7A54wgFTANBgkqhkiG9w0BAQsFAAOCAQEA
CqyytF0AWz74DPOFHbsDLb1E8CtsL9DsIJ7EcL9xdJrXYo66J3BIH4PwzkDelFID
v4aUgTYavFqC3ygzOXX1afiayKM15TwqgCQ16qW0lS14N/bv/v/K1sQNAEs2C3/g
Itq7I3sBg7npk+k2bckoa7UccLc3fj/gfaZcI6sTNq6nCc5kmBjar4S3p7Gh4ZyL
cqtECrhnBy6Az8S9se85gVD+NnhEaIgnjifPh5QhLoBn264h0CPH2dUDN1Poxo7u
Z4b4wDaws/QPHe/6jLXscatywEtVezaljj99re1JVnpFN8UzM4Rcuid+Yi6pZJED
LiASDdNrOPfTm8Zu86YRPQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFHDCCBASgAwIBAgISBLd688uTwblyd0xdFr3L6daQMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMjA4MDYwMjI1MDhaFw0yMjExMDQwMjI1MDdaMBYxFDASBgNVBAMT
C3NvbHdlYi5zaXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyX6Y
zAOtmLRKbxckleGYjS5+Z7DUxUyprRqUDr6x1WXNk04y8xil68tiDjYSduQ1KYu0
lv0hcauKHurhrVpuXOrUI9ysEIyx/xwA5cV6n96gZMv9cO6L4D2V/Nz/8n+aOJZf
i9LHaxHbXK4TRuN0uqUCRhpzVzSIHz5SYamwelr8UowzjCIubCT5tTL5t3g4KF9G
kYvY18pcUFHWk6O1N4CqvEyFWVRjOxEN9NKtEzfScov4dP/X5dU9Hgyuld2OPv6S
RAa/OxhYbO0xwcDV+ARRUGJXffwhA0vn5NTimRb49cUx3PAt+LP4sq3ed/utJPSz
O+BzwglB/oKAzwm5uQIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBTyUZZlKNRuajOyxJrw9tMlJNd4BDAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDm
H6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5v
LmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAW
BgNVHREEDzANggtzb2x3ZWIuc2l0ZTBMBgNVHSAERTBDMAgGBmeBDAECATA3Bgsr
BgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0
Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB3AN+lXqtogk8fbK3uuF9OPlrq
zaISpGpejjsSwCBEXCpzAAABgnEvaaAAAAQDAEgwRgIhAKK+fkTyJFn/HHbYj9BV
LMbGKDIRFjCoLZCAV2cTkvoYAiEA3pc6LREny7ExvkO+0lN38AC6k7jJJa9qdyoS
nucCz6YAdQBGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiFq/L8cP5tRwAAAYJxL2nI
AAAEAwBGMEQCIAuhH4/x7DCwUIu7enewxdn9roNY83x0J9Rja3TZo6W/AiAu/pDM
RweFdZcz21HyqUTVQfjBlabgH8UtNX7A54wgFTANBgkqhkiG9w0BAQsFAAOCAQEA
CqyytF0AWz74DPOFHbsDLb1E8CtsL9DsIJ7EcL9xdJrXYo66J3BIH4PwzkDelFID
v4aUgTYavFqC3ygzOXX1afiayKM15TwqgCQ16qW0lS14N/bv/v/K1sQNAEs2C3/g
Itq7I3sBg7npk+k2bckoa7UccLc3fj/gfaZcI6sTNq6nCc5kmBjar4S3p7Gh4ZyL
cqtECrhnBy6Az8S9se85gVD+NnhEaIgnjifPh5QhLoBn264h0CPH2dUDN1Poxo7u
Z4b4wDaws/QPHe/6jLXscatywEtVezaljj99re1JVnpFN8UzM4Rcuid+Yi6pZJED
LiASDdNrOPfTm8Zu86YRPQ==
-----END CERTIFICATE-----

@ -1,61 +1,61 @@
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----

@ -1,91 +1,91 @@
-----BEGIN CERTIFICATE-----
MIIFHDCCBASgAwIBAgISBIL+EiA/jua4C2la/3qwHNB7MA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMjExMTAxNTQyMjBaFw0yMzAyMDgxNTQyMTlaMBYxFDASBgNVBAMT
C3NvbHdlYi5zaXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3eA
U7zQ8iJzlHqmxPKottfLIXNBKBxW3B6x6tR5v+pBZFWF6ST3ocIByYfQtGAeNdnY
q95/DRVnSyYnVce47lI3PVB+X25iGUIQWxWcbNP5Ep4fuayA2z9PtTfF6LXbP883
xFAD9MqQzr+77JjUrm+up3RZmPa8F+o43msZuFBeQgDb9U6ZiRl3yqh67UB5PPVM
5DW18wvFvg1NRWDmAN7FQgi6AkySdLJjo0/4iHfV8e6hDhI1q0Tcnvsp8VFw5QJn
RYcQfKhYVXe62HjRDs08whKlptTcqivzXz2AxnU9yfjVsdaHCBUOyhpGdHQuiIjs
piTadNJmHOHgKZGGBQIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBS5dOhJzo+8FEcBI4VZnCHfMZfBBTAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDm
H6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5v
LmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAW
BgNVHREEDzANggtzb2x3ZWIuc2l0ZTBMBgNVHSAERTBDMAgGBmeBDAECATA3Bgsr
BgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0
Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALc++yTfnE26dfI5xbpY9Gxd
/ELPep81xJ4dCYEl7bSZAAABhGJrxCEAAAQDAEcwRQIhALvhrXlgLVUrOdDJ+v+0
1bITSoGGnx1AtE/qVsMdjZxKAiBmKFihL9ugHjH6Wp3HBOjQg97w1qf+tplKSOE8
fV0BuwB2AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutSAAABhGJrxEAA
AAQDAEcwRQIgIVOVnfb5oqoAcdmGhBMU/d65vfUvt2QP8yU1pmu0uS0CIQD++4kE
/DEYXBye634QVjmf/IcoXsPHQyQ3jIAmInteZDANBgkqhkiG9w0BAQsFAAOCAQEA
a9vkSsknNH5mH8QoFukQPIXDzYAHCaUpL7CzlHt1OWwTIIX/r8J91jrST9k/NHoh
rmvRWwlaLH0i5usYRKotSzdNETaJ5SUSMHdLv8Y/yAMGwURaDQeVwdsMsmraiUxX
84i86N1L/NSgWbgm2g7XS8TSA3mIyBF0BgLDl5pp7Tjywxj538+8a4fnHdFSobrd
3E52YQFP2mzzwgXenJebXrdaYDTAjjMDBpySC6vnRAubLedJE6Tfo2vkrwxfFTJY
ECRPtsEZQj4gyXb+PoPAYVH4DheiIN+xnAS0ZUEVYto4NfszU6dt9D31I3ZBD9a/
Xpf0e6rBigJ5FrkAlPU8rg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFHDCCBASgAwIBAgISBIL+EiA/jua4C2la/3qwHNB7MA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMjExMTAxNTQyMjBaFw0yMzAyMDgxNTQyMTlaMBYxFDASBgNVBAMT
C3NvbHdlYi5zaXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy3eA
U7zQ8iJzlHqmxPKottfLIXNBKBxW3B6x6tR5v+pBZFWF6ST3ocIByYfQtGAeNdnY
q95/DRVnSyYnVce47lI3PVB+X25iGUIQWxWcbNP5Ep4fuayA2z9PtTfF6LXbP883
xFAD9MqQzr+77JjUrm+up3RZmPa8F+o43msZuFBeQgDb9U6ZiRl3yqh67UB5PPVM
5DW18wvFvg1NRWDmAN7FQgi6AkySdLJjo0/4iHfV8e6hDhI1q0Tcnvsp8VFw5QJn
RYcQfKhYVXe62HjRDs08whKlptTcqivzXz2AxnU9yfjVsdaHCBUOyhpGdHQuiIjs
piTadNJmHOHgKZGGBQIDAQABo4ICRjCCAkIwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQW
BBS5dOhJzo+8FEcBI4VZnCHfMZfBBTAfBgNVHSMEGDAWgBQULrMXt1hWy65QCUDm
H6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5v
LmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3JnLzAW
BgNVHREEDzANggtzb2x3ZWIuc2l0ZTBMBgNVHSAERTBDMAgGBmeBDAECATA3Bgsr
BgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0
Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2ALc++yTfnE26dfI5xbpY9Gxd
/ELPep81xJ4dCYEl7bSZAAABhGJrxCEAAAQDAEcwRQIhALvhrXlgLVUrOdDJ+v+0
1bITSoGGnx1AtE/qVsMdjZxKAiBmKFihL9ugHjH6Wp3HBOjQg97w1qf+tplKSOE8
fV0BuwB2AHoyjFTYty22IOo44FIe6YQWcDIThU070ivBOlejUutSAAABhGJrxEAA
AAQDAEcwRQIgIVOVnfb5oqoAcdmGhBMU/d65vfUvt2QP8yU1pmu0uS0CIQD++4kE
/DEYXBye634QVjmf/IcoXsPHQyQ3jIAmInteZDANBgkqhkiG9w0BAQsFAAOCAQEA
a9vkSsknNH5mH8QoFukQPIXDzYAHCaUpL7CzlHt1OWwTIIX/r8J91jrST9k/NHoh
rmvRWwlaLH0i5usYRKotSzdNETaJ5SUSMHdLv8Y/yAMGwURaDQeVwdsMsmraiUxX
84i86N1L/NSgWbgm2g7XS8TSA3mIyBF0BgLDl5pp7Tjywxj538+8a4fnHdFSobrd
3E52YQFP2mzzwgXenJebXrdaYDTAjjMDBpySC6vnRAubLedJE6Tfo2vkrwxfFTJY
ECRPtsEZQj4gyXb+PoPAYVH4DheiIN+xnAS0ZUEVYto4NfszU6dt9D31I3ZBD9a/
Xpf0e6rBigJ5FrkAlPU8rg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLd4BTvNDyInOU
eqbE8qi218shc0EoHFbcHrHq1Hm/6kFkVYXpJPehwgHJh9C0YB412dir3n8NFWdL
JidVx7juUjc9UH5fbmIZQhBbFZxs0/kSnh+5rIDbP0+1N8Xotds/zzfEUAP0ypDO
v7vsmNSub66ndFmY9rwX6jjeaxm4UF5CANv1TpmJGXfKqHrtQHk89UzkNbXzC8W+
DU1FYOYA3sVCCLoCTJJ0smOjT/iId9Xx7qEOEjWrRNye+ynxUXDlAmdFhxB8qFhV
d7rYeNEOzTzCEqWm1NyqK/NfPYDGdT3J+NWx1ocIFQ7KGkZ0dC6IiOymJNp00mYc
4eApkYYFAgMBAAECggEAdst26W08saLbyL0z0Zm8V8T2nLkmOb0SGcLyLiGQVDT5
qRMl9FHZI2CamofpmubjvJ1MnExz8belb4L/RF4v4zWteuccYauZKxYfZo7fncrF
idE4xLerJFd6ulut+Yj0UfUyojP1m18J3SylFCbHRnF+j9am2WB1waDHQ1VU1v9K
W0Ny0Y3iBPQt34p6+Zt1Fi2zRfW3Wngld8o7Z09uyh5zsu8Esk7iQpSoV5XD0WRv
Ss13WrOZ33VwZxjYAsRh0koz96l4jpmCS5khnzSKmf3uaEOilrUPuGO6VMDv/yVF
guPvHWfusfT57hl6hWhLWb4bT3ZpOLiasyGKIo8lAQKBgQD1CtzQI8Ri+Hpga9XG
5kzO19sggWYxBcVi3Z+IUw9JOV80rMQUJEoFiahyvpItAyFTOcjxYaBHi0zHK5ZK
hRXoc7nvbkha4eVfg3BeSyh+8xjTB754YNfImByso+rW9W+D3n6feFcN4nB3i9qm
vdgCw3ws5XvJjKlFFfUs6sI7/QKBgQDUkLMExgccllxGmzHL7K68oqKKPZsIANBs
FUIFDoGimlfrPdja8/viEDDi15ZhH3tv1LJUeC/aMaLgU2EGarRbYpdD9vKZLvPi
ydEboGFUNJbdnfKxTHeaUrE35xUv8b499bDKsCohW1r4hvadT+8uDr23JtL4SsqH
+ug4+L1cqQKBgQDU9FLAREU46c6Ymz6W87BOlkMmNa6U7foDmK07MUv4i/aUdYyW
II9/zolo5vtsSOseQ/rA4+ICKypXcSbUrmJCuMgfL42MBgNsBXOTTufro+KwC2vZ
e/grqR5KXs4JcBUw2hzbBB9Lvr2U9yLXg+cFR9RTxD/XAfpdQt7m4UJaFQKBgQDI
44vceg6kjNHehLN2J1QowIdeoMRQtvxC+YAwaTpI4xcuIoA8xZMKXgTljZv4ZBlz
Yg+7Vu7ahkiJOyOaPeP/7dhJiixSaxZhAhzWwCbbvuvJvIlt2He8aGRGEeVrL0t9
ISKZHA5lAgKimGKf37iKzbGsU0vVDlIEWzN3DFViKQKBgHTpq5SeBp3NSnQw5a0y
6vPYtm0t/jysOy3UGIl7qf/P5Ob994E7gVluPN8r4wXcmYqw9/glT2pdqfoBeQmX
UZ0zvcbdTERoNbQP8xkoN6C/8kUxbCVY3TsvteZQbJ7XB1Lcvf5k3aVWYDKabEzX
6kat3P/xhuX2aBJ25n+DEmwt
-----END PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLd4BTvNDyInOU
eqbE8qi218shc0EoHFbcHrHq1Hm/6kFkVYXpJPehwgHJh9C0YB412dir3n8NFWdL
JidVx7juUjc9UH5fbmIZQhBbFZxs0/kSnh+5rIDbP0+1N8Xotds/zzfEUAP0ypDO
v7vsmNSub66ndFmY9rwX6jjeaxm4UF5CANv1TpmJGXfKqHrtQHk89UzkNbXzC8W+
DU1FYOYA3sVCCLoCTJJ0smOjT/iId9Xx7qEOEjWrRNye+ynxUXDlAmdFhxB8qFhV
d7rYeNEOzTzCEqWm1NyqK/NfPYDGdT3J+NWx1ocIFQ7KGkZ0dC6IiOymJNp00mYc
4eApkYYFAgMBAAECggEAdst26W08saLbyL0z0Zm8V8T2nLkmOb0SGcLyLiGQVDT5
qRMl9FHZI2CamofpmubjvJ1MnExz8belb4L/RF4v4zWteuccYauZKxYfZo7fncrF
idE4xLerJFd6ulut+Yj0UfUyojP1m18J3SylFCbHRnF+j9am2WB1waDHQ1VU1v9K
W0Ny0Y3iBPQt34p6+Zt1Fi2zRfW3Wngld8o7Z09uyh5zsu8Esk7iQpSoV5XD0WRv
Ss13WrOZ33VwZxjYAsRh0koz96l4jpmCS5khnzSKmf3uaEOilrUPuGO6VMDv/yVF
guPvHWfusfT57hl6hWhLWb4bT3ZpOLiasyGKIo8lAQKBgQD1CtzQI8Ri+Hpga9XG
5kzO19sggWYxBcVi3Z+IUw9JOV80rMQUJEoFiahyvpItAyFTOcjxYaBHi0zHK5ZK
hRXoc7nvbkha4eVfg3BeSyh+8xjTB754YNfImByso+rW9W+D3n6feFcN4nB3i9qm
vdgCw3ws5XvJjKlFFfUs6sI7/QKBgQDUkLMExgccllxGmzHL7K68oqKKPZsIANBs
FUIFDoGimlfrPdja8/viEDDi15ZhH3tv1LJUeC/aMaLgU2EGarRbYpdD9vKZLvPi
ydEboGFUNJbdnfKxTHeaUrE35xUv8b499bDKsCohW1r4hvadT+8uDr23JtL4SsqH
+ug4+L1cqQKBgQDU9FLAREU46c6Ymz6W87BOlkMmNa6U7foDmK07MUv4i/aUdYyW
II9/zolo5vtsSOseQ/rA4+ICKypXcSbUrmJCuMgfL42MBgNsBXOTTufro+KwC2vZ
e/grqR5KXs4JcBUw2hzbBB9Lvr2U9yLXg+cFR9RTxD/XAfpdQt7m4UJaFQKBgQDI
44vceg6kjNHehLN2J1QowIdeoMRQtvxC+YAwaTpI4xcuIoA8xZMKXgTljZv4ZBlz
Yg+7Vu7ahkiJOyOaPeP/7dhJiixSaxZhAhzWwCbbvuvJvIlt2He8aGRGEeVrL0t9
ISKZHA5lAgKimGKf37iKzbGsU0vVDlIEWzN3DFViKQKBgHTpq5SeBp3NSnQw5a0y
6vPYtm0t/jysOy3UGIl7qf/P5Ob994E7gVluPN8r4wXcmYqw9/glT2pdqfoBeQmX
UZ0zvcbdTERoNbQP8xkoN6C/8kUxbCVY3TsvteZQbJ7XB1Lcvf5k3aVWYDKabEzX
6kat3P/xhuX2aBJ25n+DEmwt
-----END PRIVATE KEY-----

@ -1,14 +1,14 @@
This directory contains your keys and certificates.
`privkey.pem` : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
`cert.pem` : will break many server configurations, and should not be used
without reading further documentation (see link below).
WARNING: DO NOT MOVE OR RENAME THESE FILES!
Certbot expects these files to remain in this location in order
to function properly!
We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.
This directory contains your keys and certificates.
`privkey.pem` : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
`cert.pem` : will break many server configurations, and should not be used
without reading further documentation (see link below).
WARNING: DO NOT MOVE OR RENAME THESE FILES!
Certbot expects these files to remain in this location in order
to function properly!
We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.

@ -1,26 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEZjCCA06gAwIBAgISBGHJAxiTkBlLfhA9Rwz3zwttMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzAyMTEwODUxMTNaFw0yMzA1MTIwODUxMTJaMBkxFzAVBgNVBAMM
DioucGVuYS5kaWdpdGFsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5ZKYmhoJ
q5Tczk43XnFPp3dfnhDtgrkLvtu/0WSn0O1JWOtgN+G9onGWSXhsqUMtVDcxgeep
BXt+BtkochkjOqOCAlgwggJUMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUd/lblpWt
FJN7LdbxW+1dOU2srbMwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsUwsYw
VQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5jci5v
cmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wJwYDVR0RBCAw
HoIOKi5wZW5hLmRpZ2l0YWyCDHBlbmEuZGlnaXRhbDBMBgNVHSAERTBDMAgGBmeB
DAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxl
dHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3ALc++yTfnE26
dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhj/izVIAAAQDAEgwRgIhAL5qiwhk
g803FM7IvNH43IhGStn0jkJFLNTozliuvIddAiEAykIciMuuyItVE015iL5RujqU
LW7VKXqfNLNxYgwRooQAdgDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9
bgAAAYY/4s1JAAAEAwBHMEUCIQCmcAd0nOedpkcswqTmWoY23vDchb02P7xk2svd
EpNRGwIgey5RzutYLEIagpDLah9rNIprriimqeEqhDkPbocshTUwDQYJKoZIhvcN
AQELBQADggEBABPHbpS0lshG8mJ+sWJ2Vaqd/4ld8DPZ9cCzIx/6/xQ3bUnoWACM
6qZhK9pZhmE7/m0ZOgZNay5cDGRwQv8SsrL7llyOpcvOm+pTB6u5OkapB70+BkxJ
v3EZ1CEzjJhWrbwF1eA3Bgd1c6vBoYbhVXHD1ok2i6HYdohwDKaLDwgypRb87MRM
k4fsm1+jFeZ1Y0ghhkXeSvO6HbptxSgqrQpQJ8gthFv0lB54H643IhsQkXjIAOtT
LEqMvQdF2Lums+xkWZOplgiNst+8C2HxVEErh7KDlvdBzOWkeRAdiXEqn/Zsizu3
x3sOobCSAukTYM+Kj1UoAvwU10cnCy6RZiU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEZjCCA06gAwIBAgISBGHJAxiTkBlLfhA9Rwz3zwttMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzAyMTEwODUxMTNaFw0yMzA1MTIwODUxMTJaMBkxFzAVBgNVBAMM
DioucGVuYS5kaWdpdGFsMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5ZKYmhoJ
q5Tczk43XnFPp3dfnhDtgrkLvtu/0WSn0O1JWOtgN+G9onGWSXhsqUMtVDcxgeep
BXt+BtkochkjOqOCAlgwggJUMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggr
BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUd/lblpWt
FJN7LdbxW+1dOU2srbMwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsUwsYw
VQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5sZW5jci5v
cmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wJwYDVR0RBCAw
HoIOKi5wZW5hLmRpZ2l0YWyCDHBlbmEuZGlnaXRhbDBMBgNVHSAERTBDMAgGBmeB
DAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3BzLmxl
dHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3ALc++yTfnE26
dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABhj/izVIAAAQDAEgwRgIhAL5qiwhk
g803FM7IvNH43IhGStn0jkJFLNTozliuvIddAiEAykIciMuuyItVE015iL5RujqU
LW7VKXqfNLNxYgwRooQAdgDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9
bgAAAYY/4s1JAAAEAwBHMEUCIQCmcAd0nOedpkcswqTmWoY23vDchb02P7xk2svd
EpNRGwIgey5RzutYLEIagpDLah9rNIprriimqeEqhDkPbocshTUwDQYJKoZIhvcN
AQELBQADggEBABPHbpS0lshG8mJ+sWJ2Vaqd/4ld8DPZ9cCzIx/6/xQ3bUnoWACM
6qZhK9pZhmE7/m0ZOgZNay5cDGRwQv8SsrL7llyOpcvOm+pTB6u5OkapB70+BkxJ
v3EZ1CEzjJhWrbwF1eA3Bgd1c6vBoYbhVXHD1ok2i6HYdohwDKaLDwgypRb87MRM
k4fsm1+jFeZ1Y0ghhkXeSvO6HbptxSgqrQpQJ8gthFv0lB54H643IhsQkXjIAOtT
LEqMvQdF2Lums+xkWZOplgiNst+8C2HxVEErh7KDlvdBzOWkeRAdiXEqn/Zsizu3
x3sOobCSAukTYM+Kj1UoAvwU10cnCy6RZiU=
-----END CERTIFICATE-----

@ -1,92 +1,92 @@
-----BEGIN CERTIFICATE-----
MIIFLjCCBBagAwIBAgISA2kKBRIxxoABL1n9QL6mzbWZMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzA2MTQxNzE4MDFaFw0yMzA5MTIxNzE4MDBaMB8xHTAbBgNVBAMT
FHRlbXBnZW4ucGVuYS5kaWdpdGFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAqNz9OoBpWMzrKOwtMYADgEkrVVQ8cCBvBREVx/Rnv9cLwjWBr0PD95Wf
oNdVpgpGt0vwaTYVbK6B4jsNQUnX3aMHL9A8ZqjGE65fdN5KsmIOp5RcIgL0v46Z
9puWSO1sw7F2Fx5YexYmXZgcf9Evfw1dUYnwjCtCx6l66PWKoGEmEzhCqobQ+ZbP
92hnZ7KaEGIVM0pHMkTFrpD449ZosbwVhChr5TjDkHC6Vui4ut2gZDavHWxbu2Ai
2GRU4Oem+KY3hrfw/vmaQ8/jyNBk2PJcjTLtaFmiPzM2pCR8vmJ0xjrZx5Y0GDc8
RtJg1vTTglHkRb/pwle8AP+qNJbFYwIDAQABo4ICTzCCAkswDgYDVR0PAQH/BAQD
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
MB0GA1UdDgQWBBQu9jY2x9RKv0yRqFwju2z7apwwUzAfBgNVHSMEGDAWgBQULrMX
t1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0
dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVu
Y3Iub3JnLzAfBgNVHREEGDAWghR0ZW1wZ2VuLnBlbmEuZGlnaXRhbDBMBgNVHSAE
RTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRw
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2
ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABiLsg/j0AAAQDAEcw
RQIhAMbDGXZHQI4Evauiri3lI9+HfL/kBoi41mnmM6AZgOZ5AiAWvlR5+ijoxUZF
ja62HaAIJb5/t0lRDwQvcov8WSrk/AB2AK33vvp8/xDIi509nB4+GGq0Zyldz7EM
JMqFhjTr3IKKAAABiLsg/tcAAAQDAEcwRQIhALTfrawjd5xQNOaTuwT/u2Kc25T1
lpS25L7gNP09de6cAiBejGK1TMIm9hLnWD1B/tTvoD+GWpeVlTdWLYoFLzctqTAN
BgkqhkiG9w0BAQsFAAOCAQEAQwDyqHXnGV/JvEbTHmarIYcVYkLqXbKTHphf0AKC
FmuUxL3mUlvPIel/HTy6WxeLLrkdTTd/j2UkHMLqZk2MulcOh3K3kmRZwg3dha2j
E67ef5+s8obMWJDm4kg4JJT9+lsrfOP3VHhYtBaLN8iiUr02XQfZXEWKAQF35byi
8AszB5vaPfzEM5hNeOUeW8h4RVW1sBSdrUQdxLXBUSZxMUnFMJe4Wq7ru8xPCMSd
gC/DwMa8oHijafy1eCuXsshOqSoKI0sj2u272o8yn4SWJcwpk1PRidf7cIa4hAG3
Rfw6afhczKB+9myjF2/zRNgklFtdSUEPWIjOQX9l2Z1R0A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFLjCCBBagAwIBAgISA2kKBRIxxoABL1n9QL6mzbWZMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yMzA2MTQxNzE4MDFaFw0yMzA5MTIxNzE4MDBaMB8xHTAbBgNVBAMT
FHRlbXBnZW4ucGVuYS5kaWdpdGFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAqNz9OoBpWMzrKOwtMYADgEkrVVQ8cCBvBREVx/Rnv9cLwjWBr0PD95Wf
oNdVpgpGt0vwaTYVbK6B4jsNQUnX3aMHL9A8ZqjGE65fdN5KsmIOp5RcIgL0v46Z
9puWSO1sw7F2Fx5YexYmXZgcf9Evfw1dUYnwjCtCx6l66PWKoGEmEzhCqobQ+ZbP
92hnZ7KaEGIVM0pHMkTFrpD449ZosbwVhChr5TjDkHC6Vui4ut2gZDavHWxbu2Ai
2GRU4Oem+KY3hrfw/vmaQ8/jyNBk2PJcjTLtaFmiPzM2pCR8vmJ0xjrZx5Y0GDc8
RtJg1vTTglHkRb/pwle8AP+qNJbFYwIDAQABo4ICTzCCAkswDgYDVR0PAQH/BAQD
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
MB0GA1UdDgQWBBQu9jY2x9RKv0yRqFwju2z7apwwUzAfBgNVHSMEGDAWgBQULrMX
t1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0
dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVu
Y3Iub3JnLzAfBgNVHREEGDAWghR0ZW1wZ2VuLnBlbmEuZGlnaXRhbDBMBgNVHSAE
RTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRw
Oi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2
ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAABiLsg/j0AAAQDAEcw
RQIhAMbDGXZHQI4Evauiri3lI9+HfL/kBoi41mnmM6AZgOZ5AiAWvlR5+ijoxUZF
ja62HaAIJb5/t0lRDwQvcov8WSrk/AB2AK33vvp8/xDIi509nB4+GGq0Zyldz7EM
JMqFhjTr3IKKAAABiLsg/tcAAAQDAEcwRQIhALTfrawjd5xQNOaTuwT/u2Kc25T1
lpS25L7gNP09de6cAiBejGK1TMIm9hLnWD1B/tTvoD+GWpeVlTdWLYoFLzctqTAN
BgkqhkiG9w0BAQsFAAOCAQEAQwDyqHXnGV/JvEbTHmarIYcVYkLqXbKTHphf0AKC
FmuUxL3mUlvPIel/HTy6WxeLLrkdTTd/j2UkHMLqZk2MulcOh3K3kmRZwg3dha2j
E67ef5+s8obMWJDm4kg4JJT9+lsrfOP3VHhYtBaLN8iiUr02XQfZXEWKAQF35byi
8AszB5vaPfzEM5hNeOUeW8h4RVW1sBSdrUQdxLXBUSZxMUnFMJe4Wq7ru8xPCMSd
gC/DwMa8oHijafy1eCuXsshOqSoKI0sj2u272o8yn4SWJcwpk1PRidf7cIa4hAG3
Rfw6afhczKB+9myjF2/zRNgklFtdSUEPWIjOQX9l2Z1R0A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCo3P06gGlYzOso
7C0xgAOASStVVDxwIG8FERXH9Ge/1wvCNYGvQ8P3lZ+g11WmCka3S/BpNhVsroHi
Ow1BSdfdowcv0DxmqMYTrl903kqyYg6nlFwiAvS/jpn2m5ZI7WzDsXYXHlh7FiZd
mBx/0S9/DV1RifCMK0LHqXro9YqgYSYTOEKqhtD5ls/3aGdnspoQYhUzSkcyRMWu
kPjj1mixvBWEKGvlOMOQcLpW6Li63aBkNq8dbFu7YCLYZFTg56b4pjeGt/D++ZpD
z+PI0GTY8lyNMu1oWaI/MzakJHy+YnTGOtnHljQYNzxG0mDW9NOCUeRFv+nCV7wA
/6o0lsVjAgMBAAECggEBAKbceH5ulariEx4FA0N+s/b4VpGjA3XpFsty7or/v+WZ
okzugo+l7uegOnS9VC0Wynol2BKfFtymNEJRB/AT//NLPbbJlmX7MIASNtFNN5K9
wq1ltQUG1sLTLuC5LfAybN+TZJXDrrsyUAb8YG0NUFnD4wrHu6NGvF5/4qyAtdNz
kMFEqAJeatGinVH9NKZvVz0pGPv0JWj8aG3NssGmJSNy/sWO1ZfMWAnrG3pTI13N
9bEzyC7MR++oFzGbx02SuWllyoTi5YMP7SbdTYDrmh1aXIQ+OPSAnCAoQ40GjzJ0
iDwHH8u3VnF1gq5Y+cp7DWxM/Lnu7s3WwkyLjbSCzsECgYEA2jKRay6d+h5DwVwA
AV6AC4W+YfvynPBNBkKVs5bGs+FrFhb0o0Iy5B1JoxhthExTph987bOw1/Rdok8X
yxOZk5JuJyYBoQiOEocNizXK/hseKBJXRAum/ET9PCPdX0RjHq91eTTWStk4dI3d
F0k4EWP/+Bk0TEMqdIfCgh1/Do0CgYEAxh5cfNIPjwmRzdqFLqWo12sLl//KI+WN
zNp8G0ZSYB38THYpVdBVSFLJ1y7dP/B6h/MIpy+m/tgeVg7WWqUB5X7z1aP8006Z
YoUnVlYDXYj3fZHOfDklUO+pnVCEOoD4v22E+YeO2mEiT0gDQ8jK7SlxOgmmLUY7
GTm+1vEL368CgYANYy7i6aBNuU+k7HUfoGGw00rkDiZLlL7VFH7E3wpf/QOp/kyS
O7yNnHJpLFWD2X8EYUgz+WNvTJH2JG4sSo6QrFpP0pgk/jZhUqH1GZ7TymwmP0HV
bfWX4s+6weSSNMr0FgXaIvDG9N7u38lh5V8oSf9phHpd930CW1YGz7J8/QKBgQDC
INPtdwPJ282j0UB5MdgcpQZqPTj8LZ3hoiDDtQdYqWkkHxBnxskaIg0nLgOb+G6I
MgtDjvzqcChhmQ5PKaqPDvOsXpCJGVZQ5DpkbcnEaEZiI3IZ5QD8qqofvNqkXOXg
5VbMDxlaQJ2W4/BXBjRZaSdKgFP1vwB/ukaWZQGGfwKBgDqOk629Py3DUbqNBNcd
tA5UJ0YKQ+u26gMVLzE6taZsNXxzci2cpLuUwLBaVyDiaydZeRXnss9ayUwXEVP5
xTcyVdxAoW4cpjTjMPulpjXduMCzYTweQuXVpI8aQ51adlxCvSyE4j1wffY6YVdN
E7FuJt0KKfULcJzcAc+f0qsg
-----END PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCo3P06gGlYzOso
7C0xgAOASStVVDxwIG8FERXH9Ge/1wvCNYGvQ8P3lZ+g11WmCka3S/BpNhVsroHi
Ow1BSdfdowcv0DxmqMYTrl903kqyYg6nlFwiAvS/jpn2m5ZI7WzDsXYXHlh7FiZd
mBx/0S9/DV1RifCMK0LHqXro9YqgYSYTOEKqhtD5ls/3aGdnspoQYhUzSkcyRMWu
kPjj1mixvBWEKGvlOMOQcLpW6Li63aBkNq8dbFu7YCLYZFTg56b4pjeGt/D++ZpD
z+PI0GTY8lyNMu1oWaI/MzakJHy+YnTGOtnHljQYNzxG0mDW9NOCUeRFv+nCV7wA
/6o0lsVjAgMBAAECggEBAKbceH5ulariEx4FA0N+s/b4VpGjA3XpFsty7or/v+WZ
okzugo+l7uegOnS9VC0Wynol2BKfFtymNEJRB/AT//NLPbbJlmX7MIASNtFNN5K9
wq1ltQUG1sLTLuC5LfAybN+TZJXDrrsyUAb8YG0NUFnD4wrHu6NGvF5/4qyAtdNz
kMFEqAJeatGinVH9NKZvVz0pGPv0JWj8aG3NssGmJSNy/sWO1ZfMWAnrG3pTI13N
9bEzyC7MR++oFzGbx02SuWllyoTi5YMP7SbdTYDrmh1aXIQ+OPSAnCAoQ40GjzJ0
iDwHH8u3VnF1gq5Y+cp7DWxM/Lnu7s3WwkyLjbSCzsECgYEA2jKRay6d+h5DwVwA
AV6AC4W+YfvynPBNBkKVs5bGs+FrFhb0o0Iy5B1JoxhthExTph987bOw1/Rdok8X
yxOZk5JuJyYBoQiOEocNizXK/hseKBJXRAum/ET9PCPdX0RjHq91eTTWStk4dI3d
F0k4EWP/+Bk0TEMqdIfCgh1/Do0CgYEAxh5cfNIPjwmRzdqFLqWo12sLl//KI+WN
zNp8G0ZSYB38THYpVdBVSFLJ1y7dP/B6h/MIpy+m/tgeVg7WWqUB5X7z1aP8006Z
YoUnVlYDXYj3fZHOfDklUO+pnVCEOoD4v22E+YeO2mEiT0gDQ8jK7SlxOgmmLUY7
GTm+1vEL368CgYANYy7i6aBNuU+k7HUfoGGw00rkDiZLlL7VFH7E3wpf/QOp/kyS
O7yNnHJpLFWD2X8EYUgz+WNvTJH2JG4sSo6QrFpP0pgk/jZhUqH1GZ7TymwmP0HV
bfWX4s+6weSSNMr0FgXaIvDG9N7u38lh5V8oSf9phHpd930CW1YGz7J8/QKBgQDC
INPtdwPJ282j0UB5MdgcpQZqPTj8LZ3hoiDDtQdYqWkkHxBnxskaIg0nLgOb+G6I
MgtDjvzqcChhmQ5PKaqPDvOsXpCJGVZQ5DpkbcnEaEZiI3IZ5QD8qqofvNqkXOXg
5VbMDxlaQJ2W4/BXBjRZaSdKgFP1vwB/ukaWZQGGfwKBgDqOk629Py3DUbqNBNcd
tA5UJ0YKQ+u26gMVLzE6taZsNXxzci2cpLuUwLBaVyDiaydZeRXnss9ayUwXEVP5
xTcyVdxAoW4cpjTjMPulpjXduMCzYTweQuXVpI8aQ51adlxCvSyE4j1wffY6YVdN
E7FuJt0KKfULcJzcAc+f0qsg
-----END PRIVATE KEY-----

@ -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"`