188 lines
6.1 KiB
Go
188 lines
6.1 KiB
Go
package common
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/oauth2"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/status"
|
|
"gorm.io/gorm"
|
|
"stream.api/internal/database/model"
|
|
"stream.api/internal/middleware"
|
|
videodomain "stream.api/internal/video"
|
|
"stream.api/pkg/cache"
|
|
"stream.api/pkg/logger"
|
|
"stream.api/pkg/storage"
|
|
"stream.api/pkg/token"
|
|
)
|
|
|
|
const (
|
|
AdTemplateUpgradeRequiredMessage = "Upgrade required to manage Ads & VAST"
|
|
DefaultGoogleUserInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo"
|
|
PlayerConfigFreePlanLimitMessage = "Free plan supports only 1 player config"
|
|
PlayerConfigFreePlanReconciliationMessage = "Delete extra player configs to continue managing player configs on the free plan"
|
|
WalletTransactionTypeTopup = "topup"
|
|
WalletTransactionTypeSubscriptionDebit = "subscription_debit"
|
|
WalletTransactionTypeReferralReward = "referral_reward"
|
|
PaymentMethodWallet = "wallet"
|
|
PaymentMethodTopup = "topup"
|
|
PaymentKindSubscription = "subscription"
|
|
PaymentKindWalletTopup = "wallet_topup"
|
|
DefaultReferralRewardBps = int32(500)
|
|
)
|
|
|
|
var AllowedTermMonths = map[int32]struct{}{
|
|
1: {},
|
|
3: {},
|
|
6: {},
|
|
12: {},
|
|
}
|
|
|
|
type RuntimeOptions struct {
|
|
DB *gorm.DB
|
|
Logger logger.Logger
|
|
Authenticator *middleware.Authenticator
|
|
TokenProvider token.Provider
|
|
Cache cache.Cache
|
|
GoogleOauth *oauth2.Config
|
|
GoogleStateTTL time.Duration
|
|
GoogleUserInfoURL string
|
|
FrontendBaseURL string
|
|
StorageProvider func() storage.Provider
|
|
VideoService func() *videodomain.Service
|
|
AgentRuntime func() videodomain.AgentRuntime
|
|
}
|
|
|
|
type Runtime struct {
|
|
db *gorm.DB
|
|
logger logger.Logger
|
|
authenticator *middleware.Authenticator
|
|
tokenProvider token.Provider
|
|
cache cache.Cache
|
|
googleOauth *oauth2.Config
|
|
googleStateTTL time.Duration
|
|
googleUserInfoURL string
|
|
frontendBaseURL string
|
|
storageProvider func() storage.Provider
|
|
videoService func() *videodomain.Service
|
|
agentRuntime func() videodomain.AgentRuntime
|
|
}
|
|
|
|
func NewRuntime(opts RuntimeOptions) *Runtime {
|
|
googleStateTTL := opts.GoogleStateTTL
|
|
if googleStateTTL <= 0 {
|
|
googleStateTTL = 10 * time.Minute
|
|
}
|
|
googleUserInfoURL := strings.TrimSpace(opts.GoogleUserInfoURL)
|
|
if googleUserInfoURL == "" {
|
|
googleUserInfoURL = DefaultGoogleUserInfoURL
|
|
}
|
|
return &Runtime{
|
|
db: opts.DB,
|
|
logger: opts.Logger,
|
|
authenticator: opts.Authenticator,
|
|
tokenProvider: opts.TokenProvider,
|
|
cache: opts.Cache,
|
|
googleOauth: opts.GoogleOauth,
|
|
googleStateTTL: googleStateTTL,
|
|
googleUserInfoURL: googleUserInfoURL,
|
|
frontendBaseURL: strings.TrimSpace(opts.FrontendBaseURL),
|
|
storageProvider: opts.StorageProvider,
|
|
videoService: opts.VideoService,
|
|
agentRuntime: opts.AgentRuntime,
|
|
}
|
|
}
|
|
|
|
func (r *Runtime) DB() *gorm.DB { return r.db }
|
|
func (r *Runtime) Logger() logger.Logger { return r.logger }
|
|
func (r *Runtime) Authenticator() *middleware.Authenticator { return r.authenticator }
|
|
func (r *Runtime) TokenProvider() token.Provider { return r.tokenProvider }
|
|
func (r *Runtime) Cache() cache.Cache { return r.cache }
|
|
func (r *Runtime) GoogleOauth() *oauth2.Config { return r.googleOauth }
|
|
func (r *Runtime) GoogleStateTTL() time.Duration { return r.googleStateTTL }
|
|
func (r *Runtime) GoogleUserInfoURL() string { return r.googleUserInfoURL }
|
|
func (r *Runtime) FrontendBaseURL() string { return r.frontendBaseURL }
|
|
|
|
func (r *Runtime) StorageProvider() storage.Provider {
|
|
if r == nil || r.storageProvider == nil {
|
|
return nil
|
|
}
|
|
return r.storageProvider()
|
|
}
|
|
|
|
func (r *Runtime) VideoService() *videodomain.Service {
|
|
if r == nil || r.videoService == nil {
|
|
return nil
|
|
}
|
|
return r.videoService()
|
|
}
|
|
|
|
func (r *Runtime) AgentRuntime() videodomain.AgentRuntime {
|
|
if r == nil || r.agentRuntime == nil {
|
|
return nil
|
|
}
|
|
return r.agentRuntime()
|
|
}
|
|
|
|
func (r *Runtime) Authenticate(ctx context.Context) (*middleware.AuthResult, error) {
|
|
if r == nil || r.authenticator == nil {
|
|
return nil, status.Error(codes.Unauthenticated, "Unauthorized")
|
|
}
|
|
return r.authenticator.Authenticate(ctx)
|
|
}
|
|
|
|
func (r *Runtime) RequireAdmin(ctx context.Context) (*middleware.AuthResult, error) {
|
|
result, err := r.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result.User == nil || result.User.Role == nil || strings.ToUpper(strings.TrimSpace(*result.User.Role)) != "ADMIN" {
|
|
return nil, status.Error(codes.PermissionDenied, "Admin access required")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *Runtime) IssueSessionCookies(ctx context.Context, user *model.User) error {
|
|
if user == nil {
|
|
return status.Error(codes.Unauthenticated, "Unauthorized")
|
|
}
|
|
if r == nil || r.tokenProvider == nil || r.cache == nil {
|
|
return status.Error(codes.Internal, "Error storing session")
|
|
}
|
|
tokenPair, err := r.tokenProvider.GenerateTokenPair(user.ID, user.Email, SafeRole(user.Role))
|
|
if err != nil {
|
|
if r.logger != nil {
|
|
r.logger.Error("Token generation failed", "error", err)
|
|
}
|
|
return status.Error(codes.Internal, "Error generating tokens")
|
|
}
|
|
if err := r.cache.Set(ctx, "refresh_uuid:"+tokenPair.RefreshUUID, user.ID, time.Until(time.Unix(tokenPair.RtExpires, 0))); err != nil {
|
|
if r.logger != nil {
|
|
r.logger.Error("Session storage failed", "error", err)
|
|
}
|
|
return status.Error(codes.Internal, "Error storing session")
|
|
}
|
|
if err := grpc.SetHeader(ctx, metadata.Pairs(
|
|
"set-cookie", BuildTokenCookie("access_token", tokenPair.AccessToken, int(tokenPair.AtExpires-time.Now().Unix())),
|
|
"set-cookie", BuildTokenCookie("refresh_token", tokenPair.RefreshToken, int(tokenPair.RtExpires-time.Now().Unix())),
|
|
)); err != nil && r.logger != nil {
|
|
r.logger.Error("Failed to set gRPC auth headers", "error", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BuildTokenCookie(name string, value string, maxAge int) string {
|
|
return (&http.Cookie{
|
|
Name: name,
|
|
Value: value,
|
|
Path: "/",
|
|
MaxAge: maxAge,
|
|
HttpOnly: true,
|
|
}).String()
|
|
}
|