Add counting for link visits
Some checks failed
/ docker (push) Failing after 58s

Because funny
This commit is contained in:
Melody Becker 2025-03-24 18:05:27 +01:00
parent de9439d917
commit 2920dcbfec
11 changed files with 862 additions and 89 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
db.sqlite
### JetBrains template ### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

28
cmd/dbgen/main.go Normal file
View file

@ -0,0 +1,28 @@
package main
import (
"github.com/glebarez/sqlite"
"gorm.io/gen"
"gorm.io/gorm"
"git.mstar.dev/mstar/mstar-webserver/storage/models"
)
func main() {
g := gen.NewGenerator(gen.Config{
OutPath: "./storage/generated",
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
})
gormdb, _ := gorm.Open(sqlite.Open("./db.sqlite"), &gorm.Config{})
g.UseDB(gormdb)
// Generate basic type-safe DAO API for struct `model.User` following conventions
g.ApplyBasic(models.Link{})
// Generate Type Safe API with Dynamic SQL defined on Querier interface for `model.User` and `model.Company`
g.ApplyInterface(func(models.ILink) {}, models.Link{})
// Generate the code
g.Execute()
}

25
go.mod
View file

@ -1,15 +1,36 @@
module gitlab.com/evilthings-services/mstar module git.mstar.dev/mstar/mstar-webserver
go 1.23.4 go 1.23.4
require ( require (
git.mstar.dev/mstar/goutils v1.6.1 git.mstar.dev/mstar/goutils v1.9.1
github.com/glebarez/sqlite v1.11.0
github.com/rs/zerolog v1.33.0 github.com/rs/zerolog v1.33.0
gorm.io/gen v0.3.26
gorm.io/gorm v1.25.12
) )
require ( require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rs/xid v1.5.0 // indirect github.com/rs/xid v1.5.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.12.0 // indirect golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/hints v1.1.0 // indirect
gorm.io/plugin/dbresolver v1.5.3 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
) )

141
go.sum
View file

@ -1,22 +1,151 @@
git.mstar.dev/mstar/goutils v1.5.4 h1:l/4oQe/fBk9zyXplQkGXbmQndnm0aRdHuy4wgQfNrFo= git.mstar.dev/mstar/goutils v1.9.0 h1:xF0bsGObxRuRtbB7VArFPBkYVNypc8eWnyWp8bkxyEo=
git.mstar.dev/mstar/goutils v1.5.4/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA= git.mstar.dev/mstar/goutils v1.9.0/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.6.0 h1:2K0KqoymJoEJUIYVHkjJ0GJKGNeWVfHMmpeRMtNiG3Q= git.mstar.dev/mstar/goutils v1.9.1 h1:B4km2Xj0Yq8GHIlAYo45NGMRQRdkr+hV9qdvhTJKuuA=
git.mstar.dev/mstar/goutils v1.6.0/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA= git.mstar.dev/mstar/goutils v1.9.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.6.1 h1:2yr9GYN8CJByZsJRu1pZ6WBp51Nn+3zJq49ky54xYDk=
git.mstar.dev/mstar/goutils v1.6.1/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/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/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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 v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
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/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
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/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
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/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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.5.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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
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=
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c h1:jWdr7cHgl8c/ua5vYbR2WhSp+NQmzhsj0xoY3foTzW8=
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ=
gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/hints v1.1.0 h1:Lp4z3rxREufSdxn4qmkK3TLDltrM10FLTHiuqwDPvXw=
gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y=
gorm.io/plugin/dbresolver v1.5.0 h1:XVHLxh775eP0CqVh3vcfJtYqja3uFl5Wr3cKlY8jgDY=
gorm.io/plugin/dbresolver v1.5.0/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=

84
handlers.go Normal file
View file

@ -0,0 +1,84 @@
package main
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
"git.mstar.dev/mstar/mstar-webserver/storage"
)
func handleRoot(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFS(embed_templates, "templates/index.html")
if err != nil {
http.Error(w, "Couldn't parse template file", http.StatusInternalServerError)
return
}
err = tmpl.Execute(w, nil)
if err != nil {
http.Error(w, "Failed to execute template", http.StatusInternalServerError)
}
}
func buildHTTPFileReader(embed_content string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", embed_content)
}
}
func countingRedirect(target string) http.Handler {
redirect := http.RedirectHandler(target, http.StatusMovedPermanently)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info().Msg("Counting redirect")
fmt.Println("Counting redirect")
if err := storage.IncrementLinkCounter(target); err != nil {
log.Error().Err(err).Str("target", target).Msg("Failed to update link")
}
redirect.ServeHTTP(w, r)
})
}
func awawaStream(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Disabled until I can find a good way to rate limit the stream")
return
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(206)
w.Header().Set("Status", "206")
genStub(
1024,
w,
) // Hardcoded. Firefox & Chrome both have this value and a len of 0 wouldn't work
hlog.FromRequest(r).Debug().Msg("Starting stream")
i := true
fmt.Fprint(w, "awawaw")
// Only run every 0.2 seconds, aka 5 times a second
timer := time.Tick(time.Millisecond * 100)
for {
select {
case <-r.Context().Done():
hlog.FromRequest(r).Debug().Msg("Stream done")
return
case <-timer:
hlog.FromRequest(r).Debug().Msg("Stream elem")
if i {
fmt.Fprint(w, "a")
} else {
fmt.Fprint(w, "w")
}
i = !i
}
}
}
func genStub(length int, w http.ResponseWriter) {
PreSufLen := len(HTML_PREFIX) + len(HTML_SUFFIX)
fmt.Fprint(w, HTML_PREFIX)
for i := 0; i < length-PreSufLen; i++ {
fmt.Fprint(w, '\u0020')
}
fmt.Fprint(w, HTML_SUFFIX)
}

91
main.go
View file

@ -4,17 +4,19 @@ import (
"context" "context"
"embed" "embed"
"flag" "flag"
"fmt"
"html/template"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"time" "time"
"git.mstar.dev/mstar/goutils/middleware" httputils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/other"
"github.com/rs/zerolog/hlog" "github.com/glebarez/sqlite"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gorm.io/gorm"
"git.mstar.dev/mstar/mstar-webserver/storage/generated"
"git.mstar.dev/mstar/mstar-webserver/storage/models"
) )
const HTML_PREFIX = "<!-- " const HTML_PREFIX = "<!-- "
@ -42,13 +44,24 @@ var redirectMapping = map[string]string{
"/git": "https://git.mstar.dev", "/git": "https://git.mstar.dev",
"/gitlab": "https://gitlab.com/mstarongitlab", "/gitlab": "https://gitlab.com/mstarongitlab",
"/github": "https://github.com/mstarongithub", "/github": "https://github.com/mstarongithub",
"/fedi": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"/youtube": "https://www.youtube.com/channel/UC-LklY_wt004i_30xcVohbg",
} }
func main() { func main() {
other.SetupFlags() other.SetupFlags()
portFlag := flag.String("port", "8080", "Set the port. Overwrites env vars") portFlag := flag.String("port", "8080", "Set the port. Overwrites env vars")
flag.Parse() flag.Parse()
other.ConfigureLoggingFromCliArgs() other.ConfigureLogging(nil)
db, err := gorm.Open(sqlite.Open("./db.sqlite"), &gorm.Config{})
if err != nil {
log.Fatal().Err(err).Msg("Failed to open db file")
}
generated.SetDefault(db)
if err := db.AutoMigrate(&models.Link{}); err != nil {
log.Fatal().Err(err).Msg("Failed to automigrate db setup")
}
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/{$}", handleRoot) mux.HandleFunc("/{$}", handleRoot)
@ -60,7 +73,7 @@ func main() {
) )
for k, v := range redirectMapping { for k, v := range redirectMapping {
mux.Handle(k, http.RedirectHandler(v, http.StatusMovedPermanently)) mux.Handle(k, countingRedirect(v))
} }
mux.HandleFunc("/cat/awawawa", awawaStream) mux.HandleFunc("/cat/awawawa", awawaStream)
@ -77,13 +90,10 @@ func main() {
// Static files not in /static or /.well-known // Static files not in /static or /.well-known
mux.HandleFunc("/robots.txt", buildHTTPFileReader(embed_robots_txt)) mux.HandleFunc("/robots.txt", buildHTTPFileReader(embed_robots_txt))
mux.HandleFunc("/humans.txt", buildHTTPFileReader(embed_humans_txt)) mux.HandleFunc("/humans.txt", buildHTTPFileReader(embed_humans_txt))
mux.HandleFunc("/8ztjw6bhc9.txt", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "keke")
})
server := http.Server{ server := http.Server{
Addr: ":" + *portFlag, Addr: ":" + *portFlag,
Handler: middleware.ChainMiddlewares(mux, middleware.LoggingMiddleware), Handler: httputils.ChainMiddlewares(mux, httputils.LoggingMiddleware),
} }
exitChan := make(chan os.Signal, 1) exitChan := make(chan os.Signal, 1)
signal.Notify(exitChan, os.Interrupt) signal.Notify(exitChan, os.Interrupt)
@ -97,68 +107,9 @@ func main() {
log.Info().Msg("Shutting down server due to Interrupt") log.Info().Msg("Shutting down server due to Interrupt")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel() defer cancel()
err := server.Shutdown(ctx) err = server.Shutdown(ctx)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("Failed to cleanly shut down") log.Fatal().Err(err).Msg("Failed to cleanly shut down")
} }
log.Info().Msg("Clean shutdown complete") log.Info().Msg("Clean shutdown complete")
} }
func handleRoot(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFS(embed_templates, "templates/index.html")
if err != nil {
http.Error(w, "Couldn't parse template file", http.StatusInternalServerError)
return
}
err = tmpl.Execute(w, nil)
if err != nil {
http.Error(w, "Failed to execute template", http.StatusInternalServerError)
}
}
func buildHTTPFileReader(embed_content string) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s", embed_content)
}
}
func awawaStream(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Disabled until I can find a good way to rate limit the stream")
return
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(206)
w.Header().Set("Status", "206")
genStub(
1024,
w,
) // Hardcoded. Firefox & Chrome both have this value and a len of 0 wouldn't work
hlog.FromRequest(r).Debug().Msg("Starting stream")
i := true
fmt.Fprint(w, "awawaw")
// Only run every 0.2 seconds, aka 5 times a second
timer := time.Tick(time.Millisecond * 100)
for {
select {
case <-r.Context().Done():
hlog.FromRequest(r).Debug().Msg("Stream done")
return
case <-timer:
hlog.FromRequest(r).Debug().Msg("Stream elem")
if i {
fmt.Fprint(w, "a")
} else {
fmt.Fprint(w, "w")
}
i = !i
}
}
}
func genStub(length int, w http.ResponseWriter) {
PreSufLen := len(HTML_PREFIX) + len(HTML_SUFFIX)
fmt.Fprint(w, HTML_PREFIX)
for i := 0; i < length-PreSufLen; i++ {
fmt.Fprint(w, '\u0020')
}
fmt.Fprint(w, HTML_SUFFIX)
}

35
storage/db.go Normal file
View file

@ -0,0 +1,35 @@
package storage
import (
"git.mstar.dev/mstar/goutils/other"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"git.mstar.dev/mstar/mstar-webserver/storage/generated"
"git.mstar.dev/mstar/mstar-webserver/storage/models"
)
func IncrementLinkCounter(link string) error {
model, err := generated.Link.Where(generated.Link.Link.Eq(link)).First()
switch err {
case gorm.ErrRecordNotFound:
log.Info().Msg("Creating link counter")
err = generated.Link.Create(&models.Link{Link: link, Count: 1})
if err != nil {
return other.Error("storage", "failed to create new link entry", err)
} else {
return nil
}
case nil:
log.Info().Uint64("current", model.Count).Msg("Updating link")
_, err = generated.Link.Where(generated.Link.Link.Eq(link)).
Update(generated.Link.Count_, model.Count+1)
if err != nil {
return other.Error("storage", "failed to increment link count", err)
} else {
return nil
}
default:
return other.Error("storage", "failed to find link", err)
}
}

103
storage/generated/gen.go Normal file
View file

@ -0,0 +1,103 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package generated
import (
"context"
"database/sql"
"gorm.io/gorm"
"gorm.io/gen"
"gorm.io/plugin/dbresolver"
)
var (
Q = new(Query)
Link *link
)
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
Link = &Q.Link
}
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{
db: db,
Link: newLink(db, opts...),
}
}
type Query struct {
db *gorm.DB
Link link
}
func (q *Query) Available() bool { return q.db != nil }
func (q *Query) clone(db *gorm.DB) *Query {
return &Query{
db: db,
Link: q.Link.clone(db),
}
}
func (q *Query) ReadDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}
func (q *Query) WriteDB() *Query {
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{
db: db,
Link: q.Link.replaceDB(db),
}
}
type queryCtx struct {
Link ILinkDo
}
func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{
Link: q.Link.WithContext(ctx),
}
}
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
tx := q.db.Begin(opts...)
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
}
type QueryTx struct {
*Query
Error error
}
func (q *QueryTx) Commit() error {
return q.db.Commit().Error
}
func (q *QueryTx) Rollback() error {
return q.db.Rollback().Error
}
func (q *QueryTx) SavePoint(name string) error {
return q.db.SavePoint(name).Error
}
func (q *QueryTx) RollbackTo(name string) error {
return q.db.RollbackTo(name).Error
}

View file

@ -0,0 +1,400 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package generated
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
"git.mstar.dev/mstar/mstar-webserver/storage/models"
)
func newLink(db *gorm.DB, opts ...gen.DOOption) link {
_link := link{}
_link.linkDo.UseDB(db, opts...)
_link.linkDo.UseModel(&models.Link{})
tableName := _link.linkDo.TableName()
_link.ALL = field.NewAsterisk(tableName)
_link.ID = field.NewUint(tableName, "id")
_link.CreatedAt = field.NewTime(tableName, "created_at")
_link.UpdatedAt = field.NewTime(tableName, "updated_at")
_link.DeletedAt = field.NewField(tableName, "deleted_at")
_link.Link = field.NewString(tableName, "link")
_link.Count_ = field.NewUint64(tableName, "count")
_link.fillFieldMap()
return _link
}
type link struct {
linkDo
ALL field.Asterisk
ID field.Uint
CreatedAt field.Time
UpdatedAt field.Time
DeletedAt field.Field
Link field.String
Count_ field.Uint64
fieldMap map[string]field.Expr
}
func (l link) Table(newTableName string) *link {
l.linkDo.UseTable(newTableName)
return l.updateTableName(newTableName)
}
func (l link) As(alias string) *link {
l.linkDo.DO = *(l.linkDo.As(alias).(*gen.DO))
return l.updateTableName(alias)
}
func (l *link) updateTableName(table string) *link {
l.ALL = field.NewAsterisk(table)
l.ID = field.NewUint(table, "id")
l.CreatedAt = field.NewTime(table, "created_at")
l.UpdatedAt = field.NewTime(table, "updated_at")
l.DeletedAt = field.NewField(table, "deleted_at")
l.Link = field.NewString(table, "link")
l.Count_ = field.NewUint64(table, "count")
l.fillFieldMap()
return l
}
func (l *link) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := l.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (l *link) fillFieldMap() {
l.fieldMap = make(map[string]field.Expr, 6)
l.fieldMap["id"] = l.ID
l.fieldMap["created_at"] = l.CreatedAt
l.fieldMap["updated_at"] = l.UpdatedAt
l.fieldMap["deleted_at"] = l.DeletedAt
l.fieldMap["link"] = l.Link
l.fieldMap["count"] = l.Count_
}
func (l link) clone(db *gorm.DB) link {
l.linkDo.ReplaceConnPool(db.Statement.ConnPool)
return l
}
func (l link) replaceDB(db *gorm.DB) link {
l.linkDo.ReplaceDB(db)
return l
}
type linkDo struct{ gen.DO }
type ILinkDo interface {
gen.SubQuery
Debug() ILinkDo
WithContext(ctx context.Context) ILinkDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() ILinkDo
WriteDB() ILinkDo
As(alias string) gen.Dao
Session(config *gorm.Session) ILinkDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) ILinkDo
Not(conds ...gen.Condition) ILinkDo
Or(conds ...gen.Condition) ILinkDo
Select(conds ...field.Expr) ILinkDo
Where(conds ...gen.Condition) ILinkDo
Order(conds ...field.Expr) ILinkDo
Distinct(cols ...field.Expr) ILinkDo
Omit(cols ...field.Expr) ILinkDo
Join(table schema.Tabler, on ...field.Expr) ILinkDo
LeftJoin(table schema.Tabler, on ...field.Expr) ILinkDo
RightJoin(table schema.Tabler, on ...field.Expr) ILinkDo
Group(cols ...field.Expr) ILinkDo
Having(conds ...gen.Condition) ILinkDo
Limit(limit int) ILinkDo
Offset(offset int) ILinkDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) ILinkDo
Unscoped() ILinkDo
Create(values ...*models.Link) error
CreateInBatches(values []*models.Link, batchSize int) error
Save(values ...*models.Link) error
First() (*models.Link, error)
Take() (*models.Link, error)
Last() (*models.Link, error)
Find() ([]*models.Link, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Link, err error)
FindInBatches(result *[]*models.Link, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.Link) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) ILinkDo
Assign(attrs ...field.AssignExpr) ILinkDo
Joins(fields ...field.RelationField) ILinkDo
Preload(fields ...field.RelationField) ILinkDo
FirstOrInit() (*models.Link, error)
FirstOrCreate() (*models.Link, error)
FindByPage(offset int, limit int) (result []*models.Link, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) ILinkDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (l linkDo) Debug() ILinkDo {
return l.withDO(l.DO.Debug())
}
func (l linkDo) WithContext(ctx context.Context) ILinkDo {
return l.withDO(l.DO.WithContext(ctx))
}
func (l linkDo) ReadDB() ILinkDo {
return l.Clauses(dbresolver.Read)
}
func (l linkDo) WriteDB() ILinkDo {
return l.Clauses(dbresolver.Write)
}
func (l linkDo) Session(config *gorm.Session) ILinkDo {
return l.withDO(l.DO.Session(config))
}
func (l linkDo) Clauses(conds ...clause.Expression) ILinkDo {
return l.withDO(l.DO.Clauses(conds...))
}
func (l linkDo) Returning(value interface{}, columns ...string) ILinkDo {
return l.withDO(l.DO.Returning(value, columns...))
}
func (l linkDo) Not(conds ...gen.Condition) ILinkDo {
return l.withDO(l.DO.Not(conds...))
}
func (l linkDo) Or(conds ...gen.Condition) ILinkDo {
return l.withDO(l.DO.Or(conds...))
}
func (l linkDo) Select(conds ...field.Expr) ILinkDo {
return l.withDO(l.DO.Select(conds...))
}
func (l linkDo) Where(conds ...gen.Condition) ILinkDo {
return l.withDO(l.DO.Where(conds...))
}
func (l linkDo) Order(conds ...field.Expr) ILinkDo {
return l.withDO(l.DO.Order(conds...))
}
func (l linkDo) Distinct(cols ...field.Expr) ILinkDo {
return l.withDO(l.DO.Distinct(cols...))
}
func (l linkDo) Omit(cols ...field.Expr) ILinkDo {
return l.withDO(l.DO.Omit(cols...))
}
func (l linkDo) Join(table schema.Tabler, on ...field.Expr) ILinkDo {
return l.withDO(l.DO.Join(table, on...))
}
func (l linkDo) LeftJoin(table schema.Tabler, on ...field.Expr) ILinkDo {
return l.withDO(l.DO.LeftJoin(table, on...))
}
func (l linkDo) RightJoin(table schema.Tabler, on ...field.Expr) ILinkDo {
return l.withDO(l.DO.RightJoin(table, on...))
}
func (l linkDo) Group(cols ...field.Expr) ILinkDo {
return l.withDO(l.DO.Group(cols...))
}
func (l linkDo) Having(conds ...gen.Condition) ILinkDo {
return l.withDO(l.DO.Having(conds...))
}
func (l linkDo) Limit(limit int) ILinkDo {
return l.withDO(l.DO.Limit(limit))
}
func (l linkDo) Offset(offset int) ILinkDo {
return l.withDO(l.DO.Offset(offset))
}
func (l linkDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ILinkDo {
return l.withDO(l.DO.Scopes(funcs...))
}
func (l linkDo) Unscoped() ILinkDo {
return l.withDO(l.DO.Unscoped())
}
func (l linkDo) Create(values ...*models.Link) error {
if len(values) == 0 {
return nil
}
return l.DO.Create(values)
}
func (l linkDo) CreateInBatches(values []*models.Link, batchSize int) error {
return l.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (l linkDo) Save(values ...*models.Link) error {
if len(values) == 0 {
return nil
}
return l.DO.Save(values)
}
func (l linkDo) First() (*models.Link, error) {
if result, err := l.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Link), nil
}
}
func (l linkDo) Take() (*models.Link, error) {
if result, err := l.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Link), nil
}
}
func (l linkDo) Last() (*models.Link, error) {
if result, err := l.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Link), nil
}
}
func (l linkDo) Find() ([]*models.Link, error) {
result, err := l.DO.Find()
return result.([]*models.Link), err
}
func (l linkDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Link, err error) {
buf := make([]*models.Link, 0, batchSize)
err = l.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (l linkDo) FindInBatches(result *[]*models.Link, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return l.DO.FindInBatches(result, batchSize, fc)
}
func (l linkDo) Attrs(attrs ...field.AssignExpr) ILinkDo {
return l.withDO(l.DO.Attrs(attrs...))
}
func (l linkDo) Assign(attrs ...field.AssignExpr) ILinkDo {
return l.withDO(l.DO.Assign(attrs...))
}
func (l linkDo) Joins(fields ...field.RelationField) ILinkDo {
for _, _f := range fields {
l = *l.withDO(l.DO.Joins(_f))
}
return &l
}
func (l linkDo) Preload(fields ...field.RelationField) ILinkDo {
for _, _f := range fields {
l = *l.withDO(l.DO.Preload(_f))
}
return &l
}
func (l linkDo) FirstOrInit() (*models.Link, error) {
if result, err := l.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Link), nil
}
}
func (l linkDo) FirstOrCreate() (*models.Link, error) {
if result, err := l.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Link), nil
}
}
func (l linkDo) FindByPage(offset int, limit int) (result []*models.Link, count int64, err error) {
result, err = l.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = l.Offset(-1).Limit(-1).Count()
return
}
func (l linkDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = l.Count()
if err != nil {
return
}
err = l.Offset(offset).Limit(limit).Scan(result)
return
}
func (l linkDo) Scan(result interface{}) (err error) {
return l.DO.Scan(result)
}
func (l linkDo) Delete(models ...*models.Link) (result gen.ResultInfo, err error) {
return l.DO.Delete(models)
}
func (l *linkDo) withDO(do gen.Dao) *linkDo {
l.DO = *do.(*gen.DO)
return l
}

21
storage/models/link.go Normal file
View file

@ -0,0 +1,21 @@
package models
import (
"gorm.io/gorm"
)
type Link struct {
gorm.Model
Link string
Count uint64
}
type ILink interface {
// UPDATE @@table SET count = count + 1 WHERE link = @link
// If (NOT EXISTS(SELECT * FROM @@table WHERE link = @link))
// BEGIN
// INSERT INTO @@table(@link)
// IncrementLinkCount(link string) ([]gen.T, error)
}

View file

@ -46,7 +46,7 @@
<tr> <tr>
<td>Bluesky</td> <td>Bluesky</td>
<td> <td>
<a href="https://bsky.app/profile/mstar.dev">https://bsky.app/profile/mstar.dev</a> <a href="/bsky">https://bsky.app/profile/mstar.dev</a>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -58,57 +58,56 @@
<tr> <tr>
<td>Forgejo</td> <td>Forgejo</td>
<td> <td>
<a href="https://git.mstar.dev/mstar">https://git.mstar.dev/mstar</a> <a href="/git">https://git.mstar.dev/mstar</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Gitlab (not actively used anymore)</td> <td>Gitlab (not actively used anymore)</td>
<td> <td>
<a href="https://gitlab.com/mstarongitlab">https://gitlab.com/mstarongitlab</a> <a href="/gitlab">https://gitlab.com/mstarongitlab</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Github (same as Gitlab)</td> <td>Github (same as Gitlab)</td>
<td> <td>
<a href="https://github.com/mstarongithub/">https://github.com/mstarongithub/</a> <a href="/github">https://github.com/mstarongithub/</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Twitch</td> <td>Twitch</td>
<td> <td>
<a href="https://twitch.tv/mstarontwitch">https://twitch.tv/mstarontwitch</a> <a href="/twitch">https://twitch.tv/mstarontwitch</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Youtube</td> <td>Youtube</td>
<td> <td>
<a <a href="/youtube">https://www.youtube.com/channel/UC-LklY_wt004i_30xcVohbg</a>
href="https://www.youtube.com/channel/UC-LklY_wt004i_30xcVohbg">https://www.youtube.com/channel/UC-LklY_wt004i_30xcVohbg</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<!-- Rickroll --> <!-- Rickroll -->
<td>Fedi</td> <td>Fedi</td>
<td> <td>
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">https://woem.men/@mstar</a> <a href="/fedi">https://woem.men/@mstar</a>
</td> </td>
</tr> </tr>
</table> </table>
<ul> <ul>
<li><a href="https://foxgirls.love/web">Fren Erika</a></li> <li><a href="https://foxgirls.love">Fren Rika</a></li>
<li><a href="https://linktr.ee/akijam">Fren Aki</a></li> <li><a href="https://linktr.ee/akijam">Fren Aki</a></li>
</ul> </ul>
<hr /> <hr />
<!--<p>I'm also working on a Fediverse server, <a href="https://git.mstar.dev/mstar/linstrom">Linstrom</a></p>--> <!--<p>I'm also working on a Fediverse server, <a href="https://git.mstar.dev/mstar/linstrom">Linstrom</a></p>-->
<!--<br>--> <!--<br>-->
<p>Todo: Add proper styling</p> <p>Todo: Add proper styling and more information</p>
</div> </div>
</div> </div>
<footer> <footer>
<p>Contact: <a href="mailto:me@mstar.dev">me@mstar.dev (<a <p>Contact: <a href="mailto:me@mstar.dev">me@mstar.dev (<a
href="/static/files/other/publickey.me@mstar.dev-27fcaabd3022acff023e6e2a053a2d9af0f03260.asc">Public PGP href="/static/files/other/publickey.me@mstar.dev-27fcaabd3022acff023e6e2a053a2d9af0f03260.asc">Public PGP
key</a>)</p> key</a>)</p>
<p>Last updated: 16.01.2025</p> <p>Last updated: 24.03.2025</p>
</footer> </footer>
</body> </body>