diff --git a/apps/api-gql/internal/delivery/gql/dataloader/data-loader.go b/apps/api-gql/internal/delivery/gql/dataloader/data-loader.go index 8b7a1e1e76..126a34f0d9 100644 --- a/apps/api-gql/internal/delivery/gql/dataloader/data-loader.go +++ b/apps/api-gql/internal/delivery/gql/dataloader/data-loader.go @@ -6,6 +6,9 @@ import ( "github.com/gin-gonic/gin" "github.com/google/uuid" + "github.com/vikstrous/dataloadgen" + "go.uber.org/fx" + "github.com/twirapp/twir/apps/api-gql/internal/auth" "github.com/twirapp/twir/apps/api-gql/internal/delivery/gql/gqlmodel" channelsemotesusages "github.com/twirapp/twir/apps/api-gql/internal/services/channels_emotes_usages" @@ -13,8 +16,7 @@ import ( "github.com/twirapp/twir/apps/api-gql/internal/services/commands_responses" twitchservice "github.com/twirapp/twir/apps/api-gql/internal/services/twitch" "github.com/twirapp/twir/libs/cache/twitch" - "github.com/vikstrous/dataloadgen" - "go.uber.org/fx" + plansrepository "github.com/twirapp/twir/libs/repositories/plans" ) type ctxKey string @@ -32,6 +34,7 @@ type Opts struct { CommandsResponsesService *commands_responses.Service TwitchService *twitchservice.Service EmoteStatisticService *channelsemotesusages.Service + PlansRepository plansrepository.Repository } type dataLoader struct { @@ -43,6 +46,7 @@ type dataLoader struct { commandsGroupsByIdLoader *dataloadgen.Loader[uuid.UUID, *gqlmodel.CommandGroup] commandsResponsesByIDLoader *dataloadgen.Loader[uuid.UUID, []gqlmodel.CommandResponse] emoteStatistic *dataloadgen.Loader[EmoteRangeKey, []gqlmodel.EmoteStatisticUsage] + plansByChannelIDLoader *dataloadgen.Loader[GetPlayByChannelId, *gqlmodel.Plan] } type LoaderFactory struct { @@ -90,6 +94,11 @@ func (c *LoaderFactory) Load() *dataLoader { dataloadgen.WithWait(time.Millisecond), ) + loader.plansByChannelIDLoader = dataloadgen.NewLoader( + loader.getPlansByChannelIDs, + dataloadgen.WithWait(time.Millisecond), + ) + return loader } diff --git a/apps/api-gql/internal/delivery/gql/dataloader/plans.go b/apps/api-gql/internal/delivery/gql/dataloader/plans.go new file mode 100644 index 0000000000..f51a31b28f --- /dev/null +++ b/apps/api-gql/internal/delivery/gql/dataloader/plans.go @@ -0,0 +1,77 @@ +package dataloader + +import ( + "context" + + "github.com/twirapp/twir/apps/api-gql/internal/delivery/gql/gqlmodel" + "github.com/twirapp/twir/apps/api-gql/internal/delivery/gql/mappers" +) + +func (c *dataLoader) getPlansByChannelIDs(ctx context.Context, inputs []GetPlayByChannelId) ( + []*gqlmodel.Plan, + []error, +) { + uniquePlanIDsMap := make(map[string]struct{}) + for _, i := range inputs { + if i.PlanID == nil || *i.PlanID == "" { + continue + } + + if _, ok := uniquePlanIDsMap[*i.PlanID]; ok { + continue + } + + uniquePlanIDsMap[*i.PlanID] = struct{}{} + } + + uniquePlanIDsSlice := make([]string, 0, len(uniquePlanIDsMap)) + for planID := range uniquePlanIDsMap { + uniquePlanIDsSlice = append(uniquePlanIDsSlice, planID) + } + + plans, err := c.deps.PlansRepository.GetManyByIDs(ctx, uniquePlanIDsSlice) + if err != nil { + return nil, []error{err} + } + + plansByID := make(map[string]*gqlmodel.Plan) + for _, p := range plans { + if !p.IsNil() { + plansByID[p.ID] = mappers.PlanToGql(p) + } + } + + result := make([]*gqlmodel.Plan, len(inputs)) + for i, ch := range inputs { + if ch.PlanID == nil || *ch.PlanID == "" { + continue + } + + plan, ok := plansByID[*ch.PlanID] + if !ok { + continue + } + + result[i] = plan + } + + return result, nil +} + +type GetPlayByChannelId struct { + ChannelID string + PlanID *string +} + +func GetPlanByChannelID(ctx context.Context, input GetPlayByChannelId) (*gqlmodel.Plan, error) { + loaders := GetLoaderForRequest(ctx) + return loaders.plansByChannelIDLoader.Load(ctx, input) +} + +func GetPlansByChannelIDs(ctx context.Context, inputs []GetPlayByChannelId) ( + []*gqlmodel.Plan, + error, +) { + loaders := GetLoaderForRequest(ctx) + return loaders.plansByChannelIDLoader.LoadAll(ctx, inputs) +} diff --git a/apps/api-gql/internal/delivery/gql/mappers/plan.go b/apps/api-gql/internal/delivery/gql/mappers/plan.go new file mode 100644 index 0000000000..00d36ced8b --- /dev/null +++ b/apps/api-gql/internal/delivery/gql/mappers/plan.go @@ -0,0 +1,25 @@ +package mappers + +import ( + "github.com/twirapp/twir/apps/api-gql/internal/delivery/gql/gqlmodel" + "github.com/twirapp/twir/libs/entities/plan" +) + +func PlanToGql(e plan.Plan) *gqlmodel.Plan { + return &gqlmodel.Plan{ + ID: e.ID, + Name: e.Name, + MaxCommands: e.MaxCommands, + MaxTimers: e.MaxTimers, + MaxVariables: e.MaxVariables, + MaxAlerts: e.MaxAlerts, + MaxEvents: e.MaxEvents, + MaxChatAlertsMessages: e.MaxChatAlertsMessages, + MaxCustomOverlays: e.MaxCustomOverlays, + MaxEightballAnswers: e.MaxEightballAnswers, + MaxCommandsResponses: e.MaxCommandsResponses, + MaxModerationRules: e.MaxModerationRules, + MaxKeywords: e.MaxKeywords, + MaxGreetings: e.MaxGreetings, + } +} diff --git a/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.go b/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.go index 006a270d84..3983913fd0 100644 --- a/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.go +++ b/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.go @@ -44,31 +44,11 @@ func (r *dashboardResolver) TwitchProfile(ctx context.Context, obj *gqlmodel.Das // Plan is the resolver for the plan field. func (r *dashboardResolver) Plan(ctx context.Context, obj *gqlmodel.Dashboard) (*gqlmodel.Plan, error) { - plan, err := r.deps.PlansRepository.GetByChannelID(ctx, obj.ID) - if err != nil { - return nil, fmt.Errorf("failed to get plan: %w", err) - } - - if plan.IsNil() { - return nil, fmt.Errorf("plan not found for channel") - } - - return &gqlmodel.Plan{ - ID: plan.ID, - Name: plan.Name, - MaxCommands: plan.MaxCommands, - MaxTimers: plan.MaxTimers, - MaxVariables: plan.MaxVariables, - MaxAlerts: plan.MaxAlerts, - MaxEvents: plan.MaxEvents, - MaxChatAlertsMessages: plan.MaxChatAlertsMessages, - MaxCustomOverlays: plan.MaxCustomOverlays, - MaxEightballAnswers: plan.MaxEightballAnswers, - MaxCommandsResponses: plan.MaxCommandsResponses, - MaxModerationRules: plan.MaxModerationRules, - MaxKeywords: plan.MaxKeywords, - MaxGreetings: plan.MaxGreetings, - }, nil + // Use dataloader to get plan by channel ID + return data_loader.GetPlanByChannelID(ctx, data_loader.GetPlayByChannelId{ + ChannelID: obj.ID, + PlanID: obj.PlanID, + }) } // AuthenticatedUserSelectDashboard is the resolver for the authenticatedUserSelectDashboard field. @@ -243,6 +223,7 @@ func (r *queryResolver) AuthenticatedUser(ctx context.Context) (*gqlmodel.Authen authedUser.IsEnabled = &user.Channel.IsEnabled authedUser.IsBotModerator = &user.Channel.IsBotMod authedUser.BotID = &user.Channel.BotID + authedUser.PlanID = user.Channel.PlanID } return authedUser, nil diff --git a/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.service.go b/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.service.go index fa913040fd..8096bc31ad 100644 --- a/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.service.go +++ b/apps/api-gql/internal/delivery/gql/resolvers/user.resolver.service.go @@ -4,8 +4,8 @@ import ( "context" "github.com/samber/lo" - model "github.com/twirapp/twir/libs/gomodels" "github.com/twirapp/twir/apps/api-gql/internal/delivery/gql/gqlmodel" + model "github.com/twirapp/twir/libs/gomodels" ) func (r *authenticatedUserResolver) getAvailableDashboards( @@ -25,6 +25,7 @@ func (r *authenticatedUserResolver) getAvailableDashboards( Flags: []gqlmodel.ChannelRolePermissionEnum{ gqlmodel.ChannelRolePermissionEnumCanAccessDashboard, }, + PlanID: channel.PlanID, } if channel.User != nil { @@ -38,6 +39,7 @@ func (r *authenticatedUserResolver) getAvailableDashboards( ID: obj.ID, Flags: []gqlmodel.ChannelRolePermissionEnum{gqlmodel.ChannelRolePermissionEnumCanAccessDashboard}, APIKey: obj.APIKey, + PlanID: obj.PlanID, } var roles []model.ChannelRoleUser @@ -56,7 +58,7 @@ func (r *authenticatedUserResolver) getAvailableDashboards( } for _, role := range roles { - if role.Role == nil || role.Role.Channel == nil || len(role.Role.Permissions) == 0 { + if role.Role == nil || role.Role.Channel == nil || role.Role.Channel.User == nil || len(role.Role.Permissions) == 0 { continue } @@ -66,8 +68,10 @@ func (r *authenticatedUserResolver) getAvailableDashboards( } dashboard := gqlmodel.Dashboard{ - ID: role.Role.Channel.ID, - Flags: append(dashboardsEntities[role.Role.Channel.ID].Flags, flags...), + ID: role.Role.Channel.ID, + Flags: append(dashboardsEntities[role.Role.Channel.ID].Flags, flags...), + PlanID: role.Role.Channel.PlanID, + APIKey: role.Role.Channel.User.ApiKey, } if role.Role.Channel.User != nil { diff --git a/apps/api-gql/internal/delivery/gql/schema/user.graphql b/apps/api-gql/internal/delivery/gql/schema/user.graphql index f16a65da8a..2d5ce9e783 100644 --- a/apps/api-gql/internal/delivery/gql/schema/user.graphql +++ b/apps/api-gql/internal/delivery/gql/schema/user.graphql @@ -25,6 +25,7 @@ type AuthenticatedUser implements TwirUser { twitchProfile: TwirUserTwitchInfo! @goField(forceResolver: true) selectedDashboardId: String! availableDashboards: [Dashboard!]! @goField(forceResolver: true) + planId: String } type Dashboard { @@ -32,7 +33,8 @@ type Dashboard { flags: [ChannelRolePermissionEnum!]! twitchProfile: TwirUserTwitchInfo! @goField(forceResolver: true) apiKey: String! - plan: Plan! @goField(forceResolver: true) + planId: String + plan: Plan @goField(forceResolver: true) } type Plan { diff --git a/bun.lock b/bun.lock index 26b9823ad2..5e0eda10c5 100644 --- a/bun.lock +++ b/bun.lock @@ -92,6 +92,7 @@ "tinycolor2": "1.6.0", "vaul-vue": "0.4.1", "vee-validate": "4.15.1", + "vite-bundle-analyzer": "1.3.2", "vue": "catalog:", "vue-draggable-plus": "0.5.6", "vue-i18n": "11.2.2", @@ -358,7 +359,7 @@ "graphql": "16.12.0", "graphql-ws": "6.0.6", "typescript": "5.9.3", - "vite": "8.0.0-beta.0", + "vite": "8.0.0-beta.5", "vue": "3.5.25", "vue-router": "4.3.0", "vue-tsc": "3.1.8", @@ -966,7 +967,7 @@ "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.99.0", "", { "os": "win32", "cpu": "x64" }, "sha512-sJN1Q8h7ggFOyDn0zsHaXbP/MklAVUvhrbq0LA46Qum686P3SZQHjbATqJn9yaVEvaSKXCshgl0vQ1gWkGgpcQ=="], - "@oxc-project/runtime": ["@oxc-project/runtime@0.101.0", "", {}, "sha512-t3qpfVZIqSiLQ5Kqt/MC4Ge/WCOGrrcagAdzTcDaggupjiGxUx4nJF2v6wUCXWSzWHn5Ns7XLv13fCJEwCOERQ=="], + "@oxc-project/runtime": ["@oxc-project/runtime@0.103.0", "", {}, "sha512-sQKZo5lLS1/yzbsVlZ+zaQorOkLe3OkQjyyMN29tMvCax5e5Sa9uUYKChDDMR4D41n6ApEazMN2UcIwFdHgS7g=="], "@oxc-project/types": ["@oxc-project/types@0.99.0", "", {}, "sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw=="], @@ -1232,33 +1233,33 @@ "@resvg/resvg-wasm": ["@resvg/resvg-wasm@2.6.2", "", {}, "sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.53", "", { "os": "android", "cpu": "arm64" }, "sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-beta.57", "", { "os": "android", "cpu": "arm64" }, "sha512-GoOVDy8bjw9z1K30Oo803nSzXJS/vWhFijFsW3kzvZCO8IZwFnNa6pGctmbbJstKl3Fv6UBwyjJQN6msejW0IQ=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.53", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.57", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9c4FOhRGpl+PX7zBK5p17c5efpF9aSpTPgyigv57hXf5NjQUaJOOiejPLAtFiKNBIfm5Uu6yFkvLKzOafNvlTw=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.53", "", { "os": "darwin", "cpu": "x64" }, "sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.57", "", { "os": "darwin", "cpu": "x64" }, "sha512-6RsB8Qy4LnGqNGJJC/8uWeLWGOvbRL/KG5aJ8XXpSEupg/KQtlBEiFaYU/Ma5Usj1s+bt3ItkqZYAI50kSplBA=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.53", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.57", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uA9kG7+MYkHTbqwv67Tx+5GV5YcKd33HCJIi0311iYBd25yuwyIqvJfBdt1VVB8tdOlyTb9cPAgfCki8nhwTQg=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53", "", { "os": "linux", "cpu": "arm" }, "sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.57", "", { "os": "linux", "cpu": "arm" }, "sha512-3KkS0cHsllT2T+Te+VZMKHNw6FPQihYsQh+8J4jkzwgvAQpbsbXmrqhkw3YU/QGRrD8qgcOvBr6z5y6Jid+rmw=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53", "", { "os": "linux", "cpu": "arm64" }, "sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.57", "", { "os": "linux", "cpu": "arm64" }, "sha512-A3/wu1RgsHhqP3rVH2+sM81bpk+Qd2XaHTl8LtX5/1LNR7QVBFBCpAoiXwjTdGnI5cMdBVi7Z1pi52euW760Fw=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.53", "", { "os": "linux", "cpu": "arm64" }, "sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.57", "", { "os": "linux", "cpu": "arm64" }, "sha512-d0kIVezTQtazpyWjiJIn5to8JlwfKITDqwsFv0Xc6s31N16CD2PC/Pl2OtKgS7n8WLOJbfqgIp5ixYzTAxCqMg=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.53", "", { "os": "linux", "cpu": "x64" }, "sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.57", "", { "os": "linux", "cpu": "x64" }, "sha512-E199LPijo98yrLjPCmETx8EF43sZf9t3guSrLee/ej1rCCc3zDVTR4xFfN9BRAapGVl7/8hYqbbiQPTkv73kUg=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.53", "", { "os": "linux", "cpu": "x64" }, "sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.57", "", { "os": "linux", "cpu": "x64" }, "sha512-++EQDpk/UJ33kY/BNsh7A7/P1sr/jbMuQ8cE554ZIy+tCUWCivo9zfyjDUoiMdnxqX6HLJEqqGnbGQOvzm2OMQ=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.53", "", { "os": "none", "cpu": "arm64" }, "sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-beta.57", "", { "os": "none", "cpu": "arm64" }, "sha512-voDEBcNqxbUv/GeXKFtxXVWA+H45P/8Dec4Ii/SbyJyGvCqV1j+nNHfnFUIiRQ2Q40DwPe/djvgYBs9PpETiMA=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.53", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.0" }, "cpu": "none" }, "sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.57", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.0" }, "cpu": "none" }, "sha512-bRhcF7NLlCnpkzLVlVhrDEd0KH22VbTPkPTbMjlYvqhSmarxNIq5vtlQS8qmV7LkPKHrNLWyJW/V/sOyFba26Q=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53", "", { "os": "win32", "cpu": "arm64" }, "sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.57", "", { "os": "win32", "cpu": "arm64" }, "sha512-rnDVGRks2FQ2hgJ2g15pHtfxqkGFGjJQUDWzYznEkE8Ra2+Vag9OffxdbJMZqBWXHVM0iS4dv8qSiEn7bO+n1Q=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.53", "", { "os": "win32", "cpu": "x64" }, "sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.57", "", { "os": "win32", "cpu": "x64" }, "sha512-OqIUyNid1M4xTj6VRXp/Lht/qIP8fo25QyAZlCP+p6D2ATCEhyW4ZIFLnC9zAGN/HMbXoCzvwfa8Jjg/8J4YEg=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.57", "", {}, "sha512-aQNelgx14tGA+n2tNSa9x6/jeoCL9fkDeCei7nOKnHx0fEFRRMu5ReiITo+zZD5TzWDGGRjbSYCs93IfRIyTuQ=="], "@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.1", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ=="], @@ -3044,7 +3045,7 @@ "rimraf": ["rimraf@5.0.5", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A=="], - "rolldown": ["rolldown@1.0.0-beta.53", "", { "dependencies": { "@oxc-project/types": "=0.101.0", "@rolldown/pluginutils": "1.0.0-beta.53" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.53", "@rolldown/binding-darwin-arm64": "1.0.0-beta.53", "@rolldown/binding-darwin-x64": "1.0.0-beta.53", "@rolldown/binding-freebsd-x64": "1.0.0-beta.53", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.53", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.53", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.53", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.53", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.53", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.53", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.53", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.53", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.53" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw=="], + "rolldown": ["rolldown@1.0.0-beta.57", "", { "dependencies": { "@oxc-project/types": "=0.103.0", "@rolldown/pluginutils": "1.0.0-beta.57" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-beta.57", "@rolldown/binding-darwin-arm64": "1.0.0-beta.57", "@rolldown/binding-darwin-x64": "1.0.0-beta.57", "@rolldown/binding-freebsd-x64": "1.0.0-beta.57", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.57", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.57", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.57", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.57", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.57", "@rolldown/binding-openharmony-arm64": "1.0.0-beta.57", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.57", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.57", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.57" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-lMMxcNN71GMsSko8RyeTaFoATHkCh4IWU7pYF73ziMYjhHZWfVesC6GQ+iaJCvZmVjvgSks9Ks1aaqEkBd8udg=="], "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], @@ -3394,7 +3395,9 @@ "vee-validate": ["vee-validate@4.15.1", "", { "dependencies": { "@vue/devtools-api": "^7.5.2", "type-fest": "^4.8.3" }, "peerDependencies": { "vue": "^3.4.26" } }, "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg=="], - "vite": ["vite@8.0.0-beta.0", "", { "dependencies": { "@oxc-project/runtime": "0.101.0", "fdir": "^6.5.0", "lightningcss": "^1.30.2", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rolldown": "1.0.0-beta.53", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "esbuild": "^0.25.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-bXHWmtg5hUxn/MB5zJ8qhBLphnsNmO1EYOFmBO/fVCBJekTdWDuqJ/GmUMLgrC0QUCCrxhw3JLgteWdiyqaVSQ=="], + "vite": ["vite@8.0.0-beta.5", "", { "dependencies": { "@oxc-project/runtime": "0.103.0", "fdir": "^6.5.0", "lightningcss": "^1.30.2", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rolldown": "1.0.0-beta.57", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "esbuild": "^0.25.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-wgvJ+rdGKggZ1m0KnSYF4mEdEEaAAUWKiHe9IDl8oagjUkyrD2CdgSoxiJdpLNNzCKIZdHsAi2xMRRwrCEd4AQ=="], + + "vite-bundle-analyzer": ["vite-bundle-analyzer@1.3.2", "", { "bin": { "analyze": "dist/bin.js" } }, "sha512-Od4ILUKRvBV3LuO/E+S+c1XULlxdkRZPSf6Vzzu+UAXG0D3hZYUu9imZIkSj/PU4e1FB14yB+av8g3KiljH8zQ=="], "vite-bundle-visualizer": ["vite-bundle-visualizer@1.1.0", "", { "dependencies": { "cac": "^6.7.14", "import-from-esm": "^1.3.3", "rollup-plugin-visualizer": "^5.11.0", "tmp": "^0.2.1" }, "bin": { "vite-bundle-visualizer": "bin.js" } }, "sha512-cmi5OuS7Eta5keTJmCTEbBBA7gOsUQ4K44W5dbsP+n/X0GIilIIFbJeXF120MQpTxdiZ/GIx4A9zkPEcKpPAog=="], @@ -3790,6 +3793,8 @@ "@vitejs/plugin-vue/vite": ["vite@7.2.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="], + "@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="], + "@vitejs/plugin-vue-jsx/vite": ["vite@7.2.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="], "@volar/language-server/@volar/typescript": ["@volar/typescript@2.4.23", "", { "dependencies": { "@volar/language-core": "2.4.23", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag=="], @@ -3986,7 +3991,7 @@ "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - "rolldown/@oxc-project/types": ["@oxc-project/types@0.101.0", "", {}, "sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ=="], + "rolldown/@oxc-project/types": ["@oxc-project/types@0.103.0", "", {}, "sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg=="], "shadcn-nuxt/@nuxt/kit": ["@nuxt/kit@3.20.2", "", { "dependencies": { "c12": "^3.3.2", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "errx": "^0.1.0", "exsolve": "^1.0.8", "ignore": "^7.0.5", "jiti": "^2.6.1", "klona": "^2.0.6", "knitwork": "^1.3.0", "mlly": "^1.8.0", "ohash": "^2.0.11", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "rc9": "^2.1.2", "scule": "^1.3.0", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ufo": "^1.6.1", "unctx": "^2.4.1", "untyped": "^2.0.0" } }, "sha512-laqfmMcWWNV1FsVmm1+RQUoGY8NIJvCRl0z0K8ikqPukoEry0LXMqlQ+xaf8xJRvoH2/78OhZmsEEsUBTXipcw=="], @@ -4202,6 +4207,8 @@ "@nuxt/fonts/@nuxt/devtools-kit/vite": ["vite@7.2.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ=="], + "@nuxt/vite-builder/@vitejs/plugin-vue/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="], + "@nuxt/vite-builder/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], "@nuxt/vite-builder/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], diff --git a/frontend/dashboard/codegen.ts b/frontend/dashboard/codegen.ts index 2f358a1442..dfe005067b 100644 --- a/frontend/dashboard/codegen.ts +++ b/frontend/dashboard/codegen.ts @@ -1,8 +1,8 @@ +import type { CodegenConfig } from '@graphql-codegen/cli' + import { join, resolve } from 'node:path' import * as process from 'node:process' -import type { CodegenConfig } from '@graphql-codegen/cli' - const schemaDir = resolve( join( process.cwd(), @@ -34,6 +34,15 @@ const config: CodegenConfig = { config: { useTypeImports: true, }, + presetConfig: { + // persistedDocuments: { + // hashAlgorithm: (operation: string) => { + // const h = new Bun.CryptoHasher('sha256') + // h.update(operation) + // return h.digest('hex') + // }, + // }, + }, // presetConfig: { // onExecutableDocumentNode: generatePersistHash, // }, diff --git a/frontend/dashboard/package.json b/frontend/dashboard/package.json index f26c7f5c32..a888e80a46 100644 --- a/frontend/dashboard/package.json +++ b/frontend/dashboard/package.json @@ -65,6 +65,7 @@ "tinycolor2": "1.6.0", "vaul-vue": "0.4.1", "vee-validate": "4.15.1", + "vite-bundle-analyzer": "1.3.2", "vue": "catalog:", "vue-draggable-plus": "0.5.6", "vue-i18n": "11.2.2", diff --git a/frontend/dashboard/src/api/index.ts b/frontend/dashboard/src/api/index.ts deleted file mode 100644 index e7bd564a6d..0000000000 --- a/frontend/dashboard/src/api/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './auth.js' -export * from './integrations/index.js' -export * from './crud.js' -export * from './twitch.js' -export * from './overlays/index.js' -export * from './overlays-be-right-back.js' -export * from './dashboard.js' -export * from './files.js' -export * from './moderation.js' diff --git a/frontend/dashboard/src/api/overlays/kappagen.ts b/frontend/dashboard/src/api/overlays/kappagen.ts index cdc2a26eea..f768b1a490 100644 --- a/frontend/dashboard/src/api/overlays/kappagen.ts +++ b/frontend/dashboard/src/api/overlays/kappagen.ts @@ -4,7 +4,7 @@ import { computed } from 'vue' import type { KappagenOverlaySettingsFragment } from '@/gql/graphql.ts' -import { useProfile } from '@/api' +import { useProfile } from '@/api/auth' import { graphql } from '@/gql' graphql(` @@ -137,12 +137,12 @@ export const useKappagenApi = createGlobalState(() => { }) const { executeMutation: updateKappagen, fetching: isUpdating } = useMutation( - KappagenOverlayUpdateMutation, + KappagenOverlayUpdateMutation ) const selectedDashboard = computed(() => { return profile.value?.availableDashboards.find( - (d) => d.id === profile.value?.selectedDashboardId, + (d) => d.id === profile.value?.selectedDashboardId ) }) @@ -161,12 +161,14 @@ export const useKappagenApi = createGlobalState(() => { }) const availableAnimations = computed(() => { - const data = subscriptionData.value?.overlaysKappagen as KappagenOverlaySettingsFragment | undefined + const data = subscriptionData.value?.overlaysKappagen as + | KappagenOverlaySettingsFragment + | undefined return ( - data?.animations?.map((a) => a.style) - || kappagenData.value?.overlaysKappagenAvailableAnimations - || [] + data?.animations?.map((a) => a.style) || + kappagenData.value?.overlaysKappagenAvailableAnimations || + [] ) }) diff --git a/frontend/dashboard/src/components/dashboard/audit-logs.vue b/frontend/dashboard/src/components/dashboard/audit-logs.vue index d14b281317..170677eb03 100644 --- a/frontend/dashboard/src/components/dashboard/audit-logs.vue +++ b/frontend/dashboard/src/components/dashboard/audit-logs.vue @@ -3,7 +3,7 @@ import { ExternalLinkIcon } from 'lucide-vue-next' import { UseTimeAgo } from '@vueuse/components' import { useI18n } from 'vue-i18n' -import { useProfile } from '@/api' +import { useProfile } from '@/api/auth' import { mapOperationTypeToTranslate, mapSystemToTranslate, useAuditLogs } from '@/api/audit-logs' import Card from '@/components/dashboard/card.vue' import { Badge, type BadgeVariants } from '@/components/ui/badge' diff --git a/frontend/dashboard/src/components/dashboard/chat.vue b/frontend/dashboard/src/components/dashboard/chat.vue index dfc2e97ee3..af4d223137 100644 --- a/frontend/dashboard/src/components/dashboard/chat.vue +++ b/frontend/dashboard/src/components/dashboard/chat.vue @@ -4,7 +4,7 @@ import { computed, ref } from 'vue' import Card from './card.vue' -import { useProfile } from '@/api/index.js' +import { useProfile } from '@/api/auth' import { useTheme } from '@/composables/use-theme.js' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' diff --git a/frontend/dashboard/src/components/dashboard/events.vue b/frontend/dashboard/src/components/dashboard/events.vue index e8b2a6223c..7b26420202 100644 --- a/frontend/dashboard/src/components/dashboard/events.vue +++ b/frontend/dashboard/src/components/dashboard/events.vue @@ -22,7 +22,7 @@ import ReSubscribe from './events/resubscribe.vue' import SubGift from './events/subgift.vue' import Subscribe from './events/subscribe.vue' -import { useProfile } from '@/api/index.js' +import { useProfile } from '@/api/auth' import UnbanRequestCreated from '@/components/dashboard/events/unban-request-created.vue' import UnbanRequestResolved from '@/components/dashboard/events/unban-request-resolved.vue' import { useEvents } from '@/features/dashboard/widgets/composables/events' diff --git a/frontend/dashboard/src/components/dashboard/stream.vue b/frontend/dashboard/src/components/dashboard/stream.vue index 6276325c69..adf4f6bfbf 100644 --- a/frontend/dashboard/src/components/dashboard/stream.vue +++ b/frontend/dashboard/src/components/dashboard/stream.vue @@ -3,7 +3,7 @@ import { computed } from 'vue' import Card from './card.vue' -import { useProfile } from '@/api/index.js' +import { useProfile } from '@/api/auth' const { data: profile } = useProfile() diff --git a/frontend/dashboard/src/components/files/files.vue b/frontend/dashboard/src/components/files/files.vue index 0e6d99508a..859330d28f 100644 --- a/frontend/dashboard/src/components/files/files.vue +++ b/frontend/dashboard/src/components/files/files.vue @@ -5,7 +5,7 @@ import { toast } from 'vue-sonner' import { computed, onMounted, ref } from 'vue' import { useI18n } from 'vue-i18n' -import { useFilesApi } from '@/api/index.js' +import { useFilesApi } from '@/api/files' import { Alert, AlertDescription } from '@/components/ui/alert' import { Button, FileButton } from '@/components/ui/button' import { Progress } from '@/components/ui/progress' diff --git a/frontend/dashboard/src/components/games/8ball.vue b/frontend/dashboard/src/components/games/8ball.vue index bab481776a..81a02bc037 100644 --- a/frontend/dashboard/src/components/games/8ball.vue +++ b/frontend/dashboard/src/components/games/8ball.vue @@ -3,7 +3,7 @@ import { MessageCircle, Trash } from 'lucide-vue-next' import { computed, ref, toRaw, watch } from 'vue' import { useI18n } from 'vue-i18n' -import { useProfile } from '@/api' +import { useProfile } from '@/api/auth' import { useGamesApi } from '@/api/games/games.js' import Card from '@/components/games/card.vue' import { Button } from '@/components/ui/button' @@ -21,7 +21,7 @@ const maxAnswers = computed(() => { const selectedDashboard = profile.value?.availableDashboards.find( (d) => d.id === profile.value?.selectedDashboardId ) - return selectedDashboard?.plan.maxEightballAnswers ?? 25 + return selectedDashboard?.plan?.maxEightballAnswers ?? 25 }) const gamesManager = useGamesApi() diff --git a/frontend/dashboard/src/components/games/card.vue b/frontend/dashboard/src/components/games/card.vue index 9000418183..261fee4fd0 100644 --- a/frontend/dashboard/src/components/games/card.vue +++ b/frontend/dashboard/src/components/games/card.vue @@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n' import type { FunctionalComponent } from 'vue' -import { useUserAccessFlagChecker } from '@/api' +import { useUserAccessFlagChecker } from '@/api/auth' import Card from '@/components/card/card.vue' import { Button } from '@/components/ui/button' import { ChannelRolePermissionEnum } from '@/gql/graphql' diff --git a/frontend/dashboard/src/components/integrations/donatepay.vue b/frontend/dashboard/src/components/integrations/donatepay.vue index 91298dc8dd..176b2767f0 100644 --- a/frontend/dashboard/src/components/integrations/donatepay.vue +++ b/frontend/dashboard/src/components/integrations/donatepay.vue @@ -2,7 +2,7 @@ import { ExternalLink, Eye, EyeOff } from 'lucide-vue-next' import { ref, watch } from 'vue' -import { useDonatepayIntegration } from '@/api/index.js' +import { useDonatepayIntegration } from '@/api/integrations/donatepay' import { useIntegrationsPageData } from '@/api/integrations/integrations-page.ts' import DonatePaySVG from '@/assets/integrations/donatepay.svg?use' import DonateDescription from '@/components/integrations/helpers/donateDescription.vue' diff --git a/frontend/dashboard/src/components/integrations/streamlabs.vue b/frontend/dashboard/src/components/integrations/streamlabs.vue index bd694f9fce..7b18c099df 100644 --- a/frontend/dashboard/src/components/integrations/streamlabs.vue +++ b/frontend/dashboard/src/components/integrations/streamlabs.vue @@ -1,6 +1,6 @@ diff --git a/frontend/dashboard/src/features/variables/ui/variables-actions.vue b/frontend/dashboard/src/features/variables/ui/variables-actions.vue index e2bca8aae2..7e7b35d400 100644 --- a/frontend/dashboard/src/features/variables/ui/variables-actions.vue +++ b/frontend/dashboard/src/features/variables/ui/variables-actions.vue @@ -3,7 +3,7 @@ import { PencilIcon, TrashIcon } from 'lucide-vue-next' import { ref } from 'vue' import { useI18n } from 'vue-i18n' -import { useUserAccessFlagChecker } from '@/api' +import { useUserAccessFlagChecker } from '@/api/auth' import { type CustomVariable, useVariablesApi } from '@/api/variables' import ActionConfirm from '@/components/ui/action-confirm.vue' import { Button } from '@/components/ui/button' diff --git a/frontend/dashboard/src/features/variables/ui/variables-create-button.vue b/frontend/dashboard/src/features/variables/ui/variables-create-button.vue index f7fcc96bc6..e3a76b9e19 100644 --- a/frontend/dashboard/src/features/variables/ui/variables-create-button.vue +++ b/frontend/dashboard/src/features/variables/ui/variables-create-button.vue @@ -3,7 +3,7 @@ import { PlusIcon } from 'lucide-vue-next' import { computed } from 'vue' import { useI18n } from 'vue-i18n' -import { useProfile, useUserAccessFlagChecker } from '@/api' +import { useProfile, useUserAccessFlagChecker } from '@/api/auth' import { useVariablesApi } from '@/api/variables' import { Button } from '@/components/ui/button' import { ChannelRolePermissionEnum } from '@/gql/graphql' @@ -18,7 +18,7 @@ const maxVariables = computed(() => { const selectedDashboard = profile.value?.availableDashboards.find( (d) => d.id === profile.value?.selectedDashboardId ) - return selectedDashboard?.plan.maxVariables ?? 50 + return selectedDashboard?.plan?.maxVariables ?? 50 }) const isCreateDisabled = computed(() => { diff --git a/frontend/dashboard/src/global.d.ts b/frontend/dashboard/src/global.d.ts index 35120cd8e7..87d5b73103 100644 --- a/frontend/dashboard/src/global.d.ts +++ b/frontend/dashboard/src/global.d.ts @@ -2,7 +2,7 @@ // It can also be added to a `.d.ts` file. Make sure it's included in // project's tsconfig.json "files" import 'vue-router' -import type { PermissionsType } from '@/api' +import type { PermissionsType } from '@/api/dashboard' declare module 'vue-router' { interface RouteMeta { @@ -15,49 +15,49 @@ declare module 'vue-router' { } interface Rybbit { - /** - * Tracks a page view - */ - pageview: () => void; - - /** - * Tracks a custom event - * @param name Name of the event - * @param properties Optional properties for the event - */ - event: (name: string, properties?: Record) => void; - - /** - * Sets a custom user ID for tracking logged-in users - * @param userId The user ID to set (will be stored in localStorage) - */ - identify: (userId: string) => void; - - /** - * Clears the stored user ID - */ - clearUserId: () => void; - - /** - * Gets the currently set user ID - * @returns The current user ID or null if not set - */ - getUserId: () => string | null; - - /** - * Manually tracks outbound link clicks - * @param url The URL of the outbound link - * @param text Optional text content of the link - * @param target Optional target attribute of the link - */ - trackOutbound: (url: string, text?: string, target?: string) => void; + /** + * Tracks a page view + */ + pageview: () => void + + /** + * Tracks a custom event + * @param name Name of the event + * @param properties Optional properties for the event + */ + event: (name: string, properties?: Record) => void + + /** + * Sets a custom user ID for tracking logged-in users + * @param userId The user ID to set (will be stored in localStorage) + */ + identify: (userId: string) => void + + /** + * Clears the stored user ID + */ + clearUserId: () => void + + /** + * Gets the currently set user ID + * @returns The current user ID or null if not set + */ + getUserId: () => string | null + + /** + * Manually tracks outbound link clicks + * @param url The URL of the outbound link + * @param text Optional text content of the link + * @param target Optional target attribute of the link + */ + trackOutbound: (url: string, text?: string, target?: string) => void } declare global { - interface Window { - rybbit: Rybbit; - } + interface Window { + rybbit: Rybbit + } } // To ensure it is treated as a module, add at least one `export` statement -export {} \ No newline at end of file +export {} diff --git a/frontend/dashboard/src/layout/header/header-bot-status.vue b/frontend/dashboard/src/layout/header/header-bot-status.vue index 7f53413254..85ba936838 100644 --- a/frontend/dashboard/src/layout/header/header-bot-status.vue +++ b/frontend/dashboard/src/layout/header/header-bot-status.vue @@ -2,7 +2,7 @@ import { ref, watch } from 'vue' import { ChevronsUpDown } from 'lucide-vue-next' -import { useBotJoinPart, useBotStatus } from '@/api' +import { useBotJoinPart, useBotStatus } from '@/api/dashboard' import { BotJoinLeaveAction } from '@/gql/graphql.ts' import { Button } from '@/components/ui/button' import { diff --git a/frontend/dashboard/src/layout/header/header-profile.vue b/frontend/dashboard/src/layout/header/header-profile.vue index 6c18ffd7c7..a61ffc4f22 100644 --- a/frontend/dashboard/src/layout/header/header-profile.vue +++ b/frontend/dashboard/src/layout/header/header-profile.vue @@ -12,7 +12,7 @@ import { import { computed } from 'vue' import { useI18n } from 'vue-i18n' -import { useLogout, useProfile } from '@/api' +import { useLogout, useProfile } from '@/api/auth' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { DropdownMenu, diff --git a/frontend/dashboard/src/layout/header/header.vue b/frontend/dashboard/src/layout/header/header.vue index c356588bc9..527a4effb3 100644 --- a/frontend/dashboard/src/layout/header/header.vue +++ b/frontend/dashboard/src/layout/header/header.vue @@ -7,7 +7,7 @@ import { useI18n } from 'vue-i18n' import StreamInfoEditor from '../stream-info-editor.vue' -import { useRealtimeDashboardStats } from '@/api' +import { useRealtimeDashboardStats } from '@/api/dashboard' import { padTo2Digits } from '@/helpers/convertMillisToTime' import HeaderProfile from '@/layout/header/header-profile.vue' import HeaderBotStatus from '@/layout/header/header-bot-status.vue' diff --git a/frontend/dashboard/src/layout/sidebar/sidebar-dashboard-selector.vue b/frontend/dashboard/src/layout/sidebar/sidebar-dashboard-selector.vue index 6b2518b9e0..445ecb3d85 100644 --- a/frontend/dashboard/src/layout/sidebar/sidebar-dashboard-selector.vue +++ b/frontend/dashboard/src/layout/sidebar/sidebar-dashboard-selector.vue @@ -7,7 +7,7 @@ import { useFilter } from 'reka-ui' import type { PopoverContentProps } from 'reka-ui' -import { useDashboard, useProfile } from '@/api' +import { useDashboard, useProfile } from '@/api/auth' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { diff --git a/frontend/dashboard/src/layout/sidebar/sidebar-navigation.vue b/frontend/dashboard/src/layout/sidebar/sidebar-navigation.vue index 3d3f2b9157..b55df50a30 100644 --- a/frontend/dashboard/src/layout/sidebar/sidebar-navigation.vue +++ b/frontend/dashboard/src/layout/sidebar/sidebar-navigation.vue @@ -33,7 +33,7 @@ import { computed } from 'vue' import { useI18n } from 'vue-i18n' import { useRoute } from 'vue-router' -import { useUserAccessFlagChecker } from '@/api' +import { useUserAccessFlagChecker } from '@/api/auth' import Badge from '@/components/ui/badge/Badge.vue' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { diff --git a/frontend/dashboard/src/layout/stream-info-editor.vue b/frontend/dashboard/src/layout/stream-info-editor.vue index 00aa82ed97..5cb812eb60 100644 --- a/frontend/dashboard/src/layout/stream-info-editor.vue +++ b/frontend/dashboard/src/layout/stream-info-editor.vue @@ -4,9 +4,9 @@ import { useI18n } from 'vue-i18n' import { toast } from 'vue-sonner' import { - twitchSetChannelInformationMutation, useUserAccessFlagChecker, -} from '@/api' +} from '@/api/auth' +import { twitchSetChannelInformationMutation } from '@/api/twitch' import TwitchCategorySelector from '@/components/twitch-category-selector.vue' import { Button } from '@/components/ui/button' import { diff --git a/frontend/dashboard/src/layout/use-public-page-href.ts b/frontend/dashboard/src/layout/use-public-page-href.ts index c0ac09ce32..bd56f032e6 100644 --- a/frontend/dashboard/src/layout/use-public-page-href.ts +++ b/frontend/dashboard/src/layout/use-public-page-href.ts @@ -1,12 +1,13 @@ import { computed } from 'vue' -import { useProfile } from '@/api' +import { useProfile } from '@/api/auth' export function usePublicPageHref() { const { data: profileData } = useProfile() const selectedDashboardTwitchUser = computed(() => { - return profileData.value?.availableDashboards.find((d) => d.id === profileData.value?.selectedDashboardId) - ?.twitchProfile + return profileData.value?.availableDashboards.find( + (d) => d.id === profileData.value?.selectedDashboardId + )?.twitchProfile }) return computed(() => { diff --git a/frontend/dashboard/src/pages/IntegrationsCallback.vue b/frontend/dashboard/src/pages/IntegrationsCallback.vue index 28e6ec959a..cb2e9c7584 100644 --- a/frontend/dashboard/src/pages/IntegrationsCallback.vue +++ b/frontend/dashboard/src/pages/IntegrationsCallback.vue @@ -3,7 +3,7 @@ import { Loader2Icon } from 'lucide-vue-next' import { onMounted } from 'vue' import { useRoute, useRouter } from 'vue-router' -import { useStreamlabsIntegration } from '@/api/index.js' +import { useStreamlabsIntegration } from '@/api/integrations/oauth' import { useFaceitIntegration } from '@/api/integrations/faceit.ts' import { useDiscordIntegration } from '@/features/integrations/composables/discord/use-discord-integration.js' import { diff --git a/frontend/dashboard/src/pages/Overlays.vue b/frontend/dashboard/src/pages/Overlays.vue index 98666c9de3..18741d52fa 100644 --- a/frontend/dashboard/src/pages/Overlays.vue +++ b/frontend/dashboard/src/pages/Overlays.vue @@ -5,11 +5,13 @@ import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' import { - useChannelOverlayDelete, - useChannelOverlaysQuery, useProfile, useUserAccessFlagChecker, -} from '@/api/index.js' +} from '@/api/auth' +import { + useChannelOverlayDelete, + useChannelOverlaysQuery, +} from '@/api/overlays/custom' import Brb from '@/features/overlays/brb/card.vue' import Chat from '@/components/overlays/chat.vue' import Dudes from '@/components/overlays/dudes.vue' @@ -69,7 +71,7 @@ const maxCustomOverlays = computed(() => { const selectedDashboard = profile.value?.availableDashboards.find( (d) => d.id === profile.value?.selectedDashboardId ) - return selectedDashboard?.plan.maxCustomOverlays ?? 10 + return selectedDashboard?.plan?.maxCustomOverlays ?? 10 }) const isCreateDisabled = computed(() => { diff --git a/frontend/dashboard/src/pages/chat-alerts.vue b/frontend/dashboard/src/pages/chat-alerts.vue index 63e57d99f8..d3d2357283 100644 --- a/frontend/dashboard/src/pages/chat-alerts.vue +++ b/frontend/dashboard/src/pages/chat-alerts.vue @@ -2,7 +2,7 @@ import { computed, h } from 'vue' import { useI18n } from 'vue-i18n' -import { useProfile } from '@/api' +import { useProfile } from '@/api/auth' import BanSettings from '@/features/chat-alerts/ui/ban-settings.vue' import ChatAlertsRewardsSettings from '@/features/chat-alerts/ui/chat-alerts-rewards-settings.vue' import Settings from '@/features/chat-alerts/ui/settings.vue' @@ -15,7 +15,7 @@ const maxChatAlertsMessages = computed(() => { const selectedDashboard = profile.value?.availableDashboards.find( (d) => d.id === profile.value?.selectedDashboardId ) - return selectedDashboard?.plan.maxChatAlertsMessages ?? 20 + return selectedDashboard?.plan?.maxChatAlertsMessages ?? 20 }) const pageTabs = computed(() => [ diff --git a/frontend/dashboard/src/pages/community.vue b/frontend/dashboard/src/pages/community.vue index 053ca4f0b7..4a52142275 100644 --- a/frontend/dashboard/src/pages/community.vue +++ b/frontend/dashboard/src/pages/community.vue @@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n' import type { PageLayoutTab } from '@/layout/page-layout.vue' -import { useUserAccessFlagChecker } from '@/api' +import { useUserAccessFlagChecker } from '@/api/auth' import CommunityChatMessages from '@/features/community-chat-messages/community-chat-messages.vue' import CommunityEmotesStatistic from '@/features/community-emotes-statistic/community-emotes-statistic.vue' diff --git a/frontend/dashboard/src/pages/overlays/chat/Chat.vue b/frontend/dashboard/src/pages/overlays/chat/Chat.vue index 01b94d30ce..280a124556 100644 --- a/frontend/dashboard/src/pages/overlays/chat/Chat.vue +++ b/frontend/dashboard/src/pages/overlays/chat/Chat.vue @@ -8,7 +8,8 @@ import { Plus, Trash2 } from 'lucide-vue-next' import { useChatOverlayForm } from './components/form.js' import Form from './components/Form.vue' -import { useChatOverlayApi, useUserAccessFlagChecker } from '@/api/index.js' +import { useUserAccessFlagChecker } from '@/api/auth' +import { useChatOverlayApi } from '@/api/overlays/chat.js' import { ChannelRolePermissionEnum } from '@/gql/graphql' import { Button } from '@/components/ui/button' import { diff --git a/frontend/dashboard/src/pages/overlays/chat/components/Form.vue b/frontend/dashboard/src/pages/overlays/chat/components/Form.vue index 4af812e514..ff24c62256 100644 --- a/frontend/dashboard/src/pages/overlays/chat/components/Form.vue +++ b/frontend/dashboard/src/pages/overlays/chat/components/Form.vue @@ -14,7 +14,8 @@ import { useChatOverlayForm } from './form.js' import { globalBadges } from '../constants.js' import * as faker from '../faker.js' -import { useChatOverlayApi, useUserAccessFlagChecker } from '@/api' +import { useUserAccessFlagChecker } from '@/api/auth' +import { useChatOverlayApi } from '@/api/overlays/chat' import { useCopyOverlayLink } from '@/components/overlays/copyOverlayLink.js' import { ChannelRolePermissionEnum, ChatOverlayAnimation } from '@/gql/graphql' import { Button } from '@/components/ui/button' diff --git a/frontend/dashboard/src/pages/overlays/dudes/dudes-settings-form.vue b/frontend/dashboard/src/pages/overlays/dudes/dudes-settings-form.vue index 14e81bc9cc..eb686024b9 100644 --- a/frontend/dashboard/src/pages/overlays/dudes/dudes-settings-form.vue +++ b/frontend/dashboard/src/pages/overlays/dudes/dudes-settings-form.vue @@ -20,7 +20,8 @@ import { toast } from 'vue-sonner' import { useDudesForm } from './use-dudes-form.js' import { useDudesIframe } from './use-dudes-frame.js' -import { useDudesOverlayManager, useProfile, useUserAccessFlagChecker } from '@/api/index.js' +import { useProfile, useUserAccessFlagChecker } from '@/api/auth' +import { useDudesOverlayManager } from '@/api/overlays/dudes' import { useCopyOverlayLink } from '@/components/overlays/copyOverlayLink.js' import SelectTwitchUsers from '@/components/twitchUsers/twitch-users-select.vue' import { diff --git a/frontend/dashboard/src/pages/overlays/dudes/dudes-settings.vue b/frontend/dashboard/src/pages/overlays/dudes/dudes-settings.vue index 79d0b37621..9305bd0cd9 100644 --- a/frontend/dashboard/src/pages/overlays/dudes/dudes-settings.vue +++ b/frontend/dashboard/src/pages/overlays/dudes/dudes-settings.vue @@ -8,7 +8,8 @@ import { useDudesIframe } from './use-dudes-frame.js' import type { DudesSettingsWithOptionalId } from './dudes-settings.js' -import { useDudesOverlayManager, useProfile, useUserAccessFlagChecker } from '@/api/index.js' +import { useProfile, useUserAccessFlagChecker } from '@/api/auth' +import { useDudesOverlayManager } from '@/api/overlays/dudes' import { Alert, AlertDescription } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' diff --git a/frontend/dashboard/src/pages/overlays/now-playing/now-playing-form.vue b/frontend/dashboard/src/pages/overlays/now-playing/now-playing-form.vue index 69186eb0c1..97fe296d26 100644 --- a/frontend/dashboard/src/pages/overlays/now-playing/now-playing-form.vue +++ b/frontend/dashboard/src/pages/overlays/now-playing/now-playing-form.vue @@ -6,7 +6,8 @@ import { toast } from 'vue-sonner' import { useNowPlayingForm } from './use-now-playing-form' -import { useNowPlayingOverlayApi, useProfile, useUserAccessFlagChecker } from '@/api' +import { useProfile, useUserAccessFlagChecker } from '@/api/auth' +import { useNowPlayingOverlayApi } from '@/api/overlays/now-playing' import { useCopyOverlayLink } from '@/components/overlays/copyOverlayLink' import { Button } from '@/components/ui/button' import { Card, CardContent, CardFooter } from '@/components/ui/card' diff --git a/frontend/dashboard/src/pages/overlays/now-playing/now-playing.vue b/frontend/dashboard/src/pages/overlays/now-playing/now-playing.vue index fe04363bd3..8b8133c1de 100644 --- a/frontend/dashboard/src/pages/overlays/now-playing/now-playing.vue +++ b/frontend/dashboard/src/pages/overlays/now-playing/now-playing.vue @@ -6,7 +6,8 @@ import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'reka-ui' import { computed, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' -import { useNowPlayingOverlayApi, useProfile, useUserAccessFlagChecker } from '@/api' +import { useProfile, useUserAccessFlagChecker } from '@/api/auth' +import { useNowPlayingOverlayApi } from '@/api/overlays/now-playing' import { useIntegrationsPageData } from '@/api/integrations/integrations-page.ts' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' diff --git a/frontend/dashboard/src/pages/user-settings/user-account-settings.vue b/frontend/dashboard/src/pages/user-settings/user-account-settings.vue index c526a27bc5..8292fcca25 100644 --- a/frontend/dashboard/src/pages/user-settings/user-account-settings.vue +++ b/frontend/dashboard/src/pages/user-settings/user-account-settings.vue @@ -3,7 +3,7 @@ import { CopyIcon, EyeIcon, EyeOffIcon, RefreshCwIcon } from 'lucide-vue-next' import { ref } from 'vue' import { useI18n } from 'vue-i18n' -import { useProfile, useUserSettings } from '@/api' +import { useProfile, useUserSettings } from '@/api/auth' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' diff --git a/frontend/dashboard/src/pages/user-settings/user-public-settings.vue b/frontend/dashboard/src/pages/user-settings/user-public-settings.vue index 42d52d60be..3c0909514e 100644 --- a/frontend/dashboard/src/pages/user-settings/user-public-settings.vue +++ b/frontend/dashboard/src/pages/user-settings/user-public-settings.vue @@ -8,7 +8,7 @@ import { z } from 'zod' import type { UserPublicSettingsQuery } from '@/gql/graphql' -import { useUserSettings } from '@/api' +import { useUserSettings } from '@/api/auth' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' diff --git a/frontend/dashboard/vite.config.ts b/frontend/dashboard/vite.config.ts index ec2370c351..627b1d91b4 100644 --- a/frontend/dashboard/vite.config.ts +++ b/frontend/dashboard/vite.config.ts @@ -1,6 +1,7 @@ import path from 'node:path' import process from 'node:process' import { fileURLToPath } from 'node:url' +import { analyzer, unstableRolldownAdapter } from 'vite-bundle-analyzer' import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' import { webUpdateNotice } from '@plugin-web-update-notification/vite' @@ -12,10 +13,10 @@ import tailwindcss from '@tailwindcss/vite' // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { + const analyzeMode = process.env.ANALYZE const env = loadEnv(mode, path.resolve(process.cwd(), '..', '..'), '') const plugins: PluginOption[] = [ - // @ts-ignore vue(), svgSprite(['./src/assets/*/*.svg', './src/assets/*.svg']), webUpdateNotice({ @@ -35,6 +36,11 @@ export default defineConfig(({ mode }) => { runtimeOnly: true, }), tailwindcss(), + // https://github.com/nonzzz/vite-bundle-analyzer + // ANALYZE=server bun run build + analyzeMode && unstableRolldownAdapter(analyzer({ + analyzerMode: analyzeMode === 'json' ? 'json' : 'server', + })), ] if (mode === 'development') { @@ -67,6 +73,83 @@ export default defineConfig(({ mode }) => { build: { sourcemap: true, + rolldownOptions: { + output: { + chunkFileNames: 'assets/[name]-[hash].js', + entryFileNames: 'assets/[name]-[hash].js', + assetFileNames: 'assets/[name]-[hash].[ext]', + + manualChunks: (id) => { + if (id.includes('node_modules')) { + if (id.includes('node_modules/.bun/')) { + const match = id.match(/\.bun\/([^@/]+)@/) + if (match && match[1]) { + return `vendor-${match[1]}` + } + + const simpleMatch = id.match(/\.bun\/([^/]+)/) + if (simpleMatch && simpleMatch[1] && simpleMatch[1] !== '.bun') { + return `vendor-${simpleMatch[1]}` + } + + return 'vendor-common' + } + + const match = id.match(/node_modules\/(@[^/]+\/[^/]+|[^/@]+)/) + if (match && match[1]) { + const packageName = match[1].replace('@', '').replace('/', '-') + return `vendor-${packageName}` + } + + return 'vendor-common' + } + + if (id.includes('src/gql/')) { + return 'gql' + } + + if (id.includes('src/plugins/')) { + return 'plugins' + } + + if (id.includes('src/api/')) { + const match = id.match(/src\/api\/([^/]+)/) + if (match && match[1]) { + const fileName = match[1].replace(/\.ts$/, '') + return `api-${fileName}` + } + return 'api-common' + } + + if (id.includes('src/composables/')) { + return 'composables' + } + + if (id.includes('src/features/')) { + const match = id.match(/src\/features\/([^/]+)/) + if (match) { + return `feature-${match[1]}` + } + } + + if (id.includes('src/components/')) { + const match = id.match(/src\/components\/([^/]+(?:\/[^/]+)?)/) + if (match && match[1]) { + const path = match[1].replace(/\.(ts|vue)$/, '').replace(/\//g, '-') + return `components-${path}` + } + return 'components-common' + } + + if (id.includes('src/pages/')) { + const match = id.match(/src\/pages\/([^/]+)/) + if (match) { + return `page-${match[1].replace('.vue', '').toLowerCase()}` + } + } + }, + }, + }, }, } }) diff --git a/libs/gomodels/channels.go b/libs/gomodels/channels.go index f58ca5f770..1b32af4e2c 100755 --- a/libs/gomodels/channels.go +++ b/libs/gomodels/channels.go @@ -21,6 +21,7 @@ type Channels struct { IsTwitchBanned bool `gorm:"column:isTwitchBanned;type:BOOL;" json:"isTwitchBanned"` IsBotMod bool `gorm:"column:isBotMod;type:BOOL;" json:"isBotMod"` BotID string `gorm:"column:botId;type:TEXT;" json:"botId"` + PlanID *string `gorm:"column:plan_id;type:UUID;" json:"planId"` CreatedAt time.Time `gorm:"column:created_at;type:TIMESTAMPTZ;default:now()" json:"createdAt"` Commands []ChannelsCommands `gorm:"foreignKey:ChannelID" json:"commands"` diff --git a/libs/repositories/plans/pgx/pgx.go b/libs/repositories/plans/pgx/pgx.go index 81d3cb042a..2a62dfa3b8 100644 --- a/libs/repositories/plans/pgx/pgx.go +++ b/libs/repositories/plans/pgx/pgx.go @@ -86,7 +86,7 @@ func (r *repository) GetByID(ctx context.Context, id string) (plan.Plan, error) return r.dbToEntity(dbPlan), nil } -func (r *repository) GetByNameID(ctx context.Context, nameID string) (plan.Plan, error) { +func (r *repository) GetByName(ctx context.Context, name string) (plan.Plan, error) { query, args, err := squirrel.Select( "id", "name", @@ -106,7 +106,7 @@ func (r *repository) GetByNameID(ctx context.Context, nameID string) (plan.Plan, "updated_at", ). From("plans"). - Where(squirrel.Eq{"name": nameID}). + Where(squirrel.Eq{"name": name}). PlaceholderFormat(squirrel.Dollar). ToSql() if err != nil { @@ -199,6 +199,87 @@ func (r *repository) GetByChannelID(ctx context.Context, channelID string) (plan return r.dbToEntity(dbPlan), nil } +func (r *repository) GetManyByIDs(ctx context.Context, ids []string) ([]plan.Plan, error) { + if len(ids) == 0 { + return []plan.Plan{}, nil + } + + query, args, err := squirrel.Select( + "id", + "name", + "max_commands", + "max_timers", + "max_variables", + "max_alerts", + "max_events", + "max_chat_alerts_messages", + "max_custom_overlays", + "max_eightball_answers", + "max_commands_responses", + "max_moderation_rules", + "max_keywords", + "max_greetings", + "created_at", + "updated_at", + ). + From("plans"). + Where(squirrel.Eq{"id": ids}). + PlaceholderFormat(squirrel.Dollar). + ToSql() + if err != nil { + return nil, fmt.Errorf("failed to build query: %w", err) + } + + rows, err := r.db.Query(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("failed to query plans: %w", err) + } + defer rows.Close() + + plansMap := make(map[string]plan.Plan) + for rows.Next() { + var dbPlan model.Plan + err = rows.Scan( + &dbPlan.ID, + &dbPlan.Name, + &dbPlan.MaxCommands, + &dbPlan.MaxTimers, + &dbPlan.MaxVariables, + &dbPlan.MaxAlerts, + &dbPlan.MaxEvents, + &dbPlan.MaxChatAlertsMessages, + &dbPlan.MaxCustomOverlays, + &dbPlan.MaxEightballAnswers, + &dbPlan.MaxCommandsResponses, + &dbPlan.MaxModerationRules, + &dbPlan.MaxKeywords, + &dbPlan.MaxGreetings, + &dbPlan.CreatedAt, + &dbPlan.UpdatedAt, + ) + if err != nil { + return nil, fmt.Errorf("failed to scan plan: %w", err) + } + plansMap[dbPlan.ID] = r.dbToEntity(dbPlan) + } + + if err = rows.Err(); err != nil { + return nil, fmt.Errorf("rows error: %w", err) + } + + // Return plans in the same order as requested IDs + result := make([]plan.Plan, len(ids)) + for i, id := range ids { + if p, ok := plansMap[id]; ok { + result[i] = p + } else { + result[i] = plan.Nil + } + } + + return result, nil +} + func (r *repository) dbToEntity(m model.Plan) plan.Plan { return plan.Plan{ ID: m.ID, diff --git a/libs/repositories/plans/plans.go b/libs/repositories/plans/plans.go index cebbd3d29c..5e7e0ca770 100644 --- a/libs/repositories/plans/plans.go +++ b/libs/repositories/plans/plans.go @@ -8,6 +8,7 @@ import ( type Repository interface { GetByID(ctx context.Context, id string) (plan.Plan, error) - GetByNameID(ctx context.Context, nameID string) (plan.Plan, error) + GetByName(ctx context.Context, name string) (plan.Plan, error) GetByChannelID(ctx context.Context, channelID string) (plan.Plan, error) + GetManyByIDs(ctx context.Context, ids []string) ([]plan.Plan, error) } diff --git a/package.json b/package.json index f6746f3c4a..bc47a3981f 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "vue": "3.5.25", "vue-router": "4.3.0", "vue-tsc": "3.1.8", - "vite": "8.0.0-beta.0", + "vite": "8.0.0-beta.5", "typescript": "5.9.3", "graphql": "16.12.0", "graphql-ws": "6.0.6",