Add single instance mode

This commit is contained in:
r 2020-04-19 08:18:36 +00:00
parent 5abbadfa62
commit 55ed6a480e
5 changed files with 82 additions and 43 deletions

View File

@ -25,6 +25,13 @@ client_scope=read write follow
# Example: "http://localhost:8080", "https://mydomain.com" # Example: "http://localhost:8080", "https://mydomain.com"
client_website=http://localhost:8080 client_website=http://localhost:8080
# In single instance mode, bloat will not ask for instance domain name and
# user will be directly redirected to login form. User login from other
# instances is not allowed in this mode.
# Empty value disables single instance mode.
# Example: "mydomain.com"
single_instance=
# Path of database directory. It's used to store session information. # Path of database directory. It's used to store session information.
database_path=database database_path=database

View File

@ -11,16 +11,17 @@ import (
) )
type config struct { type config struct {
ListenAddress string ListenAddress string
ClientName string ClientName string
ClientScope string ClientScope string
ClientWebsite string ClientWebsite string
StaticDirectory string SingleInstance string
TemplatesPath string StaticDirectory string
DatabasePath string TemplatesPath string
CustomCSS string DatabasePath string
PostFormats []model.PostFormat CustomCSS string
LogFile string PostFormats []model.PostFormat
LogFile string
} }
func (c *config) IsValid() bool { func (c *config) IsValid() bool {
@ -68,6 +69,8 @@ func Parse(r io.Reader) (c *config, err error) {
c.ClientScope = val c.ClientScope = val
case "client_website": case "client_website":
c.ClientWebsite = val c.ClientWebsite = val
case "single_instance":
c.SingleInstance = val
case "static_directory": case "static_directory":
c.StaticDirectory = val c.StaticDirectory = val
case "templates_path": case "templates_path":

View File

@ -101,7 +101,7 @@ func main() {
s := service.NewService(config.ClientName, config.ClientScope, s := service.NewService(config.ClientName, config.ClientScope,
config.ClientWebsite, customCSS, config.PostFormats, renderer, config.ClientWebsite, customCSS, config.PostFormats, renderer,
sessionRepo, appRepo) sessionRepo, appRepo, config.SingleInstance)
s = service.NewAuthService(sessionRepo, appRepo, s) s = service.NewAuthService(sessionRepo, appRepo, s)
s = service.NewLoggingService(logger, s) s = service.NewLoggingService(logger, s)
handler := service.NewHandler(s, config.StaticDirectory) handler := service.NewHandler(s, config.StaticDirectory)

View File

@ -35,6 +35,7 @@ type Service interface {
ServeSearchPage(ctx context.Context, c *model.Client, q string, qType string, offset int) (err error) ServeSearchPage(ctx context.Context, c *model.Client, q string, qType string, offset int) (err error)
ServeUserSearchPage(ctx context.Context, c *model.Client, id string, q string, offset int) (err error) ServeUserSearchPage(ctx context.Context, c *model.Client, id string, q string, offset int) (err error)
ServeSettingsPage(ctx context.Context, c *model.Client) (err error) ServeSettingsPage(ctx context.Context, c *model.Client) (err error)
SingleInstance(ctx context.Context) (instance string, ok bool)
NewSession(ctx context.Context, instance string) (redirectUrl string, sessionID string, err error) NewSession(ctx context.Context, instance string) (redirectUrl string, sessionID string, err error)
Signin(ctx context.Context, c *model.Client, sessionID string, Signin(ctx context.Context, c *model.Client, sessionID string,
code string) (token string, userID string, err error) code string) (token string, userID string, err error)
@ -62,14 +63,15 @@ type Service interface {
} }
type service struct { type service struct {
clientName string clientName string
clientScope string clientScope string
clientWebsite string clientWebsite string
customCSS string customCSS string
postFormats []model.PostFormat postFormats []model.PostFormat
renderer renderer.Renderer renderer renderer.Renderer
sessionRepo model.SessionRepo sessionRepo model.SessionRepo
appRepo model.AppRepo appRepo model.AppRepo
singleInstance string
} }
func NewService(clientName string, func NewService(clientName string,
@ -80,16 +82,18 @@ func NewService(clientName string,
renderer renderer.Renderer, renderer renderer.Renderer,
sessionRepo model.SessionRepo, sessionRepo model.SessionRepo,
appRepo model.AppRepo, appRepo model.AppRepo,
singleInstance string,
) Service { ) Service {
return &service{ return &service{
clientName: clientName, clientName: clientName,
clientScope: clientScope, clientScope: clientScope,
clientWebsite: clientWebsite, clientWebsite: clientWebsite,
customCSS: customCSS, customCSS: customCSS,
postFormats: postFormats, postFormats: postFormats,
renderer: renderer, renderer: renderer,
sessionRepo: sessionRepo, sessionRepo: sessionRepo,
appRepo: appRepo, appRepo: appRepo,
singleInstance: singleInstance,
} }
} }
@ -622,6 +626,14 @@ func (svc *service) ServeSettingsPage(ctx context.Context, c *model.Client) (err
return svc.renderer.Render(rCtx, c.Writer, renderer.SettingsPage, data) return svc.renderer.Render(rCtx, c.Writer, renderer.SettingsPage, data)
} }
func (svc *service) SingleInstance(ctx context.Context) (instance string, ok bool) {
if len(svc.singleInstance) > 0 {
instance = svc.singleInstance
ok = true
}
return
}
func (svc *service) NewSession(ctx context.Context, instance string) ( func (svc *service) NewSession(ctx context.Context, instance string) (
redirectUrl string, sessionID string, err error) { redirectUrl string, sessionID string, err error) {

View File

@ -14,12 +14,24 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
const (
sessionExp = 365 * 24 * time.Hour
)
func newClient(w io.Writer) *model.Client { func newClient(w io.Writer) *model.Client {
return &model.Client{ return &model.Client{
Writer: w, Writer: w,
} }
} }
func setSessionCookie(w http.ResponseWriter, sessionID string, exp time.Duration) {
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
Expires: time.Now().Add(exp),
})
}
func newCtxWithSesion(req *http.Request) context.Context { func newCtxWithSesion(req *http.Request) context.Context {
ctx := context.Background() ctx := context.Background()
sessionID, err := req.Cookie("session_id") sessionID, err := req.Cookie("session_id")
@ -93,11 +105,25 @@ func NewHandler(s Service, staticDir string) http.Handler {
signinPage := func(w http.ResponseWriter, req *http.Request) { signinPage := func(w http.ResponseWriter, req *http.Request) {
c := newClient(w) c := newClient(w)
ctx := context.Background() ctx := context.Background()
err := s.ServeSigninPage(ctx, c) instance, ok := s.SingleInstance(ctx)
if err != nil { if ok {
w.WriteHeader(http.StatusInternalServerError) url, sessionID, err := s.NewSession(ctx, instance)
s.ServeErrorPage(ctx, c, err) if err != nil {
return w.WriteHeader(http.StatusInternalServerError)
s.ServeErrorPage(ctx, c, err)
return
}
setSessionCookie(w, sessionID, sessionExp)
w.Header().Add("Location", url)
w.WriteHeader(http.StatusFound)
} else {
err := s.ServeSigninPage(ctx, c)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
s.ServeErrorPage(ctx, c, err)
return
}
} }
} }
@ -291,12 +317,7 @@ func NewHandler(s Service, staticDir string) http.Handler {
return return
} }
http.SetCookie(w, &http.Cookie{ setSessionCookie(w, sessionID, sessionExp)
Name: "session_id",
Value: sessionID,
Expires: time.Now().Add(365 * 24 * time.Hour),
})
w.Header().Add("Location", url) w.Header().Add("Location", url)
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
} }
@ -689,12 +710,8 @@ func NewHandler(s Service, staticDir string) http.Handler {
ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token")) ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
s.Signout(ctx, c) s.Signout(ctx, c)
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: "",
Expires: time.Now(),
})
setSessionCookie(w, "", 0)
w.Header().Add("Location", "/") w.Header().Add("Location", "/")
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
} }