Refactor: migrate discordbot
All checks were successful
All checks were successful
This commit is contained in:
@@ -13,6 +13,8 @@ ENTRYPOINT ["/work/backend"]
|
|||||||
CMD ["serve"]
|
CMD ["serve"]
|
||||||
|
|
||||||
FROM docker.io/library/debian:13-slim AS native
|
FROM docker.io/library/debian:13-slim AS native
|
||||||
|
RUN apt-get -y update && apt-get -y upgrade && \
|
||||||
|
apt-get -y install ca-certificates
|
||||||
|
|
||||||
COPY backend /work/backend
|
COPY backend /work/backend
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -32,7 +32,7 @@ docker-build-run:
|
|||||||
-t go2025/backend:latest
|
-t go2025/backend:latest
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
$(COMPOSE) up -d --force-recreate backend
|
$(COMPOSE) up -d --force-recreate backend dcbot
|
||||||
|
|
||||||
$(TARGET): $(SOURCE)
|
$(TARGET): $(SOURCE)
|
||||||
$(GO_ENV) go build -o $@
|
$(GO_ENV) go build -o $@
|
||||||
|
|||||||
168
bot/bot.go
Normal file
168
bot/bot.go
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/tracing"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandHandler = func(
|
||||||
|
*discordgo.Session, *discordgo.InteractionCreate)
|
||||||
|
|
||||||
|
type Command interface {
|
||||||
|
ApplicationCommand() *discordgo.ApplicationCommand
|
||||||
|
Handler() CommandHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bot struct {
|
||||||
|
session *discordgo.Session
|
||||||
|
commands map[string]Command
|
||||||
|
aliases map[string]int64
|
||||||
|
|
||||||
|
Client *resty.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() (*Bot, error) {
|
||||||
|
// Create Discord session
|
||||||
|
session, err := discordgo.New(
|
||||||
|
"Bot " + viper.GetString("discord-bot-token"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := resty.New()
|
||||||
|
client.SetBaseURL(viper.GetString("api-endpoint"))
|
||||||
|
client.SetAuthToken(viper.GetString("preshared-key"))
|
||||||
|
|
||||||
|
bot := &Bot{
|
||||||
|
session: session,
|
||||||
|
commands: make(map[string]Command),
|
||||||
|
|
||||||
|
Client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.registerHandlers()
|
||||||
|
|
||||||
|
go bot.fetchAliases()
|
||||||
|
|
||||||
|
// Set intents - only need guild messages for the ciallo listener
|
||||||
|
session.Identify.Intents = discordgo.IntentsGuildMessages |
|
||||||
|
discordgo.IntentsDirectMessages |
|
||||||
|
discordgo.IntentsMessageContent
|
||||||
|
|
||||||
|
return bot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) registerSlashCommands(ctx context.Context) error {
|
||||||
|
for _, cmd := range b.commands {
|
||||||
|
_, err := b.session.ApplicationCommandCreate(
|
||||||
|
b.session.State.User.ID, "", cmd.ApplicationCommand())
|
||||||
|
if err != nil {
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Error("failed to create command",
|
||||||
|
zap.String("command", cmd.ApplicationCommand().Name),
|
||||||
|
zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) clearSlashCommands(guildID string) error {
|
||||||
|
commands, err := b.session.ApplicationCommands(b.session.State.User.ID, guildID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range commands {
|
||||||
|
err := b.session.ApplicationCommandDelete(b.session.State.User.ID, guildID, cmd.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to delete command %s: %v", cmd.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) registerHandlers() {
|
||||||
|
b.session.AddHandler(b.onReady)
|
||||||
|
b.session.AddHandler(b.onMessageCreate)
|
||||||
|
b.session.AddHandler(b.onInteractionCreate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) RegisterCommand(newCommand func(*Bot) Command) {
|
||||||
|
command := newCommand(b)
|
||||||
|
b.commands[command.ApplicationCommand().Name] = command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) onReady(
|
||||||
|
s *discordgo.Session,
|
||||||
|
event *discordgo.Ready,
|
||||||
|
) {
|
||||||
|
ctx := context.Background()
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Info("logged in",
|
||||||
|
zap.String("username", s.State.User.Username),
|
||||||
|
zap.String("discriminator", s.State.User.Discriminator))
|
||||||
|
|
||||||
|
// For development: set your guild ID here for instant updates
|
||||||
|
// For production: use "" for global commands
|
||||||
|
guildID := "1377176828833169468" // Replace with your Discord server ID for faster testing
|
||||||
|
|
||||||
|
// clear slash commands
|
||||||
|
if err := b.clearSlashCommands(guildID); err != nil {
|
||||||
|
log.Printf("Error clearing slash commands: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register slash commands
|
||||||
|
if err := b.registerSlashCommands(ctx); err != nil {
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Error("failed to register slash commands",
|
||||||
|
zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set bot status
|
||||||
|
err := s.UpdateGameStatus(0, "/ping to check status")
|
||||||
|
if err != nil {
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Error("failed to set status",
|
||||||
|
zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) onInteractionCreate(
|
||||||
|
s *discordgo.Session,
|
||||||
|
i *discordgo.InteractionCreate,
|
||||||
|
) {
|
||||||
|
if i.Type != discordgo.InteractionApplicationCommand {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
command, ok := b.commands[i.ApplicationCommandData().Name]
|
||||||
|
if ok {
|
||||||
|
tracing.Logger.Ctx(context.Background()).
|
||||||
|
Info("run command",
|
||||||
|
zap.String("name", i.ApplicationCommandData().Name))
|
||||||
|
command.Handler()(s, i)
|
||||||
|
} else {
|
||||||
|
tracing.Logger.Ctx(context.Background()).
|
||||||
|
Error("command not exist",
|
||||||
|
zap.String("name", i.ApplicationCommandData().Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) Start() error {
|
||||||
|
return b.session.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) Stop() {
|
||||||
|
if b.session != nil {
|
||||||
|
b.session.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
53
bot/commands/echo.go
Normal file
53
bot/commands/echo.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.konchin.com/go2025/backend/bot"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EchoCommand struct {
|
||||||
|
bot *bot.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEchoCommand(bot *bot.Bot) bot.Command {
|
||||||
|
return &EchoCommand{bot: bot}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *EchoCommand) ApplicationCommand() *discordgo.ApplicationCommand {
|
||||||
|
return &discordgo.ApplicationCommand{
|
||||||
|
Name: "echo",
|
||||||
|
Description: "Bot repeats what you say",
|
||||||
|
Options: []*discordgo.ApplicationCommandOption{
|
||||||
|
{
|
||||||
|
Type: discordgo.ApplicationCommandOptionString,
|
||||||
|
Name: "message",
|
||||||
|
Description: "Message to echo",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *EchoCommand) Handler() bot.CommandHandler {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
options := i.ApplicationCommandData().Options
|
||||||
|
if len(options) == 0 {
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "No message provided!",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := options[0].StringValue()
|
||||||
|
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: message,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
43
bot/commands/greet.go
Normal file
43
bot/commands/greet.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/bot"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GreetCommand struct {
|
||||||
|
bot *bot.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGreetCommand(bot *bot.Bot) bot.Command {
|
||||||
|
return &GreetCommand{bot: bot}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GreetCommand) ApplicationCommand() *discordgo.ApplicationCommand {
|
||||||
|
return &discordgo.ApplicationCommand{
|
||||||
|
Name: "greet",
|
||||||
|
Description: "Get a friendly greeting",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GreetCommand) Handler() bot.CommandHandler {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
var username string
|
||||||
|
if i.Member != nil {
|
||||||
|
username = i.Member.User.Username
|
||||||
|
} else if i.User != nil {
|
||||||
|
username = i.User.Username
|
||||||
|
} else {
|
||||||
|
username = "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: fmt.Sprintf("Ciallo, %s!", username),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
32
bot/commands/ping.go
Normal file
32
bot/commands/ping.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.konchin.com/go2025/backend/bot"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PingCommand struct {
|
||||||
|
bot *bot.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPingCommand(bot *bot.Bot) bot.Command {
|
||||||
|
return &PingCommand{bot: bot}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PingCommand) ApplicationCommand() *discordgo.ApplicationCommand {
|
||||||
|
return &discordgo.ApplicationCommand{
|
||||||
|
Name: "ping",
|
||||||
|
Description: "Check if bot is responsive",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PingCommand) Handler() bot.CommandHandler {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "pong",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
68
bot/commands/web.go
Normal file
68
bot/commands/web.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/bot"
|
||||||
|
"gitea.konchin.com/go2025/backend/handlers/auth"
|
||||||
|
"gitea.konchin.com/go2025/backend/tracing"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebCommand struct {
|
||||||
|
bot *bot.Bot
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebCommand(bot *bot.Bot) bot.Command {
|
||||||
|
return &WebCommand{bot: bot}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WebCommand) ApplicationCommand() *discordgo.ApplicationCommand {
|
||||||
|
return &discordgo.ApplicationCommand{
|
||||||
|
Name: "web",
|
||||||
|
Description: "Get a login link to the web interface",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WebCommand) Handler() bot.CommandHandler {
|
||||||
|
return func(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||||
|
var userID string
|
||||||
|
if i.Member != nil {
|
||||||
|
userID = i.Member.User.ID
|
||||||
|
} else if i.User != nil {
|
||||||
|
userID = i.User.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call backend API
|
||||||
|
var res auth.PostGenLoginUrlOutput
|
||||||
|
resp, err := self.bot.Client.R().
|
||||||
|
SetBody(auth.PostGenLoginUrlInput{UserId: userID}).
|
||||||
|
SetResult(&res).
|
||||||
|
Post("/bot/auth/gen-login-url")
|
||||||
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
|
tracing.Logger.Ctx(context.Background()).
|
||||||
|
Error("failed to generate login url",
|
||||||
|
zap.Error(err))
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: "❌ Failed to generate login URL",
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := "🔗 **Click here to access the web page:**\n"+
|
||||||
|
res.LoginUrl + "\n\n"
|
||||||
|
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||||
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||||
|
Data: &discordgo.InteractionResponseData{
|
||||||
|
Content: content,
|
||||||
|
Flags: discordgo.MessageFlagsEphemeral,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
69
bot/onMessageCreate.go
Normal file
69
bot/onMessageCreate.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/handlers/api"
|
||||||
|
"gitea.konchin.com/go2025/backend/tracing"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/exp/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *Bot) fetchAliases() {
|
||||||
|
for {
|
||||||
|
var res []api.GetAliasesOutputAlias
|
||||||
|
resp, err := b.Client.R().
|
||||||
|
SetResult(&res).
|
||||||
|
Get("/bot/api/aliases")
|
||||||
|
if err == nil && resp.StatusCode() == http.StatusOK {
|
||||||
|
aliases := make(map[string]int64)
|
||||||
|
for _, alias := range res {
|
||||||
|
aliases[alias.Name] = alias.Id
|
||||||
|
}
|
||||||
|
b.aliases = aliases
|
||||||
|
|
||||||
|
tracing.Logger.Ctx(context.Background()).
|
||||||
|
Info("nmsl",
|
||||||
|
zap.String("aliases", fmt.Sprintf("%+v", aliases)))
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) onMessageCreate(
|
||||||
|
s *discordgo.Session,
|
||||||
|
m *discordgo.MessageCreate,
|
||||||
|
) {
|
||||||
|
// Ignore messages from the bot itself
|
||||||
|
if m.Author.ID == s.State.User.ID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.ToLower(strings.TrimSpace(m.Content))
|
||||||
|
tracing.Logger.Ctx(context.Background()).
|
||||||
|
Info("wtf",
|
||||||
|
zap.String("key", key))
|
||||||
|
|
||||||
|
if id, ok := b.aliases[key]; ok {
|
||||||
|
var res []api.GetImagesOutputImage
|
||||||
|
resp, err := b.Client.R().
|
||||||
|
SetResult(&res).
|
||||||
|
SetQueryParam("aliases", strconv.FormatInt(id, 10)).
|
||||||
|
Get("/bot/api/images")
|
||||||
|
if err == nil && resp.StatusCode() == http.StatusOK {
|
||||||
|
image := res[rand.Intn(len(res))]
|
||||||
|
s.ChannelMessageSend(m.ChannelID,
|
||||||
|
fmt.Sprintf("%s/img/%d.%s",
|
||||||
|
viper.GetString("external-url"),
|
||||||
|
image.Id, image.Extension))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
cmds/dcbot.go
Normal file
75
cmds/dcbot.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package cmds
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitea.konchin.com/go2025/backend/bot"
|
||||||
|
"gitea.konchin.com/go2025/backend/bot/commands"
|
||||||
|
"gitea.konchin.com/go2025/backend/tracing"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dcbotCmd = &cobra.Command{
|
||||||
|
Use: "dcbot",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
appname := "go2025-dcbot"
|
||||||
|
tracing.InitTracer(appname)
|
||||||
|
if viper.GetString("uptrace-dsn") != "" {
|
||||||
|
tracing.InitUptrace(appname)
|
||||||
|
defer tracing.DeferUptrace(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize bot
|
||||||
|
discordBot, err := bot.New()
|
||||||
|
if err != nil {
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Panic("failed to create bot",
|
||||||
|
zap.Error(err))
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBot.RegisterCommand(commands.NewEchoCommand)
|
||||||
|
discordBot.RegisterCommand(commands.NewGreetCommand)
|
||||||
|
discordBot.RegisterCommand(commands.NewPingCommand)
|
||||||
|
discordBot.RegisterCommand(commands.NewWebCommand)
|
||||||
|
|
||||||
|
// Start bot
|
||||||
|
if err := discordBot.Start(); err != nil {
|
||||||
|
tracing.Logger.Ctx(ctx).
|
||||||
|
Panic("failed to start bot",
|
||||||
|
zap.Error(err))
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Bot is now running. Press CTRL-C to exit.")
|
||||||
|
defer discordBot.Stop()
|
||||||
|
|
||||||
|
// Wait for interrupt signal
|
||||||
|
sc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc,
|
||||||
|
syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||||
|
<-sc
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
String("preshared-key", "poop", "preshared key")
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
String("discord-bot-token", "", "discord bot token")
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
String("api-endpoint", "http://backend:8080", "api endpoint")
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
String("external-url", "http://localhost:8080", "external url")
|
||||||
|
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
Bool("zap-production", true, "Toggle production log format")
|
||||||
|
dcbotCmd.Flags().
|
||||||
|
String("uptrace-dsn", "", "Uptrace DSN (disabled by default)")
|
||||||
|
}
|
||||||
@@ -22,4 +22,5 @@ func init() {
|
|||||||
|
|
||||||
RootCmd.AddCommand(serveCmd)
|
RootCmd.AddCommand(serveCmd)
|
||||||
RootCmd.AddCommand(genTokenCmd)
|
RootCmd.AddCommand(genTokenCmd)
|
||||||
|
RootCmd.AddCommand(dcbotCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,8 +116,16 @@ var serveCmd = &cobra.Command{
|
|||||||
|
|
||||||
authGroup := backend.NewGroup("/auth")
|
authGroup := backend.NewGroup("/auth")
|
||||||
authGroup.POST("/login", auths.PostLogin)
|
authGroup.POST("/login", auths.PostLogin)
|
||||||
authGroup.POST("/gen-login-url",
|
|
||||||
midHandlers.CheckPresharedKey(auths.PostGenLoginUrl))
|
botGroup := backend.NewGroup("/bot").
|
||||||
|
Use(midHandlers.CheckPresharedKey)
|
||||||
|
|
||||||
|
botApiGroup := botGroup.NewGroup("/api")
|
||||||
|
botApiGroup.GET("/aliases", apis.GetAliases)
|
||||||
|
botApiGroup.GET("/images", apis.GetImages)
|
||||||
|
|
||||||
|
botAuthGroup := botGroup.NewGroup("/auth")
|
||||||
|
botAuthGroup.POST("/gen-login-url", auths.PostGenLoginUrl)
|
||||||
|
|
||||||
imgGroup := backend.NewGroup("/img")
|
imgGroup := backend.NewGroup("/img")
|
||||||
imgGroup.GET("/:filename", imgs.Get)
|
imgGroup.GET("/:filename", imgs.Get)
|
||||||
|
|||||||
@@ -74,6 +74,17 @@ services:
|
|||||||
- uptrace
|
- uptrace
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
dcbot:
|
||||||
|
image: go2025/backend:latest
|
||||||
|
command: ["dcbot"]
|
||||||
|
env_file:
|
||||||
|
- path: ./.env
|
||||||
|
required: false
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
redis: {}
|
redis: {}
|
||||||
postgres: {}
|
postgres: {}
|
||||||
|
|||||||
16
docs/docs.go
16
docs/docs.go
@@ -55,7 +55,7 @@ const docTemplate = `{
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/api.getAliasesOutputAlias"
|
"$ref": "#/definitions/api.GetAliasesOutputAlias"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -186,7 +186,7 @@ const docTemplate = `{
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/api.getImagesOutputImage"
|
"$ref": "#/definitions/api.GetImagesOutputImage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -208,7 +208,7 @@ const docTemplate = `{
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/auth.postGenLoginUrlInput"
|
"$ref": "#/definitions/auth.PostGenLoginUrlInput"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -216,7 +216,7 @@ const docTemplate = `{
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Payload",
|
"description": "Payload",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/auth.postGenLoginUrlOutput"
|
"$ref": "#/definitions/auth.PostGenLoginUrlOutput"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -247,7 +247,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"api.getAliasesOutputAlias": {
|
"api.GetAliasesOutputAlias": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -258,7 +258,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.getImagesOutputImage": {
|
"api.GetImagesOutputImage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"aliasesIds": {
|
"aliasesIds": {
|
||||||
@@ -309,7 +309,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.postGenLoginUrlInput": {
|
"auth.PostGenLoginUrlInput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"userId": {
|
"userId": {
|
||||||
@@ -317,7 +317,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.postGenLoginUrlOutput": {
|
"auth.PostGenLoginUrlOutput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"loginUrl": {
|
"loginUrl": {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/api.getAliasesOutputAlias"
|
"$ref": "#/definitions/api.GetAliasesOutputAlias"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/api.getImagesOutputImage"
|
"$ref": "#/definitions/api.GetImagesOutputImage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/auth.postGenLoginUrlInput"
|
"$ref": "#/definitions/auth.PostGenLoginUrlInput"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "Payload",
|
"description": "Payload",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/auth.postGenLoginUrlOutput"
|
"$ref": "#/definitions/auth.PostGenLoginUrlOutput"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"api.getAliasesOutputAlias": {
|
"api.GetAliasesOutputAlias": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -250,7 +250,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.getImagesOutputImage": {
|
"api.GetImagesOutputImage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"aliasesIds": {
|
"aliasesIds": {
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.postGenLoginUrlInput": {
|
"auth.PostGenLoginUrlInput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"userId": {
|
"userId": {
|
||||||
@@ -309,7 +309,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auth.postGenLoginUrlOutput": {
|
"auth.PostGenLoginUrlOutput": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"loginUrl": {
|
"loginUrl": {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
basePath: /
|
basePath: /
|
||||||
definitions:
|
definitions:
|
||||||
api.getAliasesOutputAlias:
|
api.GetAliasesOutputAlias:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: integer
|
type: integer
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
api.getImagesOutputImage:
|
api.GetImagesOutputImage:
|
||||||
properties:
|
properties:
|
||||||
aliasesIds:
|
aliasesIds:
|
||||||
items:
|
items:
|
||||||
@@ -40,12 +40,12 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
auth.postGenLoginUrlInput:
|
auth.PostGenLoginUrlInput:
|
||||||
properties:
|
properties:
|
||||||
userId:
|
userId:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
auth.postGenLoginUrlOutput:
|
auth.PostGenLoginUrlOutput:
|
||||||
properties:
|
properties:
|
||||||
loginUrl:
|
loginUrl:
|
||||||
type: string
|
type: string
|
||||||
@@ -88,7 +88,7 @@ paths:
|
|||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/api.getAliasesOutputAlias'
|
$ref: '#/definitions/api.GetAliasesOutputAlias'
|
||||||
type: array
|
type: array
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
@@ -174,7 +174,7 @@ paths:
|
|||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/api.getImagesOutputImage'
|
$ref: '#/definitions/api.GetImagesOutputImage'
|
||||||
type: array
|
type: array
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
@@ -188,12 +188,12 @@ paths:
|
|||||||
name: payload
|
name: payload
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/auth.postGenLoginUrlInput'
|
$ref: '#/definitions/auth.PostGenLoginUrlInput'
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Payload
|
description: Payload
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/auth.postGenLoginUrlOutput'
|
$ref: '#/definitions/auth.PostGenLoginUrlOutput'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
/auth/login:
|
/auth/login:
|
||||||
|
|||||||
17
go.mod
17
go.mod
@@ -3,6 +3,7 @@ module gitea.konchin.com/go2025/backend
|
|||||||
go 1.25.4
|
go 1.25.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bwmarrin/discordgo v0.29.0
|
||||||
github.com/go-resty/resty/v2 v2.17.0
|
github.com/go-resty/resty/v2 v2.17.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||||
github.com/minio/minio-go/v7 v7.0.97
|
github.com/minio/minio-go/v7 v7.0.97
|
||||||
@@ -21,6 +22,7 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.38.0
|
go.opentelemetry.io/otel v1.38.0
|
||||||
go.opentelemetry.io/otel/trace v1.38.0
|
go.opentelemetry.io/otel/trace v1.38.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
|
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -38,6 +40,7 @@ require (
|
|||||||
github.com/go-openapi/swag v0.19.15 // indirect
|
github.com/go-openapi/swag v0.19.15 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
@@ -80,13 +83,13 @@ require (
|
|||||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/mod v0.29.0 // indirect
|
golang.org/x/mod v0.31.0 // indirect
|
||||||
golang.org/x/net v0.47.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.18.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
golang.org/x/tools v0.38.0 // indirect
|
golang.org/x/tools v0.40.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
|
||||||
google.golang.org/grpc v1.75.1 // indirect
|
google.golang.org/grpc v1.75.1 // indirect
|
||||||
|
|||||||
37
go.sum
37
go.sum
@@ -1,5 +1,7 @@
|
|||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno=
|
||||||
|
github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
@@ -44,6 +46,8 @@ 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/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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
@@ -186,28 +190,33 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
|||||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 h1:MDfG8Cvcqlt9XXrmEiD4epKn7VJHZO84hejP9Jmp0MM=
|
||||||
|
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
|
||||||
|
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||||
|
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ=
|
||||||
|
|||||||
@@ -29,8 +29,12 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl
|
|||||||
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
||||||
golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
|
golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
|
||||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
|
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
|
||||||
|
golang.org/x/telemetry v0.0.0-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||||
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
|
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||||
|
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type getAliasesOutputAlias struct {
|
type GetAliasesOutputAlias struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ type getAliasesOutputAlias struct {
|
|||||||
//
|
//
|
||||||
// @summary Get aliases
|
// @summary Get aliases
|
||||||
// @description get alias ids and names
|
// @description get alias ids and names
|
||||||
// @success 200 {object} []getAliasesOutputAlias
|
// @success 200 {object} []GetAliasesOutputAlias
|
||||||
// @failure 400
|
// @failure 400
|
||||||
// @failure 401
|
// @failure 401
|
||||||
// @router /api/aliases [get]
|
// @router /api/aliases [get]
|
||||||
@@ -34,10 +34,10 @@ func (self *Handlers) GetAliases(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var output []getAliasesOutputAlias
|
var output []GetAliasesOutputAlias
|
||||||
|
|
||||||
for _, alias := range aliases {
|
for _, alias := range aliases {
|
||||||
output = append(output, getAliasesOutputAlias{
|
output = append(output, GetAliasesOutputAlias{
|
||||||
Id: alias.Id,
|
Id: alias.Id,
|
||||||
Name: alias.Name,
|
Name: alias.Name,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type getImagesOutputImage struct {
|
type GetImagesOutputImage struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Extension string `json:"extension"`
|
Extension string `json:"extension"`
|
||||||
Uploader string `json:"uploadedUserId"`
|
Uploader string `json:"uploadedUserId"`
|
||||||
@@ -25,7 +25,7 @@ type getImagesOutputImage struct {
|
|||||||
//
|
//
|
||||||
// @param images query []int64 false "Image Ids" attribute(optional)
|
// @param images query []int64 false "Image Ids" attribute(optional)
|
||||||
// @param aliases query []int64 false "Alias Ids" attribute(optional)
|
// @param aliases query []int64 false "Alias Ids" attribute(optional)
|
||||||
// @success 200 {object} []getImagesOutputImage
|
// @success 200 {object} []GetImagesOutputImage
|
||||||
// @failure 400
|
// @failure 400
|
||||||
// @failure 401
|
// @failure 401
|
||||||
// @router /api/images [get]
|
// @router /api/images [get]
|
||||||
@@ -83,13 +83,13 @@ func (self *Handlers) GetImages(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var output []getImagesOutputImage
|
var output []GetImagesOutputImage
|
||||||
for _, img := range images {
|
for _, img := range images {
|
||||||
var aliases []int64
|
var aliases []int64
|
||||||
for _, alias := range img.Aliases {
|
for _, alias := range img.Aliases {
|
||||||
aliases = append(aliases, alias.Id)
|
aliases = append(aliases, alias.Id)
|
||||||
}
|
}
|
||||||
output = append(output, getImagesOutputImage{
|
output = append(output, GetImagesOutputImage{
|
||||||
Id: img.Id,
|
Id: img.Id,
|
||||||
Extension: img.Extension,
|
Extension: img.Extension,
|
||||||
Uploader: img.Uploader,
|
Uploader: img.Uploader,
|
||||||
|
|||||||
@@ -10,18 +10,18 @@ import (
|
|||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type postGenLoginUrlInput struct {
|
type PostGenLoginUrlInput struct {
|
||||||
UserId string `json:"userId"`
|
UserId string `json:"userId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type postGenLoginUrlOutput struct {
|
type PostGenLoginUrlOutput struct {
|
||||||
LoginUrl string `json:"loginUrl"`
|
LoginUrl string `json:"loginUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostGenLoginUrl
|
// PostGenLoginUrl
|
||||||
//
|
//
|
||||||
// @param payload body postGenLoginUrlInput true "Payload"
|
// @param payload body PostGenLoginUrlInput true "Payload"
|
||||||
// @success 200 {object} postGenLoginUrlOutput "Payload"
|
// @success 200 {object} PostGenLoginUrlOutput "Payload"
|
||||||
// @failure 400
|
// @failure 400
|
||||||
// @router /auth/gen-login-url [post]
|
// @router /auth/gen-login-url [post]
|
||||||
func (self *Handlers) PostGenLoginUrl(
|
func (self *Handlers) PostGenLoginUrl(
|
||||||
@@ -38,7 +38,7 @@ func (self *Handlers) PostGenLoginUrl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var input postGenLoginUrlInput
|
var input PostGenLoginUrlInput
|
||||||
if err := json.Unmarshal(b, &input); err != nil {
|
if err := json.Unmarshal(b, &input); err != nil {
|
||||||
return middlewares.HTTPError{
|
return middlewares.HTTPError{
|
||||||
StatusCode: http.StatusBadRequest,
|
StatusCode: http.StatusBadRequest,
|
||||||
@@ -56,7 +56,7 @@ func (self *Handlers) PostGenLoginUrl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bunrouter.JSON(w, postGenLoginUrlOutput{
|
return bunrouter.JSON(w, PostGenLoginUrlOutput{
|
||||||
LoginUrl: viper.GetString("external-url") +
|
LoginUrl: viper.GetString("external-url") +
|
||||||
"/login?" +
|
"/login?" +
|
||||||
"token=" + token,
|
"token=" + token,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func Test_01_Login(t *testing.T) {
|
|||||||
t.Run("check preshared key failed", func(t *testing.T) {
|
t.Run("check preshared key failed", func(t *testing.T) {
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
SetBody(`{"userId": "testuser1"}`).
|
SetBody(`{"userId": "testuser1"}`).
|
||||||
Post("/auth/gen-login-url")
|
Post("/bot/auth/gen-login-url")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("request failed")
|
t.Fatal("request failed")
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ func Test_01_Login(t *testing.T) {
|
|||||||
SetBody(`{"userId": "testuser1"}`).
|
SetBody(`{"userId": "testuser1"}`).
|
||||||
SetAuthToken("poop").
|
SetAuthToken("poop").
|
||||||
SetResult(&payload).
|
SetResult(&payload).
|
||||||
Post("/auth/gen-login-url")
|
Post("/bot/auth/gen-login-url")
|
||||||
|
|
||||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
t.Fatal("failed to get login url")
|
t.Fatal("failed to get login url")
|
||||||
@@ -46,7 +46,8 @@ func Test_01_Login(t *testing.T) {
|
|||||||
|
|
||||||
resp, err = client.R().
|
resp, err = client.R().
|
||||||
SetBody(loginPayload{Token: loginUrl.Query().Get("token")}).
|
SetBody(loginPayload{Token: loginUrl.Query().Get("token")}).
|
||||||
Post(loginUrl.Scheme + "://" + loginUrl.Host + "/auth/login")
|
SetAuthToken("poop").
|
||||||
|
Post("/auth/login")
|
||||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
t.Fatal("failed to login")
|
t.Fatal("failed to login")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ var aliases []aliasPayload
|
|||||||
func Test_04_GetAliases(t *testing.T) {
|
func Test_04_GetAliases(t *testing.T) {
|
||||||
resp, err := client.R().
|
resp, err := client.R().
|
||||||
SetResult(&aliases).
|
SetResult(&aliases).
|
||||||
Get("http://localhost:8080/api/aliases")
|
Get("/api/aliases")
|
||||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
t.Logf("%+v", resp)
|
t.Logf("%+v", resp)
|
||||||
t.Fatal("failed to get aliases")
|
t.Fatal("failed to get aliases")
|
||||||
|
|||||||
Reference in New Issue
Block a user