Amorçage du projet
This commit is contained in:
34
cmd/server/container.go
Normal file
34
cmd/server/container.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"forge.cadoles.com/wpetit/gitea-kan/cmd/server/config"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/service"
|
||||
"gitlab.com/wpetit/goweb/service/session"
|
||||
"gitlab.com/wpetit/goweb/session/gorilla"
|
||||
)
|
||||
|
||||
func getServiceContainer(conf *config.Config) (*service.Container, error) {
|
||||
// Initialize and configure service container
|
||||
ctn := service.NewContainer()
|
||||
|
||||
// Create and initialize HTTP session service provider
|
||||
cookieStore, err := gorilla.CreateCookieSessionStore(64, 32, &sessions.Options{
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create cookie store")
|
||||
}
|
||||
|
||||
ctn.Provide(
|
||||
session.ServiceName,
|
||||
gorilla.ServiceProvider("gitea-kan", cookieStore),
|
||||
)
|
||||
|
||||
// Create and expose config service provider
|
||||
ctn.Provide(config.ServiceName, config.ServiceProvider(conf))
|
||||
|
||||
return ctn, nil
|
||||
}
|
87
cmd/server/main.go
Normal file
87
cmd/server/main.go
Normal file
@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"forge.cadoles.com/wpetit/gitea-kan/cmd/server/route"
|
||||
"forge.cadoles.com/wpetit/gitea-kan/config"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
)
|
||||
|
||||
//nolint: gochecknoglobals
|
||||
var (
|
||||
configFile = ""
|
||||
workdir = ""
|
||||
dumpConfig = false
|
||||
)
|
||||
|
||||
//nolint: gochecknoinits
|
||||
func init() {
|
||||
flag.StringVar(&configFile, "config", configFile, "configuration file")
|
||||
flag.StringVar(&workdir, "workdir", workdir, "working directory")
|
||||
flag.BoolVar(&dumpConfig, "dump-config", dumpConfig, "dump configuration and exit")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Switch to new working directory if defined
|
||||
if workdir != "" {
|
||||
if err := os.Chdir(workdir); err != nil {
|
||||
panic(errors.Wrapf(err, "change working directory to '%s'", workdir))
|
||||
}
|
||||
}
|
||||
|
||||
// Load configuration file if defined, use default configuration otherwise
|
||||
var conf *config.Config
|
||||
|
||||
var err error
|
||||
|
||||
if configFile != "" {
|
||||
conf, err = config.NewFromFile(configFile)
|
||||
if err != nil {
|
||||
panic(errors.Wrapf(err, "load config file '%s'", configFile))
|
||||
}
|
||||
} else {
|
||||
conf = config.NewDefault()
|
||||
}
|
||||
|
||||
// Dump configuration if asked
|
||||
if dumpConfig {
|
||||
if err := config.Dump(conf, os.Stdout); err != nil {
|
||||
log.Fatalf("%+v", errors.Wrapf(err, "dump config"))
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
// Define base middlewares
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
// Create service container
|
||||
ctn, err := getServiceContainer(conf)
|
||||
if err != nil {
|
||||
log.Fatalf("%+v", errors.Wrap(err, "create service container"))
|
||||
}
|
||||
|
||||
// Expose service container on router
|
||||
r.Use(container.ServiceContainer(ctn))
|
||||
|
||||
// Define routes
|
||||
route.Mount(r, conf)
|
||||
|
||||
log.Printf("listening on '%s'", conf.HTTP.Address)
|
||||
|
||||
if err := http.ListenAndServe(conf.HTTP.Address, r); err != nil {
|
||||
log.Fatalf("%+v", errors.Wrapf(err, "listen '%s'", conf.HTTP.Address))
|
||||
}
|
||||
}
|
31
cmd/server/route/helper.go
Normal file
31
cmd/server/route/helper.go
Normal file
@ -0,0 +1,31 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/template"
|
||||
"gitlab.com/wpetit/goweb/template/html"
|
||||
)
|
||||
|
||||
func extendTemplateData(w http.ResponseWriter, r *http.Request, data template.Data) template.Data {
|
||||
ctn := container.Must(r.Context())
|
||||
data, err := template.Extend(data,
|
||||
html.WithFlashes(w, r, ctn),
|
||||
)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "error while extending template data"))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func generateRandomBytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
23
cmd/server/route/logout.go
Normal file
23
cmd/server/route/logout.go
Normal file
@ -0,0 +1,23 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/gitea-kan/config"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/session"
|
||||
)
|
||||
|
||||
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
ctn := container.Must(r.Context())
|
||||
conf := config.Must(ctn)
|
||||
sess, err := session.Must(ctn).Get(w, r)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not retrieve session"))
|
||||
}
|
||||
if err := sess.Delete(w, r); err != nil {
|
||||
panic(errors.Wrap(err, "could not delete session"))
|
||||
}
|
||||
http.Redirect(w, r, conf.Gitea.LogoutURL, http.StatusSeeOther)
|
||||
}
|
46
cmd/server/route/oauth2.go
Normal file
46
cmd/server/route/oauth2.go
Normal file
@ -0,0 +1,46 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/gitea-kan/middleware"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/session"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ctn := container.Must(r.Context())
|
||||
sess, err := session.Must(ctn).Get(w, r)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not retrieve session"))
|
||||
}
|
||||
|
||||
expectedState := sess.Get(middleware.SessionOAuth2State)
|
||||
state := r.FormValue("state")
|
||||
|
||||
if state != expectedState {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
giteaOAuth2Config := middleware.GiteaOAuth2Config(ctn)
|
||||
|
||||
code := r.FormValue("code")
|
||||
token, err := giteaOAuth2Config.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not exchange oauth2 token"))
|
||||
}
|
||||
|
||||
sess.Set(middleware.SessionOAuth2AccessToken, token.AccessToken)
|
||||
sess.Set(middleware.SessionOAuth2State, "")
|
||||
|
||||
if err := sess.Save(w, r); err != nil {
|
||||
panic(errors.Wrap(err, "could not save session"))
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
|
||||
}
|
41
cmd/server/route/proxy.go
Normal file
41
cmd/server/route/proxy.go
Normal file
@ -0,0 +1,41 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"forge.cadoles.com/wpetit/gitea-kan/config"
|
||||
"forge.cadoles.com/wpetit/gitea-kan/middleware"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/service/session"
|
||||
)
|
||||
|
||||
func proxyAPIRequest(w http.ResponseWriter, r *http.Request) {
|
||||
ctn := container.Must(r.Context())
|
||||
conf := config.Must(ctn)
|
||||
|
||||
apiBaseURL, err := url.Parse(conf.Gitea.APIBaseURL)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not parse api base url"))
|
||||
}
|
||||
|
||||
sess, err := session.Must(ctn).Get(w, r)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "could not retrieve session"))
|
||||
}
|
||||
|
||||
accessToken := sess.Get(middleware.SessionOAuth2AccessToken)
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(apiBaseURL)
|
||||
proxy.Director = func(r *http.Request) {
|
||||
r.Host = apiBaseURL.Host
|
||||
r.URL.Scheme = apiBaseURL.Scheme
|
||||
r.URL.Host = apiBaseURL.Host
|
||||
r.Header.Add("Authorization", fmt.Sprintf("token %s", accessToken))
|
||||
}
|
||||
|
||||
proxy.ServeHTTP(w, r)
|
||||
}
|
33
cmd/server/route/route.go
Normal file
33
cmd/server/route/route.go
Normal file
@ -0,0 +1,33 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"forge.cadoles.com/wpetit/gitea-kan/config"
|
||||
"forge.cadoles.com/wpetit/gitea-kan/middleware"
|
||||
"github.com/go-chi/chi"
|
||||
"gitlab.com/wpetit/goweb/middleware/container"
|
||||
"gitlab.com/wpetit/goweb/static"
|
||||
)
|
||||
|
||||
func Mount(r *chi.Mux, config *config.Config) {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/callback", handleOAuth2Callback)
|
||||
|
||||
// Authenticated routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.Authenticate)
|
||||
r.Get("/logout", handleLogout)
|
||||
r.Get("/gitea/api/*", http.StripPrefix("/gitea", http.HandlerFunc(proxyAPIRequest)).ServeHTTP)
|
||||
r.Get("/*", static.Dir(config.HTTP.PublicDir, "", html5PushStateHandler))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func html5PushStateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ctn := container.Must(r.Context())
|
||||
conf := config.Must(ctn)
|
||||
indexFile := path.Join(conf.HTTP.PublicDir, "index.html")
|
||||
http.ServeFile(w, r, indexFile)
|
||||
}
|
17
cmd/server/template/blocks/base.html.tmpl
Normal file
17
cmd/server/template/blocks/base.html.tmpl
Normal file
@ -0,0 +1,17 @@
|
||||
{{define "base"}}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>{{block "title" . -}}{{- end}}</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
{{- block "head_style" . -}}{{end}}
|
||||
{{- block "head_script" . -}}{{end}}
|
||||
</head>
|
||||
<body>
|
||||
{{- block "body" . -}}{{- end -}}
|
||||
{{- block "body_script" . -}}{{end}}
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
8
cmd/server/template/layouts/index.html.tmpl
Normal file
8
cmd/server/template/layouts/index.html.tmpl
Normal file
@ -0,0 +1,8 @@
|
||||
{{define "title"}}Gitea Apps{{end}}
|
||||
{{define "body"}}
|
||||
<div id="gitea-apps" class="is-fullheight"></div>
|
||||
{{end}}
|
||||
{{define "body_scripts"}}
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
{{end}}
|
||||
{{template "base" .}}
|
Reference in New Issue
Block a user