mirror of
https://github.com/mentos1386/zdravko.git
synced 2024-11-25 08:58:50 +00:00
feat: folder structure change and initial oauth flow
This commit is contained in:
parent
4984ed2250
commit
92453200ce
25 changed files with 225 additions and 40 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,4 +1,7 @@
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
gorm.db
|
zdravko.db
|
||||||
|
|
||||||
|
# Config
|
||||||
|
.env
|
||||||
|
|
|
@ -20,6 +20,9 @@ Demo is available at https://zdravko.fly.dev.
|
||||||
* [justfile](https://github.com/casey/just)
|
* [justfile](https://github.com/casey/just)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
# Configure
|
||||||
|
cp example.env .env
|
||||||
|
|
||||||
# Start development environment
|
# Start development environment
|
||||||
just run
|
just run
|
||||||
```
|
```
|
||||||
|
|
|
@ -3,37 +3,53 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/internal"
|
"code.tjo.space/mentos1386/zdravko/internal"
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/pages"
|
"code.tjo.space/mentos1386/zdravko/internal/handlers"
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/static"
|
"code.tjo.space/mentos1386/zdravko/web/static"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
port := os.Getenv("PORT")
|
config := internal.NewConfig()
|
||||||
if port == "" {
|
|
||||||
port = "8000"
|
|
||||||
}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
db, err := internal.ConnectToDatabase()
|
db, query, err := internal.ConnectToDatabase(config.SQLITE_DB_PATH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Println("Connected to database")
|
log.Println("Connected to database")
|
||||||
|
|
||||||
page := pages.NewPageHandler(db)
|
h := handlers.NewBaseHandler(db, query, config)
|
||||||
|
|
||||||
|
// Health
|
||||||
|
r.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
d, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
err = d.Ping()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
_, err = w.Write([]byte("OK"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Server static files
|
// Server static files
|
||||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(static.Static))))
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(static.Static))))
|
||||||
|
|
||||||
r.HandleFunc("/", page.Index).Methods("GET")
|
r.HandleFunc("/", h.Index).Methods("GET")
|
||||||
r.HandleFunc("/settings", page.Settings).Methods("GET")
|
r.HandleFunc("/settings", h.Settings).Methods("GET")
|
||||||
|
|
||||||
log.Println("Server started on", port)
|
// OAuth2
|
||||||
log.Fatal(http.ListenAndServe(":"+port, r))
|
r.HandleFunc("/oauth2/login", h.OAuth2LoginGET).Methods("GET")
|
||||||
|
r.HandleFunc("/oauth2/callback", h.OAuth2CallbackGET).Methods("GET")
|
||||||
|
|
||||||
|
log.Println("Server started on", config.PORT)
|
||||||
|
log.Fatal(http.ListenAndServe(":"+config.PORT, r))
|
||||||
}
|
}
|
||||||
|
|
18
example.env
Normal file
18
example.env
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# PORT
|
||||||
|
PORT=8000
|
||||||
|
ROOT_URL=http://localhost:8000
|
||||||
|
|
||||||
|
# SQLite
|
||||||
|
SQLITE_DB_PATH=zdravko.db
|
||||||
|
|
||||||
|
# Session
|
||||||
|
SESSION_SECRET=your_secret
|
||||||
|
|
||||||
|
# OAUTH2
|
||||||
|
# The redirect/callback url is ${ROOT_URL}/auth/callback
|
||||||
|
OAUTH2_CLIENT_ID=your_client_id
|
||||||
|
OAUTH2_CLIENT_SECRET=your_client_secret
|
||||||
|
OAUTH2_SCOPES=openid,profile,email
|
||||||
|
OAUTH2_ENDPOINT_TOKEN_URL=https://your_oauth2_provider/token
|
||||||
|
OAUTH2_ENDPOINT_AUTH_URL=https://your_oauth2_provider/auth
|
||||||
|
OAUTH2_ENDPOINT_USER_INFO_URL=https://your_oauth2_provider/userinfo
|
7
fly.toml
7
fly.toml
|
@ -9,6 +9,13 @@ primary_region = 'waw'
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
PORT = '8080'
|
PORT = '8080'
|
||||||
|
ROOT_URL = 'https://zdravko.fly.dev'
|
||||||
|
SQLITE_DB_PATH = 'zdravko.db'
|
||||||
|
# Other are defined in secrets
|
||||||
|
OAUTH2_SCOPES = 'openid,profile,email'
|
||||||
|
OAUTH2_ENDPOINT_TOKEN_URL = 'https://id.tjo.space/application/o/token/'
|
||||||
|
OAUTH2_ENDPOINT_AUTH_URL = 'https://id.tjo.space/application/o/authorize/'
|
||||||
|
OAUTH2_ENDPOINT_USER_INFO_URL = 'https://id.tjo.space/application/o/userinfo/'
|
||||||
|
|
||||||
[processes]
|
[processes]
|
||||||
server = "server"
|
server = "server"
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -38,11 +38,13 @@ require (
|
||||||
go.temporal.io/api v1.24.0 // indirect
|
go.temporal.io/api v1.24.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
golang.org/x/mod v0.15.0 // indirect
|
golang.org/x/mod v0.15.0 // indirect
|
||||||
golang.org/x/net v0.20.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.17.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
golang.org/x/tools v0.17.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 // indirect
|
google.golang.org/genproto v0.0.0-20230815205213-6bfd019c3878 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20230815205213-6bfd019c3878 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -1130,6 +1130,7 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
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/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
|
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -1253,6 +1254,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
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/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -1283,6 +1286,8 @@ golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||||
|
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||||
|
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -1572,6 +1577,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
|
53
internal/config.go
Normal file
53
internal/config.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
PORT string
|
||||||
|
ROOT_URL string // Needed for oauth2 redirect
|
||||||
|
|
||||||
|
SQLITE_DB_PATH string
|
||||||
|
|
||||||
|
SESSION_SECRET string
|
||||||
|
|
||||||
|
OAUTH2_CLIENT_ID string
|
||||||
|
OAUTH2_CLIENT_SECRET string
|
||||||
|
OAUTH2_SCOPES []string
|
||||||
|
OAUTH2_ENDPOINT_TOKEN_URL string
|
||||||
|
OAUTH2_ENDPOINT_AUTH_URL string
|
||||||
|
OAUTH2_ENDPOINT_USER_INFO_URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnv(key, fallback string) string {
|
||||||
|
if value, ok := os.LookupEnv(key); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnvRequired(key string) string {
|
||||||
|
if value, ok := os.LookupEnv(key); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
panic("Environment variable " + key + " is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
PORT: getEnv("PORT", "8000"),
|
||||||
|
ROOT_URL: getEnvRequired("ROOT_URL"),
|
||||||
|
|
||||||
|
SQLITE_DB_PATH: getEnv("SQLITE_DB_PATH", "zdravko.db"),
|
||||||
|
SESSION_SECRET: getEnvRequired("SESSION_SECRET"),
|
||||||
|
|
||||||
|
OAUTH2_CLIENT_ID: getEnvRequired("OAUTH2_CLIENT_ID"),
|
||||||
|
OAUTH2_CLIENT_SECRET: getEnvRequired("OAUTH2_CLIENT_SECRET"),
|
||||||
|
OAUTH2_SCOPES: strings.Split(getEnvRequired("OAUTH2_SCOPES"), ","),
|
||||||
|
OAUTH2_ENDPOINT_TOKEN_URL: getEnvRequired("OAUTH2_ENDPOINT_TOKEN_URL"),
|
||||||
|
OAUTH2_ENDPOINT_AUTH_URL: getEnvRequired("OAUTH2_ENDPOINT_AUTH_URL"),
|
||||||
|
OAUTH2_ENDPOINT_USER_INFO_URL: getEnvRequired("OAUTH2_ENDPOINT_USER_INFO_URL"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,22 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/models"
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/models/query"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate just _generate-gorm
|
//go:generate just _generate-gorm
|
||||||
func ConnectToDatabase() (*gorm.DB, error) {
|
func ConnectToDatabase(path string) (*gorm.DB, *query.Query, error) {
|
||||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open(path), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
db.AutoMigrate(&models.Healthcheck{})
|
||||||
|
|
||||||
|
q := query.Use(db)
|
||||||
|
|
||||||
|
return db, q, nil
|
||||||
}
|
}
|
||||||
|
|
17
internal/handlers/handlers.go
Normal file
17
internal/handlers/handlers.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal"
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal/models/query"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseHandler struct {
|
||||||
|
db *gorm.DB
|
||||||
|
query *query.Query
|
||||||
|
config *internal.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseHandler(db *gorm.DB, q *query.Query, config *internal.Config) *BaseHandler {
|
||||||
|
return &BaseHandler{db, q, config}
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package pages
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/ui"
|
"code.tjo.space/mentos1386/zdravko/web/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *PageHandler) Index(w http.ResponseWriter, r *http.Request) {
|
func (h *BaseHandler) Index(w http.ResponseWriter, r *http.Request) {
|
||||||
ts, err := template.ParseFS(ui.Templates,
|
ts, err := template.ParseFS(templates.Templates,
|
||||||
"components/base.tmpl",
|
"components/base.tmpl",
|
||||||
"pages/index.tmpl",
|
"pages/index.tmpl",
|
||||||
)
|
)
|
63
internal/handlers/oauth2.go
Normal file
63
internal/handlers/oauth2.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.tjo.space/mentos1386/zdravko/internal"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newOAuth2(config *internal.Config) *oauth2.Config {
|
||||||
|
return &oauth2.Config{
|
||||||
|
ClientID: config.OAUTH2_CLIENT_ID,
|
||||||
|
ClientSecret: config.OAUTH2_CLIENT_SECRET,
|
||||||
|
Scopes: config.OAUTH2_SCOPES,
|
||||||
|
RedirectURL: config.ROOT_URL + "/oauth2/callback",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
TokenURL: config.OAUTH2_ENDPOINT_TOKEN_URL,
|
||||||
|
AuthURL: config.OAUTH2_ENDPOINT_AUTH_URL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BaseHandler) OAuth2LoginGET(w http.ResponseWriter, r *http.Request) {
|
||||||
|
conf := newOAuth2(h.config)
|
||||||
|
|
||||||
|
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
|
||||||
|
|
||||||
|
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *BaseHandler) OAuth2CallbackGET(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.Background()
|
||||||
|
conf := newOAuth2(h.config)
|
||||||
|
|
||||||
|
// Exchange the code for a new token.
|
||||||
|
tok, err := conf.Exchange(r.Context(), r.URL.Query().Get("code"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ge the user information.
|
||||||
|
client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(tok))
|
||||||
|
resp, err := client.Get(h.config.OAUTH2_ENDPOINT_USER_INFO_URL)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package pages
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"code.tjo.space/mentos1386/zdravko/internal/ui"
|
"code.tjo.space/mentos1386/zdravko/web/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *PageHandler) Settings(w http.ResponseWriter, r *http.Request) {
|
func (h *BaseHandler) Settings(w http.ResponseWriter, r *http.Request) {
|
||||||
ts, err := template.ParseFS(ui.Templates,
|
ts, err := template.ParseFS(templates.Templates,
|
||||||
"components/base.tmpl",
|
"components/base.tmpl",
|
||||||
"pages/settings.tmpl",
|
"pages/settings.tmpl",
|
||||||
)
|
)
|
|
@ -1,11 +0,0 @@
|
||||||
package pages
|
|
||||||
|
|
||||||
import "gorm.io/gorm"
|
|
||||||
|
|
||||||
type PageHandler struct {
|
|
||||||
db *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPageHandler(db *gorm.DB) *PageHandler {
|
|
||||||
return &PageHandler{db}
|
|
||||||
}
|
|
4
justfile
4
justfile
|
@ -1,5 +1,7 @@
|
||||||
# Always use devbox environment to run commands.
|
# Always use devbox environment to run commands.
|
||||||
set shell := ["devbox", "run"]
|
set shell := ["devbox", "run"]
|
||||||
|
# Load dotenv
|
||||||
|
set dotenv-load
|
||||||
|
|
||||||
STATIC_DIR := "./internal/static"
|
STATIC_DIR := "./internal/static"
|
||||||
|
|
||||||
|
@ -45,4 +47,4 @@ _feather-icons-download:
|
||||||
curl -sLo {{STATIC_DIR}}/icons/feather-sprite.svg https://unpkg.com/feather-icons/dist/feather-sprite.svg
|
curl -sLo {{STATIC_DIR}}/icons/feather-sprite.svg https://unpkg.com/feather-icons/dist/feather-sprite.svg
|
||||||
|
|
||||||
_generate-gorm:
|
_generate-gorm:
|
||||||
go run cmd/generate/main.go
|
go run tools/generate/main.go
|
||||||
|
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
@ -1,4 +1,4 @@
|
||||||
package ui
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
Loading…
Reference in a new issue