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

@ -15,6 +15,7 @@ type config struct {
ClientName string ClientName string
ClientScope string ClientScope string
ClientWebsite string ClientWebsite string
SingleInstance string
StaticDirectory string StaticDirectory string
TemplatesPath string TemplatesPath string
DatabasePath string DatabasePath string
@ -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)
@ -70,6 +71,7 @@ type service struct {
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,6 +82,7 @@ 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,
@ -90,6 +93,7 @@ func NewService(clientName string,
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,6 +105,19 @@ 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()
instance, ok := s.SingleInstance(ctx)
if ok {
url, sessionID, err := s.NewSession(ctx, instance)
if err != nil {
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) err := s.ServeSigninPage(ctx, c)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -100,6 +125,7 @@ func NewHandler(s Service, staticDir string) http.Handler {
return return
} }
} }
}
timelinePage := func(w http.ResponseWriter, req *http.Request) { timelinePage := func(w http.ResponseWriter, req *http.Request) {
c := newClient(w) c := newClient(w)
@ -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)
} }