This commit is contained in:
madipo2611 2025-05-05 14:15:18 +03:00
parent 6f5298d420
commit 95bdb56e70
12 changed files with 11080 additions and 16 deletions

2
.env
View File

@ -3,6 +3,8 @@ SERVER_PORT=3006
DB_DSN=postgres://tailly_v2:U%26bB0y%25GYn9r%2681%23@79.174.89.104:15452/tailly_v2?sslmode=disable DB_DSN=postgres://tailly_v2:U%26bB0y%25GYn9r%2681%23@79.174.89.104:15452/tailly_v2?sslmode=disable
ACCESS_TOKEN_SECRET="e5159eb2a712a47fb578284c36d507664f922f968ee99fa0b68260208d85688b" ACCESS_TOKEN_SECRET="e5159eb2a712a47fb578284c36d507664f922f968ee99fa0b68260208d85688b"
REFRESH_TOKEN_SECRET="75afdd4abdc49f647c57bfa700f0cc01fb931b5f6b2176386f93f64692179946" REFRESH_TOKEN_SECRET="75afdd4abdc49f647c57bfa700f0cc01fb931b5f6b2176386f93f64692179946"
ACCESS_TOKEN_EXPIRY="15m"
REFRESH_TOKEN_EXPIRY="168h"
SMTP_HOST=mail.altomta.ru SMTP_HOST=mail.altomta.ru
SMTP_PORT=465 SMTP_PORT=465
SMTP_USERNAME=info@tailly.ru SMTP_USERNAME=info@tailly.ru

34
go.mod Normal file
View File

@ -0,0 +1,34 @@
module tailly_back_v2
go 1.24.1
require (
github.com/99designs/gqlgen v0.17.72
github.com/caarlos0/env/v8 v8.0.0
github.com/go-chi/chi/v5 v5.2.1
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/gorilla/websocket v1.5.0
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
github.com/prometheus/client_golang v1.22.0
github.com/vektah/gqlparser/v2 v2.5.25
golang.org/x/crypto v0.37.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)
require (
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/sosodev/duration v1.3.1 // indirect
golang.org/x/sys v0.32.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
)

78
go.sum Normal file
View File

@ -0,0 +1,78 @@
github.com/99designs/gqlgen v0.17.72 h1:2JDAuutIYtAN26BAtigfLZFnTN53fpYbIENL8bVgAKY=
github.com/99designs/gqlgen v0.17.72/go.mod h1:BoL4C3j9W2f95JeWMrSArdDNGWmZB9MOS2EMHJDZmUc=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8=
github.com/vektah/gqlparser/v2 v2.5.25/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -50,6 +50,7 @@ type Tokens struct {
RefreshToken string `json:"refreshToken"` RefreshToken string `json:"refreshToken"`
AccessTokenExpires time.Time `json:"accessTokenExpires"` AccessTokenExpires time.Time `json:"accessTokenExpires"`
RefreshTokenExpires time.Time `json:"refreshTokenExpires"` RefreshTokenExpires time.Time `json:"refreshTokenExpires"`
EmailConfirmed bool `json:"emailConfirmed"`
} }
type RegisterInput struct { type RegisterInput struct {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package graph
import (
"tailly_back_v2/internal/domain"
)
type ChatSession struct {
User *domain.User `json:"user"`
LastMessage *domain.Message `json:"lastMessage"`
UnreadCount int `json:"unreadCount"`
}
type Mutation struct {
}
type Query struct {
}
type Subscription struct {
}

View File

@ -0,0 +1,81 @@
package graph
import (
"context"
"database/sql"
"errors"
"fmt"
"tailly_back_v2/internal/repository"
"tailly_back_v2/internal/service"
)
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct {
Services *service.Services
DeviceRepo repository.DeviceRepository // Добавляем репозиторий устройств напрямую
chatRepo repository.ChatRepository
}
func NewResolver(
services *service.Services,
db *sql.DB, // Принимаем подключение к БД
) *Resolver {
return &Resolver{
Services: services,
DeviceRepo: repository.NewDeviceRepository(db), // Инициализируем репозиторий
}
}
// Mutation returns MutationResolver implementation.
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
func (r *Resolver) Message() MessageResolver { return &messageResolver{r} }
type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
// RequestEmailConfirmation - запрашивает подтверждение email
func (r *mutationResolver) RequestEmailConfirmation(ctx context.Context) (bool, error) {
userID, err := getUserIDFromContext(ctx)
if err != nil {
return false, errors.New("не авторизован")
}
user, err := r.Services.User.GetByID(ctx, userID)
if err != nil {
return false, fmt.Errorf("ошибка получения пользователя: %v", err)
}
token := user.EmailConfirmationToken
err = r.Services.Mail.SendConfirmationEmail(user.Email, token)
if err != nil {
return false, fmt.Errorf("ошибка отправки email: %v", err)
}
return true, nil
}
// ConfirmEmail - подтверждает email
func (r *mutationResolver) ConfirmEmail(ctx context.Context, token string) (bool, error) {
confirm, err := r.Services.Auth.ConfirmEmail(ctx, token)
if err != nil {
return false, fmt.Errorf("Ошибка подтверждения email: %v", err)
}
if confirm != true {
return false, fmt.Errorf("Ошибка подтверждения email: %v", err)
}
return true, nil
}
// ResendEmailConfirmation - повторно отправляет подтверждение
func (r *mutationResolver) ResendEmailConfirmation(ctx context.Context) (bool, error) {
return r.RequestEmailConfirmation(ctx)
}

View File

@ -0,0 +1,5 @@
package graph
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.72

View File

@ -4,7 +4,9 @@ package graph
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"tailly_back_v2/internal/domain" "tailly_back_v2/internal/domain"
"time"
) )
type tokensResolver struct{ *Resolver } type tokensResolver struct{ *Resolver }
@ -24,14 +26,30 @@ func (r *tokensResolver) EmailConfirmed(ctx context.Context, obj *domain.Tokens)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to get user: %w", err) return false, fmt.Errorf("failed to get user: %w", err)
} }
return user.EmailConfirmedAt != nil, nil return user.EmailConfirmedAt != nil, nil
} }
// RefreshTokens is the resolver for the refreshTokens field. // RefreshTokens is the resolver for the refreshTokens field.
func (r *mutationResolver) RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error) { func (r *mutationResolver) RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error) {
tokens, err := r.Services.Auth.RefreshTokens(ctx, refreshToken) tokens, err := r.Services.Auth.RefreshTokens(ctx, refreshToken)
log.Printf("резолвер RefreshTokens получение токена с фронта: %v", refreshToken)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to refresh tokens: %w", err) return nil, fmt.Errorf("failed to refresh tokens: %w", err)
} }
return tokens, nil return tokens, nil
} }
func (r *tokensResolver) AccessTokenExpires(ctx context.Context, obj *domain.Tokens) (string, error) {
if obj == nil {
return "", fmt.Errorf("nil tokens object")
}
return obj.AccessTokenExpires.UTC().Format(time.RFC3339), nil
}
func (r *tokensResolver) RefreshTokenExpires(ctx context.Context, obj *domain.Tokens) (string, error) {
if obj == nil {
return "", fmt.Errorf("nil tokens object")
}
return obj.RefreshTokenExpires.UTC().Format(time.RFC3339), nil
}

View File

@ -4,6 +4,7 @@ package graph
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"tailly_back_v2/internal/domain" "tailly_back_v2/internal/domain"
"tailly_back_v2/internal/service" "tailly_back_v2/internal/service"
"time" "time"
@ -22,15 +23,18 @@ func (r *queryResolver) User(ctx context.Context, id int) (*domain.User, error)
// Me is the resolver for the me field. // Me is the resolver for the me field.
func (r *queryResolver) Me(ctx context.Context) (*domain.User, error) { func (r *queryResolver) Me(ctx context.Context) (*domain.User, error) {
log.Println("Контекст Me: ", ctx)
userID, err := getUserIDFromContext(ctx) userID, err := getUserIDFromContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Println("userID из контекста в Me: ", userID)
user, err := r.Services.User.GetByID(ctx, userID) user, err := r.Services.User.GetByID(ctx, userID)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get current user: %w", err) return nil, fmt.Errorf("failed to get current user: %w", err)
} }
log.Println("Данные user из бд в Me: ", user)
return user, nil return user, nil
} }
@ -98,6 +102,7 @@ func (r *mutationResolver) Register(ctx context.Context, input domain.RegisterIn
// Login is the resolver for the login field. // Login is the resolver for the login field.
func (r *mutationResolver) Login(ctx context.Context, input domain.LoginInput) (*domain.Tokens, error) { func (r *mutationResolver) Login(ctx context.Context, input domain.LoginInput) (*domain.Tokens, error) {
log.Printf("input login: %v", input)
tokens, err := r.Services.Auth.Login(ctx, input.Email, input.Password) tokens, err := r.Services.Auth.Login(ctx, input.Email, input.Password)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to login: %w", err) return nil, fmt.Errorf("failed to login: %w", err)

View File

@ -114,16 +114,31 @@ func (s *authService) Login(ctx context.Context, email, password string) (*domai
} }
func (s *authService) RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error) { func (s *authService) RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error) {
// Валидируем старый refresh token
userID, err := s.tokenAuth.ValidateRefreshToken(refreshToken) userID, err := s.tokenAuth.ValidateRefreshToken(refreshToken)
if err != nil { if err != nil {
return nil, errors.New("invalid refresh token") log.Printf("Refresh token validation failed: %v", err)
return nil, fmt.Errorf("invalid refresh token: %w", err)
} }
if _, err = s.userRepo.GetByID(ctx, userID); err != nil { // Проверяем существование пользователя
return nil, errors.New("user not found") user, err := s.userRepo.GetByID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("user not found: %w", err)
} }
return s.tokenAuth.GenerateTokens(userID) // Генерируем новые токены, используя старый refresh token
tokens, err := s.tokenAuth.GenerateTokens(user.ID, refreshToken)
if err != nil {
return nil, fmt.Errorf("failed to generate tokens: %w", err)
}
// Обновляем время последней активности пользователя
//if err := s.userRepo.UpdateLastActive(ctx, user.ID); err != nil {
// log.Printf("Failed to update user last active time: %v", err)
//}
return tokens, nil
} }
func (s *authService) ConfirmEmail(ctx context.Context, token string) (bool, error) { func (s *authService) ConfirmEmail(ctx context.Context, token string) (bool, error) {

View File

@ -3,6 +3,8 @@ package auth
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"strconv"
"tailly_back_v2/internal/domain" "tailly_back_v2/internal/domain"
"time" "time"
@ -31,19 +33,36 @@ func NewTokenAuth(accessSecret, refreshSecret string, accessExpiry, refreshExpir
} }
// GenerateTokens создает пару access и refresh токенов // GenerateTokens создает пару access и refresh токенов
func (a *TokenAuth) GenerateTokens(userID int) (*domain.Tokens, error) { func (a *TokenAuth) GenerateTokens(userID int, existingRefreshToken ...string) (*domain.Tokens, error) {
accessExpires := time.Now().Add(a.accessTokenExpiry) accessExpires := time.Now().UTC().Add(a.accessTokenExpiry)
refreshExpires := time.Now().Add(a.refreshTokenExpiry) refreshExpires := time.Now().UTC().Add(a.refreshTokenExpiry)
// Генерируем новый access token
accessToken, err := a.generateAccessToken(userID) accessToken, err := a.generateAccessToken(userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
refreshToken, err := a.generateRefreshToken(userID) // Используем существующий refresh token, если он передан и валиден
var refreshToken string
if len(existingRefreshToken) > 0 && existingRefreshToken[0] != "" {
// Проверяем, что существующий токен еще действителен
if _, err := a.ValidateRefreshToken(existingRefreshToken[0]); err == nil {
refreshToken = existingRefreshToken[0]
} else {
log.Printf("Existing refresh token is invalid, generating new one: %v", err)
refreshToken, err = a.generateRefreshToken(userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
} else {
// Генерируем новый refresh token
refreshToken, err = a.generateRefreshToken(userID)
if err != nil {
return nil, err
}
}
return &domain.Tokens{ return &domain.Tokens{
AccessToken: accessToken, AccessToken: accessToken,
@ -108,21 +127,20 @@ func (a *TokenAuth) ValidateRefreshToken(tokenString string) (int, error) {
} }
return []byte(a.refreshTokenSecret), nil return []byte(a.refreshTokenSecret), nil
}) })
log.Printf("ValidateRefreshToken валидация токена: %v", token)
if err != nil { if err != nil {
return 0, err return 0, fmt.Errorf("token validation failed: %w", err)
} }
if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid { if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok && token.Valid {
var userID int userID, err := strconv.Atoi(claims.Subject)
_, err := fmt.Sscanf(claims.Subject, "%d", &userID)
if err != nil { if err != nil {
return 0, ErrInvalidToken return 0, errors.New("invalid user ID in token")
} }
return userID, nil return userID, nil
} }
return 0, ErrInvalidToken return 0, errors.New("invalid token claims")
} }
// HashPassword хеширует пароль // HashPassword хеширует пароль