Fly time
This commit is contained in:
parent
8e4edf31c7
commit
ec1f7a5a9d
20 changed files with 251 additions and 493 deletions
11
.env.example
11
.env.example
|
@ -1,12 +1,11 @@
|
|||
# This file is used to store environment variables for the application
|
||||
# Database Connection Information
|
||||
POSTGRES_PASSWORD=password
|
||||
POSTGRES_USER=username
|
||||
DB_URL="libsql://your-database-name.turso.io"
|
||||
DB_TOKEN="your-auth-token"
|
||||
|
||||
# Security
|
||||
ENCRYPTION_KEY="hOzXzSwDSuU41PMtMHm9O/nqf1X+jTB3MOgVDSPXC5o="
|
||||
SIGNING_KEY="hOzXzSwDSuU41PMtMHm9O/nqf1X+jTB3MOgVDSPXC5o="
|
||||
AUTH_SECRET="hOzXzSwDSuU41PMtMHm9O/nqf1X+jTB3MOgVDSPXC5o="
|
||||
ENCRYPTION_KEY=""
|
||||
SIGNING_KEY=""
|
||||
AUTH_SECRET=""
|
||||
|
||||
# Feature Flags
|
||||
DEVMODE=true
|
||||
|
|
39
.github/workflows/deploy.yml
vendored
39
.github/workflows/deploy.yml
vendored
|
@ -1,39 +0,0 @@
|
|||
name: Build and Push to GitHub Container Registry
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build the Docker image
|
||||
run: docker build . --file Dockerfile --tag ${{ github.event.repository.name }}:${{ github.sha }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Tag images
|
||||
run: |
|
||||
docker tag ${{ github.event.repository.name }}:${{ github.sha }} ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}
|
||||
docker tag ${{ github.event.repository.name }}:${{ github.sha }} ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest
|
||||
|
||||
- name: Push images to GitHub Container Registry
|
||||
run: |
|
||||
docker push ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}
|
||||
docker push ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest
|
18
.github/workflows/fly-deploy.yml
vendored
Normal file
18
.github/workflows/fly-deploy.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
|
||||
|
||||
name: Fly Deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy app
|
||||
runs-on: ubuntu-latest
|
||||
concurrency: deploy-group # optional: ensure only one action runs at a time
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
- run: flyctl deploy --remote-only
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.22.3 as build
|
||||
FROM golang:1.23.1 as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -11,4 +11,4 @@ FROM gcr.io/distroless/base-debian12
|
|||
|
||||
COPY --from=build /go/bin/app /
|
||||
|
||||
CMD [ "/app" ]
|
||||
CMD [ "/app" ]
|
||||
|
|
|
@ -20,14 +20,14 @@ func CreateRoomHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "room name is required"})
|
||||
}
|
||||
|
||||
_, err = lib.CreateRoom(lib.GetDBPool(), currentSession.UserID, roomName)
|
||||
_, err = lib.CreateRoom(lib.GetDB(), currentSession.UserID, roomName)
|
||||
if err != nil {
|
||||
println("Error creating room: ", err.Error())
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to create room"})
|
||||
}
|
||||
|
||||
// Retrieve the updated list of rooms for the user
|
||||
rooms, err := lib.GetRoomsByUserID(lib.GetDBPool(), currentSession.UserID)
|
||||
rooms, err := lib.GetRoomsByUserID(lib.GetDB(), currentSession.UserID)
|
||||
if err != nil {
|
||||
println("Error retrieving rooms: ", err.Error())
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to retrieve rooms"})
|
||||
|
|
|
@ -21,13 +21,13 @@ func DeleteRoomHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "room ID is required"})
|
||||
}
|
||||
|
||||
deletionError := lib.DeleteRoom(lib.GetDBPool(), roomID)
|
||||
deletionError := lib.DeleteRoom(lib.GetDB(), roomID)
|
||||
if deletionError != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to delete room"})
|
||||
}
|
||||
|
||||
// Retrieve the updated list of rooms for the user
|
||||
rooms, fetchError := lib.GetRoomsByUserID(lib.GetDBPool(), currentSession.UserID)
|
||||
rooms, fetchError := lib.GetRoomsByUserID(lib.GetDB(), currentSession.UserID)
|
||||
if fetchError != nil {
|
||||
println("Error retrieving rooms: ", fetchError.Error())
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to retrieve rooms"})
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
// GetRoomHandler handles the request to get a room by ID.
|
||||
func GetRoomHandler(c echo.Context) error {
|
||||
roomID := c.Param("id")
|
||||
room, err := lib.GetRoomById(lib.GetDBPool(), roomID)
|
||||
room, err := lib.GetRoomById(lib.GetDB(), roomID)
|
||||
if err != nil {
|
||||
// Log the error
|
||||
println("Error retrieving room: ", err.Error())
|
||||
|
|
|
@ -15,7 +15,7 @@ func GetAllRoomsHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "unauthorized"})
|
||||
}
|
||||
|
||||
rooms, err := lib.GetRoomsByUserID(lib.GetDBPool(), currentSession.UserID)
|
||||
rooms, err := lib.GetRoomsByUserID(lib.GetDB(), currentSession.UserID)
|
||||
if err != nil {
|
||||
// Log the error and return an internal server error response
|
||||
println("Error retrieving rooms: ", err.Error())
|
||||
|
|
|
@ -13,7 +13,7 @@ func RegisterUserHandler(c echo.Context) error {
|
|||
password := c.FormValue("password")
|
||||
|
||||
// Check if the email already exists
|
||||
existingUser, err := lib.GetUserByEmail(lib.GetDBPool(), email)
|
||||
existingUser, err := lib.GetUserByEmail(lib.GetDB(), email)
|
||||
if err == nil && existingUser != nil {
|
||||
// User with the given email already exists
|
||||
errorMessage := `<div id="error-message" style="color: red;">An account with this email already exists!</div>`
|
||||
|
@ -28,7 +28,7 @@ func RegisterUserHandler(c echo.Context) error {
|
|||
|
||||
user := lib.User{Name: name, Email: email, Password: hashedPassword}
|
||||
println("User: ", user.Name, user.Email, user.Password)
|
||||
if err := lib.SaveUser(lib.GetDBPool(), &user); err != nil {
|
||||
if err := lib.SaveUser(lib.GetDB(), &user); err != nil {
|
||||
println("Error saving user: ", err.Error())
|
||||
return c.JSON(http.StatusInternalServerError, "Failed to save user")
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ func SignInUserHandler(c echo.Context) error {
|
|||
password := c.FormValue("password")
|
||||
|
||||
// Example validation logic
|
||||
user, err := lib.GetUserByEmail(lib.GetDBPool(), email)
|
||||
user, err := lib.GetUserByEmail(lib.GetDB(), email)
|
||||
if err != nil || !lib.CheckPasswordHash(password, user.Password) {
|
||||
errorMessage := `<div id="error-message" style="color: red;">Incorrect email or password</div>`
|
||||
return c.HTML(http.StatusUnauthorized, errorMessage)
|
||||
|
|
|
@ -12,7 +12,7 @@ func SignOutUserHandler(c echo.Context) error {
|
|||
sessionData, err := lib.GetSessionCookie(c.Request(), "session")
|
||||
if err == nil && sessionData != nil {
|
||||
// Delete the session from the database
|
||||
err = lib.DeleteSession(lib.GetDBPool(), sessionData.SessionID)
|
||||
err = lib.DeleteSession(lib.GetDB(), sessionData.SessionID)
|
||||
if err != nil {
|
||||
log.Printf("Error deleting session: %v", err)
|
||||
}
|
||||
|
|
20
fly.toml
Normal file
20
fly.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# fly.toml app configuration file generated for polloapp on 2024-11-21T00:41:17-06:00
|
||||
#
|
||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||
#
|
||||
|
||||
app = 'polloapp'
|
||||
primary_region = 'ord'
|
||||
|
||||
[build]
|
||||
|
||||
[http_service]
|
||||
internal_port = 3000
|
||||
force_https = true
|
||||
auto_stop_machines = 'stop'
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
processes = ['app']
|
||||
|
||||
[[vm]]
|
||||
size = 'shared-cpu-1x'
|
35
go.mod
35
go.mod
|
@ -1,39 +1,34 @@
|
|||
module pollo
|
||||
|
||||
go 1.22.0
|
||||
go 1.23
|
||||
|
||||
require github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d
|
||||
|
||||
require (
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/coder/websocket v1.8.12 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.14.3 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgtype v1.14.3 // indirect
|
||||
github.com/jackc/puddle v1.3.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
gopkg.in/validator.v2 v2.0.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/gorilla/sessions v1.3.0
|
||||
github.com/jackc/pgx/v4 v4.18.3
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/labstack/echo-contrib v0.17.1
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/svix/svix-webhooks v1.24.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
github.com/svix/svix-webhooks v1.41.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
)
|
||||
|
|
262
go.sum
262
go.sum
|
@ -1,95 +1,28 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus=
|
||||
github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU=
|
||||
github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA=
|
||||
|
@ -97,167 +30,42 @@ github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+k
|
|||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/svix/svix-webhooks v1.24.0 h1:NL8WVC+GpXXJdt0JJ4jfUsCK0CjYxZiU2bM2nRvXaW0=
|
||||
github.com/svix/svix-webhooks v1.24.0/go.mod h1:MPOB9jpp6jPh652WuKeuKIV5m2APqXXOjLxxOe4mDmM=
|
||||
github.com/svix/svix-webhooks v1.41.0 h1:zyVY9eF1L+f1Q3iqXIagzThw4KYoNxAwBD3Q6hojUxc=
|
||||
github.com/svix/svix-webhooks v1.41.0/go.mod h1:MHZT9p7h83h+yuSsBBqZjK7YUOJtv/gukZpmvDDtGQg=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d h1:dOMI4+zEbDI37KGb0TI44GUAwxHF9cMsIoDTJ7UmgfU=
|
||||
github.com/tursodatabase/libsql-client-go v0.0.0-20240902231107-85af5b9d094d/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
|
||||
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
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/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
|
||||
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
|
180
lib/db.go
180
lib/db.go
|
@ -1,139 +1,85 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql"
|
||||
)
|
||||
|
||||
var dbPool *pgxpool.Pool
|
||||
|
||||
// Migration represents a database migration.
|
||||
type Migration struct {
|
||||
Version string
|
||||
SQL string
|
||||
}
|
||||
var db *sql.DB
|
||||
|
||||
// GenerateNewID generates a DB with the format "prefix_randomstring".
|
||||
func GenerateNewID(prefix string) string {
|
||||
randomBytes := make([]byte, 16)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randomBytes))
|
||||
randomBytes := make([]byte, 16)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randomBytes))
|
||||
}
|
||||
|
||||
// Initializes the global database connection pool.
|
||||
func InitializeDBPool(host, user, password, dbname string, port int) error {
|
||||
connString := fmt.Sprintf("postgres://%s:%s@%s:%d/%s", user, password, host, port, dbname)
|
||||
var err error
|
||||
dbPool, err = pgxpool.Connect(context.Background(), connString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// Initialize the database connection
|
||||
func InitializeDB(url, authToken string) error {
|
||||
var err error
|
||||
connectionString := fmt.Sprintf("%s?authToken=%s", url, authToken)
|
||||
db, err = sql.Open("libsql", connectionString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening database: %v", err)
|
||||
}
|
||||
|
||||
// Test the connection
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error connecting to the database: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitializeSchema(dbPool *pgxpool.Pool) error {
|
||||
migrations := []Migration{
|
||||
{
|
||||
Version: "1_create_users_table",
|
||||
SQL: `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password VARCHAR(255) NOT NULL
|
||||
);`,
|
||||
},
|
||||
{
|
||||
Version: "2_create_rooms_table",
|
||||
SQL: `
|
||||
CREATE TABLE IF NOT EXISTS rooms (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
userId TEXT NOT NULL,
|
||||
roomName TEXT,
|
||||
topicName TEXT,
|
||||
visible BOOLEAN NOT NULL DEFAULT false,
|
||||
scale TEXT NOT NULL DEFAULT '0.5,1,2,3,5'
|
||||
);`,
|
||||
},
|
||||
{
|
||||
Version: "3_add_foreign_key_to_rooms",
|
||||
SQL: `
|
||||
ALTER TABLE rooms
|
||||
ADD CONSTRAINT fk_user_id
|
||||
FOREIGN KEY (userId) REFERENCES users(id);`,
|
||||
},
|
||||
{
|
||||
Version: "4_create_index_on_users_email",
|
||||
SQL: `
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
`,
|
||||
},
|
||||
{
|
||||
Version: "5_create_index_on_rooms_userid",
|
||||
SQL: `
|
||||
CREATE INDEX IF NOT EXISTS idx_rooms_userid ON rooms(userId);
|
||||
`,
|
||||
},
|
||||
{
|
||||
Version: "6_create_sessions_table",
|
||||
SQL: `
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
|
||||
`,
|
||||
},
|
||||
}
|
||||
func InitializeSchema() error {
|
||||
if db == nil {
|
||||
return fmt.Errorf("database not initialized")
|
||||
}
|
||||
|
||||
// Ensure the migrations table exists
|
||||
_, err := dbPool.Exec(context.Background(), `
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
version VARCHAR(255) PRIMARY KEY
|
||||
);
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
migrations := []string{
|
||||
`CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS rooms (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
userId TEXT NOT NULL,
|
||||
roomName TEXT,
|
||||
topicName TEXT,
|
||||
visible BOOLEAN DEFAULT false,
|
||||
scale TEXT DEFAULT '0.5,1,2,3,5',
|
||||
FOREIGN KEY (userId) REFERENCES users(id)
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)`,
|
||||
}
|
||||
|
||||
// Apply each migration
|
||||
for _, migration := range migrations {
|
||||
// Check if this migration has already been applied
|
||||
var exists bool
|
||||
err := dbPool.QueryRow(context.Background(), "SELECT EXISTS(SELECT 1 FROM migrations WHERE version = $1)", migration.Version).Scan(&exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, migration := range migrations {
|
||||
_, err := db.Exec(migration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error executing migration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// Apply the migration
|
||||
_, err = dbPool.Exec(context.Background(), migration.SQL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark this migration as applied
|
||||
_, err = dbPool.Exec(context.Background(), "INSERT INTO migrations (version) VALUES ($1)", migration.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns the initialized database connection pool.
|
||||
func GetDBPool() *pgxpool.Pool {
|
||||
return dbPool
|
||||
// GetDB returns the database connection
|
||||
func GetDB() *sql.DB {
|
||||
return db
|
||||
}
|
||||
|
|
45
lib/room.go
45
lib/room.go
|
@ -1,13 +1,10 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
type Room struct {
|
||||
|
@ -21,16 +18,18 @@ type Room struct {
|
|||
}
|
||||
|
||||
// CreateRoom creates a new room in the database.
|
||||
func CreateRoom(dbPool *pgxpool.Pool, userID, roomName string) (*Room, error) {
|
||||
if dbPool == nil {
|
||||
return nil, errors.New("database connection pool is not initialized")
|
||||
func CreateRoom(db *sql.DB, userID, roomName string) (*Room, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("database is not initialized")
|
||||
}
|
||||
|
||||
// Generate a new ID for the room
|
||||
newRoomID := GenerateNewID("room")
|
||||
|
||||
// Insert the new room into the database
|
||||
_, err := dbPool.Exec(context.Background(), "INSERT INTO rooms (id, userid, roomname, topicname, visible, scale) VALUES ($1, $2, $3, $4, $5, $6)", newRoomID, userID, roomName, "My First Topic", false, "0.5,1,2,3,5")
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO rooms (id, userid, roomname, topicname, visible, scale) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
newRoomID, userID, roomName, "My First Topic", false, "0.5,1,2,3,5")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -46,12 +45,14 @@ func CreateRoom(dbPool *pgxpool.Pool, userID, roomName string) (*Room, error) {
|
|||
}
|
||||
|
||||
// DeleteRoom deletes a room from the database by its ID.
|
||||
func DeleteRoom(dbPool *pgxpool.Pool, roomID string) error {
|
||||
if dbPool == nil {
|
||||
return errors.New("database connection pool is not initialized")
|
||||
func DeleteRoom(db *sql.DB, roomID string) error {
|
||||
if db == nil {
|
||||
return errors.New("database is not initialized")
|
||||
}
|
||||
|
||||
_, err := dbPool.Exec(context.Background(), "DELETE FROM rooms WHERE id = $1", roomID)
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM rooms WHERE id = ?",
|
||||
roomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -60,13 +61,15 @@ func DeleteRoom(dbPool *pgxpool.Pool, roomID string) error {
|
|||
}
|
||||
|
||||
// GetRoomsByUserID retrieves all rooms for a given userID.
|
||||
func GetRoomsByUserID(dbPool *pgxpool.Pool, userID string) ([]Room, error) {
|
||||
if dbPool == nil {
|
||||
return nil, errors.New("database connection pool is not initialized")
|
||||
func GetRoomsByUserID(db *sql.DB, userID string) ([]Room, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("database is not initialized")
|
||||
}
|
||||
|
||||
var rooms []Room
|
||||
rows, err := dbPool.Query(context.Background(), "SELECT id, created_at, userid, roomname, topicname, visible, scale FROM rooms WHERE userid = $1", userID)
|
||||
rows, err := db.Query(
|
||||
"SELECT id, created_at, userid, roomname, topicname, visible, scale FROM rooms WHERE userid = ?",
|
||||
userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -89,11 +92,15 @@ func GetRoomsByUserID(dbPool *pgxpool.Pool, userID string) ([]Room, error) {
|
|||
}
|
||||
|
||||
// GetRoomById retrieves a room by its ID from the database.
|
||||
func GetRoomById(dbPool *pgxpool.Pool, roomId string) (*Room, error) {
|
||||
var room Room
|
||||
func GetRoomById(db *sql.DB, roomId string) (*Room, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("database is not initialized")
|
||||
}
|
||||
|
||||
query := "SELECT id, created_at, userid, roomname, topicname, visible, scale FROM rooms WHERE id = $1"
|
||||
err := dbPool.QueryRow(context.Background(), query, roomId).Scan(&room.ID, &room.CreatedAt, &room.UserID, &room.RoomName, &room.TopicName, &room.Visible, &room.Scale)
|
||||
var room Room
|
||||
err := db.QueryRow(
|
||||
"SELECT id, created_at, userid, roomname, topicname, visible, scale FROM rooms WHERE id = ?",
|
||||
roomId).Scan(&room.ID, &room.CreatedAt, &room.UserID, &room.RoomName, &room.TopicName, &room.Visible, &room.Scale)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("no room found with ID %s", roomId)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
@ -10,7 +10,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/labstack/echo-contrib/session"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
@ -36,8 +35,8 @@ func InitSessionMiddleware() echo.MiddlewareFunc {
|
|||
// Adjusted SetSessionCookie to include more user info
|
||||
func SetSessionCookie(w http.ResponseWriter, name string, sessionData SessionData) error {
|
||||
// Create session in database
|
||||
expiresAt := time.Now().Add(48 * time.Hour) // Set expiration to 1 hour from now
|
||||
sessionID, err := CreateSession(GetDBPool(), sessionData.UserID, expiresAt)
|
||||
expiresAt := time.Now().Add(48 * time.Hour) // Set expiration to 48 hours from now
|
||||
sessionID, err := CreateSession(GetDB(), sessionData.UserID, expiresAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,7 +114,7 @@ func IsSignedIn(c echo.Context) bool {
|
|||
}
|
||||
|
||||
// Validate the session in the database
|
||||
validSessionData, err := ValidateSession(GetDBPool(), sessionData.SessionID)
|
||||
validSessionData, err := ValidateSession(GetDB(), sessionData.SessionID)
|
||||
if err != nil || validSessionData == nil {
|
||||
log.Printf("Invalid session: %v", err)
|
||||
ClearSessionCookie(c.Response().Writer, "session")
|
||||
|
@ -134,10 +133,10 @@ func GenerateSessionID() (string, error) {
|
|||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func CreateSession(dbPool *pgxpool.Pool, userID string, expiresAt time.Time) (string, error) {
|
||||
func CreateSession(db *sql.DB, userID string, expiresAt time.Time) (string, error) {
|
||||
sessionID := GenerateNewID("session")
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"INSERT INTO sessions (id, user_id, expires_at) VALUES ($1, $2, $3)",
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO sessions (id, user_id, expires_at) VALUES (?, ?, ?)",
|
||||
sessionID, userID, expiresAt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -145,17 +144,17 @@ func CreateSession(dbPool *pgxpool.Pool, userID string, expiresAt time.Time) (st
|
|||
return sessionID, nil
|
||||
}
|
||||
|
||||
func ValidateSession(dbPool *pgxpool.Pool, sessionID string) (*SessionData, error) {
|
||||
func ValidateSession(db *sql.DB, sessionID string) (*SessionData, error) {
|
||||
var userID string
|
||||
var expiresAt time.Time
|
||||
err := dbPool.QueryRow(context.Background(),
|
||||
"SELECT user_id, expires_at FROM sessions WHERE id = $1 AND expires_at > NOW()",
|
||||
err := db.QueryRow(
|
||||
"SELECT user_id, expires_at FROM sessions WHERE id = ? AND expires_at > datetime('now')",
|
||||
sessionID).Scan(&userID, &expiresAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := GetUserByID(dbPool, userID)
|
||||
user, err := GetUserByID(db, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -169,8 +168,9 @@ func ValidateSession(dbPool *pgxpool.Pool, sessionID string) (*SessionData, erro
|
|||
}, nil
|
||||
}
|
||||
|
||||
func DeleteSession(dbPool *pgxpool.Pool, sessionID string) error {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"DELETE FROM sessions WHERE id = $1", sessionID)
|
||||
func DeleteSession(db *sql.DB, sessionID string) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM sessions WHERE id = ?",
|
||||
sessionID)
|
||||
return err
|
||||
}
|
||||
|
|
43
lib/user.go
43
lib/user.go
|
@ -1,11 +1,10 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/labstack/echo/v4"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
@ -30,14 +29,17 @@ func CheckPasswordHash(password, hash string) bool {
|
|||
}
|
||||
|
||||
// GetUserByEmail fetches a user by email from the database.
|
||||
func GetUserByEmail(dbPool *pgxpool.Pool, email string) (*User, error) {
|
||||
if dbPool == nil {
|
||||
return nil, errors.New("database connection pool is not initialized")
|
||||
func GetUserByEmail(db *sql.DB, email string) (*User, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("database client is not initialized")
|
||||
}
|
||||
|
||||
var user User
|
||||
// Ensure the ID is being scanned as a string.
|
||||
err := dbPool.QueryRow(context.Background(), "SELECT id::text, name, email, password FROM users WHERE email = $1", email).Scan(&user.ID, &user.Name, &user.Email, &user.Password)
|
||||
row := db.QueryRow(
|
||||
"SELECT id, name, email, password FROM users WHERE email = ?",
|
||||
email)
|
||||
|
||||
err := row.Scan(&user.ID, &user.Name, &user.Email, &user.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -45,14 +47,17 @@ func GetUserByEmail(dbPool *pgxpool.Pool, email string) (*User, error) {
|
|||
}
|
||||
|
||||
// GetUserByID fetches a user by ID from the database.
|
||||
func GetUserByID(dbPool *pgxpool.Pool, id string) (*User, error) {
|
||||
if dbPool == nil {
|
||||
return nil, errors.New("database connection pool is not initialized")
|
||||
func GetUserByID(db *sql.DB, id string) (*User, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("database client is not initialized")
|
||||
}
|
||||
|
||||
var user User
|
||||
// Ensure the ID is being scanned as a string.
|
||||
err := dbPool.QueryRow(context.Background(), "SELECT id::text, name, email, password FROM users WHERE id = $1", id).Scan(&user.ID, &user.Name, &user.Email, &user.Password)
|
||||
row := db.QueryRow(
|
||||
"SELECT id, name, email, password FROM users WHERE id = ?",
|
||||
id)
|
||||
|
||||
err := row.Scan(&user.ID, &user.Name, &user.Email, &user.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -60,16 +65,20 @@ func GetUserByID(dbPool *pgxpool.Pool, id string) (*User, error) {
|
|||
}
|
||||
|
||||
// SaveUser saves a new user to the database.
|
||||
func SaveUser(dbPool *pgxpool.Pool, user *User) error {
|
||||
if dbPool == nil {
|
||||
return errors.New("database connection pool is not initialized")
|
||||
func SaveUser(db *sql.DB, user *User) error {
|
||||
if db == nil {
|
||||
return errors.New("database client is not initialized")
|
||||
}
|
||||
|
||||
commandTag, err := dbPool.Exec(context.Background(), "INSERT INTO users (id, name, email, password) VALUES ($1, $2, $3, $4)", GenerateNewID("user"), user.Name, user.Email, user.Password)
|
||||
result, err := db.Exec(
|
||||
"INSERT INTO users (id, name, email, password) VALUES (?, ?, ?, ?)",
|
||||
GenerateNewID("user"), user.Name, user.Email, user.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if commandTag.RowsAffected() != 1 {
|
||||
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected != 1 {
|
||||
return errors.New("expected one row to be affected")
|
||||
}
|
||||
return nil
|
||||
|
|
35
main.go
35
main.go
|
@ -7,7 +7,6 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"pollo/api"
|
||||
"pollo/api/webhooks"
|
||||
|
@ -24,30 +23,26 @@ var PublicFS embed.FS
|
|||
|
||||
func main() {
|
||||
// Load environment variables
|
||||
godotenv.Load(".env")
|
||||
|
||||
// Initialize the database connection pool
|
||||
postgresHost := os.Getenv("POSTGRES_HOST")
|
||||
postgresPort := os.Getenv("POSTGRES_PORT")
|
||||
postgresUser := os.Getenv("POSTGRES_USER")
|
||||
postgresPassword := os.Getenv("POSTGRES_PASSWORD")
|
||||
postgresDB := os.Getenv("POSTGRES_DB")
|
||||
if postgresHost == "" || postgresPort == "" || postgresUser == "" || postgresPassword == "" || postgresDB == "" {
|
||||
log.Fatal("DB environment variables not set")
|
||||
}
|
||||
|
||||
portNumber, err := strconv.Atoi(postgresPort)
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid database port: %v", err)
|
||||
log.Printf("Warning: Error loading .env file: %v", err)
|
||||
}
|
||||
|
||||
if err := lib.InitializeDBPool(postgresHost, postgresUser, postgresPassword, postgresDB, portNumber); err != nil {
|
||||
log.Fatalf("Failed to initialize DB pool: %v", err)
|
||||
// Initialize database connection
|
||||
dbUrl := os.Getenv("DB_URL")
|
||||
dbToken := os.Getenv("DB_TOKEN")
|
||||
|
||||
if dbUrl == "" || dbToken == "" {
|
||||
log.Fatal("DB_URL and DB_TOKEN environment variables must be set")
|
||||
}
|
||||
|
||||
// Initialize the database schema
|
||||
dbPool := lib.GetDBPool()
|
||||
if err := lib.InitializeSchema(dbPool); err != nil {
|
||||
// Initialize the database
|
||||
if err := lib.InitializeDB(dbUrl, dbToken); err != nil {
|
||||
log.Fatalf("Failed to initialize DB: %v", err)
|
||||
}
|
||||
|
||||
// Initialize schema
|
||||
if err := lib.InitializeSchema(); err != nil {
|
||||
log.Fatalf("Failed to initialize schema: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ func Room(c echo.Context) error {
|
|||
pageError := lib.Error{}
|
||||
|
||||
// Get the room from the database
|
||||
room, err := lib.GetRoomById(lib.GetDBPool(), roomID)
|
||||
room, err := lib.GetRoomById(lib.GetDB(), roomID)
|
||||
if err != nil {
|
||||
pageError = lib.Error{
|
||||
Code: 404,
|
||||
|
|
Loading…
Add table
Reference in a new issue