🎉 Init project from scratch
This commit is contained in:
		
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
| # Datasource local storage ignored files | ||||
| /dataSources/ | ||||
| /dataSources.local.xml | ||||
							
								
								
									
										9
									
								
								.idea/Interactive.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.idea/Interactive.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="Go" enabled="true" /> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$" /> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/Interactive.iml" filepath="$PROJECT_DIR$/.idea/Interactive.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="$PROJECT_DIR$" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										72
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| module code.smartsheep.studio/hydrogen/interactive | ||||
|  | ||||
| go 1.21.6 | ||||
|  | ||||
| require ( | ||||
| 	code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a | ||||
| 	github.com/go-playground/validator/v10 v10.17.0 | ||||
| 	github.com/gofiber/fiber/v2 v2.52.0 | ||||
| 	github.com/json-iterator/go v1.1.12 | ||||
| 	github.com/rs/zerolog v1.31.0 | ||||
| 	github.com/samber/lo v1.39.0 | ||||
| 	github.com/spf13/viper v1.18.2 | ||||
| 	gorm.io/driver/postgres v1.5.4 | ||||
| 	gorm.io/gorm v1.25.6 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/andybalholm/brotli v1.0.5 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.7.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-sql-driver/mysql v1.7.1 // indirect | ||||
| 	github.com/golang-jwt/jwt/v5 v5.2.0 // indirect | ||||
| 	github.com/golang/protobuf v1.5.3 // indirect | ||||
| 	github.com/google/uuid v1.5.0 // indirect | ||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||
| 	github.com/jackc/pgpassfile v1.0.0 // indirect | ||||
| 	github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect | ||||
| 	github.com/jackc/pgx/v5 v5.5.1 // indirect | ||||
| 	github.com/jackc/puddle/v2 v2.2.1 // indirect | ||||
| 	github.com/jinzhu/inflection v1.0.0 // indirect | ||||
| 	github.com/jinzhu/now v1.1.5 // indirect | ||||
| 	github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect | ||||
| 	github.com/klauspost/compress v1.17.0 // indirect | ||||
| 	github.com/leodido/go-urn v1.2.4 // indirect | ||||
| 	github.com/magiconair/properties v1.8.7 // indirect | ||||
| 	github.com/mattn/go-colorable v0.1.13 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.15 // indirect | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.1.1 // indirect | ||||
| 	github.com/philhofer/fwd v1.1.2 // indirect | ||||
| 	github.com/rivo/uniseg v0.2.0 // indirect | ||||
| 	github.com/sagikazarmark/locafero v0.4.0 // indirect | ||||
| 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect | ||||
| 	github.com/sourcegraph/conc v0.3.0 // indirect | ||||
| 	github.com/spf13/afero v1.11.0 // indirect | ||||
| 	github.com/spf13/cast v1.6.0 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/subosito/gotenv v1.6.0 // indirect | ||||
| 	github.com/tinylib/msgp v1.1.8 // indirect | ||||
| 	github.com/valyala/bytebufferpool v1.0.0 // indirect | ||||
| 	github.com/valyala/fasthttp v1.51.0 // indirect | ||||
| 	github.com/valyala/tcplisten v1.0.0 // indirect | ||||
| 	go.uber.org/multierr v1.11.0 // indirect | ||||
| 	golang.org/x/crypto v0.18.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect | ||||
| 	golang.org/x/net v0.20.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.16.0 // indirect | ||||
| 	golang.org/x/sync v0.5.0 // indirect | ||||
| 	golang.org/x/sys v0.16.0 // indirect | ||||
| 	golang.org/x/text v0.14.0 // indirect | ||||
| 	google.golang.org/appengine v1.6.8 // indirect | ||||
| 	google.golang.org/protobuf v1.32.0 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| 	gorm.io/datatypes v1.2.0 // indirect | ||||
| 	gorm.io/driver/mysql v1.5.2 // indirect | ||||
| ) | ||||
							
								
								
									
										225
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a h1:66YEiBiB+S7QaUnNqQ/Q8zzgGNkQwcHlJdE2s/RdV6k= | ||||
| code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a/go.mod h1:LM5I1tdQLXY6n+hou3TPNV/v9hA/cD59zlYpspdzGks= | ||||
| github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= | ||||
| github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||||
| 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | ||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||
| github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= | ||||
| github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | ||||
| github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= | ||||
| github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= | ||||
| github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||||
| github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||||
| github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= | ||||
| github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= | ||||
| github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | ||||
| github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= | ||||
| github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | ||||
| github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||||
| github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= | ||||
| github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= | ||||
| github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= | ||||
| github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= | ||||
| 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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||||
| github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= | ||||
| github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||
| github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||||
| 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/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= | ||||
| github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= | ||||
| github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI= | ||||
| github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= | ||||
| github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= | ||||
| github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= | ||||
| 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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= | ||||
| github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | ||||
| github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= | ||||
| github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= | ||||
| github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= | ||||
| github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= | ||||
| github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= | ||||
| github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= | ||||
| github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||||
| github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||||
| github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||
| github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| 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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= | ||||
| github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= | ||||
| github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= | ||||
| github.com/mattn/go-sqlite3 v1.14.15/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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= | ||||
| github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | ||||
| 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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= | ||||
| github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||||
| github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= | ||||
| github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||||
| github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= | ||||
| github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= | ||||
| github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= | ||||
| github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= | ||||
| github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= | ||||
| github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= | ||||
| github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= | ||||
| github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= | ||||
| github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= | ||||
| github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= | ||||
| github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= | ||||
| github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= | ||||
| github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= | ||||
| github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= | ||||
| github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= | ||||
| github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= | ||||
| 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= | ||||
| github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= | ||||
| 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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||||
| github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||||
| github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= | ||||
| github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= | ||||
| 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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= | ||||
| go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| 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.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= | ||||
| golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= | ||||
| golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= | ||||
| golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= | ||||
| golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= | ||||
| golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| 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.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= | ||||
| golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= | ||||
| golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= | ||||
| golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= | ||||
| golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= | ||||
| golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= | ||||
| 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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= | ||||
| golang.org/x/sync v0.5.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.3.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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= | ||||
| golang.org/x/sys v0.16.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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||||
| golang.org/x/text v0.5.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.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= | ||||
| google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= | ||||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||||
| google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= | ||||
| google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||||
| gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||
| 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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco= | ||||
| gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04= | ||||
| gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= | ||||
| gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= | ||||
| gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo= | ||||
| gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0= | ||||
| 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/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= | ||||
| gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A= | ||||
| gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= | ||||
							
								
								
									
										52
									
								
								pkg/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								pkg/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/server" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
|  | ||||
| 	interactive "code.smartsheep.studio/hydrogen/interactive/pkg" | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/database" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix | ||||
| 	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	// Configure settings | ||||
| 	viper.AddConfigPath(".") | ||||
| 	viper.AddConfigPath("..") | ||||
| 	viper.SetConfigName("settings") | ||||
| 	viper.SetConfigType("toml") | ||||
|  | ||||
| 	// Load settings | ||||
| 	if err := viper.ReadInConfig(); err != nil { | ||||
| 		log.Panic().Err(err).Msg("An error occurred when loading settings.") | ||||
| 	} | ||||
|  | ||||
| 	// Connect to database | ||||
| 	if err := database.NewSource(); err != nil { | ||||
| 		log.Fatal().Err(err).Msg("An error occurred when connect to database.") | ||||
| 	} else if err := database.RunMigration(database.C); err != nil { | ||||
| 		log.Fatal().Err(err).Msg("An error occurred when running database auto migration.") | ||||
| 	} | ||||
|  | ||||
| 	// Server | ||||
| 	server.NewServer() | ||||
| 	go server.Listen() | ||||
|  | ||||
| 	// Messages | ||||
| 	log.Info().Msgf("Passport v%s is started...", interactive.AppVersion) | ||||
|  | ||||
| 	quit := make(chan os.Signal, 1) | ||||
| 	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) | ||||
| 	<-quit | ||||
|  | ||||
| 	log.Info().Msgf("Passport v%s is quitting...", interactive.AppVersion) | ||||
| } | ||||
							
								
								
									
										16
									
								
								pkg/database/migrator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								pkg/database/migrator.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| package database | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| func RunMigration(source *gorm.DB) error { | ||||
| 	if err := source.AutoMigrate( | ||||
| 		&models.Account{}, | ||||
| 	); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										28
									
								
								pkg/database/source.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								pkg/database/source.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package database | ||||
|  | ||||
| import ( | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"gorm.io/driver/postgres" | ||||
| 	"gorm.io/gorm" | ||||
| 	"gorm.io/gorm/logger" | ||||
| 	"gorm.io/gorm/schema" | ||||
| ) | ||||
|  | ||||
| var C *gorm.DB | ||||
|  | ||||
| func NewSource() error { | ||||
| 	var err error | ||||
|  | ||||
| 	dialector := postgres.Open(viper.GetString("database.dsn")) | ||||
| 	C, err = gorm.Open(dialector, &gorm.Config{NamingStrategy: schema.NamingStrategy{ | ||||
| 		TablePrefix: viper.GetString("database.prefix"), | ||||
| 	}, Logger: logger.New(&log.Logger, logger.Config{ | ||||
| 		Colorful:                  true, | ||||
| 		IgnoreRecordNotFoundError: true, | ||||
| 		LogLevel:                  lo.Ternary(viper.GetBool("debug"), logger.Info, logger.Silent), | ||||
| 	})}) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										5
									
								
								pkg/meta.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/meta.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| package pkg | ||||
|  | ||||
| const ( | ||||
| 	AppVersion = "1.0.0" | ||||
| ) | ||||
							
								
								
									
										14
									
								
								pkg/models/accounts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								pkg/models/accounts.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package models | ||||
|  | ||||
| // Account profiles basically fetched from Hydrogen.Passport | ||||
| // But cache at here for better usage | ||||
| // At the same time this model can make relations between local models | ||||
| type Account struct { | ||||
| 	BaseModel | ||||
|  | ||||
| 	Name         string `json:"name"` | ||||
| 	Avatar       string `json:"avatar"` | ||||
| 	EmailAddress string `json:"email_address"` | ||||
| 	PowerLevel   int    `json:"power_level"` | ||||
| 	ExternalID   uint   `json:"external_id"` | ||||
| } | ||||
							
								
								
									
										17
									
								
								pkg/models/base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/models/base.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package models | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"gorm.io/datatypes" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| type JSONMap = datatypes.JSONType[map[string]any] | ||||
|  | ||||
| type BaseModel struct { | ||||
| 	ID        uint           `json:"id" gorm:"primaryKey"` | ||||
| 	CreatedAt time.Time      `json:"created_at"` | ||||
| 	UpdatedAt time.Time      `json:"updated_at"` | ||||
| 	DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"` | ||||
| } | ||||
							
								
								
									
										12
									
								
								pkg/security/encryptor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								pkg/security/encryptor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| package security | ||||
|  | ||||
| import "golang.org/x/crypto/bcrypt" | ||||
|  | ||||
| func HashPassword(raw string) string { | ||||
| 	data, _ := bcrypt.GenerateFromPassword([]byte(raw), 12) | ||||
| 	return string(data) | ||||
| } | ||||
|  | ||||
| func VerifyPassword(text string, password string) bool { | ||||
| 	return bcrypt.CompareHashAndPassword([]byte(password), []byte(text)) == nil | ||||
| } | ||||
							
								
								
									
										56
									
								
								pkg/security/jwt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								pkg/security/jwt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| package security | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/golang-jwt/jwt/v5" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| type PayloadClaims struct { | ||||
| 	jwt.RegisteredClaims | ||||
|  | ||||
| 	Type string `json:"typ"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	JwtAccessType  = "access" | ||||
| 	JwtRefreshType = "refresh" | ||||
| ) | ||||
|  | ||||
| func EncodeJwt(id string, typ, sub string, aud []string, exp time.Time) (string, error) { | ||||
| 	tk := jwt.NewWithClaims(jwt.SigningMethodHS512, PayloadClaims{ | ||||
| 		jwt.RegisteredClaims{ | ||||
| 			Subject:   sub, | ||||
| 			Audience:  aud, | ||||
| 			Issuer:    fmt.Sprintf("https://%s", viper.GetString("domain")), | ||||
| 			ExpiresAt: jwt.NewNumericDate(exp), | ||||
| 			NotBefore: jwt.NewNumericDate(time.Now()), | ||||
| 			IssuedAt:  jwt.NewNumericDate(time.Now()), | ||||
| 			ID:        id, | ||||
| 		}, | ||||
| 		typ, | ||||
| 	}) | ||||
|  | ||||
| 	return tk.SignedString([]byte(viper.GetString("secret"))) | ||||
| } | ||||
|  | ||||
| func DecodeJwt(str string) (PayloadClaims, error) { | ||||
| 	var claims PayloadClaims | ||||
| 	tk, err := jwt.ParseWithClaims(str, &claims, func(token *jwt.Token) (interface{}, error) { | ||||
| 		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { | ||||
| 			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) | ||||
| 		} | ||||
| 		return []byte(viper.GetString("secret")), nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return claims, err | ||||
| 	} | ||||
|  | ||||
| 	if data, ok := tk.Claims.(*PayloadClaims); ok { | ||||
| 		return *data, nil | ||||
| 	} else { | ||||
| 		return claims, fmt.Errorf("unexpected token payload: not payload claims type") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										35
									
								
								pkg/server/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/server/auth.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/database" | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | ||||
| 	"code.smartsheep.studio/hydrogen/passport/pkg/security" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/keyauth" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| var auth = keyauth.New(keyauth.Config{ | ||||
| 	KeyLookup:  "header:Authorization", | ||||
| 	AuthScheme: "Bearer", | ||||
| 	Validator: func(c *fiber.Ctx, token string) (bool, error) { | ||||
| 		claims, err := security.DecodeJwt(token) | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} | ||||
|  | ||||
| 		id, _ := strconv.Atoi(claims.Subject) | ||||
|  | ||||
| 		var user models.Account | ||||
| 		if err := database.C.Where(&models.Account{ | ||||
| 			BaseModel: models.BaseModel{ID: uint(id)}, | ||||
| 		}).First(&user).Error; err != nil { | ||||
| 			return false, err | ||||
| 		} | ||||
|  | ||||
| 		c.Locals("principal", user) | ||||
|  | ||||
| 		return true, nil | ||||
| 	}, | ||||
| 	ContextKey: "token", | ||||
| }) | ||||
							
								
								
									
										87
									
								
								pkg/server/auth_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								pkg/server/auth_api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/services" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"golang.org/x/oauth2" | ||||
| ) | ||||
|  | ||||
| var cfg = oauth2.Config{ | ||||
| 	RedirectURL:  fmt.Sprintf("https://%s/api/auth/callback", viper.GetString("domain")), | ||||
| 	ClientID:     viper.GetString("passport.client_id"), | ||||
| 	ClientSecret: viper.GetString("passport.client_secret"), | ||||
| 	Scopes:       []string{"openid"}, | ||||
| 	Endpoint: oauth2.Endpoint{ | ||||
| 		AuthURL:   fmt.Sprintf("%s/auth/o/connect", viper.GetString("passport.endpoint")), | ||||
| 		TokenURL:  fmt.Sprintf("%s/api/auth/token", viper.GetString("passport.endpoint")), | ||||
| 		AuthStyle: oauth2.AuthStyleInParams, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func doLogin(c *fiber.Ctx) error { | ||||
| 	url := cfg.AuthCodeURL(uuid.NewString()) | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"target": url, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func doPostLogin(c *fiber.Ctx) error { | ||||
| 	code := c.Query("code") | ||||
|  | ||||
| 	token, err := cfg.Exchange(context.Background(), code) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to exchange token: %q", err)) | ||||
| 	} | ||||
|  | ||||
| 	agent := fiber. | ||||
| 		Get(fmt.Sprintf("%s/api/users/me", viper.GetString("passport.endpoint"))). | ||||
| 		Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", token.AccessToken)) | ||||
|  | ||||
| 	_, body, errs := agent.Bytes() | ||||
| 	if len(errs) > 0 { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to get userinfo: %q", errs)) | ||||
| 	} | ||||
|  | ||||
| 	var userinfo services.PassportUserinfo | ||||
| 	err = json.Unmarshal(body, &userinfo) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to parse userinfo: %q", err)) | ||||
| 	} | ||||
|  | ||||
| 	account, err := services.LinkAccount(userinfo) | ||||
| 	access, refresh, err := services.GetToken(account) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to get token: %q", err)) | ||||
| 	} | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"access_token":  access, | ||||
| 		"refresh_token": refresh, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func doRefreshToken(c *fiber.Ctx) error { | ||||
| 	var data struct { | ||||
| 		RefreshToken string `json:"refresh_token" validate:"required"` | ||||
| 	} | ||||
|  | ||||
| 	if err := BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	access, refresh, err := services.RefreshToken(data.RefreshToken) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to get token: %q", err)) | ||||
| 	} | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"access_token":  access, | ||||
| 		"refresh_token": refresh, | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										79
									
								
								pkg/server/startup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								pkg/server/startup.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/view" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/cache" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/cors" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/filesystem" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/idempotency" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/logger" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| var A *fiber.App | ||||
|  | ||||
| func NewServer() { | ||||
| 	A = fiber.New(fiber.Config{ | ||||
| 		DisableStartupMessage: true, | ||||
| 		EnableIPValidation:    true, | ||||
| 		ServerHeader:          "Hydrogen.Interactive", | ||||
| 		AppName:               "Hydrogen.Interactive", | ||||
| 		ProxyHeader:           fiber.HeaderXForwardedFor, | ||||
| 		JSONEncoder:           jsoniter.ConfigCompatibleWithStandardLibrary.Marshal, | ||||
| 		JSONDecoder:           jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal, | ||||
| 		EnablePrintRoutes:     viper.GetBool("debug"), | ||||
| 	}) | ||||
|  | ||||
| 	A.Use(idempotency.New()) | ||||
| 	A.Use(cors.New(cors.Config{ | ||||
| 		AllowCredentials: true, | ||||
| 		AllowMethods: strings.Join([]string{ | ||||
| 			fiber.MethodGet, | ||||
| 			fiber.MethodPost, | ||||
| 			fiber.MethodHead, | ||||
| 			fiber.MethodOptions, | ||||
| 			fiber.MethodPut, | ||||
| 			fiber.MethodDelete, | ||||
| 			fiber.MethodPatch, | ||||
| 		}, ","), | ||||
| 		AllowOriginsFunc: func(origin string) bool { | ||||
| 			return true | ||||
| 		}, | ||||
| 	})) | ||||
|  | ||||
| 	A.Use(logger.New(logger.Config{ | ||||
| 		Format: "${status} | ${latency} | ${method} ${path}\n", | ||||
| 		Output: log.Logger, | ||||
| 	})) | ||||
|  | ||||
| 	A.Get("/.well-known", getMetadata) | ||||
|  | ||||
| 	api := A.Group("/api").Name("API") | ||||
| 	{ | ||||
| 		api.Get("/auth", doLogin) | ||||
| 		api.Get("/auth/callback", doPostLogin) | ||||
| 		api.Post("/auth/refresh", doRefreshToken) | ||||
| 	} | ||||
|  | ||||
| 	A.Use("/", cache.New(cache.Config{ | ||||
| 		Expiration:   24 * time.Hour, | ||||
| 		CacheControl: true, | ||||
| 	}), filesystem.New(filesystem.Config{ | ||||
| 		Root:         http.FS(view.FS), | ||||
| 		PathPrefix:   "dist", | ||||
| 		Index:        "index.html", | ||||
| 		NotFoundFile: "dist/index.html", | ||||
| 	})) | ||||
| } | ||||
|  | ||||
| func Listen() { | ||||
| 	if err := A.Listen(viper.GetString("bind")); err != nil { | ||||
| 		log.Fatal().Err(err).Msg("An error occurred when starting server...") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										18
									
								
								pkg/server/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/server/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"github.com/go-playground/validator/v10" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| var validation = validator.New(validator.WithRequiredStructEnabled()) | ||||
|  | ||||
| func BindAndValidate(c *fiber.Ctx, out any) error { | ||||
| 	if err := c.BodyParser(out); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} else if err := validation.Struct(out); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										13
									
								
								pkg/server/well_known_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/server/well_known_api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| func getMetadata(c *fiber.Ctx) error { | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"name":   viper.GetString("name"), | ||||
| 		"domain": viper.GetString("domain"), | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										90
									
								
								pkg/services/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								pkg/services/auth.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/database" | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | ||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/security" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/google/uuid" | ||||
| 	"gorm.io/gorm" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type PassportUserinfo struct { | ||||
| 	Sub                uint   `json:"sub"` | ||||
| 	Email              string `json:"email"` | ||||
| 	Picture            string `json:"picture"` | ||||
| 	PreferredUsernames string `json:"preferred_usernames"` | ||||
| } | ||||
|  | ||||
| func LinkAccount(userinfo PassportUserinfo) (models.Account, error) { | ||||
| 	var account models.Account | ||||
| 	if err := database.C.Where(&models.Account{ | ||||
| 		ExternalID: userinfo.Sub, | ||||
| 	}).First(&account).Error; err != nil { | ||||
| 		if errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 			account = models.Account{ | ||||
| 				Name:         userinfo.PreferredUsernames, | ||||
| 				Avatar:       userinfo.Picture, | ||||
| 				EmailAddress: userinfo.Email, | ||||
| 				PowerLevel:   0, | ||||
| 				ExternalID:   userinfo.Sub, | ||||
| 			} | ||||
| 			return account, database.C.Save(&account).Error | ||||
| 		} | ||||
| 		return account, err | ||||
| 	} | ||||
|  | ||||
| 	return account, nil | ||||
| } | ||||
|  | ||||
| func GetToken(account models.Account) (string, string, error) { | ||||
| 	var err error | ||||
| 	var refresh, access string | ||||
|  | ||||
| 	sub := strconv.Itoa(int(account.ID)) | ||||
| 	access, err = security.EncodeJwt( | ||||
| 		uuid.NewString(), | ||||
| 		security.JwtAccessType, | ||||
| 		sub, | ||||
| 		[]string{"interactive"}, | ||||
| 		time.Now().Add(30*time.Minute), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return refresh, access, err | ||||
| 	} | ||||
| 	refresh, err = security.EncodeJwt( | ||||
| 		uuid.NewString(), | ||||
| 		security.JwtRefreshType, | ||||
| 		sub, | ||||
| 		[]string{"interactive"}, | ||||
| 		time.Now().Add(30*24*time.Hour), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return refresh, access, err | ||||
| 	} | ||||
|  | ||||
| 	return access, refresh, nil | ||||
| } | ||||
|  | ||||
| func RefreshToken(token string) (string, string, error) { | ||||
| 	parseInt := func(str string) int { | ||||
| 		val, _ := strconv.Atoi(str) | ||||
| 		return val | ||||
| 	} | ||||
|  | ||||
| 	var account models.Account | ||||
| 	if claims, err := security.DecodeJwt(token); err != nil { | ||||
| 		return "404", "403", err | ||||
| 	} else if claims.Type != security.JwtRefreshType { | ||||
| 		return "404", "403", fmt.Errorf("invalid token type, expected refresh token") | ||||
| 	} else if err := database.C.Where(models.Account{ | ||||
| 		BaseModel: models.BaseModel{ID: uint(parseInt(claims.Subject))}, | ||||
| 	}).First(&account).Error; err != nil { | ||||
| 		return "404", "403", err | ||||
| 	} | ||||
|  | ||||
| 	return GetToken(account) | ||||
| } | ||||
							
								
								
									
										51
									
								
								pkg/services/mailer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								pkg/services/mailer.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"net/smtp" | ||||
| 	"net/textproto" | ||||
|  | ||||
| 	"github.com/jordan-wright/email" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| func SendMail(target string, subject string, content string) error { | ||||
| 	mail := &email.Email{ | ||||
| 		To:      []string{target}, | ||||
| 		From:    viper.GetString("mailer.name"), | ||||
| 		Subject: subject, | ||||
| 		Text:    []byte(content), | ||||
| 		Headers: textproto.MIMEHeader{}, | ||||
| 	} | ||||
| 	return mail.SendWithTLS( | ||||
| 		fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")), | ||||
| 		smtp.PlainAuth( | ||||
| 			"", | ||||
| 			viper.GetString("mailer.username"), | ||||
| 			viper.GetString("mailer.password"), | ||||
| 			viper.GetString("mailer.smtp_host"), | ||||
| 		), | ||||
| 		&tls.Config{ServerName: viper.GetString("mailer.smtp_host")}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func SendMailHTML(target string, subject string, content string) error { | ||||
| 	mail := &email.Email{ | ||||
| 		To:      []string{target}, | ||||
| 		From:    viper.GetString("mailer.name"), | ||||
| 		Subject: subject, | ||||
| 		HTML:    []byte(content), | ||||
| 		Headers: textproto.MIMEHeader{}, | ||||
| 	} | ||||
| 	return mail.SendWithTLS( | ||||
| 		fmt.Sprintf("%s:%d", viper.GetString("mailer.smtp_host"), viper.GetInt("mailer.smtp_port")), | ||||
| 		smtp.PlainAuth( | ||||
| 			"", | ||||
| 			viper.GetString("mailer.username"), | ||||
| 			viper.GetString("mailer.password"), | ||||
| 			viper.GetString("mailer.smtp_host"), | ||||
| 		), | ||||
| 		&tls.Config{ServerName: viper.GetString("mailer.smtp_host")}, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										5
									
								
								pkg/view/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/view/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| /dist | ||||
| /node_modules | ||||
|  | ||||
| package-lock.json | ||||
| yarn.lock | ||||
							
								
								
									
										28
									
								
								pkg/view/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								pkg/view/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| ## Usage | ||||
|  | ||||
| ```bash | ||||
| $ npm install # or pnpm install or yarn install | ||||
| ``` | ||||
|  | ||||
| ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) | ||||
|  | ||||
| ## Available Scripts | ||||
|  | ||||
| In the project directory, you can run: | ||||
|  | ||||
| ### `npm run dev` | ||||
|  | ||||
| Runs the app in the development mode.<br> | ||||
| Open [http://localhost:5173](http://localhost:5173) to view it in the browser. | ||||
|  | ||||
| ### `npm run build` | ||||
|  | ||||
| Builds the app for production to the `dist` folder.<br> | ||||
| It correctly bundles Solid in production mode and optimizes the build for the best performance. | ||||
|  | ||||
| The build is minified and the filenames include the hashes.<br> | ||||
| Your app is ready to be deployed! | ||||
|  | ||||
| ## Deployment | ||||
|  | ||||
| Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html) | ||||
							
								
								
									
										6
									
								
								pkg/view/embed.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/view/embed.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| package view | ||||
|  | ||||
| import "embed" | ||||
|  | ||||
| //go:embed all:dist | ||||
| var FS embed.FS | ||||
							
								
								
									
										13
									
								
								pkg/view/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/view/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Goatpass</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="/src/index.tsx"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										26
									
								
								pkg/view/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								pkg/view/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "name": "@hydrogen/interactive-web", | ||||
|   "private": true, | ||||
|   "version": "0.0.0", | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
|     "dev": "vite", | ||||
|     "build": "tsc && vite build", | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@solidjs/router": "^0.10.10", | ||||
|     "solid-js": "^1.8.7", | ||||
|     "universal-cookie": "^7.0.2" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "autoprefixer": "^10.4.17", | ||||
|     "daisyui": "^4.6.0", | ||||
|     "postcss": "^8.4.33", | ||||
|     "solid-devtools": "^0.29.3", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "typescript": "^5.2.2", | ||||
|     "vite": "^5.0.8", | ||||
|     "vite-plugin-solid": "^2.8.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										6
									
								
								pkg/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export default { | ||||
|   plugins: { | ||||
|     tailwindcss: {}, | ||||
|     autoprefixer: {}, | ||||
|   }, | ||||
| } | ||||
							
								
								
									
										21
									
								
								pkg/view/public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								pkg/view/public/favicon.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										5
									
								
								pkg/view/src/.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/view/src/.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "printWidth": 120, | ||||
|   "tabWidth": 2, | ||||
|   "singleQuote": false | ||||
| } | ||||
							
								
								
									
										197
									
								
								pkg/view/src/assets/fonts/fonts.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								pkg/view/src/assets/fonts/fonts.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| :root { | ||||
|   --bs-body-font-family: "IBM Plex Serif", "Noto Serif SC", sans-serif !important; | ||||
| } | ||||
|  | ||||
| html, | ||||
| body { | ||||
|   font-family: var(--bs-body-font-family); | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-100 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 100; | ||||
|   src: url("./ibm-plex-serif-v19-latin-100.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-100italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 100; | ||||
|   src: url("./ibm-plex-serif-v19-latin-100italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-200 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 200; | ||||
|   src: url("./ibm-plex-serif-v19-latin-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-200italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 200; | ||||
|   src: url("./ibm-plex-serif-v19-latin-200italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-300 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   src: url("./ibm-plex-serif-v19-latin-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-300italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 300; | ||||
|   src: url("./ibm-plex-serif-v19-latin-300italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-regular - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 400; | ||||
|   src: url("./ibm-plex-serif-v19-latin-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 400; | ||||
|   src: url("./ibm-plex-serif-v19-latin-italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-500 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
|   src: url("./ibm-plex-serif-v19-latin-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-500italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 500; | ||||
|   src: url("./ibm-plex-serif-v19-latin-500italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-600 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 600; | ||||
|   src: url("./ibm-plex-serif-v19-latin-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-600italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 600; | ||||
|   src: url("./ibm-plex-serif-v19-latin-600italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-700 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 700; | ||||
|   src: url("./ibm-plex-serif-v19-latin-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-700italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 700; | ||||
|   src: url("./ibm-plex-serif-v19-latin-700italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-200 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 200; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-300 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-regular - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 400; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-500 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-600 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 600; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-700 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 700; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-900 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 900; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-900.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										8
									
								
								pkg/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| @tailwind base; | ||||
| @tailwind components; | ||||
| @tailwind utilities; | ||||
|  | ||||
| html, body { | ||||
|     padding: 0; | ||||
|     margin: 0; | ||||
| } | ||||
							
								
								
									
										32
									
								
								pkg/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								pkg/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import "solid-devtools"; | ||||
|  | ||||
| /* @refresh reload */ | ||||
| import { render } from "solid-js/web"; | ||||
|  | ||||
| import "./index.css"; | ||||
| import "./assets/fonts/fonts.css"; | ||||
| import { lazy } from "solid-js"; | ||||
| import { Route, Router } from "@solidjs/router"; | ||||
|  | ||||
| import RootLayout from "./layouts/RootLayout.tsx"; | ||||
| import { UserinfoProvider } from "./stores/userinfo.tsx"; | ||||
| import { WellKnownProvider } from "./stores/wellKnown.tsx"; | ||||
|  | ||||
| const root = document.getElementById("root"); | ||||
|  | ||||
| render(() => ( | ||||
|   <WellKnownProvider> | ||||
|     <UserinfoProvider> | ||||
|       <Router root={RootLayout}> | ||||
|         <Route path="/" component={lazy(() => import("./pages/dashboard.tsx"))} /> | ||||
|         <Route path="/security" component={lazy(() => import("./pages/security.tsx"))} /> | ||||
|         <Route path="/personalise" component={lazy(() => import("./pages/personalise.tsx"))} /> | ||||
|         <Route path="/auth/login" component={lazy(() => import("./pages/auth/login.tsx"))} /> | ||||
|         <Route path="/auth/register" component={lazy(() => import("./pages/auth/register.tsx"))} /> | ||||
|         <Route path="/auth/oauth/connect" component={lazy(() => import("./pages/auth/connect.tsx"))} /> | ||||
|         <Route path="/auth/oauth/callback" component={lazy(() => import("./pages/auth/callback.tsx"))} /> | ||||
|         <Route path="/users/me/confirm" component={lazy(() => import("./pages/users/confirm.tsx"))} /> | ||||
|       </Router> | ||||
|     </UserinfoProvider> | ||||
|   </WellKnownProvider> | ||||
| ), root!); | ||||
							
								
								
									
										46
									
								
								pkg/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import Navbar from "./shared/Navbar.tsx"; | ||||
| import { readProfiles, useUserinfo } from "../stores/userinfo.tsx"; | ||||
| import { createEffect, createSignal, Show } from "solid-js"; | ||||
| import { readWellKnown } from "../stores/wellKnown.tsx"; | ||||
| import { BeforeLeaveEventArgs, useBeforeLeave, useLocation, useNavigate } from "@solidjs/router"; | ||||
|  | ||||
| export default function RootLayout(props: any) { | ||||
|   const [ready, setReady] = createSignal(false); | ||||
|  | ||||
|   Promise.all([readWellKnown(), readProfiles()]).then(() => setReady(true)); | ||||
|  | ||||
|   const navigate = useNavigate(); | ||||
|   const userinfo = useUserinfo(); | ||||
|  | ||||
|   const location = useLocation(); | ||||
|  | ||||
|   createEffect(() => { | ||||
|     if (ready()) { | ||||
|       keepGate(location.pathname); | ||||
|     } | ||||
|   }, [ready, userinfo]); | ||||
|  | ||||
|   function keepGate(path: string, e?: BeforeLeaveEventArgs) { | ||||
|     const whitelist = ["/auth/login", "/auth/register", "/users/me/confirm"]; | ||||
|  | ||||
|     if (!userinfo?.isLoggedIn && !whitelist.includes(path)) { | ||||
|       if (!e?.defaultPrevented) e?.preventDefault(); | ||||
|       navigate(`/auth/login?redirect_uri=${path}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   useBeforeLeave((e: BeforeLeaveEventArgs) => keepGate(e.to.toString().split("?")[0], e)); | ||||
|  | ||||
|   return ( | ||||
|     <Show when={ready()} fallback={ | ||||
|       <div class="h-screen w-screen flex justify-center items-center"> | ||||
|         <div> | ||||
|           <span class="loading loading-lg loading-infinity"></span> | ||||
|         </div> | ||||
|       </div> | ||||
|     }> | ||||
|       <Navbar /> | ||||
|       <main class="h-[calc(100vh-68px)] mt-[68px]">{props.children}</main> | ||||
|     </Show> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										118
									
								
								pkg/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								pkg/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| import { For, Match, Show, Switch } from "solid-js"; | ||||
| import { clearUserinfo, useUserinfo } from "../../stores/userinfo.tsx"; | ||||
| import { useNavigate } from "@solidjs/router"; | ||||
| import { useWellKnown } from "../../stores/wellKnown.tsx"; | ||||
|  | ||||
| interface MenuItem { | ||||
|   label: string; | ||||
|   href?: string; | ||||
|   children?: MenuItem[]; | ||||
| } | ||||
|  | ||||
| export default function Navbar() { | ||||
|   const nav: MenuItem[] = [ | ||||
|     { | ||||
|       label: "You", children: [ | ||||
|         { label: "Dashboard", href: "/" }, | ||||
|         { label: "Security", href: "/security" }, | ||||
|         { label: "Personalise", href: "/personalise" } | ||||
|       ] | ||||
|     } | ||||
|   ]; | ||||
|  | ||||
|   const wellKnown = useWellKnown(); | ||||
|   const userinfo = useUserinfo(); | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   function logout() { | ||||
|     clearUserinfo(); | ||||
|     navigate("/auth/login"); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div class="navbar bg-base-100 shadow-md px-5 z-10 fixed top-0"> | ||||
|       <div class="navbar-start"> | ||||
|         <div class="dropdown"> | ||||
|           <div tabIndex={0} role="button" class="btn btn-ghost lg:hidden"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               class="h-5 w-5" | ||||
|               fill="none" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke="currentColor" | ||||
|             > | ||||
|               <path | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 stroke-width="2" | ||||
|                 d="M4 6h16M4 12h8m-8 6h16" | ||||
|               /> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <ul | ||||
|             tabIndex={0} | ||||
|             class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" | ||||
|           > | ||||
|             <For each={nav}> | ||||
|               {(item) => ( | ||||
|                 <li> | ||||
|                   <a href={item.href}>{item.label}</a> | ||||
|                   <Show when={item.children}> | ||||
|                     <ul class="p-2"> | ||||
|                       <For each={item.children}> | ||||
|                         {(item) => | ||||
|                           <li> | ||||
|                             <a href={item.href}>{item.label}</a> | ||||
|                           </li> | ||||
|                         } | ||||
|                       </For> | ||||
|                     </ul> | ||||
|                   </Show> | ||||
|                 </li> | ||||
|               )} | ||||
|             </For> | ||||
|           </ul> | ||||
|         </div> | ||||
|         <a href="/" class="btn btn-ghost text-xl"> | ||||
|           {wellKnown?.name ?? "Goatpass"} | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="navbar-center hidden lg:flex"> | ||||
|         <ul class="menu menu-horizontal px-1"> | ||||
|           <For each={nav}> | ||||
|             {(item) => ( | ||||
|               <li> | ||||
|                 <Show when={item.children} fallback={<a href={item.href}>{item.label}</a>}> | ||||
|                   <details> | ||||
|                     <summary> | ||||
|                       <a href={item.href}>{item.label}</a> | ||||
|                     </summary> | ||||
|                     <ul class="p-2"> | ||||
|                       <For each={item.children}> | ||||
|                         {(item) => | ||||
|                           <li> | ||||
|                             <a href={item.href}>{item.label}</a> | ||||
|                           </li> | ||||
|                         } | ||||
|                       </For> | ||||
|                     </ul> | ||||
|                   </details> | ||||
|                 </Show> | ||||
|               </li> | ||||
|             )} | ||||
|           </For> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div class="navbar-end pe-5"> | ||||
|         <Switch> | ||||
|           <Match when={userinfo?.isLoggedIn}> | ||||
|             <button type="button" class="btn btn-sm btn-ghost" onClick={() => logout()}>Logout</button> | ||||
|           </Match> | ||||
|           <Match when={!userinfo?.isLoggedIn}> | ||||
|             <a href="/auth/login" class="btn btn-sm btn-primary">Login</a> | ||||
|           </Match> | ||||
|         </Switch> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										108
									
								
								pkg/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								pkg/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| import { getAtk, readProfiles, useUserinfo } from "../stores/userinfo.tsx"; | ||||
| import { createSignal, For, Show } from "solid-js"; | ||||
|  | ||||
| export default function DashboardPage() { | ||||
|   const userinfo = useUserinfo(); | ||||
|  | ||||
|   const [error, setError] = createSignal<string | null>(null); | ||||
|  | ||||
|   function getGreeting() { | ||||
|     const currentHour = new Date().getHours(); | ||||
|  | ||||
|     if (currentHour >= 0 && currentHour < 12) { | ||||
|       return "Good morning! Wishing you a day filled with joy and success. ☀️"; | ||||
|     } else if (currentHour >= 12 && currentHour < 18) { | ||||
|       return "Afternoon! Hope you have a productive and joyful afternoon! ☀️"; | ||||
|     } else { | ||||
|       return "Good evening! Wishing you a relaxing and pleasant evening. 🌙"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function readNotification(item: any) { | ||||
|     const res = await fetch(`/api/notifications/${item.id}/read`, { | ||||
|       method: "PUT", | ||||
|       headers: { Authorization: `Bearer ${getAtk()}` } | ||||
|     }); | ||||
|     if (res.status !== 200) { | ||||
|       setError(await res.text()); | ||||
|     } else { | ||||
|       await readProfiles(); | ||||
|       setError(null); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div class="max-w-[720px] mx-auto px-5 pt-12"> | ||||
|       <div id="greeting" class="px-5"> | ||||
|         <h1 class="text-2xl font-bold">{userinfo?.displayName}</h1> | ||||
|         <p>{getGreeting()}</p> | ||||
|       </div> | ||||
|  | ||||
|       <div id="alerts"> | ||||
|         <Show when={!userinfo?.meta?.confirmed_at}> | ||||
|           <div role="alert" class="alert alert-warning mt-5"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
|                     d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> | ||||
|             </svg> | ||||
|             <div> | ||||
|               <span>Your account isn't confirmed yet. Please check your inbox and confirm your account.</span> <br /> | ||||
|               <span>Otherwise your account will be deactivate after 48 hours.</span> | ||||
|             </div> | ||||
|           </div> | ||||
|         </Show> | ||||
|         <Show when={error()}> | ||||
|           <div role="alert" class="alert alert-error mt-5"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
|                     d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||
|             </svg> | ||||
|             <span class="capitalize">{error()}</span> | ||||
|           </div> | ||||
|         </Show> | ||||
|       </div> | ||||
|  | ||||
|       <div class="card shadow-xl mt-5"> | ||||
|         <div class="card-body"> | ||||
|           <h2 class="card-title">Notifications</h2> | ||||
|           <div class="bg-base-200 mt-3 mx-[-32px]"> | ||||
|             <Show when={userinfo?.meta?.notifications?.length <= 0}> | ||||
|               <table class="table"> | ||||
|                 <tbody> | ||||
|                 <tr> | ||||
|                   <td class="px-[32px]">You're done! There are no notifications unread for you.</td> | ||||
|                 </tr> | ||||
|                 </tbody> | ||||
|               </table> | ||||
|             </Show> | ||||
|             <Show when={userinfo?.meta?.notifications?.length > 0}> | ||||
|               <table class="table"> | ||||
|                 <tbody> | ||||
|                 <For each={userinfo?.meta?.notifications}> | ||||
|                   {item => | ||||
|                     <tr> | ||||
|                       <td class="px-[32px]"> | ||||
|                         <h2 class="font-bold">{item.subject}</h2> | ||||
|                         <p>{item.content}</p> | ||||
|                         <div class="flex gap-2"> | ||||
|                           <Show when={item.is_important}> | ||||
|                             <span class="font-bold">Important</span> | ||||
|                           </Show> | ||||
|                           <a class="link" onClick={() => readNotification(item)}>Mark as read</a> | ||||
|                         </div> | ||||
|                       </td> | ||||
|                     </tr> | ||||
|                   } | ||||
|                 </For> | ||||
|                 </tbody> | ||||
|               </table> | ||||
|             </Show> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										95
									
								
								pkg/view/src/stores/userinfo.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								pkg/view/src/stores/userinfo.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| import Cookie from "universal-cookie"; | ||||
| import { createContext, useContext } from "solid-js"; | ||||
| import { createStore } from "solid-js/store"; | ||||
|  | ||||
| export interface Userinfo { | ||||
|   isLoggedIn: boolean, | ||||
|   displayName: string, | ||||
|   profiles: any, | ||||
|   meta: any | ||||
| } | ||||
|  | ||||
| const UserinfoContext = createContext<Userinfo>(); | ||||
|  | ||||
| const defaultUserinfo: Userinfo = { | ||||
|   isLoggedIn: false, | ||||
|   displayName: "Citizen", | ||||
|   profiles: null, | ||||
|   meta: null | ||||
| }; | ||||
|  | ||||
| const [userinfo, setUserinfo] = createStore<Userinfo>(structuredClone(defaultUserinfo)); | ||||
|  | ||||
| export function getAtk(): string { | ||||
|   return new Cookie().get("access_token"); | ||||
| } | ||||
|  | ||||
| export async function refreshAtk() { | ||||
|   const rtk = new Cookie().get("refresh_token"); | ||||
|  | ||||
|   const res = await fetch("/api/auth/token", { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ | ||||
|       refresh_token: rtk, | ||||
|       grant_type: "refresh_token" | ||||
|     }) | ||||
|   }); | ||||
|   if (res.status !== 200) { | ||||
|     console.error(await res.text()) | ||||
|   } else { | ||||
|     const data = await res.json(); | ||||
|     new Cookie().set("access_token", data["access_token"], { path: "/", maxAge: undefined }); | ||||
|     new Cookie().set("refresh_token", data["refresh_token"], { path: "/", maxAge: undefined }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function checkLoggedIn(): boolean { | ||||
|   return new Cookie().get("access_token"); | ||||
| } | ||||
|  | ||||
| export async function readProfiles(recovering = true) { | ||||
|   if (!checkLoggedIn()) return; | ||||
|  | ||||
|   const res = await fetch("/api/users/me", { | ||||
|     headers: { "Authorization": `Bearer ${getAtk()}` } | ||||
|   }); | ||||
|  | ||||
|   if (res.status !== 200) { | ||||
|     if (recovering) { | ||||
|       // Auto retry after refresh access token | ||||
|       await refreshAtk(); | ||||
|       return await readProfiles(false); | ||||
|     } else { | ||||
|       clearUserinfo(); | ||||
|       window.location.reload(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const data = await res.json(); | ||||
|  | ||||
|   setUserinfo({ | ||||
|     isLoggedIn: true, | ||||
|     displayName: data["nick"], | ||||
|     profiles: null, | ||||
|     meta: data | ||||
|   }); | ||||
| } | ||||
|  | ||||
| export function clearUserinfo() { | ||||
|   new Cookie().remove("access_token", { path: "/", maxAge: undefined }); | ||||
|   new Cookie().remove("refresh_token", { path: "/", maxAge: undefined }); | ||||
|   setUserinfo(defaultUserinfo); | ||||
| } | ||||
|  | ||||
| export function UserinfoProvider(props: any) { | ||||
|   return ( | ||||
|     <UserinfoContext.Provider value={userinfo}> | ||||
|       {props.children} | ||||
|     </UserinfoContext.Provider> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function useUserinfo() { | ||||
|   return useContext(UserinfoContext); | ||||
| } | ||||
							
								
								
									
										23
									
								
								pkg/view/src/stores/wellKnown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								pkg/view/src/stores/wellKnown.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import { createContext, useContext } from "solid-js"; | ||||
| import { createStore } from "solid-js/store"; | ||||
|  | ||||
| const WellKnownContext = createContext<any>(); | ||||
|  | ||||
| const [wellKnown, setWellKnown] = createStore<any>(null); | ||||
|  | ||||
| export async function readWellKnown() { | ||||
|   const res = await fetch("/.well-known") | ||||
|   setWellKnown(await res.json()) | ||||
| } | ||||
|  | ||||
| export function WellKnownProvider(props: any) { | ||||
|   return ( | ||||
|     <WellKnownContext.Provider value={wellKnown}> | ||||
|       {props.children} | ||||
|     </WellKnownContext.Provider> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function useWellKnown() { | ||||
|   return useContext(WellKnownContext); | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/view/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/view/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /// <reference types="vite/client" /> | ||||
							
								
								
									
										44
									
								
								pkg/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /** @type {import("tailwindcss").Config} */ | ||||
| export default { | ||||
|   content: [ | ||||
|     "./src/**/*.{js,jsx,ts,tsx}" | ||||
|   ], | ||||
|   daisyui: { | ||||
|     themes: [ | ||||
|       { | ||||
|         light: { | ||||
|           ...require("daisyui/src/theming/themes")["light"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0" | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         dark: { | ||||
|           ...require("daisyui/src/theming/themes")["dark"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0" | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   plugins: [require("daisyui")] | ||||
| }; | ||||
|  | ||||
							
								
								
									
										26
									
								
								pkg/view/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								pkg/view/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "target": "ES2020", | ||||
|     "useDefineForClassFields": true, | ||||
|     "module": "ESNext", | ||||
|     "lib": ["ES2020", "ES2015", "DOM", "DOM.Iterable"], | ||||
|     "skipLibCheck": true, | ||||
|  | ||||
|     /* Bundler mode */ | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "resolveJsonModule": true, | ||||
|     "isolatedModules": true, | ||||
|     "noEmit": true, | ||||
|     "jsx": "preserve", | ||||
|     "jsxImportSource": "solid-js", | ||||
|  | ||||
|     /* Linting */ | ||||
|     "strict": true, | ||||
|     "noUnusedLocals": true, | ||||
|     "noUnusedParameters": true, | ||||
|     "noFallthroughCasesInSwitch": true | ||||
|   }, | ||||
|   "include": ["src"], | ||||
|   "references": [{ "path": "./tsconfig.node.json" }] | ||||
| } | ||||
							
								
								
									
										10
									
								
								pkg/view/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								pkg/view/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "skipLibCheck": true, | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowSyntheticDefaultImports": true | ||||
|   }, | ||||
|   "include": ["vite.config.ts"] | ||||
| } | ||||
							
								
								
									
										13
									
								
								pkg/view/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/view/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import { defineConfig } from "vite"; | ||||
| import solid from "vite-plugin-solid"; | ||||
| import devtools from "solid-devtools/vite"; | ||||
|  | ||||
| export default defineConfig({ | ||||
|   plugins: [devtools({ autoname: true }), solid()], | ||||
|   server: { | ||||
|     proxy: { | ||||
|       "/api": "http://localhost:8444", | ||||
|       "/.well-known": "http://localhost:8444" | ||||
|     } | ||||
|   } | ||||
| }); | ||||
							
								
								
									
										28
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| debug = true | ||||
|  | ||||
| name = "Goatplaza" | ||||
| maintainer = "SmartSheep Studio" | ||||
|  | ||||
| bind = "0.0.0.0:8445" | ||||
| domain = "feed.smartsheep.studio" | ||||
| secret = "LtTjzAGFLshwXhN4ZD4nG5KlMv1MWcsvfv03TSZYnT1VhiAnLIZFTnHUwR0XhGgi" | ||||
|  | ||||
| content = "uploads" | ||||
|  | ||||
| everyone_postable = false | ||||
|  | ||||
| [passport] | ||||
| client_id = "goatplaza" | ||||
| client_secret = "Z9k9AFTj^p" | ||||
| endpoint = "https://id.smartsheep.studio" | ||||
|  | ||||
| [mailer] | ||||
| name = "Alphabot <alphabot@smartsheep.studio>" | ||||
| smtp_host = "smtp.exmail.qq.com" | ||||
| smtp_port = 465 | ||||
| username = "alphabot@smartsheep.studio" | ||||
| password = "gz937Zxxzfcd9SeH" | ||||
|  | ||||
| [database] | ||||
| dsn = "host=localhost dbname=hy_interactive port=5432 sslmode=disable" | ||||
| prefix = "interactive_" | ||||
		Reference in New Issue
	
	Block a user