added workflows and fixed router
This commit is contained in:
51
.gitea/workflows/build.yml
Normal file
51
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Build and Push Docker Image
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and push image
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUNNER_TOOL_CACHE: /toolcache
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-docker-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-docker-
|
||||
|
||||
- name: Login to Docker Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.syntinel.dev
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: |
|
||||
registry.syntinel.dev/junk2jive-server:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
|
||||
26
.gitea/workflows/linter.yml
Normal file
26
.gitea/workflows/linter.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v7
|
||||
with:
|
||||
version: v2.0
|
||||
32
.gitea/workflows/test.yaml
Normal file
32
.gitea/workflows/test.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Run Go Tests
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
|
||||
- name: Cache Go Modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Build
|
||||
run: go build ./cmd/junk2jive
|
||||
|
||||
- name: Go Test
|
||||
run: NONLOCAL_TESTS=1 go test ./... -v
|
||||
|
||||
- name: Coverage Test
|
||||
run:
|
||||
|
||||
@@ -9,32 +9,10 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse command line flags
|
||||
var configPath string
|
||||
flag.StringVar(&configPath, "config", "", "Path to config file")
|
||||
flag.Parse()
|
||||
|
||||
// If no config file specified, look for it in default locations
|
||||
if configPath == "" {
|
||||
// Check current directory
|
||||
if _, err := os.Stat("config.json"); err == nil {
|
||||
configPath = "config.json"
|
||||
} else {
|
||||
// Check config directory relative to executable
|
||||
exePath, err := os.Executable()
|
||||
if err == nil {
|
||||
potentialPath := filepath.Join(filepath.Dir(exePath), "../config/config.json")
|
||||
if _, err := os.Stat(potentialPath); err == nil {
|
||||
configPath = potentialPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
cfg, err := config.Load(configPath)
|
||||
|
||||
99
coverage.out
Normal file
99
coverage.out
Normal file
@@ -0,0 +1,99 @@
|
||||
mode: set
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config/config.go:13.27,19.2 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config/config.go:21.57,22.45 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config/config.go:22.45,24.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config/config.go:25.5,25.24 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:20.61,21.57 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:21.57,23.68 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:23.68,26.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:28.9,30.23 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:30.23,33.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/text.go:35.9,41.44 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:16.59,17.57 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:17.57,19.62 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:19.62,22.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:24.9,25.23 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:25.23,28.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:29.9,33.23 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:33.23,36.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:37.9,41.57 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:41.57,44.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:47.9,49.23 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:49.23,52.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:55.9,58.23 4 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:58.23,61.10 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers/visual.go:64.9,70.44 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:29.44,34.2 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:36.44,66.55 13 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:66.55,67.46 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:67.46,70.74 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:70.74,72.5 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:74.4,75.65 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:78.2,78.11 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/routes/routes.go:82.39,84.2 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:33.53,35.2 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:37.67,58.19 5 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:58.19,60.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:62.5,63.19 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:63.19,65.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:67.5,72.19 5 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:72.19,74.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:75.5,78.72 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:78.72,80.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:82.5,82.35 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:82.35,84.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/openai.go:86.5,86.52 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:25.57,27.2 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:29.77,32.19 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:32.19,34.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:35.5,43.19 5 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:43.19,45.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:48.5,48.49 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:48.49,50.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:53.5,58.19 4 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:58.19,60.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:62.5,67.19 4 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:67.19,69.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:70.5,74.72 3 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:74.72,76.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:79.5,80.53 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:80.53,82.6 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services/robowflow.go:84.5,84.24 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:25.13,31.62 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:31.62,32.31 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:32.31,33.27 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:34.22,38.8 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:41.5,41.13 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:45.2,45.25 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:48.66,49.39 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:49.39,50.20 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:50.20,52.4 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:53.3,55.26 2 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/application.go:55.26,57.4 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:13.69,14.46 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:14.46,15.72 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:15.72,32.47 5 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:32.47,34.5 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:36.4,36.80 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:36.80,38.5 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger/request.go:38.10,40.5 1 0
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:13.43,16.2 2 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:18.38,19.63 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:19.63,21.3 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:22.2,22.12 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:25.76,30.2 4 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response/response.go:32.100,35.2 2 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:19.25,23.2 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:25.36,27.16 2 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:27.16,29.3 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:30.2,30.11 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:33.91,37.45 3 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:37.45,39.3 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:41.2,41.54 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:41.54,43.3 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:45.2,47.16 3 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:50.100,51.46 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:51.46,52.72 1 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:52.72,56.24 3 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:56.24,59.5 2 1
|
||||
gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter/limiter.go:61.4,61.24 1 1
|
||||
20
go.mod
20
go.mod
@@ -1,3 +1,23 @@
|
||||
module gitea.miguelmuniz.com/rogueking/junk2jive-server
|
||||
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lmittmann/tint v1.0.7
|
||||
github.com/stretchr/testify v1.10.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/time v0.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
41
go.sum
Normal file
41
go.sum
Normal file
@@ -0,0 +1,41 @@
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
|
||||
github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1,26 +1,56 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
OpenAIKey string
|
||||
RoboflowKey string
|
||||
Port string
|
||||
OllamaKey string
|
||||
RoboflowKey string
|
||||
Port string
|
||||
}
|
||||
|
||||
func LoadConfig() *Config {
|
||||
return &Config{
|
||||
OpenAIKey: os.Getenv("OPENAI_API_KEY"),
|
||||
RoboflowKey: os.Getenv("ROBOFLOW_API_KEY"),
|
||||
Port: getEnvWithDefault("PORT", "8080"),
|
||||
}
|
||||
type Flags struct {
|
||||
OllamaKey string
|
||||
RoboflowKey int
|
||||
Port string
|
||||
}
|
||||
|
||||
func getEnvWithDefault(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
func LoadEnv(filePath string) {
|
||||
if err := godotenv.Load(filePath); err != nil {
|
||||
logger.Fatal("Error loading .env file")
|
||||
}
|
||||
}
|
||||
|
||||
func DeclareFlags() *Flags {
|
||||
env := flag.String("e", "development", "Set the environment ( development | production )")
|
||||
port := flag.Int("p", 0, "Set the port that will be used")
|
||||
envFile := flag.String("ef", "", "Set the imported env file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
return &Flags{
|
||||
OllamaKey: *env,
|
||||
RoboflowKey: *port,
|
||||
Port: *envFile,
|
||||
}
|
||||
}
|
||||
|
||||
func ConfigPort(flags *Flags) string {
|
||||
var port string
|
||||
if flags.Port != 0 {
|
||||
port = fmt.Sprintf(":%d", flags.Port)
|
||||
} else {
|
||||
if os.Getenv("APP_PORT") == "" {
|
||||
logger.Warn("No port specified, default port :80 used...")
|
||||
return ":80"
|
||||
}
|
||||
port = fmt.Sprintf(":%s", os.Getenv("APP_PORT"))
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/services"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services"
|
||||
)
|
||||
|
||||
type TextPromptRequest struct {
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/services"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/services"
|
||||
)
|
||||
|
||||
func VisualAIHandler(cfg *config.Config) http.HandlerFunc {
|
||||
|
||||
64
internal/limiter/limiter.go
Normal file
64
internal/limiter/limiter.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response"
|
||||
)
|
||||
|
||||
type RateLimiter struct {
|
||||
limiters map[string]map[string]*rate.Limiter // map[path][ip] => limiter
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func New() *RateLimiter {
|
||||
return &RateLimiter{
|
||||
limiters: make(map[string]map[string]*rate.Limiter),
|
||||
}
|
||||
}
|
||||
|
||||
func GetIP(r *http.Request) string {
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func (rl *RateLimiter) Get(path string, ip string, r rate.Limit, burst int) *rate.Limiter {
|
||||
rl.mu.Lock()
|
||||
defer rl.mu.Unlock()
|
||||
|
||||
if _, exists := rl.limiters[path]; !exists {
|
||||
rl.limiters[path] = make(map[string]*rate.Limiter)
|
||||
}
|
||||
|
||||
if limiter, exists := rl.limiters[path][ip]; exists {
|
||||
return limiter
|
||||
}
|
||||
|
||||
limiter := rate.NewLimiter(r, burst)
|
||||
rl.limiters[path][ip] = limiter
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (rl *RateLimiter) Middleware(rateLimit rate.Limit, burst int) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ip := GetIP(r)
|
||||
limiter := rl.Get(r.URL.Path, ip, rateLimit, burst)
|
||||
|
||||
if !limiter.Allow() {
|
||||
response.RespondWithError(w, r, http.StatusTooManyRequests, http.StatusText(http.StatusTooManyRequests), fmt.Errorf("too many requests"))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
136
internal/limiter/limiter_test.go
Normal file
136
internal/limiter/limiter_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
func TestGetIP(t *testing.T) {
|
||||
req1 := &http.Request{RemoteAddr: "192.168.1.100:12345"}
|
||||
ip1 := GetIP(req1)
|
||||
assert.Equal(t, "192.168.1.100", ip1)
|
||||
|
||||
req2 := &http.Request{RemoteAddr: "invalid-address"}
|
||||
ip2 := GetIP(req2)
|
||||
assert.Equal(t, "invalid-address", ip2)
|
||||
}
|
||||
|
||||
func TestGetLimiter(t *testing.T) {
|
||||
rl := New()
|
||||
|
||||
limiter1 := rl.Get("/test", "127.0.0.1", 1, 1)
|
||||
require.NotNil(t, limiter1)
|
||||
|
||||
limiter2 := rl.Get("/test", "127.0.0.1", 1, 1)
|
||||
assert.Same(t, limiter1, limiter2)
|
||||
|
||||
limiter3 := rl.Get("/another", "127.0.0.1", 1, 1)
|
||||
assert.NotSame(t, limiter1, limiter3)
|
||||
|
||||
limiter4 := rl.Get("/test", "192.168.0.1", 1, 1)
|
||||
assert.NotSame(t, limiter1, limiter4)
|
||||
}
|
||||
|
||||
func TestMiddleware(t *testing.T) {
|
||||
rl := New()
|
||||
rateLimit := rate.Limit(1)
|
||||
burst := 1
|
||||
|
||||
var nextCalled int32
|
||||
|
||||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
atomic.StoreInt32(&nextCalled, 1)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
handler := rl.Middleware(rateLimit, burst)(nextHandler)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
req.RemoteAddr = net.JoinHostPort("127.0.0.1", "12345")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
atomic.StoreInt32(&nextCalled, 0)
|
||||
handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, int32(1), atomic.LoadInt32(&nextCalled))
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
atomic.StoreInt32(&nextCalled, 0)
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.NotEqual(t, http.StatusOK, rr.Code)
|
||||
|
||||
assert.Equal(t, http.StatusTooManyRequests, rr.Code)
|
||||
assert.Equal(t, int32(0), atomic.LoadInt32(&nextCalled))
|
||||
}
|
||||
|
||||
func TestMiddlewareIndependent(t *testing.T) {
|
||||
rl := New()
|
||||
rateLimit := rate.Limit(1)
|
||||
burst := 1
|
||||
|
||||
var handlerCallCount int32
|
||||
|
||||
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
atomic.AddInt32(&handlerCallCount, 1)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
middleware := rl.Middleware(rateLimit, burst)
|
||||
|
||||
req1 := httptest.NewRequest(http.MethodGet, "/path1", nil)
|
||||
req1.RemoteAddr = net.JoinHostPort("127.0.0.1", "1111")
|
||||
|
||||
req2 := httptest.NewRequest(http.MethodGet, "/path2", nil)
|
||||
req2.RemoteAddr = net.JoinHostPort("127.0.0.2", "2222")
|
||||
|
||||
rr1 := httptest.NewRecorder()
|
||||
middleware(nextHandler).ServeHTTP(rr1, req1)
|
||||
assert.Equal(t, http.StatusOK, rr1.Code)
|
||||
|
||||
rr2 := httptest.NewRecorder()
|
||||
middleware(nextHandler).ServeHTTP(rr2, req2)
|
||||
assert.Equal(t, http.StatusOK, rr2.Code)
|
||||
|
||||
assert.Equal(t, int32(2), atomic.LoadInt32(&handlerCallCount))
|
||||
}
|
||||
|
||||
func TestLimiterConcurrency(t *testing.T) {
|
||||
rl := New()
|
||||
path := "/concurrent"
|
||||
ip := "127.0.0.1"
|
||||
|
||||
const goroutines = 50
|
||||
limiterCh := make(chan *rate.Limiter, goroutines)
|
||||
doneCh := make(chan struct{})
|
||||
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
limiter := rl.Get(path, ip, 5, 10)
|
||||
limiterCh <- limiter
|
||||
doneCh <- struct{}{}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < goroutines; i++ {
|
||||
<-doneCh
|
||||
}
|
||||
|
||||
close(limiterCh)
|
||||
var firstLimiter *rate.Limiter
|
||||
for lim := range limiterCh {
|
||||
if firstLimiter == nil {
|
||||
firstLimiter = lim
|
||||
} else {
|
||||
assert.Equal(t, firstLimiter, lim)
|
||||
}
|
||||
}
|
||||
}
|
||||
59
internal/logger/application.go
Normal file
59
internal/logger/application.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/lmittmann/tint"
|
||||
)
|
||||
|
||||
var logger *slog.Logger
|
||||
|
||||
const LevelFatal slog.Level = slog.LevelError + 4
|
||||
|
||||
var (
|
||||
Debug = makeLogFunc(slog.LevelDebug)
|
||||
Info = makeLogFunc(slog.LevelInfo)
|
||||
Warn = makeLogFunc(slog.LevelWarn)
|
||||
Error = makeLogFunc(slog.LevelError)
|
||||
Fatal = makeLogFunc(LevelFatal)
|
||||
)
|
||||
|
||||
func init() {
|
||||
w := os.Stderr
|
||||
logger = slog.New(
|
||||
tint.NewHandler(w, &tint.Options{
|
||||
Level: slog.LevelDebug,
|
||||
TimeFormat: time.RFC3339,
|
||||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||
if a.Key == slog.LevelKey {
|
||||
switch a.Value.Any() {
|
||||
case LevelFatal:
|
||||
return slog.Attr{
|
||||
Key: slog.LevelKey,
|
||||
Value: slog.StringValue("\x1b[91mFATAL\x1b[0m"),
|
||||
}
|
||||
}
|
||||
}
|
||||
return a
|
||||
},
|
||||
}),
|
||||
)
|
||||
slog.SetDefault(logger)
|
||||
}
|
||||
|
||||
func makeLogFunc(level slog.Level) func(msg string, args ...any) {
|
||||
return func(msg string, args ...any) {
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(msg, args...)
|
||||
}
|
||||
logger.Log(context.Background(), level, msg)
|
||||
|
||||
if level == LevelFatal {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
43
internal/logger/request.go
Normal file
43
internal/logger/request.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response"
|
||||
)
|
||||
|
||||
func Middleware(logger *zap.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||
|
||||
next.ServeHTTP(ww, r)
|
||||
|
||||
logFields := []zap.Field{
|
||||
zap.String("method", r.Method),
|
||||
zap.String("path", r.URL.Path),
|
||||
zap.String("query", r.URL.RawQuery),
|
||||
zap.Int("status", ww.Status()),
|
||||
zap.String("ip", r.RemoteAddr),
|
||||
zap.String("user-agent", r.UserAgent()),
|
||||
zap.Duration("latency", time.Since(start)),
|
||||
zap.String("time", time.Now().Format(time.RFC3339)),
|
||||
}
|
||||
|
||||
if err := response.GetError(r); err != nil {
|
||||
logFields = append(logFields, zap.Error(err))
|
||||
}
|
||||
|
||||
if ww.Status() >= http.StatusBadRequest && ww.Status() != http.StatusTeapot {
|
||||
logger.Error("Request failed", logFields...)
|
||||
} else {
|
||||
logger.Info("Request", logFields...)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
35
internal/response/response.go
Normal file
35
internal/response/response.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type errorKeyType struct{}
|
||||
|
||||
var errorContextKey = errorKeyType{}
|
||||
|
||||
func SetError(r *http.Request, err error) {
|
||||
ctx := context.WithValue(r.Context(), errorContextKey, err)
|
||||
*r = *r.WithContext(ctx)
|
||||
}
|
||||
|
||||
func GetError(r *http.Request) error {
|
||||
if err, ok := r.Context().Value(errorContextKey).(error); ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RespondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
|
||||
response, _ := json.Marshal(payload)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(response)
|
||||
}
|
||||
|
||||
func RespondWithError(w http.ResponseWriter, r *http.Request, code int, message string, err error) {
|
||||
SetError(r, err)
|
||||
RespondWithJSON(w, code, map[string]string{"error": message})
|
||||
}
|
||||
65
internal/response/response_test.go
Normal file
65
internal/response/response_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetAndGetError(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/test", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Nil(t, GetError(req))
|
||||
|
||||
testErr := errors.New("test error")
|
||||
SetError(req, testErr)
|
||||
errFromReq := GetError(req)
|
||||
require.NotNil(t, errFromReq)
|
||||
assert.Equal(t, testErr.Error(), errFromReq.Error())
|
||||
}
|
||||
|
||||
func TestRespondWithJSON(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
payload := map[string]string{"message": "hello"}
|
||||
code := http.StatusOK
|
||||
|
||||
RespondWithJSON(rr, code, payload)
|
||||
|
||||
assert.Equal(t, code, rr.Code)
|
||||
assert.Equal(t, "application/json", rr.Header().Get("Content-Type"))
|
||||
|
||||
var data map[string]string
|
||||
err := json.Unmarshal(rr.Body.Bytes(), &data)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, payload, data)
|
||||
}
|
||||
|
||||
func TestRespondWithError(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("GET", "/test", nil)
|
||||
require.NoError(t, err)
|
||||
code := http.StatusBadRequest
|
||||
message := "error occurred"
|
||||
testErr := errors.New("test error")
|
||||
|
||||
RespondWithError(rr, req, code, message, testErr)
|
||||
|
||||
errFromReq := GetError(req)
|
||||
require.NotNil(t, errFromReq)
|
||||
assert.Equal(t, testErr.Error(), errFromReq.Error())
|
||||
|
||||
assert.Equal(t, code, rr.Code)
|
||||
assert.Equal(t, "application/json", rr.Header().Get("Content-Type"))
|
||||
|
||||
var data map[string]string
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &data)
|
||||
require.NoError(t, err)
|
||||
expected := map[string]string{"error": message}
|
||||
assert.Equal(t, expected, data)
|
||||
}
|
||||
@@ -2,37 +2,83 @@ package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/junk2jive-server/internal/handlers"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/config"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/handlers"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/limiter"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/logger"
|
||||
"gitea.miguelmuniz.com/rogueking/junk2jive-server/internal/response"
|
||||
)
|
||||
|
||||
func SetupRoutes(cfg *config.Config) *chi.Mux {
|
||||
r := chi.NewRouter()
|
||||
// Router encapsulates the Chi router and its dependencies
|
||||
type Router struct {
|
||||
router *chi.Mux
|
||||
config *config.Config
|
||||
logger *zap.Logger
|
||||
rateLimiter *limiter.RateLimiter
|
||||
}
|
||||
|
||||
// Middleware
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
// NewRouter creates and configures a new Router instance
|
||||
func NewRouter(cfg *config.Config) *Router {
|
||||
return &Router{
|
||||
router: chi.NewRouter(),
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func SetupRouter(origins []string) *Router {
|
||||
router := chi.NewRouter()
|
||||
|
||||
router.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: origins,
|
||||
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Content-Type"},
|
||||
AllowedHeaders: []string{"Content-Type", "X-CSRF-Token"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
|
||||
// Static files
|
||||
fileServer := http.FileServer(http.Dir("./static"))
|
||||
r.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
||||
zlogger, _ := zap.NewProduction()
|
||||
defer zlogger.Sync()
|
||||
|
||||
// API routes
|
||||
r.Get("/", handlers.HomeHandler)
|
||||
r.Route("/api", func(r chi.Router) {
|
||||
r.Post("/text-prompt", handlers.TextPromptHandler(cfg))
|
||||
r.Post("/ai-prompt", handlers.VisualAIHandler(cfg))
|
||||
rl := limiter.New()
|
||||
|
||||
router.Use(middleware.RequestID)
|
||||
router.Use(middleware.RealIP)
|
||||
router.Use(middleware.Logger)
|
||||
router.Use(middleware.Recoverer)
|
||||
router.Use(middleware.Timeout(60 * time.Second))
|
||||
|
||||
router.Use(logger.Middleware(zlogger))
|
||||
|
||||
r := Router{
|
||||
router: router,
|
||||
logger: zlogger,
|
||||
rateLimiter: rl,
|
||||
}
|
||||
|
||||
r.router.Route("/v1/api", func(apiRouter chi.Router) {
|
||||
apiRouter.Group(func(subRouter chi.Router) {
|
||||
subRouter.Use(r.rateLimiter.Middleware(rate.Every(1*time.Second), 30))
|
||||
|
||||
subRouter.Get("/coffee", func(w http.ResponseWriter, r *http.Request) {
|
||||
response.RespondWithJSON(w, http.StatusTeapot, map[string]string{"error": "I'm A Teapot!"})
|
||||
})
|
||||
|
||||
subRouter.Post("/text", handlers.TextPromptHandler(r.config))
|
||||
subRouter.Post("/visual", handlers.VisualAIHandler(r.config))
|
||||
})
|
||||
})
|
||||
return &r
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Router) GetRouter() *chi.Mux {
|
||||
return r.router
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user