Compare commits
2 Commits
main
...
7639b3ded7
| Author | SHA1 | Date | |
|---|---|---|---|
|
7639b3ded7
|
|||
|
a2b00e6976
|
@@ -33,9 +33,6 @@ jobs:
|
||||
- run-go-vet
|
||||
- check-swagger-up-to-date
|
||||
runs-on: imgbuilder
|
||||
env:
|
||||
COMPOSE_ARGS: '-p go2025-backend'
|
||||
DOCKER_BUILD_ARGS: '--quiet'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -56,8 +53,6 @@ jobs:
|
||||
needs:
|
||||
- run-go-test
|
||||
runs-on: imgbuilder
|
||||
env:
|
||||
COMPOSE_ARGS: '-p go2025-backend'
|
||||
steps:
|
||||
- name: Build and push image
|
||||
uses: https://gitea.konchin.com/action/docker@main
|
||||
@@ -65,4 +60,3 @@ jobs:
|
||||
registry-certificate: ${{ vars.ROOTCA }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
target: build-run
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -1,24 +1,6 @@
|
||||
FROM docker.io/library/golang:1.25 AS build
|
||||
FROM docker.io/library/debian:13-slim
|
||||
|
||||
WORKDIR /work
|
||||
COPY . .
|
||||
RUN make backend
|
||||
|
||||
FROM docker.io/library/debian:13-slim AS build-run
|
||||
RUN apt-get -y update && apt-get -y upgrade && \
|
||||
apt-get -y install ca-certificates
|
||||
|
||||
COPY --from=build /work/backend /work/backend
|
||||
WORKDIR /work
|
||||
|
||||
ENTRYPOINT ["/work/backend"]
|
||||
CMD ["serve"]
|
||||
|
||||
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
|
||||
ADD backend /work/backend
|
||||
WORKDIR /work
|
||||
|
||||
ENTRYPOINT ["/work/backend"]
|
||||
|
||||
30
Makefile
30
Makefile
@@ -1,38 +1,22 @@
|
||||
.PHONY: all swagger install postgres test mac \
|
||||
.PHONY: all swagger install postgres test test-ci \
|
||||
docker docker-quiet docker-clean
|
||||
|
||||
SWAG ?= go run github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
DOCKER ?= docker
|
||||
DOCKER_BUILD_ARGS +=
|
||||
COMPOSE_ARGS += --progress plain
|
||||
COMPOSE ?= $(DOCKER) compose $(COMPOSE_ARGS)
|
||||
COMPOSE ?= $(DOCKER) compose -p go2025-backend --progress plain
|
||||
|
||||
GO_ENV += CGO_ENABLED=0
|
||||
SOURCE := $(shell find . -type f -name '*.go')
|
||||
TARGET := backend
|
||||
|
||||
all: swagger docker-build-native docker
|
||||
|
||||
mac: swagger docker-build-run docker
|
||||
all: swagger docker
|
||||
|
||||
swagger:
|
||||
$(SWAG) fmt
|
||||
$(SWAG) init -o docs -g cmds/serve.go -pdl 1
|
||||
|
||||
docker-build-native: $(TARGET)
|
||||
$(DOCKER) build . $(DOCKER_BUILD_ARGS) \
|
||||
--target native \
|
||||
-t go2025/backend:native \
|
||||
-t go2025/backend:latest
|
||||
|
||||
docker-build-run:
|
||||
$(DOCKER) build . $(DOCKER_BUILD_ARGS) \
|
||||
--target build-run \
|
||||
-t go2025/backend:build-run \
|
||||
-t go2025/backend:latest
|
||||
|
||||
docker:
|
||||
$(COMPOSE) up -d --force-recreate backend dcbot
|
||||
docker: $(TARGET)
|
||||
$(DOCKER) compose up -d --force-recreate --build backend
|
||||
|
||||
$(TARGET): $(SOURCE)
|
||||
$(GO_ENV) go build -o $@
|
||||
@@ -47,7 +31,9 @@ postgres:
|
||||
$(COMPOSE) exec postgres psql \
|
||||
postgres://go2025:go2025@postgres:5432/go2025?sslmode=disable
|
||||
|
||||
docker-quiet: docker-build-native
|
||||
test-ci: docker-quiet test docker-clean
|
||||
|
||||
docker-quiet: $(TARGET)
|
||||
$(COMPOSE) up -d \
|
||||
--force-recreate --build backend \
|
||||
--quiet-build --quiet-pull
|
||||
|
||||
168
bot/bot.go
168
bot/bot.go
@@ -1,168 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
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,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.konchin.com/go2025/backend/handlers/api"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/spf13/viper"
|
||||
"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
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
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)")
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type genLoginUrlPayload struct {
|
||||
LoginUrl string `json:"loginUrl"`
|
||||
}
|
||||
|
||||
type loginPayload struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
var genTokenCmd = &cobra.Command{
|
||||
Use: "gen-token",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
client := resty.New()
|
||||
|
||||
var payload genLoginUrlPayload
|
||||
resp, err := client.R().
|
||||
SetBody(`{"userId": "testuser1"}`).
|
||||
SetAuthToken("poop").
|
||||
SetResult(&payload).
|
||||
Post("http://localhost:8080/auth/gen-login-url")
|
||||
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("url: %s\n", payload.LoginUrl)
|
||||
},
|
||||
}
|
||||
@@ -21,6 +21,4 @@ func init() {
|
||||
cobra.EnableTraverseRunHooks = true
|
||||
|
||||
RootCmd.AddCommand(serveCmd)
|
||||
RootCmd.AddCommand(genTokenCmd)
|
||||
RootCmd.AddCommand(dcbotCmd)
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ var serveCmd = &cobra.Command{
|
||||
Use(middlewares.AccessLog).
|
||||
Use(middlewares.CORSHandler)
|
||||
|
||||
backend.OPTIONS("/*any", utils.GetHealthz)
|
||||
backend.GET("/healthz", utils.GetHealthz)
|
||||
|
||||
apiGroup := backend.NewGroup("/api").
|
||||
@@ -116,16 +115,8 @@ var serveCmd = &cobra.Command{
|
||||
|
||||
authGroup := backend.NewGroup("/auth")
|
||||
authGroup.POST("/login", auths.PostLogin)
|
||||
|
||||
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)
|
||||
authGroup.POST("/gen-login-url",
|
||||
midHandlers.CheckPresharedKey(auths.PostGenLoginUrl))
|
||||
|
||||
imgGroup := backend.NewGroup("/img")
|
||||
imgGroup.GET("/:filename", imgs.Get)
|
||||
@@ -145,16 +136,12 @@ var serveCmd = &cobra.Command{
|
||||
func init() {
|
||||
serveCmd.Flags().
|
||||
String("port", "8080", "Port to listen on")
|
||||
serveCmd.Flags().
|
||||
Bool("https", false, "Enable https mode")
|
||||
serveCmd.Flags().
|
||||
String("external-url", "http://localhost:8080", "External url for login")
|
||||
serveCmd.Flags().
|
||||
String("cors-origin", "", "CORS origin")
|
||||
serveCmd.Flags().
|
||||
String("preshared-key", "poop", "Preshared key for Discord Bot")
|
||||
serveCmd.Flags().
|
||||
Bool("reset", false, "Reset database")
|
||||
|
||||
serveCmd.Flags().
|
||||
Int64("access-token-timeout", 300, "Timeout of Access Token JWT")
|
||||
|
||||
@@ -60,7 +60,8 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
backend:
|
||||
image: go2025/backend:latest
|
||||
build:
|
||||
context: .
|
||||
env_file:
|
||||
- path: ./.env
|
||||
required: false
|
||||
@@ -74,16 +75,6 @@ services:
|
||||
- uptrace
|
||||
restart: unless-stopped
|
||||
|
||||
dcbot:
|
||||
image: go2025/backend:latest
|
||||
command: ["dcbot"]
|
||||
env_file:
|
||||
- path: ./.env
|
||||
required: false
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
redis: {}
|
||||
postgres: {}
|
||||
|
||||
16
docs/docs.go
16
docs/docs.go
@@ -55,7 +55,7 @@ const docTemplate = `{
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.GetAliasesOutputAlias"
|
||||
"$ref": "#/definitions/api.getAliasesOutputAlias"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -186,7 +186,7 @@ const docTemplate = `{
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.GetImagesOutputImage"
|
||||
"$ref": "#/definitions/api.getImagesOutputImage"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -208,7 +208,7 @@ const docTemplate = `{
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.PostGenLoginUrlInput"
|
||||
"$ref": "#/definitions/auth.postGenLoginUrlInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -216,7 +216,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "Payload",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.PostGenLoginUrlOutput"
|
||||
"$ref": "#/definitions/auth.postGenLoginUrlOutput"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -247,7 +247,7 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.GetAliasesOutputAlias": {
|
||||
"api.getAliasesOutputAlias": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -258,7 +258,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.GetImagesOutputImage": {
|
||||
"api.getImagesOutputImage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aliasesIds": {
|
||||
@@ -309,7 +309,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.PostGenLoginUrlInput": {
|
||||
"auth.postGenLoginUrlInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"userId": {
|
||||
@@ -317,7 +317,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.PostGenLoginUrlOutput": {
|
||||
"auth.postGenLoginUrlOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loginUrl": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.GetAliasesOutputAlias"
|
||||
"$ref": "#/definitions/api.getAliasesOutputAlias"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -178,7 +178,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/api.GetImagesOutputImage"
|
||||
"$ref": "#/definitions/api.getImagesOutputImage"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -200,7 +200,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.PostGenLoginUrlInput"
|
||||
"$ref": "#/definitions/auth.postGenLoginUrlInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -208,7 +208,7 @@
|
||||
"200": {
|
||||
"description": "Payload",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.PostGenLoginUrlOutput"
|
||||
"$ref": "#/definitions/auth.postGenLoginUrlOutput"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -239,7 +239,7 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.GetAliasesOutputAlias": {
|
||||
"api.getAliasesOutputAlias": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
@@ -250,7 +250,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"api.GetImagesOutputImage": {
|
||||
"api.getImagesOutputImage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aliasesIds": {
|
||||
@@ -301,7 +301,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.PostGenLoginUrlInput": {
|
||||
"auth.postGenLoginUrlInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"userId": {
|
||||
@@ -309,7 +309,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"auth.PostGenLoginUrlOutput": {
|
||||
"auth.postGenLoginUrlOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loginUrl": {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
basePath: /
|
||||
definitions:
|
||||
api.GetAliasesOutputAlias:
|
||||
api.getAliasesOutputAlias:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
api.GetImagesOutputImage:
|
||||
api.getImagesOutputImage:
|
||||
properties:
|
||||
aliasesIds:
|
||||
items:
|
||||
@@ -40,12 +40,12 @@ definitions:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
auth.PostGenLoginUrlInput:
|
||||
auth.postGenLoginUrlInput:
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
type: object
|
||||
auth.PostGenLoginUrlOutput:
|
||||
auth.postGenLoginUrlOutput:
|
||||
properties:
|
||||
loginUrl:
|
||||
type: string
|
||||
@@ -88,7 +88,7 @@ paths:
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/api.GetAliasesOutputAlias'
|
||||
$ref: '#/definitions/api.getAliasesOutputAlias'
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
@@ -174,7 +174,7 @@ paths:
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/api.GetImagesOutputImage'
|
||||
$ref: '#/definitions/api.getImagesOutputImage'
|
||||
type: array
|
||||
"400":
|
||||
description: Bad Request
|
||||
@@ -188,12 +188,12 @@ paths:
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/auth.PostGenLoginUrlInput'
|
||||
$ref: '#/definitions/auth.postGenLoginUrlInput'
|
||||
responses:
|
||||
"200":
|
||||
description: Payload
|
||||
schema:
|
||||
$ref: '#/definitions/auth.PostGenLoginUrlOutput'
|
||||
$ref: '#/definitions/auth.postGenLoginUrlOutput'
|
||||
"400":
|
||||
description: Bad Request
|
||||
/auth/login:
|
||||
|
||||
17
go.mod
17
go.mod
@@ -3,7 +3,6 @@ module gitea.konchin.com/go2025/backend
|
||||
go 1.25.4
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.29.0
|
||||
github.com/go-resty/resty/v2 v2.17.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/minio/minio-go/v7 v7.0.97
|
||||
@@ -22,7 +21,6 @@ require (
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/exp v0.0.0-20251209150349-8475f28825e9
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,7 +38,6 @@ require (
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.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/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
@@ -83,13 +80,13 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/mod v0.31.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // 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/grpc v1.75.1 // indirect
|
||||
|
||||
37
go.sum
37
go.sum
@@ -1,7 +1,5 @@
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
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/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
@@ -46,8 +44,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.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/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -190,33 +186,28 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
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/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
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/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
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=
|
||||
|
||||
@@ -29,12 +29,8 @@ 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-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-20251203150158-8fff8a5912fc/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
||||
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.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=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/uptrace/bunrouter"
|
||||
)
|
||||
|
||||
type GetAliasesOutputAlias struct {
|
||||
type getAliasesOutputAlias struct {
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
@@ -16,7 +16,7 @@ type GetAliasesOutputAlias struct {
|
||||
//
|
||||
// @summary Get aliases
|
||||
// @description get alias ids and names
|
||||
// @success 200 {object} []GetAliasesOutputAlias
|
||||
// @success 200 {object} []getAliasesOutputAlias
|
||||
// @failure 400
|
||||
// @failure 401
|
||||
// @router /api/aliases [get]
|
||||
@@ -34,10 +34,10 @@ func (self *Handlers) GetAliases(
|
||||
}
|
||||
}
|
||||
|
||||
var output []GetAliasesOutputAlias
|
||||
var output []getAliasesOutputAlias
|
||||
|
||||
for _, alias := range aliases {
|
||||
output = append(output, GetAliasesOutputAlias{
|
||||
output = append(output, getAliasesOutputAlias{
|
||||
Id: alias.Id,
|
||||
Name: alias.Name,
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type GetImagesOutputImage struct {
|
||||
type getImagesOutputImage struct {
|
||||
Id int64 `json:"id"`
|
||||
Extension string `json:"extension"`
|
||||
Uploader string `json:"uploadedUserId"`
|
||||
@@ -25,7 +25,7 @@ type GetImagesOutputImage struct {
|
||||
//
|
||||
// @param images query []int64 false "Image Ids" attribute(optional)
|
||||
// @param aliases query []int64 false "Alias Ids" attribute(optional)
|
||||
// @success 200 {object} []GetImagesOutputImage
|
||||
// @success 200 {object} []getImagesOutputImage
|
||||
// @failure 400
|
||||
// @failure 401
|
||||
// @router /api/images [get]
|
||||
@@ -83,13 +83,13 @@ func (self *Handlers) GetImages(
|
||||
}
|
||||
}
|
||||
|
||||
var output []GetImagesOutputImage
|
||||
var output []getImagesOutputImage
|
||||
for _, img := range images {
|
||||
var aliases []int64
|
||||
for _, alias := range img.Aliases {
|
||||
aliases = append(aliases, alias.Id)
|
||||
}
|
||||
output = append(output, GetImagesOutputImage{
|
||||
output = append(output, getImagesOutputImage{
|
||||
Id: img.Id,
|
||||
Extension: img.Extension,
|
||||
Uploader: img.Uploader,
|
||||
|
||||
@@ -10,18 +10,18 @@ import (
|
||||
"github.com/uptrace/bunrouter"
|
||||
)
|
||||
|
||||
type PostGenLoginUrlInput struct {
|
||||
type postGenLoginUrlInput struct {
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
type PostGenLoginUrlOutput struct {
|
||||
type postGenLoginUrlOutput struct {
|
||||
LoginUrl string `json:"loginUrl"`
|
||||
}
|
||||
|
||||
// PostGenLoginUrl
|
||||
//
|
||||
// @param payload body PostGenLoginUrlInput true "Payload"
|
||||
// @success 200 {object} PostGenLoginUrlOutput "Payload"
|
||||
// @param payload body postGenLoginUrlInput true "Payload"
|
||||
// @success 200 {object} postGenLoginUrlOutput "Payload"
|
||||
// @failure 400
|
||||
// @router /auth/gen-login-url [post]
|
||||
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 {
|
||||
return middlewares.HTTPError{
|
||||
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") +
|
||||
"/login?" +
|
||||
"token=" + token,
|
||||
|
||||
@@ -63,14 +63,10 @@ func (self *Handlers) PostLogin(
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "refresh_token",
|
||||
Value: session.RefreshToken,
|
||||
Path: "/",
|
||||
Secure: viper.GetBool("https"),
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Name: "refresh_token",
|
||||
Value: session.RefreshToken,
|
||||
Expires: time.Now().Add(time.Duration(
|
||||
viper.GetInt64("refresh-token-timeout")) * time.Second),
|
||||
viper.GetInt64("REFRESH_TOKEN_TIMEOUT")) * time.Second),
|
||||
})
|
||||
|
||||
return utils.Success(w)
|
||||
|
||||
@@ -184,22 +184,6 @@ func (self *BunDatabase) UpdateAliases(
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.NewSelect().
|
||||
Model(&aliases).
|
||||
Where("name IN (?)", bun.In(aliasNames)).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.NewDelete().
|
||||
Model((*models.AliasImage)(nil)).
|
||||
Where("image_id = ?", imageId).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rels []models.AliasImage
|
||||
for _, alias := range aliases {
|
||||
rels = append(rels, models.AliasImage{
|
||||
@@ -215,17 +199,6 @@ func (self *BunDatabase) UpdateAliases(
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.NewDelete().
|
||||
Model((*models.Alias)(nil)).
|
||||
Where("NOT EXISTS (?)", tx.NewSelect().
|
||||
Model((*models.AliasImage)(nil)).
|
||||
Where("alias.id = alias_image.alias_id").
|
||||
Limit(1)).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -259,16 +232,6 @@ func (self *BunDatabase) DeleteImage(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.NewDelete().
|
||||
Model((*models.Alias)(nil)).
|
||||
Where("NOT EXISTS (?)", tx.NewSelect().
|
||||
Model((*models.AliasImage)(nil)).
|
||||
Where("alias.id = alias_image.alias_id").
|
||||
Limit(1)).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -292,16 +255,6 @@ func (self *BunDatabase) DeleteAlias(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.NewDelete().
|
||||
Model((*models.Image)(nil)).
|
||||
Where("NOT EXISTS (?)", tx.NewSelect().
|
||||
Model((*models.AliasImage)(nil)).
|
||||
Where("image.id = alias_image.image_id").
|
||||
Limit(1)).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package middlewares
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gitea.konchin.com/go2025/backend/interfaces"
|
||||
"gitea.konchin.com/go2025/backend/models"
|
||||
@@ -47,12 +46,10 @@ func refreshAccessToken(
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "access_token",
|
||||
Value: ret,
|
||||
Path: "/",
|
||||
Secure: viper.GetBool("https"),
|
||||
Expires: time.Now().Add(time.Duration(
|
||||
viper.GetInt64("access-token-timeout")) * time.Second),
|
||||
Name: "access_token",
|
||||
Value: ret,
|
||||
Path: "/",
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
@@ -62,8 +62,7 @@ func (self *Handlers) CheckRefreshToken(
|
||||
Name: "refresh_token",
|
||||
Value: session.RefreshToken,
|
||||
Path: "/",
|
||||
Secure: viper.GetBool("https"),
|
||||
Expires: claim.ExpiresAt.Time,
|
||||
Secure: false,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ var client *resty.Client
|
||||
func Test_00_Healthz(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
resp, err := client.R().
|
||||
Get("/healthz")
|
||||
Get("http://localhost:8080/healthz")
|
||||
if err == nil && resp.StatusCode() == http.StatusOK {
|
||||
return
|
||||
}
|
||||
@@ -24,5 +24,4 @@ func Test_00_Healthz(t *testing.T) {
|
||||
|
||||
func init() {
|
||||
client = resty.New()
|
||||
client.SetBaseURL("http://localhost:8080")
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func Test_01_Login(t *testing.T) {
|
||||
t.Run("check preshared key failed", func(t *testing.T) {
|
||||
resp, err := client.R().
|
||||
SetBody(`{"userId": "testuser1"}`).
|
||||
Post("/bot/auth/gen-login-url")
|
||||
Post("http://localhost:8080/auth/gen-login-url")
|
||||
if err != nil {
|
||||
t.Fatal("request failed")
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func Test_01_Login(t *testing.T) {
|
||||
SetBody(`{"userId": "testuser1"}`).
|
||||
SetAuthToken("poop").
|
||||
SetResult(&payload).
|
||||
Post("/bot/auth/gen-login-url")
|
||||
Post("http://localhost:8080/auth/gen-login-url")
|
||||
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Fatal("failed to get login url")
|
||||
@@ -46,8 +46,7 @@ func Test_01_Login(t *testing.T) {
|
||||
|
||||
resp, err = client.R().
|
||||
SetBody(loginPayload{Token: loginUrl.Query().Get("token")}).
|
||||
SetAuthToken("poop").
|
||||
Post("/auth/login")
|
||||
Post(loginUrl.Scheme + "://" + loginUrl.Host + "/auth/login")
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Fatal("failed to login")
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func Test_02_PostImage(t *testing.T) {
|
||||
resp, err := client.R().
|
||||
SetBody(rawImage).
|
||||
SetResult(&image).
|
||||
Post("/api/image")
|
||||
Post("http://localhost:8080/api/image")
|
||||
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
|
||||
@@ -3,7 +3,6 @@ package tests
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -12,62 +11,15 @@ type putImageAliasPayload struct {
|
||||
}
|
||||
|
||||
func Test_03_PutImageAliases(t *testing.T) {
|
||||
t.Run("Put one alias", func(t *testing.T) {
|
||||
payload := putImageAliasPayload{
|
||||
Aliases: []string{"huh"},
|
||||
}
|
||||
resp, err := client.R().
|
||||
SetBody(payload).
|
||||
Put(fmt.Sprintf("/api/image/%d/aliases", image.Id))
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to put image alias")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Put many alias", func(t *testing.T) {
|
||||
payload := putImageAliasPayload{
|
||||
Aliases: []string{"huh", "testalias1", "testalias2"},
|
||||
}
|
||||
resp, err := client.R().
|
||||
SetBody(payload).
|
||||
Put(fmt.Sprintf("/api/image/%d/aliases", image.Id))
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to put image alias")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Remove alias", func(t *testing.T) {
|
||||
payload := putImageAliasPayload{
|
||||
Aliases: []string{"huh", "testalias1"},
|
||||
}
|
||||
resp, err := client.R().
|
||||
SetBody(payload).
|
||||
Put(fmt.Sprintf("/api/image/%d/aliases", image.Id))
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to put image alias")
|
||||
}
|
||||
|
||||
resp, err = client.R().
|
||||
SetResult(&aliases).
|
||||
Get("/api/aliases")
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to get aliases")
|
||||
}
|
||||
|
||||
for _, alias := range aliases {
|
||||
if alias.Name == "testalias2" {
|
||||
t.Logf("%+v", resp)
|
||||
resp, _ = client.R().
|
||||
SetQueryParam("aliases",
|
||||
strconv.FormatInt(alias.Id, 10)).
|
||||
Get("/api/images")
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("alias should be deleted")
|
||||
}
|
||||
}
|
||||
})
|
||||
payload := putImageAliasPayload{
|
||||
Aliases: []string{"huh"},
|
||||
}
|
||||
resp, err := client.R().
|
||||
SetBody(payload).
|
||||
Put(fmt.Sprintf("http://localhost:8080/api/image/%d/aliases",
|
||||
image.Id))
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to put image alias")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ var aliases []aliasPayload
|
||||
func Test_04_GetAliases(t *testing.T) {
|
||||
resp, err := client.R().
|
||||
SetResult(&aliases).
|
||||
Get("/api/aliases")
|
||||
Get("http://localhost:8080/api/aliases")
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to get aliases")
|
||||
|
||||
@@ -7,11 +7,9 @@ import (
|
||||
)
|
||||
|
||||
func Test_07_DeleteAlias(t *testing.T) {
|
||||
deleteId := aliases[0].Id
|
||||
|
||||
resp, err := client.R().
|
||||
Delete(fmt.Sprintf("http://localhost:8080/api/alias/%d",
|
||||
deleteId))
|
||||
aliases[0].Id))
|
||||
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("failed to delete alias")
|
||||
@@ -25,10 +23,7 @@ func Test_07_DeleteAlias(t *testing.T) {
|
||||
t.Fatal("failed to get aliases")
|
||||
}
|
||||
|
||||
for _, alias := range aliases {
|
||||
if alias.Id == deleteId {
|
||||
t.Logf("%+v", resp)
|
||||
t.Fatal("alias not deleted")
|
||||
}
|
||||
if len(aliases) > 0 {
|
||||
t.Fatal("alias not deleted")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,40 +4,14 @@ import (
|
||||
"context"
|
||||
|
||||
"gitea.konchin.com/go2025/backend/models"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func InitDB(ctx context.Context, db *bun.DB) error {
|
||||
db.RegisterModel(
|
||||
return db.ResetModel(ctx,
|
||||
(*models.AliasImage)(nil),
|
||||
(*models.Alias)(nil),
|
||||
(*models.Image)(nil),
|
||||
(*models.Session)(nil),
|
||||
)
|
||||
if viper.GetBool("reset") {
|
||||
return db.ResetModel(ctx,
|
||||
(*models.AliasImage)(nil),
|
||||
(*models.Alias)(nil),
|
||||
(*models.Image)(nil),
|
||||
(*models.Session)(nil),
|
||||
)
|
||||
} else {
|
||||
modls := []any{
|
||||
(*models.AliasImage)(nil),
|
||||
(*models.Alias)(nil),
|
||||
(*models.Image)(nil),
|
||||
(*models.Session)(nil),
|
||||
}
|
||||
for _, model := range modls {
|
||||
_, err := db.NewCreateTable().
|
||||
Model(model).
|
||||
IfNotExists().
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user