Add single instance mode
authorr <r@freesoftwareextremist.com>
Sun, 19 Apr 2020 08:18:36 +0000 (08:18 +0000)
committerr <r@freesoftwareextremist.com>
Sun, 19 Apr 2020 08:18:36 +0000 (08:18 +0000)
bloat.conf
config/config.go
main.go
service/service.go
service/transport.go

index e4ea384d391632c8f2f410b1ce1f693053a665ed..ae2f0078beb0355b1cac0bea5373e66e4b65e5d3 100644 (file)
@@ -25,6 +25,13 @@ client_scope=read write follow
 # Example: "http://localhost:8080", "https://mydomain.com"
 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.
 database_path=database
 
index 2d4fb8d360846d9a064ff2fc82ae873d3f8a1ec1..d6140e5c92100d7a729e90e16eaf85cd02adfa70 100644 (file)
@@ -11,16 +11,17 @@ import (
 )
 
 type config struct {
-       ListenAddress        string
-       ClientName           string
-       ClientScope          string
-       ClientWebsite        string
-       StaticDirectory      string
-       TemplatesPath string
-       DatabasePath         string
-       CustomCSS            string
-       PostFormats          []model.PostFormat
-       LogFile              string
+       ListenAddress   string
+       ClientName      string
+       ClientScope     string
+       ClientWebsite   string
+       SingleInstance  string
+       StaticDirectory string
+       TemplatesPath   string
+       DatabasePath    string
+       CustomCSS       string
+       PostFormats     []model.PostFormat
+       LogFile         string
 }
 
 func (c *config) IsValid() bool {
@@ -68,6 +69,8 @@ func Parse(r io.Reader) (c *config, err error) {
                        c.ClientScope = val
                case "client_website":
                        c.ClientWebsite = val
+               case "single_instance":
+                       c.SingleInstance = val
                case "static_directory":
                        c.StaticDirectory = val
                case "templates_path":
diff --git a/main.go b/main.go
index aac774112dc83aa5c05a364d3d6bccc79e394050..99adb7ca6f7ef74f307ed5c211da88a29146e2e1 100644 (file)
--- a/main.go
+++ b/main.go
@@ -101,7 +101,7 @@ func main() {
 
        s := service.NewService(config.ClientName, config.ClientScope,
                config.ClientWebsite, customCSS, config.PostFormats, renderer,
-               sessionRepo, appRepo)
+               sessionRepo, appRepo, config.SingleInstance)
        s = service.NewAuthService(sessionRepo, appRepo, s)
        s = service.NewLoggingService(logger, s)
        handler := service.NewHandler(s, config.StaticDirectory)
index be83bd02f51c51852f53ed7da2ab8bc9373e9eee..02c55cbe800f9beb04e227d60aeadd9c07f4b2cd 100644 (file)
@@ -35,6 +35,7 @@ type Service interface {
        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)
        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)
        Signin(ctx context.Context, c *model.Client, sessionID string,
                code string) (token string, userID string, err error)
@@ -62,14 +63,15 @@ type Service interface {
 }
 
 type service struct {
-       clientName    string
-       clientScope   string
-       clientWebsite string
-       customCSS     string
-       postFormats   []model.PostFormat
-       renderer      renderer.Renderer
-       sessionRepo   model.SessionRepo
-       appRepo       model.AppRepo
+       clientName     string
+       clientScope    string
+       clientWebsite  string
+       customCSS      string
+       postFormats    []model.PostFormat
+       renderer       renderer.Renderer
+       sessionRepo    model.SessionRepo
+       appRepo        model.AppRepo
+       singleInstance string
 }
 
 func NewService(clientName string,
@@ -80,16 +82,18 @@ func NewService(clientName string,
        renderer renderer.Renderer,
        sessionRepo model.SessionRepo,
        appRepo model.AppRepo,
+       singleInstance string,
 ) Service {
        return &service{
-               clientName:    clientName,
-               clientScope:   clientScope,
-               clientWebsite: clientWebsite,
-               customCSS:     customCSS,
-               postFormats:   postFormats,
-               renderer:      renderer,
-               sessionRepo:   sessionRepo,
-               appRepo:       appRepo,
+               clientName:     clientName,
+               clientScope:    clientScope,
+               clientWebsite:  clientWebsite,
+               customCSS:      customCSS,
+               postFormats:    postFormats,
+               renderer:       renderer,
+               sessionRepo:    sessionRepo,
+               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)
 }
 
+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) (
        redirectUrl string, sessionID string, err error) {
 
index 69b28ec087c656005b4ba61d382cddb360f855fe..f52cca00a3f58499cbf8d1e4c8fb607d9f8c7244 100644 (file)
@@ -14,12 +14,24 @@ import (
        "github.com/gorilla/mux"
 )
 
+const (
+       sessionExp = 365 * 24 * time.Hour
+)
+
 func newClient(w io.Writer) *model.Client {
        return &model.Client{
                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 {
        ctx := context.Background()
        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) {
                c := newClient(w)
                ctx := context.Background()
-               err := s.ServeSigninPage(ctx, c)
-               if err != nil {
-                       w.WriteHeader(http.StatusInternalServerError)
-                       s.ServeErrorPage(ctx, c, err)
-                       return
+               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)
+                       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
                }
 
-               http.SetCookie(w, &http.Cookie{
-                       Name:    "session_id",
-                       Value:   sessionID,
-                       Expires: time.Now().Add(365 * 24 * time.Hour),
-               })
-
+               setSessionCookie(w, sessionID, sessionExp)
                w.Header().Add("Location", url)
                w.WriteHeader(http.StatusFound)
        }
@@ -689,12 +710,8 @@ func NewHandler(s Service, staticDir string) http.Handler {
                ctx := newCtxWithSesionCSRF(req, req.FormValue("csrf_token"))
 
                s.Signout(ctx, c)
-               http.SetCookie(w, &http.Cookie{
-                       Name:    "session_id",
-                       Value:   "",
-                       Expires: time.Now(),
-               })
 
+               setSessionCookie(w, "", 0)
                w.Header().Add("Location", "/")
                w.WriteHeader(http.StatusFound)
        }