
- Добавлена проверка токенов при запросе /amo/state - Исправлена работа с amo oauth-токенами - Добавлены примеры шаблонизатора - Добавлена возможность генерации и создания шаблонов из примеров - В ответ генератора добавлены ссылки для скачивания сгенерированных файлов - Вернул очиску temp после загрузки файла в Яндекс.Диск - yadisk.UploadResources - добавлено ожидание окончания отправки файла и таймаут в 5 секунд для отправки файла - Добавлены эндпоинты для загрузки файлов в хранилища - Актуализирован генератор по вебхуку - Обновлены генератор по данным - Обновлен генератор по лиду
336 lines
8.5 KiB
Go
336 lines
8.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/model"
|
|
"github.com/Pena-Co-Ltd/amocrm_templategen_back/tools"
|
|
"github.com/gorilla/schema"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/oauth2"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type ReqAmoSaveToken struct {
|
|
AccessToken string `json:"access_token" schema:"access_token"`
|
|
Code string `json:"code" schema:"code"`
|
|
ClientID string `json:"client_id" schema:"client_id"`
|
|
ExpiresIn int64 `json:"expires_in" schema:"expires_in"`
|
|
TokenType string `json:"token_type" schema:"token_type"`
|
|
RefreshToken string `json:"refresh_token" schema:"refresh_token"`
|
|
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"` // Вообще без понятия, что это
|
|
}
|
|
|
|
func (h *Handlers) AmoSaveToken(w http.ResponseWriter, r *http.Request) {
|
|
var req ReqAmoSaveToken
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
err = schema.NewDecoder().Decode(&req, r.Form)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if req.AccessToken == "" && req.Code == "" {
|
|
err = errors.New("token required")
|
|
h.reportError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if req.State == "" {
|
|
err = errors.New("state required")
|
|
h.reportError(w, err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var state tools.StateToken
|
|
err = tools.DecryptTokenRC4(req.State, &state)
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
token := &oauth2.Token{
|
|
AccessToken: req.AccessToken,
|
|
RefreshToken: req.RefreshToken,
|
|
TokenType: req.TokenType,
|
|
Expiry: time.Now().Add(time.Duration(req.ExpiresIn) * time.Second),
|
|
}
|
|
|
|
amoClient, err := h.Amo.NewClient(r.Context(), req.Referer, token, req.Code)
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
token = amoClient.Token
|
|
|
|
amoAcc, err := amoClient.GetAccount()
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Insert/Update token in DB
|
|
_, err = h.dal.Amo.InsertOrUpdate(r.Context(), &model.Amo{
|
|
UserID: state.UserID,
|
|
AccountID: strconv.FormatInt(amoAcc.Id, 10),
|
|
AccessToken: token.AccessToken,
|
|
RefreshToken: token.RefreshToken,
|
|
FromWidget: req.FromWidget,
|
|
Referer: req.Referer,
|
|
Subdomain: amoAcc.Subdomain,
|
|
ExpiresIn: token.Expiry,
|
|
TokenType: token.TokenType,
|
|
})
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
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"`
|
|
Storages map[string]interface{} `json:"storages"`
|
|
Examples any `json:"examples"`
|
|
InvalidTokens map[string]any `json:"invalid_tokens"` // Невалидные токены, которые не получилось обновить
|
|
Visibility []int64 `json:"visibility"`
|
|
Creation []int64 `json:"creation"`
|
|
Delete []int64 `json:"delete"`
|
|
}
|
|
|
|
// AmoState - получить актуальное состояние аккаунта пользователя в Amo
|
|
func (h *Handlers) AmoState(w http.ResponseWriter, r *http.Request) {
|
|
// TODO: get pena jwt
|
|
|
|
amoData := getAmoByJwt(r)
|
|
|
|
if amoData == nil {
|
|
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
yaStorages, err := h.dal.YaDisk.GetListByUserID(r.Context(), amoData.UserID)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
gStorages, err := h.dal.GDisk.GetListByUserID(r.Context(), amoData.UserID)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
storages := map[string]interface{}{
|
|
"yadisk": yaStorages,
|
|
"gdisk": gStorages,
|
|
}
|
|
|
|
redirectUri := "https://" + amoData.Referer + h.opts.AmoRedirectUrn
|
|
authYandexUrl, err := h.YaDisk.GenerateOAuthUrl(amoData.UserID, redirectUri)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
authGoogleUrl, err := h.GDisk.GenerateOAuthUrl(amoData.UserID, redirectUri)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
authAmoUrl, err := h.Amo.GenerateOAuthUrl(amoData.UserID, redirectUri)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
//Add examples
|
|
var examples any
|
|
|
|
pathExamples := "./static/examples/docx"
|
|
fsExamples, err := os.ReadDir(pathExamples)
|
|
|
|
if err != nil {
|
|
examples = fmt.Sprintf("err = %v", err)
|
|
} else {
|
|
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
|
|
}
|
|
examples = result
|
|
}
|
|
|
|
// Check tokens
|
|
invalidTokens := map[string]any{}
|
|
|
|
// Yandex tokens
|
|
for _, storage := range yaStorages {
|
|
if storage.Token().Valid() {
|
|
continue
|
|
}
|
|
|
|
token, err := h.YaDisk.RefreshToken(r.Context(), storage.Token())
|
|
|
|
if err == nil {
|
|
_, err = h.dal.YaDisk.InsertOrUpdate(r.Context(), &model.YaDisk{
|
|
ID: storage.ID,
|
|
AccessToken: token.AccessToken,
|
|
RefreshToken: token.RefreshToken,
|
|
ExpiresIn: token.Expiry,
|
|
TokenType: token.TokenType,
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
h.logger.Error("ErrorCheckYandexTokens", zap.Error(err))
|
|
|
|
invalidTokens["yadisk"] = storage
|
|
}
|
|
}
|
|
|
|
// Google tokens
|
|
for _, storage := range gStorages {
|
|
if storage.Token().Valid() {
|
|
continue
|
|
}
|
|
|
|
token, err := h.GDisk.RefreshToken(r.Context(), storage.Token())
|
|
|
|
if err == nil {
|
|
_, err = h.dal.GDisk.InsertOrUpdate(r.Context(), &model.GDisk{
|
|
ID: storage.ID,
|
|
RefreshToken: token.RefreshToken,
|
|
ExpiresIn: token.Expiry,
|
|
TokenType: token.TokenType,
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
h.logger.Error("ErrorCheckGoogleTokens", zap.Error(err))
|
|
invalidTokens["gdisk"] = storage
|
|
}
|
|
}
|
|
|
|
// Amo tokens
|
|
amoList, err := h.dal.Amo.GetListByUserID(r.Context(), amoData.UserID)
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
for _, amo := range amoList {
|
|
if amo.Token().Valid() {
|
|
continue
|
|
}
|
|
|
|
token, err := h.Amo.RefreshToken(r.Context(), amo.Token(), amo.Referer)
|
|
|
|
if err == nil {
|
|
_, err = h.dal.Amo.InsertOrUpdate(r.Context(), &model.Amo{
|
|
ID: amo.ID,
|
|
AccountID: amo.AccountID,
|
|
AccessToken: token.AccessToken,
|
|
RefreshToken: token.RefreshToken,
|
|
ExpiresIn: token.Expiry,
|
|
TokenType: token.TokenType,
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
h.logger.Error("ErrorCheckAmoTokens", zap.Error(err))
|
|
invalidTokens["amo"] = amo
|
|
}
|
|
}
|
|
|
|
resp := RespAmoState{
|
|
GenCount: 97,
|
|
AuthYandexUrl: authYandexUrl,
|
|
AuthGoogleUrl: authGoogleUrl,
|
|
AuthAmoUrl: authAmoUrl,
|
|
Storages: storages,
|
|
Visibility: amoData.AccessRules.Visibility,
|
|
Creation: amoData.AccessRules.Creation,
|
|
Delete: amoData.AccessRules.Delete,
|
|
Examples: examples,
|
|
InvalidTokens: invalidTokens,
|
|
}
|
|
|
|
sendResponse(w, 200, resp)
|
|
}
|
|
|
|
type ReqAmoAccessRules struct {
|
|
Visibility []int64 `json:"visibility"`
|
|
Creation []int64 `json:"creation"`
|
|
Delete []int64 `json:"delete"`
|
|
}
|
|
|
|
// AmoAccessRules - задать правила для пользователя
|
|
func (h *Handlers) AmoAccessRules(w http.ResponseWriter, r *http.Request) {
|
|
var req ReqAmoAccessRules
|
|
|
|
amoData := getAmoByJwt(r)
|
|
|
|
if amoData == nil {
|
|
h.reportError(w, ErrorUnauthorized, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
isAdmin := r.Context().Value("amoIsAdmin").(bool)
|
|
|
|
if !isAdmin {
|
|
h.reportError(w, errors.New("need admin access"), http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
err := decodePost(&req, r)
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusBadRequest)
|
|
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)
|
|
return
|
|
}
|
|
|
|
err = h.dal.Amo.UpdateAccessRules(r.Context(), amoData.ID, &model.AmoAccessRules{
|
|
Visibility: req.Visibility,
|
|
Creation: req.Creation,
|
|
Delete: req.Delete,
|
|
})
|
|
|
|
if err != nil {
|
|
h.reportError(w, err, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(200)
|
|
}
|