migrate to fiber

This commit is contained in:
Zoe
2024-10-01 03:45:43 -05:00
parent e39e5f51fd
commit e64b9fba7f
17 changed files with 587 additions and 372 deletions

34
db/db.go Normal file
View File

@@ -0,0 +1,34 @@
package db
import (
"database/sql"
"fmt"
"os"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
)
var db *bun.DB
func DBConnect() {
dbHost := os.Getenv("DB_HOST")
dbName := os.Getenv("DB_NAME")
dbUser := os.Getenv("DB_USER")
dbPasswd := os.Getenv("DB_PASSWD")
if dbHost == "" || dbName == "" || dbUser == "" || os.Getenv("STORAGE_PATH") == "" {
panic("Missing database environment variabled!")
}
// TODO: retry connection or only connect at the first moment that we need the db
dbUrl := fmt.Sprintf("postgres://%s:%s@%s/%s?dial_timeout=10s&sslmode=disable", dbUser, dbPasswd, dbHost, dbName)
sqlDB := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dbUrl)))
db = bun.NewDB(sqlDB, pgdialect.New())
}
func GetDB() *bun.DB {
return db
}

35
go.mod
View File

@@ -2,7 +2,38 @@ module filething
go 1.23.0 go 1.23.0
require github.com/google/uuid v1.6.0 require (
github.com/bytedance/sonic v1.12.3
github.com/gofiber/fiber/v3 v3.0.0-beta.3
github.com/google/uuid v1.6.0
)
require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/fasthttp/websocket v1.5.8 // indirect
github.com/gofiber/contrib v1.0.1 // indirect
github.com/gofiber/contrib/websocket v1.3.2 // indirect
github.com/gofiber/fiber/v2 v2.52.5 // indirect
github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64 // indirect
github.com/yeqown/fasthttp-reverse-proxy/v2 v2.2.3 // indirect
github.com/yeqown/log v1.0.3 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
)
require ( require (
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
@@ -22,7 +53,7 @@ require (
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/crypto v0.27.0 golang.org/x/crypto v0.27.0
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect

113
go.sum
View File

@@ -1,13 +1,46 @@
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fasthttp/websocket v1.4.1 h1:fisgNMCNCbIPM5GRRRTAckRrynbSzf76fevcJYJYnSM=
github.com/fasthttp/websocket v1.4.1/go.mod h1:toetUvZ3KISxtZERe0wzPPpnaN8GZCKHCowWctwA50o=
github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8=
github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0=
github.com/gofiber/contrib v1.0.1 h1:pQ8pQ2e8qBQ4koUGRZ4+wCSHUOip8FpjmPOhRTp+DlU=
github.com/gofiber/contrib v1.0.1/go.mod h1:e15MOdipEOlXrU5SUT5p0tfkZkhzDqfdiT2kfRYy1c0=
github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg=
github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/fiber/v3 v3.0.0-beta.3 h1:7Q2I+HsIqnIEEDB+9oe7Gadpakh6ZLhXpTYz/L20vrg=
github.com/gofiber/fiber/v3 v3.0.0-beta.3/go.mod h1:kcMur0Dxqk91R7p4vxEpJfDWZ9u5IfvrtQc8Bvv/JmY=
github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co=
github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 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/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= 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 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
@@ -17,14 +50,38 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274 h1:F52t1X2ziOrMcQMVHo8ZxwOrDTMAq6MrlKtL1Atu2wU=
github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274/go.mod h1:w803/Fg1m0hrp1ZT9KNfQe4E4+WOMMFLcgzPvOcye10=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/uptrace/bun v1.2.3 h1:6KDc6YiNlXde38j9ATKufb8o7MS8zllhAOeIyELKrk0= github.com/uptrace/bun v1.2.3 h1:6KDc6YiNlXde38j9ATKufb8o7MS8zllhAOeIyELKrk0=
github.com/uptrace/bun v1.2.3/go.mod h1:8frYFHrO/Zol3I4FEjoXam0HoNk+t5k7aJRl3FXp0mk= github.com/uptrace/bun v1.2.3/go.mod h1:8frYFHrO/Zol3I4FEjoXam0HoNk+t5k7aJRl3FXp0mk=
github.com/uptrace/bun/dialect/pgdialect v1.2.3 h1:YyCxxqeL0lgFWRZzKCOt6mnxUsjqITcxSo0mLqgwMUA= github.com/uptrace/bun/dialect/pgdialect v1.2.3 h1:YyCxxqeL0lgFWRZzKCOt6mnxUsjqITcxSo0mLqgwMUA=
@@ -33,25 +90,73 @@ github.com/uptrace/bun/driver/pgdriver v1.2.3 h1:VA5TKB0XW7EtreQq2R8Qu/vCAUX2ECa
github.com/uptrace/bun/driver/pgdriver v1.2.3/go.mod h1:yDiYTZYd4FfXFtV01m4I/RkI33IGj9N254jLStaeJLs= github.com/uptrace/bun/driver/pgdriver v1.2.3/go.mod h1:yDiYTZYd4FfXFtV01m4I/RkI33IGj9N254jLStaeJLs=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 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/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 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/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64 h1:4dmnKp9YO5u6MACmB2p4FrEtDV8MNx438A9/N0kPIfg=
github.com/yeqown/fasthttp-reverse-proxy v0.0.0-20200930023507-ed73ac32bc64/go.mod h1:hgX6t+JE03QfTU4mpF0ls1FJa+Y/49wcG7++6d6apu8=
github.com/yeqown/fasthttp-reverse-proxy/v2 v2.2.3 h1:qAWBk2OIFbtM3jB6ujXMwNybdV5h4XnRzGhJ/RkLXZQ=
github.com/yeqown/fasthttp-reverse-proxy/v2 v2.2.3/go.mod h1:hiv7dfheax7undE0lIpPI8OaMBCVT9nWfxRP8KWz4o0=
github.com/yeqown/log v1.0.3 h1:kWwBMytMSkYkr2fmi20PLgDuJNXkq3mOpVbfsTS1soI=
github.com/yeqown/log v1.0.3/go.mod h1:RTslXFTg+8Uj5AizIxdfichvBZi/OOKao6yP3tMtTns=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 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/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

103
main.go
View File

@@ -2,39 +2,28 @@ package main
import ( import (
"context" "context"
"database/sql" "filething/db"
"filething/middleware" "filething/middleware"
"filething/models" "filething/models"
"filething/routes" "filething/routes"
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/labstack/echo/v4" "github.com/bytedance/sonic"
echoMiddleware "github.com/labstack/echo/v4/middleware" "github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/cors"
"github.com/gofiber/fiber/v3/middleware/helmet"
"github.com/gofiber/fiber/v3/middleware/pprof"
"github.com/uptrace/bun" "github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
) )
var initUi func(e *echo.Echo) var initUi func(app *fiber.App)
func main() { func main() {
dbHost := os.Getenv("DB_HOST") db.DBConnect()
dbName := os.Getenv("DB_NAME")
dbUser := os.Getenv("DB_USER")
dbPasswd := os.Getenv("DB_PASSWD")
if dbHost == "" || dbName == "" || dbUser == "" || os.Getenv("STORAGE_PATH") == "" { db := db.GetDB()
panic("Missing database environment variabled!")
}
// TODO: retry connection or only connect at the first moment that we need the db
dbUrl := fmt.Sprintf("postgres://%s:%s@%s/%s?dial_timeout=10s&sslmode=disable", dbUser, dbPasswd, dbHost, dbName)
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dbUrl)))
db := bun.NewDB(sqldb, pgdialect.New())
err := createSchema(db) err := createSchema(db)
if err != nil { if err != nil {
@@ -46,66 +35,66 @@ func main() {
panic(err) panic(err)
} }
e := echo.New() app := fiber.New(fiber.Config{
JSONEncoder: sonic.Marshal,
// insert the db into the echo context so it is easily accessible in routes and middleware JSONDecoder: sonic.Unmarshal,
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { DisablePreParseMultipartForm: true,
return func(c echo.Context) error { BodyLimit: 100 * 1024 * 1024 * 1024,
c.Set("db", db)
return next(c)
}
}) })
e.Use(echoMiddleware.Gzip()) app.Use(pprof.New())
e.Use(echoMiddleware.CORS()) // app.Use(compress.New())
e.Use(echoMiddleware.CSRFWithConfig(echoMiddleware.CSRFConfig{ app.Use(cors.New())
TokenLookup: "cookie:_csrf", // TODO: make this not a constant pain in my ass
CookiePath: "/", // app.Use(csrf.New(csrf.Config{
CookieSecure: true, // KeyLookup: "cookie:_csrf",
CookieHTTPOnly: true, // CookieName: "_csrf",
CookieSameSite: http.SameSiteStrictMode, // CookieSameSite: "Strict",
})) // Expiration: time.Hour * 24,
e.Use(echoMiddleware.Secure()) // CookieSecure: true,
// CookieHTTPOnly: true,
// }))
app.Use(helmet.New())
api := e.Group("/api") api := app.Group("/api")
{ {
api.POST("/login", routes.LoginHandler) api.Post("/login", routes.LoginHandler)
api.POST("/signup", routes.SignupHandler) api.Post("/signup", routes.SignupHandler)
// everything past this needs auth // everything past this needs auth
api.Use(middleware.SessionMiddleware(db)) api.Use(middleware.SessionMiddleware(db))
api.POST("/logout", routes.LogoutHandler) api.Post("/logout", routes.LogoutHandler)
api.GET("/user", routes.GetUser) api.Get("/user", routes.GetUser)
api.POST("/files/upload*", routes.UploadFile) api.Post("/files/upload*", routes.UploadFile)
api.GET("/files/get/*", routes.GetFiles) api.Get("/files/get/*", routes.GetFiles)
api.GET("/files/download*", routes.GetFile) api.Get("/files/download*", routes.GetFile)
api.POST("/files/delete*", routes.DeleteFiles) api.Post("/files/delete*", routes.DeleteFiles)
admin := api.Group("/admin") admin := api.Group("/admin")
{ {
admin.Use(middleware.AdminMiddleware()) admin.Use(middleware.AdminMiddleware(db))
admin.GET("/status", routes.SystemStatus) admin.Get("/status", routes.SystemStatus)
admin.GET("/plans", routes.GetPlans) admin.Get("/plans", routes.GetPlans)
admin.GET("/users", routes.GetUsers) admin.Get("/users", routes.GetUsers)
admin.GET("/users/:id", routes.GetUser) admin.Get("/users/:id", routes.GetUser)
admin.POST("/users/edit/:id", routes.EditUser) admin.Post("/users/edit/:id", routes.EditUser)
admin.POST("/users/new", routes.CreateUser) admin.Post("/users/new", routes.CreateUser)
} }
} }
// redirects to the proper pages if you are trying to access one that expects you have/dont have an api key // redirects to the proper pages if you are trying to access one that expects you have/dont have an api key
// this isnt explicitly required, but it provides a better experience than doing this same thing clientside // this isnt explicitly required, but it provides a better experience than doing this same thing clientside
e.Use(middleware.AuthCheckMiddleware) app.Use(middleware.AuthCheckMiddleware)
// calls out to a function set by either server.go server_dev.go based on the presence of the dev tag, and hosts // calls out to a function set by either server.go server_dev.go based on the presence of the dev tag, and hosts
// either the static files that get embedded into the binary in ui/embed.go or proxies the dev server that gets // either the static files that get embedded into the binary in ui/embed.go or proxies the dev server that gets
// run in the provided function // run in the provided function
initUi(e) initUi(app)
routes.AppStartTime = time.Now().UTC() routes.AppStartTime = time.Now().UTC()
if err := e.Start(":1323"); err != nil && err != http.ErrServerClosed { if err = app.Listen(":1323"); err != nil && err != http.ErrServerClosed {
fmt.Println("Error starting HTTP server:", err) fmt.Println("Error starting HTTP server:", err)
} }
} }

View File

@@ -4,19 +4,19 @@ import (
"filething/models" "filething/models"
"net/http" "net/http"
"github.com/gofiber/fiber/v3"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/uptrace/bun"
) )
func AdminMiddleware() echo.MiddlewareFunc { func AdminMiddleware(db *bun.DB) func(c fiber.Ctx) error {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c fiber.Ctx) error {
return func(c echo.Context) error { user := c.Locals("user").(*models.User)
user := c.Get("user").(*models.User)
if !user.Admin { if !user.Admin {
return echo.NewHTTPError(http.StatusForbidden, "You are not an administrator") return echo.NewHTTPError(http.StatusForbidden, "You are not an administrator")
} }
return next(c) return c.Next()
}
} }
} }

View File

@@ -7,41 +7,39 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/uptrace/bun" "github.com/uptrace/bun"
) )
const UserContextKey = "user" const UserContextKey = "user"
func SessionMiddleware(db *bun.DB) echo.MiddlewareFunc { func SessionMiddleware(db *bun.DB) func(c fiber.Ctx) error {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c fiber.Ctx) error {
return func(c echo.Context) error { // Extract session token from the cookie
// Extract sessionToken from the cookie sessionToken := c.Cookies("sessionToken")
cookie, err := c.Cookie("sessionToken") if sessionToken == "" {
if err != nil { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"message": "Session token missing"})
if err == http.ErrNoCookie {
return echo.NewHTTPError(http.StatusUnauthorized, "Session token missing")
}
return echo.NewHTTPError(http.StatusBadRequest, "Bad request")
} }
sessionId, err := uuid.Parse(cookie.Value) // Parse session ID
sessionId, err := uuid.Parse(sessionToken)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Bad request") return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "Invalid session token"})
} }
// Fetch session from database
session := &models.Session{ session := &models.Session{
ID: sessionId, ID: sessionId,
} }
err = db.NewSelect().Model(session).WherePK().Scan(context.Background()) err = db.NewSelect().Model(session).WherePK().Scan(context.Background())
if err != nil { if err != nil {
fmt.Println(err)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid session token") return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid session token"})
} }
return echo.NewHTTPError(http.StatusInternalServerError, "Database error") fmt.Println(err)
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Database error"})
} }
user := &models.User{ user := &models.User{
@@ -51,17 +49,14 @@ func SessionMiddleware(db *bun.DB) echo.MiddlewareFunc {
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid session token") return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid session token"})
} }
fmt.Println(err) fmt.Println(err)
return echo.NewHTTPError(http.StatusInternalServerError, "Database error") return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Database error"})
} }
// Store the user in the context c.Locals("user", user)
c.Set(UserContextKey, user)
// Continue to the next handler return c.Next()
return next(c)
}
} }
} }

View File

@@ -1,10 +1,9 @@
package middleware package middleware
import ( import (
"net/http"
"strings" "strings"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
) )
var unauthenticatedPages = []string{ var unauthenticatedPages = []string{
@@ -17,36 +16,34 @@ var authenticatedPages = []string{
"/home", "/home",
} }
func AuthCheckMiddleware(next echo.HandlerFunc) echo.HandlerFunc { func AuthCheckMiddleware(c fiber.Ctx) error {
return func(c echo.Context) error { path := c.Path()
path := c.Request().URL.Path
// bypass auth checks for static and dev resources // bypass auth checks for static and dev resources
if strings.HasPrefix(path, "/_nuxt/") || strings.HasSuffix(path, ".js") || strings.HasSuffix(path, ".css") { if strings.HasPrefix(path, "/_nuxt/") || strings.HasSuffix(path, ".js") || strings.HasSuffix(path, ".css") {
return next(c) return c.Next()
} }
_, cookieErr := c.Cookie("sessionToken") cookie := c.Cookies("sessionToken")
authenticated := cookieErr == nil authenticated := cookie != ""
if Contains(unauthenticatedPages, path) && authenticated { if Contains(unauthenticatedPages, path) && authenticated {
return c.Redirect(http.StatusFound, "/home") return c.Redirect().To("/home")
} }
if Contains(authenticatedPages, path) && !authenticated { if Contains(authenticatedPages, path) && !authenticated {
return c.Redirect(http.StatusFound, "/login") return c.Redirect().To("/login")
} }
if strings.Contains(path, "/home") && !authenticated { if strings.Contains(path, "/home") && !authenticated {
return c.Redirect(http.StatusFound, "/login") return c.Redirect().To("/login")
} }
if strings.Contains(path, "/admin") && !authenticated { if strings.Contains(path, "/admin") && !authenticated {
return c.Redirect(http.StatusFound, "/login") return c.Redirect().To("/login")
} }
return next(c) return c.Next()
}
} }
func Contains(s []string, element string) bool { func Contains(s []string, element string) bool {

View File

@@ -2,6 +2,7 @@ package routes
import ( import (
"context" "context"
"filething/db"
"filething/models" "filething/models"
"fmt" "fmt"
"net/http" "net/http"
@@ -12,24 +13,23 @@ import (
"time" "time"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/uptrace/bun"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func GetUsers(c echo.Context) error { func GetUsers(c fiber.Ctx) error {
db := c.Get("db").(*bun.DB) db := db.GetDB()
count, err := db.NewSelect().Model((*models.User)(nil)).Count(context.Background()) count, err := db.NewSelect().Model((*models.User)(nil)).Count(context.Background())
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Invalid page number"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Invalid page number"})
} }
// this should be a query param not a URL param // this should be a query param not a URL param
pageStr := c.QueryParam("page") pageStr := c.Query("page")
if pageStr == "" { if pageStr == "" {
pageStr = "0" pageStr = "0"
} }
@@ -43,7 +43,7 @@ func GetUsers(c echo.Context) error {
limit := 30 limit := 30
if offset > count { if offset > count {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "Invalid page number"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "Invalid page number"})
} }
var users []models.User var users []models.User
@@ -54,14 +54,14 @@ func GetUsers(c echo.Context) error {
Order("created_at ASC"). Order("created_at ASC").
Scan(context.Background()) Scan(context.Background())
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Failed to retrieve users"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Failed to retrieve users"})
} }
if users == nil { if users == nil {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "Invalid page number"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "Invalid page number"})
} }
return c.JSON(http.StatusOK, map[string]interface{}{"users": users, "total_users": count}) return c.Status(http.StatusOK).JSON(fiber.Map{"users": users, "total_users": count})
} }
type UserEdit struct { type UserEdit struct {
@@ -72,18 +72,18 @@ type UserEdit struct {
Admin bool `json:"is_admin"` Admin bool `json:"is_admin"`
} }
func EditUser(c echo.Context) error { func EditUser(c fiber.Ctx) error {
db := c.Get("db").(*bun.DB) db := db.GetDB()
id := c.Param("id") id := c.Params("id")
var userEditData UserEdit userEditData := new(UserEdit)
if err := c.Bind(&userEditData); err != nil { if err := c.Bind().JSON(userEditData); err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(userEditData.Email) { if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(userEditData.Email) {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A valid email is required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A valid email is required!"})
} }
plan := models.Plan{ plan := models.Plan{
@@ -91,12 +91,12 @@ func EditUser(c echo.Context) error {
} }
planCount, err := db.NewSelect().Model(&plan).WherePK().Count(context.Background()) planCount, err := db.NewSelect().Model(&plan).WherePK().Count(context.Background())
if err != nil || planCount == 0 { if err != nil || planCount == 0 {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "Invalid plan id!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Invalid plan id!"})
} }
userId, err := uuid.Parse(id) userId, err := uuid.Parse(id)
if err != nil { if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
var userData models.User var userData models.User
@@ -104,7 +104,7 @@ func EditUser(c echo.Context) error {
err = db.NewSelect().Model(&userData).WherePK().Relation("Plan").Scan(context.Background()) err = db.NewSelect().Model(&userData).WherePK().Relation("Plan").Scan(context.Background())
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
if userEditData.Username != "" { if userEditData.Username != "" {
@@ -118,7 +118,7 @@ func EditUser(c echo.Context) error {
if userEditData.Password != "" { if userEditData.Password != "" {
hash, err := bcrypt.GenerateFromPassword([]byte(userEditData.Password), 12) hash, err := bcrypt.GenerateFromPassword([]byte(userEditData.Password), 12)
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
userData.PasswordHash = string(hash) userData.PasswordHash = string(hash)
@@ -134,48 +134,48 @@ func EditUser(c echo.Context) error {
_, err = db.NewUpdate().Model(&userData).WherePK().Exec(context.Background()) _, err = db.NewUpdate().Model(&userData).WherePK().Exec(context.Background())
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
return c.JSON(http.StatusOK, map[string]string{"message": "Successfully updated user"}) return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Successfully updated user"})
} }
func GetPlans(c echo.Context) error { func GetPlans(c fiber.Ctx) error {
db := c.Get("db").(*bun.DB) db := db.GetDB()
var plans []models.Plan var plans []models.Plan
err := db.NewSelect().Model(&plans).Scan(context.Background()) err := db.NewSelect().Model(&plans).Scan(context.Background())
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
return c.JSON(http.StatusOK, plans) return c.Status(http.StatusOK).JSON(plans)
} }
func CreateUser(c echo.Context) error { func CreateUser(c fiber.Ctx) error {
var signupData models.SignupData signupData := new(models.SignupData)
if err := c.Bind(&signupData); err != nil { if err := c.Bind().JSON(signupData); err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
if signupData.Username == "" || signupData.Password == "" || signupData.Email == "" { if signupData.Username == "" || signupData.Password == "" || signupData.Email == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A password, username and email are required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A password, username and email are required!"})
} }
// if email is not valid // if email is not valid
if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(signupData.Email) { if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(signupData.Email) {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A valid email is required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A valid email is required!"})
} }
db := c.Get("db").(*bun.DB) db := db.GetDB()
hash, err := bcrypt.GenerateFromPassword([]byte(signupData.Password), 12) hash, err := bcrypt.GenerateFromPassword([]byte(signupData.Password), 12)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
user := &models.User{ user := &models.User{
@@ -187,13 +187,13 @@ func CreateUser(c echo.Context) error {
_, err = db.NewInsert().Model(user).Exec(context.Background()) _, err = db.NewInsert().Model(user).Exec(context.Background())
if err != nil { if err != nil {
return c.JSON(http.StatusConflict, map[string]string{"message": "A user with that email or username already exists!"}) return c.Status(http.StatusConflict).JSON(fiber.Map{"message": "A user with that email or username already exists!"})
} }
err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background()) err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background())
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusNotFound, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
err = os.Mkdir(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID), os.ModePerm) err = os.Mkdir(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID), os.ModePerm)
@@ -202,13 +202,13 @@ func CreateUser(c echo.Context) error {
return err return err
} }
return c.JSON(http.StatusOK, map[string]string{"message": "Successfully created user"}) return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Successfully created user"})
} }
// Stolen from Gitea https://github.com/go-gitea/gitea // Stolen from Gitea https://github.com/go-gitea/gitea
func SystemStatus(c echo.Context) error { func SystemStatus(c fiber.Ctx) error {
updateSystemStatus() updateSystemStatus()
return c.JSON(http.StatusOK, map[string]interface{}{ return c.Status(http.StatusOK).JSON(fiber.Map{
"uptime": sysStatus.StartTime, "uptime": sysStatus.StartTime,
"num_goroutine": sysStatus.NumGoroutine, "num_goroutine": sysStatus.NumGoroutine,
"cur_mem_usage": sysStatus.MemAllocated, "cur_mem_usage": sysStatus.MemAllocated,

View File

@@ -2,6 +2,7 @@ package routes
import ( import (
"context" "context"
"filething/db"
"filething/models" "filething/models"
"fmt" "fmt"
"net/http" "net/http"
@@ -9,31 +10,34 @@ import (
"regexp" "regexp"
"time" "time"
"github.com/gofiber/fiber/v3"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/uptrace/bun" "github.com/uptrace/bun"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func LoginHandler(c echo.Context) error { func LoginHandler(c fiber.Ctx) error {
var loginData models.LoginData loginData := new(models.LoginData)
if err := c.Bind(&loginData); err != nil { if err := c.Bind().JSON(loginData); err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
// Validate required fields
if loginData.UsernameOrEmail == "" || loginData.Password == "" { if loginData.UsernameOrEmail == "" || loginData.Password == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A password, and username or email are required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A Username/Email and Password are required"})
} }
db := c.Get("db").(*bun.DB) db := db.GetDB()
user := new(models.User) user := new(models.User)
err := db.NewSelect().Model(user).Where("email = ?", loginData.UsernameOrEmail).Relation("Plan").Scan(context.Background()) var err error
// Try both username and email for login attempts
if err = db.NewSelect().Model(user).Where("email = ?", loginData.UsernameOrEmail).Relation("Plan").Scan(context.Background()); err != nil {
err = db.NewSelect().Model(user).Where("username = ?", loginData.UsernameOrEmail).Relation("Plan").Scan(context.Background())
if err != nil { if err != nil {
err := db.NewSelect().Model(user).Where("username = ?", loginData.UsernameOrEmail).Relation("Plan").Scan(context.Background()) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "User not found"})
if err != nil {
return c.JSON(http.StatusNotFound, map[string]string{"message": "User with that username or email not found!"})
} }
} }
@@ -46,49 +50,48 @@ func LoginHandler(c echo.Context) error {
user.Usage = storageUsage user.Usage = storageUsage
session, err := GenerateSessionToken(db, user.ID) session, err := GenerateSessionToken(db, user.ID)
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "Internal server error"})
} }
expiration := time.Now().Add(time.Hour * 24 * 365 * 100) expiration := time.Now().Add(time.Hour * 24 * 365 * 100)
c.SetCookie(&http.Cookie{ c.Cookie(&fiber.Cookie{
Name: "sessionToken", Name: "sessionToken",
Value: session.ID.String(), Value: session.ID.String(),
SameSite: http.SameSiteStrictMode, SameSite: "Strict",
Expires: expiration, Expires: expiration,
Path: "/", Path: "/",
}) })
return c.JSON(http.StatusOK, user) // Return user data with status code 200 (OK)
return c.JSON(user)
} }
var firstUserCreated *bool var firstUserCreated *bool
func SignupHandler(c echo.Context) error { func SignupHandler(c fiber.Ctx) error {
var signupData models.SignupData signupData := new(models.SignupData)
if err := c.Bind(&signupData); err != nil { if err := c.Bind().JSON(signupData); err != nil {
fmt.Println(err) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "An unknown error occoured!"})
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"})
} }
if signupData.Username == "" || signupData.Password == "" || signupData.Email == "" { if signupData.Username == "" || signupData.Password == "" || signupData.Email == "" {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A password, username and email are required!"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "A password, username and email are required!"})
} }
// if email is not valid // if email is not valid
if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(signupData.Email) { if !regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`).MatchString(signupData.Email) {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A valid email is required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A valid email is required!"})
} }
db := c.Get("db").(*bun.DB) db := db.GetDB()
hash, err := bcrypt.GenerateFromPassword([]byte(signupData.Password), 12) hash, err := bcrypt.GenerateFromPassword([]byte(signupData.Password), 12)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
if firstUserCreated == nil { if firstUserCreated == nil {
@@ -111,7 +114,7 @@ func SignupHandler(c echo.Context) error {
_, err = db.NewInsert().Model(user).Exec(context.Background()) _, err = db.NewInsert().Model(user).Exec(context.Background())
if err != nil { if err != nil {
return c.JSON(http.StatusConflict, map[string]string{"message": "A user with that email or username already exists!"}) return c.Status(http.StatusConflict).JSON(fiber.Map{"message": "A user with that email or username already exists!"})
} }
if !*firstUserCreated { if !*firstUserCreated {
@@ -121,7 +124,7 @@ func SignupHandler(c echo.Context) error {
err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background()) err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background())
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusNotFound, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
err = os.Mkdir(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID), os.ModePerm) err = os.Mkdir(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID), os.ModePerm)
@@ -134,20 +137,20 @@ func SignupHandler(c echo.Context) error {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
expiration := time.Now().Add(time.Hour * 24 * 365 * 100) expiration := time.Now().Add(time.Hour * 24 * 365 * 100)
c.SetCookie(&http.Cookie{ c.Cookie(&fiber.Cookie{
Name: "sessionToken", Name: "sessionToken",
Value: session.ID.String(), Value: session.ID.String(),
SameSite: http.SameSiteStrictMode, SameSite: "Strict",
Expires: expiration, Expires: expiration,
Path: "/", Path: "/",
}) })
return c.JSON(http.StatusOK, user) return c.Status(http.StatusOK).JSON(user)
} }
func GenerateSessionToken(db *bun.DB, userId uuid.UUID) (*models.Session, error) { func GenerateSessionToken(db *bun.DB, userId uuid.UUID) (*models.Session, error) {
@@ -160,11 +163,11 @@ func GenerateSessionToken(db *bun.DB, userId uuid.UUID) (*models.Session, error)
return session, err return session, err
} }
func GetUser(c echo.Context) error { func GetUser(c fiber.Ctx) error {
if c.Param("id") == "" { if c.Params("id") == "" {
user := c.Get("user") user := c.Locals("user")
if user == nil { if user == nil {
return c.JSON(http.StatusNotFound, map[string]string{"message": "User not found"}) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "User not found"})
} }
basePath := fmt.Sprintf("%s/%s/", os.Getenv("STORAGE_PATH"), user.(*models.User).ID) basePath := fmt.Sprintf("%s/%s/", os.Getenv("STORAGE_PATH"), user.(*models.User).ID)
@@ -175,23 +178,23 @@ func GetUser(c echo.Context) error {
user.(*models.User).Usage = storageUsage user.(*models.User).Usage = storageUsage
return c.JSON(http.StatusOK, user.(*models.User)) return c.Status(http.StatusOK).JSON(user.(*models.User))
} else { } else {
// get a user from the db using the id parameter, this *should* only be used for admin since /api/admin/users/:id has // get a user from the db using the id parameter, this *should* only be used for admin since /api/admin/users/:id has
// a middleware that checks if the user is an admin, and it should be impossible to pass a param to this endpoint if it isnt that route // a middleware that checks if the user is an admin, and it should be impossible to pass a param to this endpoint if it isnt that route
db := c.Get("db").(*bun.DB) db := db.GetDB()
user := new(models.User) user := new(models.User)
userId, err := uuid.Parse(c.Param("id")) userId, err := uuid.Parse(c.Params("id"))
if err != nil { if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
user.ID = userId user.ID = userId
err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background()) err = db.NewSelect().Model(user).WherePK().Relation("Plan").Scan(context.Background())
if err != nil { if err != nil {
return c.JSON(http.StatusNotFound, map[string]string{"message": "User not found"}) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "User not found"})
} }
basePath := fmt.Sprintf("%s/%s/", os.Getenv("STORAGE_PATH"), user.ID) basePath := fmt.Sprintf("%s/%s/", os.Getenv("STORAGE_PATH"), user.ID)
@@ -202,24 +205,21 @@ func GetUser(c echo.Context) error {
user.Usage = storageUsage user.Usage = storageUsage
return c.JSON(http.StatusOK, user) return c.Status(http.StatusOK).JSON(user)
} }
} }
func LogoutHandler(c echo.Context) error { func LogoutHandler(c fiber.Ctx) error {
db := c.Get("db").(*bun.DB) db := db.GetDB()
cookie, err := c.Cookie("sessionToken") cookie := c.Cookies("sessionToken")
if cookie == "" {
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"message": "Session token missing"})
}
sessionId, err := uuid.Parse(cookie)
if err != nil { if err != nil {
if err == http.ErrNoCookie { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "Bad request"})
return echo.NewHTTPError(http.StatusUnauthorized, "Session token missing")
}
return echo.NewHTTPError(http.StatusBadRequest, "Bad request")
}
sessionId, err := uuid.Parse(cookie.Value)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Bad request")
} }
session := &models.Session{ session := &models.Session{
@@ -229,8 +229,8 @@ func LogoutHandler(c echo.Context) error {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
return c.JSON(http.StatusOK, map[string]string{"message": "Succesfully logged out"}) return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Succesfully logged out"})
} }

View File

@@ -12,7 +12,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
) )
type UploadResponse struct { type UploadResponse struct {
@@ -20,10 +20,10 @@ type UploadResponse struct {
File File `json:"file"` File File `json:"file"`
} }
func UploadFile(c echo.Context) error { func UploadFile(c fiber.Ctx) error {
user := c.Get("user").(*models.User) user := c.Locals("user").(*models.User)
fullPath := strings.Trim(c.Param("*"), "/") fullPath := strings.Trim(c.Params("*"), "/")
basePath := fmt.Sprintf("%s/%s/%s/", os.Getenv("STORAGE_PATH"), user.ID, fullPath) basePath := fmt.Sprintf("%s/%s/%s/", os.Getenv("STORAGE_PATH"), user.ID, fullPath)
currentUsage, err := calculateStorageUsage(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID)) currentUsage, err := calculateStorageUsage(fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID))
@@ -44,12 +44,12 @@ func UploadFile(c echo.Context) error {
} }
} }
reader, err := c.Request().MultipartReader() form, err := c.MultipartForm()
if err != nil { if err != nil {
if err == http.ErrNotMultipart { if err == http.ErrNotMultipart {
if directoryExists { if directoryExists {
// Directories exist, but no file was uploaded // Directories exist, but no file was uploaded
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A folder with that name already exists"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A folder with that name already exists"})
} }
// Directories were just created, and no file was provided // Directories were just created, and no file was provided
entry, err := os.Stat(basePath) entry, err := os.Stat(basePath)
@@ -69,22 +69,18 @@ func UploadFile(c echo.Context) error {
}, },
} }
return c.JSON(http.StatusOK, uploadFile) return c.Status(http.StatusOK).JSON(uploadFile)
} }
fmt.Println(err) fmt.Println(err)
return err return err
} }
part, err := reader.NextPart() file := form.File["file"][0]
if err != nil {
fmt.Println(err)
return err
}
filepath := filepath.Join(basePath, part.FileName()) filepath := filepath.Join(basePath, file.Filename)
if _, err = os.Stat(filepath); err == nil { if _, err = os.Stat(filepath); err == nil {
return c.JSON(http.StatusConflict, map[string]string{"message": "File with that name already exists"}) return c.Status(http.StatusConflict).JSON(fiber.Map{"message": "File with that name already exists"})
} }
dst, err := os.Create(filepath) dst, err := os.Create(filepath)
@@ -95,16 +91,22 @@ func UploadFile(c echo.Context) error {
defer dst.Close() defer dst.Close()
// Read the file manually because otherwise we are limited by the arbitrarily small size of /tmp // Read the file manually because otherwise we are limited by the arbitrarily small size of /tmp
buffer := make([]byte, 4096) buffer := make([]byte, 1*1024*1024)
totalSize := int64(0) totalSize := int64(0)
fd, err := file.Open()
if err != nil {
fmt.Println(err)
return err
}
for { for {
n, readErr := part.Read(buffer) n, readErr := fd.Read(buffer)
if readErr != nil && readErr == io.ErrUnexpectedEOF { if readErr != nil && readErr == io.ErrUnexpectedEOF {
dst.Close() dst.Close()
os.Remove(filepath) os.Remove(filepath)
return c.JSON(http.StatusRequestTimeout, map[string]string{"message": "Upload canceled"}) return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{"message": "Upload canceled"})
} }
if readErr != nil && readErr != io.EOF { if readErr != nil && readErr != io.EOF {
@@ -117,7 +119,7 @@ func UploadFile(c echo.Context) error {
if currentUsage+totalSize > user.Plan.MaxStorage { if currentUsage+totalSize > user.Plan.MaxStorage {
dst.Close() dst.Close()
os.Remove(filepath) os.Remove(filepath)
return c.JSON(http.StatusInsufficientStorage, map[string]string{"message": "Insufficient storage space"}) return c.Status(http.StatusInsufficientStorage).JSON(fiber.Map{"message": "Insufficient storage space"})
} }
if _, err := dst.Write(buffer[:n]); err != nil { if _, err := dst.Write(buffer[:n]); err != nil {
@@ -143,7 +145,7 @@ func UploadFile(c echo.Context) error {
}, },
} }
return c.JSON(http.StatusOK, uploadFile) return c.Status(http.StatusOK).JSON(uploadFile)
} }
} }
} }
@@ -185,10 +187,10 @@ type File struct {
LastModified string `json:"last_modified"` LastModified string `json:"last_modified"`
} }
func GetFiles(c echo.Context) error { func GetFiles(c fiber.Ctx) error {
user := c.Get("user").(*models.User) user := c.Locals("user").(*models.User)
fullPath := strings.Trim(c.Param("*"), "/") fullPath := strings.Trim(c.Params("*"), "/")
basePath := fmt.Sprintf("%s/%s/%s/", os.Getenv("STORAGE_PATH"), user.ID, fullPath) basePath := fmt.Sprintf("%s/%s/%s/", os.Getenv("STORAGE_PATH"), user.ID, fullPath)
f, err := os.Open(basePath) f, err := os.Open(basePath)
@@ -214,22 +216,22 @@ func GetFiles(c echo.Context) error {
}) })
} }
return c.JSON(http.StatusOK, jsonFiles) return c.Status(http.StatusOK).JSON(jsonFiles)
} }
func GetFile(c echo.Context) error { func GetFile(c fiber.Ctx) error {
user := c.Get("user").(*models.User) user := c.Locals("user").(*models.User)
fullPath := strings.Trim(c.Param("*"), "/") fullPath := strings.Trim(c.Params("*"), "/")
fileNamesParam := c.QueryParam("filenames") fileNamesParam := c.Query("filenames")
var fileNames []string var fileNames []string
if fileNamesParam != "" { if fileNamesParam != "" {
fileNames = strings.Split(fileNamesParam, ",") fileNames = strings.Split(fileNamesParam, ",")
} }
if fullPath == "" && len(fileNames) == 0 { if fullPath == "" && len(fileNames) == 0 {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "A file is required"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "A file is required"})
} }
basePath := fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID) basePath := fmt.Sprintf("%s/%s", os.Getenv("STORAGE_PATH"), user.ID)
@@ -239,12 +241,12 @@ func GetFile(c echo.Context) error {
fileInfo, err := os.Stat(basePath) fileInfo, err := os.Stat(basePath)
if err != nil { if err != nil {
return c.JSON(http.StatusNotFound, map[string]string{"message": "No file found!"}) return c.Status(http.StatusNotFound).JSON(fiber.Map{"message": "No file found!"})
} }
var buf bytes.Buffer var buf bytes.Buffer
if fileInfo.IsDir() { if fileInfo.IsDir() {
c.Response().Header().Set(echo.HeaderContentType, "application/zip") c.Type("application/zip")
if len(fileNames) != 0 { if len(fileNames) != 0 {
err := zipFiles(&buf, filepath.Join(basePath, fullPath), fileNames) err := zipFiles(&buf, filepath.Join(basePath, fullPath), fileNames)
@@ -253,7 +255,7 @@ func GetFile(c echo.Context) error {
return err return err
} }
_, err = buf.WriteTo(c.Response().Writer) _, err = buf.WriteTo(c.Response().BodyWriter())
return err return err
} }
@@ -263,10 +265,10 @@ func GetFile(c echo.Context) error {
return err return err
} }
_, err = buf.WriteTo(c.Response().Writer) _, err = buf.WriteTo(c.Response().BodyWriter())
return err return err
} else { } else {
return c.File(basePath) return c.SendFile(basePath)
} }
} }
@@ -343,21 +345,21 @@ type DeleteRequest struct {
Files []File `json:"files"` Files []File `json:"files"`
} }
func DeleteFiles(c echo.Context) error { func DeleteFiles(c fiber.Ctx) error {
var deleteData DeleteRequest deleteData := new(DeleteRequest)
if err := c.Bind(&deleteData); err != nil { if err := c.Bind().JSON(deleteData); err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
if len(deleteData.Files) == 0 { if len(deleteData.Files) == 0 {
return c.JSON(http.StatusBadRequest, map[string]string{"message": "Files are required!"}) return c.Status(http.StatusBadRequest).JSON(fiber.Map{"message": "Files are required!"})
} }
user := c.Get("user").(*models.User) user := c.Locals("user").(*models.User)
fullPath := strings.Trim(c.Param("*"), "/") fullPath := strings.Trim(c.Params("*"), "/")
basePath := fmt.Sprintf("%s/%s/%s", os.Getenv("STORAGE_PATH"), user.ID, fullPath) basePath := fmt.Sprintf("%s/%s/%s", os.Getenv("STORAGE_PATH"), user.ID, fullPath)
for _, file := range deleteData.Files { for _, file := range deleteData.Files {
@@ -365,7 +367,7 @@ func DeleteFiles(c echo.Context) error {
err := os.RemoveAll(path) err := os.RemoveAll(path)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "An unknown error occoured!"}) return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"message": "An unknown error occoured!"})
} }
} }
@@ -376,5 +378,5 @@ func DeleteFiles(c echo.Context) error {
word = word + "s" word = word + "s"
} }
return c.JSON(http.StatusOK, map[string]string{"message": fmt.Sprintf("Successfully deleted %d %s", fileLen, word)}) return c.Status(http.StatusOK).JSON(fiber.Map{"message": fmt.Sprintf("Successfully deleted %d %s", fileLen, word)})
} }

View File

@@ -5,12 +5,13 @@ package main
import ( import (
"filething/ui" "filething/ui"
"fmt"
"io/fs" "io/fs"
"net/http"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/static"
) )
type embeddedFS struct { type embeddedFS struct {
@@ -21,55 +22,61 @@ type embeddedFS struct {
func (fs *embeddedFS) Open(name string) (fs.File, error) { func (fs *embeddedFS) Open(name string) (fs.File, error) {
// Prepend the prefix to the requested file name // Prepend the prefix to the requested file name
publicPath := filepath.Join(fs.prefix, name) publicPath := filepath.Join(fs.prefix, name)
return fs.baseFS.Open(publicPath) fmt.Println("Reading file:", publicPath)
file, err := fs.baseFS.Open(publicPath)
if err != nil {
return nil, fmt.Errorf("file not found: %s", publicPath)
}
fmt.Println("File found:", publicPath, file)
return file, err
} }
var publicFS = &embeddedFS{ var publicFS = &embeddedFS{
baseFS: ui.DistDirFS, baseFS: ui.DistDir,
prefix: "public/", prefix: "public/",
} }
func init() { func init() {
initUi = func(e *echo.Echo) { initUi = func(app *fiber.App) {
e.GET("/*", echo.StaticDirectoryHandler(publicFS, false)) app.Get("/*", static.New("", static.Config{
FS: publicFS,
}))
e.HTTPErrorHandler = customHTTPErrorHandler app.Use(func(c fiber.Ctx) error {
} err := c.Next()
if err == nil {
return nil
} }
// Custom Error handling since Nuxt relies on the 404 page for dynamic pages we still want api routes to use the default if fiber.ErrNotFound == err {
// error handling built into echo path := c.Path()
func customHTTPErrorHandler(err error, c echo.Context) {
if he, ok := err.(*echo.HTTPError); ok && he.Code == http.StatusNotFound {
path := c.Request().URL.Path
if !strings.HasPrefix(path, "/api") { if !strings.HasPrefix(path, "/api") {
file, err := publicFS.Open("404.html") file, err := publicFS.Open("404.html")
if err != nil { if err != nil {
c.Logger().Error(err) c.App().Server().Logger.Printf("Error opening 404.html: %s", err)
return err
} }
defer file.Close()
fileInfo, err := file.Stat() fileInfo, err := file.Stat()
if err != nil { if err != nil {
c.Logger().Error(err) c.App().Server().Logger.Printf("An error occurred while getting the file info: %s", err)
return err
} }
fileBuf := make([]byte, fileInfo.Size()) fileBuf := make([]byte, fileInfo.Size())
_, err = file.Read(fileBuf) _, err = file.Read(fileBuf)
defer func() {
if err := file.Close(); err != nil {
panic(err)
}
}()
if err != nil { if err != nil {
c.Logger().Error(err) c.App().Server().Logger.Printf("An error occurred while reading the file: %s", err)
panic(err) return err
} }
c.HTML(http.StatusNotFound, string(fileBuf)) return c.Status(fiber.StatusNotFound).SendString(string(fileBuf))
return
} }
} }
return err
c.Echo().DefaultHTTPErrorHandler(err, c) })
}
} }

View File

@@ -4,24 +4,53 @@
package main package main
import ( import (
"net/url" "strings"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
echoMiddleware "github.com/labstack/echo/v4/middleware" "github.com/gofiber/fiber/v3/middleware/proxy"
fastProxy "github.com/yeqown/fasthttp-reverse-proxy/v2"
) )
func init() { func init() {
initUi = func(e *echo.Echo) { initUi = func(app *fiber.App) {
spawnProcess("bun", []string{"--cwd=ui", "run", "dev"}, e) if !fiber.IsChild() {
spawnProcess("bun", []string{"--cwd=ui", "run", "dev"}, app)
}
target := "localhost:3000" target := "localhost:3000"
e.Group("/*").Use(echoMiddleware.ProxyWithConfig(echoMiddleware.ProxyConfig{ app.All("/*", func(c fiber.Ctx) error {
Balancer: echoMiddleware.NewRoundRobinBalancer([]*echoMiddleware.ProxyTarget{ path := c.Path()
{URL: &url.URL{ if strings.HasPrefix(path, "/api") {
Scheme: "http", return c.Next()
Host: target, }
}},
}), request := c.Request().URI()
})) if string(request.RequestURI()) == "/_nuxt/" {
return proxyWebSocket(c, target)
}
return proxy.Do(c, "http://"+target+string(request.RequestURI()))
})
} }
} }
var proxyServer *fastProxy.ReverseProxy
func proxyWebSocket(c fiber.Ctx, target string) error {
path := c.Path()
// proxyServer, err := fastProxy.NewWSReverseProxyWith(
// fastProxy.WithURL_OptionWS("ws://localhost:3000"+path),
// fastProxy.WithDynamicPath_OptionWS(true, fastProxy.DefaultOverrideHeader),
// )
if proxyServer == nil {
proxyServer, err = fastProxy.NewWSReverseProxyWith(
fastProxy.WithURL_OptionWS("ws://localhost:3000"+path),
fastProxy.WithDynamicPath_OptionWS(true, fastProxy.DefaultOverrideHeader),
)
if err != nil {
panic(err)
}
}
proxyServer.ServeHTTP(c.Context())
return nil
}

View File

@@ -7,16 +7,45 @@ import (
"embed" "embed"
"filething/ui" "filething/ui"
"io" "io"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
echoMiddleware "github.com/labstack/echo/v4/middleware" "github.com/gofiber/fiber/v3/middleware/proxy"
) )
// func init() {
// initUi = func(app *fiber.App) {
// tmpDir, err := os.MkdirTemp("", "filething-ssr")
// if err != nil {
// panic(err)
// }
// err = copyEmbeddedFiles(ui.DistDir, ".output", tmpDir)
// if err != nil {
// panic(err)
// }
// path := filepath.Join(tmpDir, "server/index.mjs")
// spawnProcess("node", []string{path}, app)
// // target := "localhost:3000"
// // e.Group("/*").Use(echoMiddleware.ProxyWithConfig(echoMiddleware.ProxyConfig{
// // Balancer: echoMiddleware.NewRoundRobinBalancer([]*echoMiddleware.ProxyTarget{
// // {URL: &url.URL{
// // Scheme: "http",
// // Host: target,
// // }},
// // }),
// // }))
// app.Get("/*", proxy.Forward("http://localhost:3000"))
// }
// }
func init() { func init() {
initUi = func(e *echo.Echo) { initUi = func(app *fiber.App) {
if !fiber.IsChild() {
tmpDir, err := os.MkdirTemp("", "filething-ssr") tmpDir, err := os.MkdirTemp("", "filething-ssr")
if err != nil { if err != nil {
panic(err) panic(err)
@@ -28,17 +57,20 @@ func init() {
} }
path := filepath.Join(tmpDir, "server/index.mjs") path := filepath.Join(tmpDir, "server/index.mjs")
spawnProcess("node", []string{path}, e)
spawnProcess("node", []string{path}, app)
}
target := "localhost:3000" target := "localhost:3000"
e.Group("/*").Use(echoMiddleware.ProxyWithConfig(echoMiddleware.ProxyConfig{ app.All("/*", func(c fiber.Ctx) error {
Balancer: echoMiddleware.NewRoundRobinBalancer([]*echoMiddleware.ProxyTarget{ path := c.Path()
{URL: &url.URL{ if strings.HasPrefix(path, "/api") {
Scheme: "http", return c.Next()
Host: target, }
}},
}), request := c.Request().URI()
})) return proxy.Do(c, "http://"+target+string(request.RequestURI()))
})
} }
} }

View File

@@ -20,7 +20,7 @@ export const useUser = () => {
// Fetch the user only if it's uninitialized (i.e., null) // Fetch the user only if it's uninitialized (i.e., null)
const getUser = async () => { const getUser = async () => {
if (!user.value.fetched) { if (!user.value.fetched && useCookie('sessionToken').value) {
await fetchUser() await fetchUser()
} }

View File

@@ -3,12 +3,7 @@ package ui
import ( import (
"embed" "embed"
"github.com/labstack/echo/v4"
) )
//go:embed all:.output //go:embed all:.output
var DistDir embed.FS var DistDir embed.FS
// DistDirFS contains the embedded dist directory files (without the "dist" prefix)
var DistDirFS = echo.MustSubFS(DistDir, ".output/")

View File

@@ -42,8 +42,8 @@ onUnmounted(() => {
<div class="min-h-screen min-w-screen grid place-content-center bg-base"> <div class="min-h-screen min-w-screen grid place-content-center bg-base">
<div class="flex flex-col text-center bg-surface border shadow-md px-10 py-8 rounded-2xl min-w-0 max-w-[313px]"> <div class="flex flex-col text-center bg-surface border shadow-md px-10 py-8 rounded-2xl min-w-0 max-w-[313px]">
<h2 class="font-semibold text-2xl mb-2">Login</h2> <h2 class="font-semibold text-2xl mb-2">Login</h2>
<Input class="my-2" v-model="username_or_email" placeholder="Username or Email..." /> <Input class="my-2" :value="username_or_email" v-model="username_or_email" placeholder="Username or Email..." />
<Input class="my-2" v-model="password" type="password" placeholder="Password..." /> <Input class="my-2" :value="password" v-model="password" type="password" placeholder="Password..." />
<p class="text-love">{{ error }}</p> <p class="text-love">{{ error }}</p>
<button @click="submitForm" <button @click="submitForm"
class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25 focus:outline-none focus:ring focus:ring-inset">Login</button> class="py-2 px-4 my-2 bg-pine/10 text-pine rounded-md transition-colors hover:bg-pine/15 active:bg-pine/25 focus:outline-none focus:ring focus:ring-inset">Login</button>

View File

@@ -1,17 +1,16 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/labstack/echo/v4" "github.com/gofiber/fiber/v3"
) )
func spawnProcess(cmd string, args []string, e *echo.Echo) error { func spawnProcess(cmd string, args []string, app *fiber.App) error {
shutdown := make(chan os.Signal, 1) shutdown := make(chan os.Signal, 1)
signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() { go func() {
@@ -45,7 +44,7 @@ func spawnProcess(cmd string, args []string, e *echo.Echo) error {
fmt.Println("sub process server stopped") fmt.Println("sub process server stopped")
if err := e.Shutdown(context.Background()); err != nil { if err := app.Shutdown(); err != nil {
fmt.Println("Error shutting down HTTP server:", err) fmt.Println("Error shutting down HTTP server:", err)
} }
}() }()