You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-17 00:17:59 +02:00
completed updating web-api
This commit is contained in:
@ -10,14 +10,13 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account represents the Account API method handler set.
|
// Account represents the Account API method handler set.
|
||||||
type Account struct {
|
type Account struct {
|
||||||
MasterDB *sqlx.DB
|
*account.Repository
|
||||||
|
|
||||||
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||||
}
|
}
|
||||||
@ -35,7 +34,7 @@ type Account struct {
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /accounts/{id} [get]
|
// @Router /accounts/{id} [get]
|
||||||
func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -52,7 +51,7 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
includeArchived = b
|
includeArchived = b
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := account.Read(ctx, claims, a.MasterDB, account.AccountReadRequest{
|
res, err := h.Repository.Read(ctx, claims, account.AccountReadRequest{
|
||||||
ID: params["id"],
|
ID: params["id"],
|
||||||
IncludeArchived: includeArchived,
|
IncludeArchived: includeArchived,
|
||||||
})
|
})
|
||||||
@ -82,7 +81,7 @@ func (a *Account) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /accounts [patch]
|
// @Router /accounts [patch]
|
||||||
func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
|
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -102,7 +101,7 @@ func (a *Account) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = account.Update(ctx, claims, a.MasterDB, req, v.Now)
|
err = h.Repository.Update(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
|
48
cmd/web-api/handlers/example.go
Normal file
48
cmd/web-api/handlers/example.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Example represents the Example API method handler set.
|
||||||
|
type Example struct {
|
||||||
|
Project *project.Repository
|
||||||
|
|
||||||
|
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorResponse returns example error messages.
|
||||||
|
func (h *Example) ErrorResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
|
v, err := webcontext.ContextValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-validation-error"); qv != "" {
|
||||||
|
_, err := h.Project.Create(ctx, auth.Claims{}, project.ProjectCreateRequest{}, v.Now)
|
||||||
|
return web.RespondJsonError(ctx, w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-web-error"); qv != "" {
|
||||||
|
terr := errors.New("Some random error")
|
||||||
|
terr = errors.WithMessage(terr, "Actual error message")
|
||||||
|
rerr := weberror.NewError(ctx, terr, http.StatusBadRequest).(*weberror.Error)
|
||||||
|
rerr.Message = "Test Web Error Message"
|
||||||
|
return web.RespondJsonError(ctx, w, rerr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-error"); qv != "" {
|
||||||
|
terr := errors.New("Test error")
|
||||||
|
terr = errors.WithMessage(terr, "Error message")
|
||||||
|
return web.RespondJsonError(ctx, w, terr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -11,14 +11,13 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Project represents the Project API method handler set.
|
// Project represents the Project API method handler set.
|
||||||
type Project struct {
|
type Project struct {
|
||||||
MasterDB *sqlx.DB
|
*project.Repository
|
||||||
|
|
||||||
// ADD OTHER STATE LIKE THE LOGGER IF NEEDED.
|
// ADD OTHER STATE LIKE THE LOGGER IF NEEDED.
|
||||||
}
|
}
|
||||||
@ -41,7 +40,7 @@ type Project struct {
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects [get]
|
// @Router /projects [get]
|
||||||
func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -108,7 +107,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
// return web.RespondJsonError(ctx, w, err)
|
// return web.RespondJsonError(ctx, w, err)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
res, err := project.Find(ctx, claims, p.MasterDB, req)
|
res, err := h.Repository.Find(ctx, claims, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -134,7 +133,7 @@ func (p *Project) Find(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects/{id} [get]
|
// @Router /projects/{id} [get]
|
||||||
func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -151,7 +150,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
includeArchived = b
|
includeArchived = b
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := project.Read(ctx, claims, p.MasterDB, project.ProjectReadRequest{
|
res, err := h.Repository.Read(ctx, claims, project.ProjectReadRequest{
|
||||||
ID: params["id"],
|
ID: params["id"],
|
||||||
IncludeArchived: includeArchived,
|
IncludeArchived: includeArchived,
|
||||||
})
|
})
|
||||||
@ -182,7 +181,7 @@ func (p *Project) Read(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects [post]
|
// @Router /projects [post]
|
||||||
func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -201,7 +200,7 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := project.Create(ctx, claims, p.MasterDB, req, v.Now)
|
res, err := h.Repository.Create(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -232,7 +231,7 @@ func (p *Project) Create(ctx context.Context, w http.ResponseWriter, r *http.Req
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects [patch]
|
// @Router /projects [patch]
|
||||||
func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -251,7 +250,7 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = project.Update(ctx, claims, p.MasterDB, req, v.Now)
|
err = h.Repository.Update(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -283,7 +282,7 @@ func (p *Project) Update(ctx context.Context, w http.ResponseWriter, r *http.Req
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects/archive [patch]
|
// @Router /projects/archive [patch]
|
||||||
func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -302,7 +301,7 @@ func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Re
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = project.Archive(ctx, claims, p.MasterDB, req, v.Now)
|
err = h.Repository.Archive(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -334,13 +333,13 @@ func (p *Project) Archive(ctx context.Context, w http.ResponseWriter, r *http.Re
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /projects/{id} [delete]
|
// @Router /projects/{id} [delete]
|
||||||
func (p *Project) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Project) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, err := auth.ClaimsFromContext(ctx)
|
claims, err := auth.ClaimsFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = project.Delete(ctx, claims, p.MasterDB,
|
err = h.Repository.Delete(ctx, claims,
|
||||||
project.ProjectDeleteRequest{ID: params["id"]})
|
project.ProjectDeleteRequest{ID: params["id"]})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
|
@ -1,122 +1,134 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/account"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
|
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
|
||||||
saasSwagger "geeks-accelerator/oss/saas-starter-kit/internal/mid/saas-swagger"
|
saasSwagger "geeks-accelerator/oss/saas-starter-kit/internal/mid/saas-swagger"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
|
||||||
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
||||||
_ "geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
_ "geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type AppContext struct {
|
type AppContext struct {
|
||||||
Log *log.Logger
|
Log *log.Logger
|
||||||
Env webcontext.Env
|
Env webcontext.Env
|
||||||
Repo *user.Repository
|
MasterDB *sqlx.DB
|
||||||
MasterDB *sqlx.DB
|
Redis *redis.Client
|
||||||
Redis *redis.Client
|
UserRepo *user.Repository
|
||||||
Authenticator *auth.Authenticator
|
UserAccountRepo *user_account.Repository
|
||||||
PreAppMiddleware []web.Middleware
|
AccountRepo *account.Repository
|
||||||
|
AccountPrefRepo *account_preference.Repository
|
||||||
|
AuthRepo *user_auth.Repository
|
||||||
|
SignupRepo *signup.Repository
|
||||||
|
InviteRepo *invite.Repository
|
||||||
|
ProjectRepo *project.Repository
|
||||||
|
Authenticator *auth.Authenticator
|
||||||
|
PreAppMiddleware []web.Middleware
|
||||||
PostAppMiddleware []web.Middleware
|
PostAppMiddleware []web.Middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// API returns a handler for a set of routes.
|
// API returns a handler for a set of routes.
|
||||||
func API(shutdown chan os.Signal, appContext *AppContext ) http.Handler {
|
func API(shutdown chan os.Signal, appCtx *AppContext) http.Handler {
|
||||||
|
|
||||||
// Include the pre middlewares first.
|
// Include the pre middlewares first.
|
||||||
middlewares := appContext.PreAppMiddleware
|
middlewares := appCtx.PreAppMiddleware
|
||||||
|
|
||||||
// Define app middlewares applied to all requests.
|
// Define app middlewares applied to all requests.
|
||||||
middlewares = append(middlewares,
|
middlewares = append(middlewares,
|
||||||
mid.Trace(),
|
mid.Trace(),
|
||||||
mid.Logger(appContext.Log),
|
mid.Logger(appCtx.Log),
|
||||||
mid.Errors(appContext.Log, nil),
|
mid.Errors(appCtx.Log, nil),
|
||||||
mid.Metrics(),
|
mid.Metrics(),
|
||||||
mid.Panics())
|
mid.Panics())
|
||||||
|
|
||||||
// Append any global middlewares that should be included after the app middlewares.
|
// Append any global middlewares that should be included after the app middlewares.
|
||||||
if len(appContext.PostAppMiddleware) > 0 {
|
if len(appCtx.PostAppMiddleware) > 0 {
|
||||||
middlewares = append(middlewares, appContext.PostAppMiddleware...)
|
middlewares = append(middlewares, appCtx.PostAppMiddleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the web.App which holds all routes as well as common Middleware.
|
// Construct the web.App which holds all routes as well as common Middleware.
|
||||||
app := web.NewApp(shutdown, appContext.Log, appContext.Env, middlewares...)
|
app := web.NewApp(shutdown, appCtx.Log, appCtx.Env, middlewares...)
|
||||||
|
|
||||||
// Register health check endpoint. This route is not authenticated.
|
// Register health check endpoint. This route is not authenticated.
|
||||||
check := Check{
|
check := Check{
|
||||||
MasterDB: appContext.MasterDB,
|
MasterDB: appCtx.MasterDB,
|
||||||
Redis: appContext.Redis,
|
Redis: appCtx.Redis,
|
||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/health", check.Health)
|
app.Handle("GET", "/v1/health", check.Health)
|
||||||
app.Handle("GET", "/ping", check.Ping)
|
app.Handle("GET", "/ping", check.Ping)
|
||||||
|
|
||||||
|
// Register example endpoints.
|
||||||
|
ex := Example{
|
||||||
|
Project: appCtx.ProjectRepo,
|
||||||
|
}
|
||||||
|
app.Handle("GET", "/v1/examples/error-response", ex.ErrorResponse)
|
||||||
|
|
||||||
// Register user management and authentication endpoints.
|
// Register user management and authentication endpoints.
|
||||||
u := User{
|
u := User{
|
||||||
MasterDB: appContext.MasterDB,
|
Repository: appCtx.UserRepo,
|
||||||
TokenGenerator: authenticator,
|
Auth: appCtx.AuthRepo,
|
||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/users", u.Find, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/users", u.Find, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("POST", "/v1/users", u.Create, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("POST", "/v1/users", u.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("GET", "/v1/users/:id", u.Read, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/users/:id", u.Read, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/users", u.Update, mid.AuthenticateHeader(authenticator))
|
app.Handle("PATCH", "/v1/users", u.Update, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/users/password", u.UpdatePassword, mid.AuthenticateHeader(authenticator))
|
app.Handle("PATCH", "/v1/users/password", u.UpdatePassword, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/users/archive", u.Archive, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/users/archive", u.Archive, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("DELETE", "/v1/users/:id", u.Delete, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("DELETE", "/v1/users/:id", u.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("PATCH", "/v1/users/switch-account/:account_id", u.SwitchAccount, mid.AuthenticateHeader(authenticator))
|
app.Handle("PATCH", "/v1/users/switch-account/:account_id", u.SwitchAccount, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
|
|
||||||
// This route is not authenticated
|
// This route is not authenticated
|
||||||
app.Handle("POST", "/v1/oauth/token", u.Token)
|
app.Handle("POST", "/v1/oauth/token", u.Token)
|
||||||
|
|
||||||
// Register user account management endpoints.
|
// Register user account management endpoints.
|
||||||
ua := UserAccount{
|
ua := UserAccount{
|
||||||
MasterDB: masterDB,
|
Repository: appCtx.UserAccountRepo,
|
||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/user_accounts", ua.Find, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/user_accounts", ua.Find, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("POST", "/v1/user_accounts", ua.Create, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("POST", "/v1/user_accounts", ua.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("GET", "/v1/user_accounts/:user_id/:account_id", ua.Read, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/user_accounts/:user_id/:account_id", ua.Read, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/user_accounts", ua.Update, mid.AuthenticateHeader(authenticator))
|
app.Handle("PATCH", "/v1/user_accounts", ua.Update, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/user_accounts/archive", ua.Archive, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/user_accounts/archive", ua.Archive, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("DELETE", "/v1/user_accounts", ua.Delete, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("DELETE", "/v1/user_accounts", ua.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
// Register account endpoints.
|
// Register account endpoints.
|
||||||
a := Account{
|
a := Account{
|
||||||
MasterDB: masterDB,
|
Repository: appCtx.AccountRepo,
|
||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/accounts/:id", a.Read, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/accounts/:id", a.Read, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/accounts", a.Update, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/accounts", a.Update, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
// Register signup endpoints.
|
// Register signup endpoints.
|
||||||
s := Signup{
|
s := Signup{
|
||||||
MasterDB: masterDB,
|
Repository: appCtx.SignupRepo,
|
||||||
}
|
}
|
||||||
app.Handle("POST", "/v1/signup", s.Signup)
|
app.Handle("POST", "/v1/signup", s.Signup)
|
||||||
|
|
||||||
// Register project.
|
// Register project.
|
||||||
p := Project{
|
p := Project{
|
||||||
MasterDB: masterDB,
|
Repository: appCtx.ProjectRepo,
|
||||||
}
|
}
|
||||||
app.Handle("GET", "/v1/projects", p.Find, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/projects", p.Find, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("POST", "/v1/projects", p.Create, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("POST", "/v1/projects", p.Create, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("GET", "/v1/projects/:id", p.Read, mid.AuthenticateHeader(authenticator))
|
app.Handle("GET", "/v1/projects/:id", p.Read, mid.AuthenticateHeader(appCtx.Authenticator))
|
||||||
app.Handle("PATCH", "/v1/projects", p.Update, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/projects", p.Update, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("PATCH", "/v1/projects/archive", p.Archive, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/projects/archive", p.Archive, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.AuthenticateHeader(appCtx.Authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
app.Handle("GET", "/v1/examples/error-response", ExampleErrorResponse)
|
|
||||||
|
|
||||||
// Register swagger documentation.
|
// Register swagger documentation.
|
||||||
// TODO: Add authentication. Current authenticator requires an Authorization header
|
// TODO: Add authentication. Current authenticator requires an Authorization header
|
||||||
@ -127,36 +139,6 @@ func API(shutdown chan os.Signal, appContext *AppContext ) http.Handler {
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleErrorResponse returns example error messages.
|
|
||||||
func ExampleErrorResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
|
||||||
v, err := webcontext.ContextValues(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if qv := r.URL.Query().Get("test-validation-error"); qv != "" {
|
|
||||||
_, err := project.Create(ctx, auth.Claims{}, nil, project.ProjectCreateRequest{}, v.Now)
|
|
||||||
return web.RespondJsonError(ctx, w, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if qv := r.URL.Query().Get("test-web-error"); qv != "" {
|
|
||||||
terr := errors.New("Some random error")
|
|
||||||
terr = errors.WithMessage(terr, "Actual error message")
|
|
||||||
rerr := weberror.NewError(ctx, terr, http.StatusBadRequest).(*weberror.Error)
|
|
||||||
rerr.Message = "Test Web Error Message"
|
|
||||||
return web.RespondJsonError(ctx, w, rerr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if qv := r.URL.Query().Get("test-error"); qv != "" {
|
|
||||||
terr := errors.New("Test error")
|
|
||||||
terr = errors.WithMessage(terr, "Error message")
|
|
||||||
return web.RespondJsonError(ctx, w, terr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types godoc
|
// Types godoc
|
||||||
// @Summary List of types.
|
// @Summary List of types.
|
||||||
// @Param data body weberror.FieldError false "Field Error"
|
// @Param data body weberror.FieldError false "Field Error"
|
||||||
|
@ -10,14 +10,13 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signup represents the Signup API method handler set.
|
// Signup represents the Signup API method handler set.
|
||||||
type Signup struct {
|
type Signup struct {
|
||||||
MasterDB *sqlx.DB
|
*signup.Repository
|
||||||
|
|
||||||
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||||
}
|
}
|
||||||
@ -33,7 +32,7 @@ type Signup struct {
|
|||||||
// @Failure 400 {object} weberror.ErrorResponse
|
// @Failure 400 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /signup [post]
|
// @Router /signup [post]
|
||||||
func (c *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -50,7 +49,7 @@ func (c *Signup) Signup(ctx context.Context, w http.ResponseWriter, r *http.Requ
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := signup.Signup(ctx, claims, c.MasterDB, req, v.Now)
|
res, err := h.Repository.Signup(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch errors.Cause(err) {
|
switch errors.Cause(err) {
|
||||||
case account.ErrForbidden:
|
case account.ErrForbidden:
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
@ -24,8 +23,8 @@ var sessionTtl = time.Hour * 24
|
|||||||
|
|
||||||
// User represents the User API method handler set.
|
// User represents the User API method handler set.
|
||||||
type User struct {
|
type User struct {
|
||||||
MasterDB *sqlx.DB
|
*user.Repository
|
||||||
TokenGenerator user_auth.TokenGenerator
|
Auth *user_auth.Repository
|
||||||
|
|
||||||
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||||
}
|
}
|
||||||
@ -47,7 +46,7 @@ type User struct {
|
|||||||
// @Failure 400 {object} weberror.ErrorResponse
|
// @Failure 400 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users [get]
|
// @Router /users [get]
|
||||||
func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -114,7 +113,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
// return web.RespondJsonError(ctx, w, err)
|
// return web.RespondJsonError(ctx, w, err)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
res, err := user.Find(ctx, claims, u.MasterDB, req)
|
res, err := h.Repository.Find(ctx, claims, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -140,7 +139,7 @@ func (u *User) Find(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users/{id} [get]
|
// @Router /users/{id} [get]
|
||||||
func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -157,7 +156,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
includeArchived = b
|
includeArchived = b
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := user.Read(ctx, claims, u.MasterDB, user.UserReadRequest{
|
res, err := h.Repository.Read(ctx, claims, user.UserReadRequest{
|
||||||
ID: params["id"],
|
ID: params["id"],
|
||||||
IncludeArchived: includeArchived,
|
IncludeArchived: includeArchived,
|
||||||
})
|
})
|
||||||
@ -187,7 +186,7 @@ func (u *User) Read(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users [post]
|
// @Router /users [post]
|
||||||
func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -206,7 +205,7 @@ func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := user.Create(ctx, claims, u.MasterDB, req, v.Now)
|
res, err := h.Repository.Create(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -238,7 +237,7 @@ func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users [patch]
|
// @Router /users [patch]
|
||||||
func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -257,7 +256,7 @@ func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user.Update(ctx, claims, u.MasterDB, req, v.Now)
|
err = h.Repository.Update(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -289,7 +288,7 @@ func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users/password [patch]
|
// @Router /users/password [patch]
|
||||||
func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -308,7 +307,7 @@ func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *htt
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user.UpdatePassword(ctx, claims, u.MasterDB, req, v.Now)
|
err = h.Repository.UpdatePassword(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -342,7 +341,7 @@ func (u *User) UpdatePassword(ctx context.Context, w http.ResponseWriter, r *htt
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users/archive [patch]
|
// @Router /users/archive [patch]
|
||||||
func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -361,7 +360,7 @@ func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user.Archive(ctx, claims, u.MasterDB, req, v.Now)
|
err = h.Repository.Archive(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -393,13 +392,13 @@ func (u *User) Archive(ctx context.Context, w http.ResponseWriter, r *http.Reque
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users/{id} [delete]
|
// @Router /users/{id} [delete]
|
||||||
func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, err := auth.ClaimsFromContext(ctx)
|
claims, err := auth.ClaimsFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user.Delete(ctx, claims, u.MasterDB,
|
err = h.Repository.Delete(ctx, claims,
|
||||||
user.UserDeleteRequest{ID: params["id"]})
|
user.UserDeleteRequest{ID: params["id"]})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
@ -432,7 +431,7 @@ func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Reques
|
|||||||
// @Failure 401 {object} weberror.ErrorResponse
|
// @Failure 401 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /users/switch-account/{account_id} [patch]
|
// @Router /users/switch-account/{account_id} [patch]
|
||||||
func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -443,7 +442,7 @@ func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, err := user_auth.SwitchAccount(ctx, u.MasterDB, u.TokenGenerator, claims, user_auth.SwitchAccountRequest{
|
tkn, err := h.Auth.SwitchAccount(ctx, claims, user_auth.SwitchAccountRequest{
|
||||||
AccountID: params["account_id"],
|
AccountID: params["account_id"],
|
||||||
}, sessionTtl, v.Now)
|
}, sessionTtl, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -479,7 +478,7 @@ func (u *User) SwitchAccount(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
// @Failure 401 {object} weberror.ErrorResponse
|
// @Failure 401 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /oauth/token [post]
|
// @Router /oauth/token [post]
|
||||||
func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -534,7 +533,7 @@ func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request
|
|||||||
scopes = strings.Split(qv, ",")
|
scopes = strings.Split(qv, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, err := user_auth.Authenticate(ctx, u.MasterDB, u.TokenGenerator, authReq, sessionTtl, v.Now, scopes...)
|
tkn, err := h.Auth.Authenticate(ctx, authReq, sessionTtl, v.Now, scopes...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
|
@ -11,14 +11,13 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserAccount represents the UserAccount API method handler set.
|
// UserAccount represents the UserAccount API method handler set.
|
||||||
type UserAccount struct {
|
type UserAccount struct {
|
||||||
MasterDB *sqlx.DB
|
*user_account.Repository
|
||||||
|
|
||||||
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
|
||||||
}
|
}
|
||||||
@ -41,7 +40,7 @@ type UserAccount struct {
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts [get]
|
// @Router /user_accounts [get]
|
||||||
func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -108,7 +107,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||||||
// return web.RespondJsonError(ctx, w, err)
|
// return web.RespondJsonError(ctx, w, err)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
res, err := user_account.Find(ctx, claims, u.MasterDB, req)
|
res, err := h.Repository.Find(ctx, claims, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -134,7 +133,7 @@ func (u *UserAccount) Find(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts/{user_id}/{account_id} [get]
|
// @Router /user_accounts/{user_id}/{account_id} [get]
|
||||||
func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
claims, ok := ctx.Value(auth.Key).(auth.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("claims missing from context")
|
return errors.New("claims missing from context")
|
||||||
@ -151,7 +150,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||||||
includeArchived = b
|
includeArchived = b
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := user_account.Read(ctx, claims, u.MasterDB, user_account.UserAccountReadRequest{
|
res, err := h.Repository.Read(ctx, claims, user_account.UserAccountReadRequest{
|
||||||
UserID: params["user_id"],
|
UserID: params["user_id"],
|
||||||
AccountID: params["account_id"],
|
AccountID: params["account_id"],
|
||||||
IncludeArchived: includeArchived,
|
IncludeArchived: includeArchived,
|
||||||
@ -183,7 +182,7 @@ func (u *UserAccount) Read(ctx context.Context, w http.ResponseWriter, r *http.R
|
|||||||
// @Failure 404 {object} weberror.ErrorResponse
|
// @Failure 404 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts [post]
|
// @Router /user_accounts [post]
|
||||||
func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -202,7 +201,7 @@ func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := user_account.Create(ctx, claims, u.MasterDB, req, v.Now)
|
res, err := h.Repository.Create(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -234,7 +233,7 @@ func (u *UserAccount) Create(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts [patch]
|
// @Router /user_accounts [patch]
|
||||||
func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -253,7 +252,7 @@ func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user_account.Update(ctx, claims, u.MasterDB, req, v.Now)
|
err = h.Repository.Update(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -285,7 +284,7 @@ func (u *UserAccount) Update(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts/archive [patch]
|
// @Router /user_accounts/archive [patch]
|
||||||
func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
v, err := webcontext.ContextValues(ctx)
|
v, err := webcontext.ContextValues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -304,7 +303,7 @@ func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *htt
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user_account.Archive(ctx, claims, u.MasterDB, req, v.Now)
|
err = h.Repository.Archive(ctx, claims, req, v.Now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
@ -336,7 +335,7 @@ func (u *UserAccount) Archive(ctx context.Context, w http.ResponseWriter, r *htt
|
|||||||
// @Failure 403 {object} weberror.ErrorResponse
|
// @Failure 403 {object} weberror.ErrorResponse
|
||||||
// @Failure 500 {object} weberror.ErrorResponse
|
// @Failure 500 {object} weberror.ErrorResponse
|
||||||
// @Router /user_accounts [delete]
|
// @Router /user_accounts [delete]
|
||||||
func (u *UserAccount) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *UserAccount) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
claims, err := auth.ClaimsFromContext(ctx)
|
claims, err := auth.ClaimsFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -350,7 +349,7 @@ func (u *UserAccount) Delete(ctx context.Context, w http.ResponseWriter, r *http
|
|||||||
return web.RespondJsonError(ctx, w, err)
|
return web.RespondJsonError(ctx, w, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = user_account.Delete(ctx, claims, u.MasterDB, req)
|
err = h.Repository.Delete(ctx, claims, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cause := errors.Cause(err)
|
cause := errors.Cause(err)
|
||||||
switch cause {
|
switch cause {
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"expvar"
|
"expvar"
|
||||||
"fmt"
|
"fmt"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -21,18 +20,30 @@ import (
|
|||||||
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/docs"
|
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/docs"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/handlers"
|
"geeks-accelerator/oss/saas-starter-kit/cmd/web-api/handlers"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/account"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
|
"geeks-accelerator/oss/saas-starter-kit/internal/mid"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/devops"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/devops"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/flag"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/flag"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/notify"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project_route"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_auth"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis"
|
||||||
|
"github.com/gorilla/securecookie"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/crypto/acme"
|
"golang.org/x/crypto/acme"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws"
|
awstrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/aws/aws-sdk-go/aws"
|
||||||
@ -66,10 +77,9 @@ func main() {
|
|||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Logging
|
// Logging
|
||||||
log.SetFlags(log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile)
|
||||||
log.SetPrefix(service+" : ")
|
log.SetPrefix(service + " : ")
|
||||||
log := log.New(os.Stdout, log.Prefix() , log.Flags())
|
log := log.New(os.Stdout, log.Prefix(), log.Flags())
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Configuration
|
// Configuration
|
||||||
@ -87,16 +97,21 @@ func main() {
|
|||||||
DisableHTTP2 bool `default:"false" envconfig:"DISABLE_HTTP2"`
|
DisableHTTP2 bool `default:"false" envconfig:"DISABLE_HTTP2"`
|
||||||
}
|
}
|
||||||
Service struct {
|
Service struct {
|
||||||
Name string `default:"web-api" envconfig:"NAME"`
|
Name string `default:"web-api" envconfig:"SERVICE"`
|
||||||
Project string `default:"" envconfig:"PROJECT"`
|
|
||||||
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://api.example.saasstartupkit.com"`
|
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://api.example.saasstartupkit.com"`
|
||||||
HostNames []string `envconfig:"HOST_NAMES" example:"alternative-subdomain.example.saasstartupkit.com"`
|
HostNames []string `envconfig:"HOST_NAMES" example:"alternative-subdomain.example.saasstartupkit.com"`
|
||||||
EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"`
|
EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"`
|
||||||
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
||||||
WebAppBaseUrl string `default:"http://127.0.0.1:3000" envconfig:"WEB_APP_BASE_URL" example:"www.example.saasstartupkit.com"`
|
|
||||||
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
||||||
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
||||||
}
|
}
|
||||||
|
Project struct {
|
||||||
|
Name string `default:"" envconfig:"PROJECT"`
|
||||||
|
SharedTemplateDir string `default:"../../resources/templates/shared" envconfig:"SHARED_TEMPLATE_DIR"`
|
||||||
|
SharedSecretKey string `default:"" envconfig:"SHARED_SECRET_KEY"`
|
||||||
|
EmailSender string `default:"test@example.saasstartupkit.com" envconfig:"EMAIL_SENDER"`
|
||||||
|
WebAppBaseUrl string `default:"http://127.0.0.1:3000" envconfig:"WEB_APP_BASE_URL" example:"www.example.saasstartupkit.com"`
|
||||||
|
}
|
||||||
Redis struct {
|
Redis struct {
|
||||||
Host string `default:":6379" envconfig:"HOST"`
|
Host string `default:":6379" envconfig:"HOST"`
|
||||||
DB int `default:"1" envconfig:"DB"`
|
DB int `default:"1" envconfig:"DB"`
|
||||||
@ -185,8 +200,8 @@ func main() {
|
|||||||
// deployments and distributed to each instance of the service running.
|
// deployments and distributed to each instance of the service running.
|
||||||
if cfg.Aws.SecretsManagerConfigPrefix == "" {
|
if cfg.Aws.SecretsManagerConfigPrefix == "" {
|
||||||
var pts []string
|
var pts []string
|
||||||
if cfg.Service.Project != "" {
|
if cfg.Project.Name != "" {
|
||||||
pts = append(pts, cfg.Service.Project)
|
pts = append(pts, cfg.Project.Name)
|
||||||
}
|
}
|
||||||
pts = append(pts, cfg.Env, cfg.Service.Name)
|
pts = append(pts, cfg.Env, cfg.Service.Name)
|
||||||
|
|
||||||
@ -276,6 +291,37 @@ func main() {
|
|||||||
awsSession = awstrace.WrapSession(awsSession)
|
awsSession = awstrace.WrapSession(awsSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Shared Secret Key used for encrypting sessions and links.
|
||||||
|
|
||||||
|
// Set the secret key if not provided in the config.
|
||||||
|
if cfg.Project.SharedSecretKey == "" {
|
||||||
|
|
||||||
|
// AWS secrets manager ID for storing the session key. This is optional and only will be used
|
||||||
|
// if a valid AWS session is provided.
|
||||||
|
secretID := filepath.Join(cfg.Aws.SecretsManagerConfigPrefix, "sharedSecretKey")
|
||||||
|
|
||||||
|
// If AWS is enabled, check the Secrets Manager for the session key.
|
||||||
|
if awsSession != nil {
|
||||||
|
cfg.Project.SharedSecretKey, err = devops.SecretManagerGetString(awsSession, secretID)
|
||||||
|
if err != nil && errors.Cause(err) != devops.ErrSecreteNotFound {
|
||||||
|
log.Fatalf("main : Session : %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the session key is still empty, generate a new key.
|
||||||
|
if cfg.Project.SharedSecretKey == "" {
|
||||||
|
cfg.Project.SharedSecretKey = string(securecookie.GenerateRandomKey(32))
|
||||||
|
|
||||||
|
if awsSession != nil {
|
||||||
|
err = devops.SecretManagerPutString(awsSession, secretID, cfg.Project.SharedSecretKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("main : Session : %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start Redis
|
// Start Redis
|
||||||
// Ensure the eviction policy on the redis cluster is set correctly.
|
// Ensure the eviction policy on the redis cluster is set correctly.
|
||||||
@ -346,6 +392,31 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer masterDb.Close()
|
defer masterDb.Close()
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Notify Email
|
||||||
|
var notifyEmail notify.Email
|
||||||
|
if awsSession != nil {
|
||||||
|
// Send emails with AWS SES. Alternative to use SMTP with notify.NewEmailSmtp.
|
||||||
|
notifyEmail, err = notify.NewEmailAws(awsSession, cfg.Project.SharedTemplateDir, cfg.Project.EmailSender)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("main : Notify Email : %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = notifyEmail.Verify()
|
||||||
|
if err != nil {
|
||||||
|
switch errors.Cause(err) {
|
||||||
|
case notify.ErrAwsSesIdentityNotVerified:
|
||||||
|
log.Printf("main : Notify Email : %s\n", err)
|
||||||
|
case notify.ErrAwsSesSendingDisabled:
|
||||||
|
log.Printf("main : Notify Email : %s\n", err)
|
||||||
|
default:
|
||||||
|
log.Fatalf("main : Notify Email Verify : %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notifyEmail = notify.NewEmailDisabled()
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Init new Authenticator
|
// Init new Authenticator
|
||||||
var authenticator *auth.Authenticator
|
var authenticator *auth.Authenticator
|
||||||
@ -360,11 +431,41 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Load middlewares that need to be configured specific for the service.
|
// Init repositories and AppContext
|
||||||
var serviceMiddlewares = []web.Middleware{
|
|
||||||
mid.Translator(webcontext.UniversalTranslator()),
|
projectRoute, err := project_route.New(cfg.Service.BaseUrl, cfg.Project.WebAppBaseUrl)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("main : project routes : %+v", cfg.Service.BaseUrl, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usrRepo := user.NewRepository(masterDb, projectRoute.UserResetPassword, notifyEmail, cfg.Project.SharedSecretKey)
|
||||||
|
usrAccRepo := user_account.NewRepository(masterDb)
|
||||||
|
accRepo := account.NewRepository(masterDb)
|
||||||
|
accPrefRepo := account_preference.NewRepository(masterDb)
|
||||||
|
authRepo := user_auth.NewRepository(masterDb, authenticator, usrRepo, usrAccRepo, accPrefRepo)
|
||||||
|
signupRepo := signup.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo)
|
||||||
|
inviteRepo := invite.NewRepository(masterDb, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, cfg.Project.SharedSecretKey)
|
||||||
|
prjRepo := project.NewRepository(masterDb)
|
||||||
|
|
||||||
|
appCtx := &handlers.AppContext{
|
||||||
|
Log: log,
|
||||||
|
Env: cfg.Env,
|
||||||
|
MasterDB: masterDb,
|
||||||
|
Redis: redisClient,
|
||||||
|
UserRepo: usrRepo,
|
||||||
|
UserAccountRepo: usrAccRepo,
|
||||||
|
AccountRepo: accRepo,
|
||||||
|
AccountPrefRepo: accPrefRepo,
|
||||||
|
AuthRepo: authRepo,
|
||||||
|
SignupRepo: signupRepo,
|
||||||
|
InviteRepo: inviteRepo,
|
||||||
|
ProjectRepo: prjRepo,
|
||||||
|
Authenticator: authenticator,
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Load middlewares that need to be configured specific for the service.
|
||||||
|
|
||||||
// Init redirect middleware to ensure all requests go to the primary domain contained in the base URL.
|
// Init redirect middleware to ensure all requests go to the primary domain contained in the base URL.
|
||||||
if primaryServiceHost != "127.0.0.1" && primaryServiceHost != "localhost" {
|
if primaryServiceHost != "127.0.0.1" && primaryServiceHost != "localhost" {
|
||||||
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
redirect := mid.DomainNameRedirect(mid.DomainNameRedirectConfig{
|
||||||
@ -380,9 +481,12 @@ func main() {
|
|||||||
DomainName: primaryServiceHost,
|
DomainName: primaryServiceHost,
|
||||||
HTTPSEnabled: cfg.Service.EnableHTTPS,
|
HTTPSEnabled: cfg.Service.EnableHTTPS,
|
||||||
})
|
})
|
||||||
serviceMiddlewares = append(serviceMiddlewares, redirect)
|
appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, redirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the translator middleware for localization.
|
||||||
|
appCtx.PostAppMiddleware = append(appCtx.PostAppMiddleware, mid.Translator(webcontext.UniversalTranslator()))
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start Tracing Support
|
// Start Tracing Support
|
||||||
th := fmt.Sprintf("%s:%d", cfg.Trace.Host, cfg.Trace.Port)
|
th := fmt.Sprintf("%s:%d", cfg.Trace.Host, cfg.Trace.Port)
|
||||||
@ -443,7 +547,7 @@ func main() {
|
|||||||
if cfg.HTTP.Host != "" {
|
if cfg.HTTP.Host != "" {
|
||||||
api := http.Server{
|
api := http.Server{
|
||||||
Addr: cfg.HTTP.Host,
|
Addr: cfg.HTTP.Host,
|
||||||
Handler: handlers.API(shutdown, log, cfg.Env, masterDb, redisClient, authenticator, serviceMiddlewares...),
|
Handler: handlers.API(shutdown, appCtx),
|
||||||
ReadTimeout: cfg.HTTP.ReadTimeout,
|
ReadTimeout: cfg.HTTP.ReadTimeout,
|
||||||
WriteTimeout: cfg.HTTP.WriteTimeout,
|
WriteTimeout: cfg.HTTP.WriteTimeout,
|
||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
@ -460,7 +564,7 @@ func main() {
|
|||||||
if cfg.HTTPS.Host != "" {
|
if cfg.HTTPS.Host != "" {
|
||||||
api := http.Server{
|
api := http.Server{
|
||||||
Addr: cfg.HTTPS.Host,
|
Addr: cfg.HTTPS.Host,
|
||||||
Handler: handlers.API(shutdown, log, cfg.Env, masterDb, redisClient, authenticator, serviceMiddlewares...),
|
Handler: handlers.API(shutdown, appCtx),
|
||||||
ReadTimeout: cfg.HTTPS.ReadTimeout,
|
ReadTimeout: cfg.HTTPS.ReadTimeout,
|
||||||
WriteTimeout: cfg.HTTPS.WriteTimeout,
|
WriteTimeout: cfg.HTTPS.WriteTimeout,
|
||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
|
@ -27,7 +27,7 @@ func mockProjectCreateRequest(accountID string) project.ProjectCreateRequest {
|
|||||||
// mockProject creates a new project for testing and associates it with the supplied account ID.
|
// mockProject creates a new project for testing and associates it with the supplied account ID.
|
||||||
func newMockProject(accountID string) *project.Project {
|
func newMockProject(accountID string) *project.Project {
|
||||||
req := mockProjectCreateRequest(accountID)
|
req := mockProjectCreateRequest(accountID)
|
||||||
p, err := project.Create(tests.Context(), auth.Claims{}, test.MasterDB, req, time.Now().UTC().AddDate(-1, -1, -1))
|
p, err := appCtx.ProjectRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,13 @@ func mockSignupRequest() signup.SignupRequest {
|
|||||||
func newMockSignup() mockSignup {
|
func newMockSignup() mockSignup {
|
||||||
req := mockSignupRequest()
|
req := mockSignupRequest()
|
||||||
now := time.Now().UTC().AddDate(-1, -1, -1)
|
now := time.Now().UTC().AddDate(-1, -1, -1)
|
||||||
s, err := signup.Signup(tests.Context(), auth.Claims{}, test.MasterDB, req, now)
|
s, err := appCtx.SignupRepo.Signup(tests.Context(), auth.Claims{}, req, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expires := time.Now().UTC().Sub(s.User.CreatedAt) + time.Hour
|
expires := time.Now().UTC().Sub(s.User.CreatedAt) + time.Hour
|
||||||
tkn, err := user_auth.Authenticate(tests.Context(), test.MasterDB, authenticator, user_auth.AuthenticateRequest{
|
tkn, err := appCtx.AuthRepo.Authenticate(tests.Context(), user_auth.AuthenticateRequest{
|
||||||
Email: req.User.Email,
|
Email: req.User.Email,
|
||||||
Password: req.User.Password,
|
Password: req.User.Password,
|
||||||
}, expires, now)
|
}, expires, now)
|
||||||
|
@ -5,6 +5,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/notify"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project_route"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account/invite"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -31,9 +36,12 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var a http.Handler
|
var (
|
||||||
var test *tests.Test
|
a http.Handler
|
||||||
var authenticator *auth.Authenticator
|
test *tests.Test
|
||||||
|
authenticator *auth.Authenticator
|
||||||
|
appCtx *handlers.AppContext
|
||||||
|
)
|
||||||
|
|
||||||
// Information about the users we have created for testing.
|
// Information about the users we have created for testing.
|
||||||
type roleTest struct {
|
type roleTest struct {
|
||||||
@ -84,18 +92,51 @@ func testMain(m *testing.M) int {
|
|||||||
|
|
||||||
log := test.Log
|
log := test.Log
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(ioutil.Discard)
|
||||||
a = handlers.API(shutdown, log, webcontext.Env_Dev, test.MasterDB, nil, authenticator)
|
|
||||||
|
projectRoute, err := project_route.New("http://web-api.com", "http://web-app.com")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyEmail := notify.NewEmailDisabled()
|
||||||
|
|
||||||
|
usrRepo := user.MockRepository(test.MasterDB)
|
||||||
|
usrAccRepo := user_account.NewRepository(test.MasterDB)
|
||||||
|
accRepo := account.NewRepository(test.MasterDB)
|
||||||
|
accPrefRepo := account_preference.NewRepository(test.MasterDB)
|
||||||
|
authRepo := user_auth.NewRepository(test.MasterDB, authenticator, usrRepo, usrAccRepo, accPrefRepo)
|
||||||
|
signupRepo := signup.NewRepository(test.MasterDB, usrRepo, usrAccRepo, accRepo)
|
||||||
|
inviteRepo := invite.NewRepository(test.MasterDB, usrRepo, usrAccRepo, accRepo, projectRoute.UserInviteAccept, notifyEmail, "6368616e676520746869732070613434")
|
||||||
|
prjRepo := project.NewRepository(test.MasterDB)
|
||||||
|
|
||||||
|
appCtx = &handlers.AppContext{
|
||||||
|
Log: log,
|
||||||
|
Env: webcontext.Env_Dev,
|
||||||
|
MasterDB: test.MasterDB,
|
||||||
|
Redis: nil,
|
||||||
|
UserRepo: usrRepo,
|
||||||
|
UserAccountRepo: usrAccRepo,
|
||||||
|
AccountRepo: accRepo,
|
||||||
|
AccountPrefRepo: accPrefRepo,
|
||||||
|
AuthRepo: authRepo,
|
||||||
|
SignupRepo: signupRepo,
|
||||||
|
InviteRepo: inviteRepo,
|
||||||
|
ProjectRepo: prjRepo,
|
||||||
|
Authenticator: authenticator,
|
||||||
|
}
|
||||||
|
|
||||||
|
a = handlers.API(shutdown, appCtx)
|
||||||
|
|
||||||
// Create a new account directly business logic. This creates an
|
// Create a new account directly business logic. This creates an
|
||||||
// initial account and user that we will use for admin validated endpoints.
|
// initial account and user that we will use for admin validated endpoints.
|
||||||
signupReq1 := mockSignupRequest()
|
signupReq1 := mockSignupRequest()
|
||||||
signup1, err := signup.Signup(tests.Context(), auth.Claims{}, test.MasterDB, signupReq1, now)
|
signup1, err := signupRepo.Signup(tests.Context(), auth.Claims{}, signupReq1, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expires := time.Now().UTC().Sub(signup1.User.CreatedAt) + time.Hour
|
expires := time.Now().UTC().Sub(signup1.User.CreatedAt) + time.Hour
|
||||||
adminTkn, err := user_auth.Authenticate(tests.Context(), test.MasterDB, authenticator, user_auth.AuthenticateRequest{
|
adminTkn, err := authRepo.Authenticate(tests.Context(), user_auth.AuthenticateRequest{
|
||||||
Email: signupReq1.User.Email,
|
Email: signupReq1.User.Email,
|
||||||
Password: signupReq1.User.Password,
|
Password: signupReq1.User.Password,
|
||||||
}, expires, now)
|
}, expires, now)
|
||||||
@ -110,7 +151,7 @@ func testMain(m *testing.M) int {
|
|||||||
|
|
||||||
// Create a second account that the first account user should not have access to.
|
// Create a second account that the first account user should not have access to.
|
||||||
signupReq2 := mockSignupRequest()
|
signupReq2 := mockSignupRequest()
|
||||||
signup2, err := signup.Signup(tests.Context(), auth.Claims{}, test.MasterDB, signupReq2, now)
|
signup2, err := signupRepo.Signup(tests.Context(), auth.Claims{}, signupReq2, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -134,12 +175,12 @@ func testMain(m *testing.M) int {
|
|||||||
Password: "akTechFr0n!ier",
|
Password: "akTechFr0n!ier",
|
||||||
PasswordConfirm: "akTechFr0n!ier",
|
PasswordConfirm: "akTechFr0n!ier",
|
||||||
}
|
}
|
||||||
usr, err := user.Create(tests.Context(), adminClaims, test.MasterDB, userReq, now)
|
usr, err := usrRepo.Create(tests.Context(), adminClaims, userReq, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = user_account.Create(tests.Context(), adminClaims, test.MasterDB, user_account.UserAccountCreateRequest{
|
_, err = usrAccRepo.Create(tests.Context(), adminClaims, user_account.UserAccountCreateRequest{
|
||||||
UserID: usr.ID,
|
UserID: usr.ID,
|
||||||
AccountID: signup1.Account.ID,
|
AccountID: signup1.Account.ID,
|
||||||
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
||||||
@ -149,7 +190,7 @@ func testMain(m *testing.M) int {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userTkn, err := user_auth.Authenticate(tests.Context(), test.MasterDB, authenticator, user_auth.AuthenticateRequest{
|
userTkn, err := authRepo.Authenticate(tests.Context(), user_auth.AuthenticateRequest{
|
||||||
Email: usr.Email,
|
Email: usr.Email,
|
||||||
Password: userReq.Password,
|
Password: userReq.Password,
|
||||||
}, expires, now)
|
}, expires, now)
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
)
|
)
|
||||||
@ -22,12 +21,12 @@ import (
|
|||||||
// newMockUserAccount creates a new user user for testing and associates it with the supplied account ID.
|
// newMockUserAccount creates a new user user for testing and associates it with the supplied account ID.
|
||||||
func newMockUserAccount(accountID string, role user_account.UserAccountRole) *user_account.UserAccount {
|
func newMockUserAccount(accountID string, role user_account.UserAccountRole) *user_account.UserAccount {
|
||||||
req := mockUserCreateRequest()
|
req := mockUserCreateRequest()
|
||||||
u, err := user.Create(tests.Context(), auth.Claims{}, test.MasterDB, req, time.Now().UTC().AddDate(-1, -1, -1))
|
u, err := appCtx.UserRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ua, err := user_account.Create(tests.Context(), auth.Claims{}, test.MasterDB, user_account.UserAccountCreateRequest{
|
ua, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
|
||||||
UserID: u.ID,
|
UserID: u.ID,
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Roles: []user_account.UserAccountRole{role},
|
Roles: []user_account.UserAccountRole{role},
|
||||||
@ -65,7 +64,7 @@ func TestUserAccountCRUDAdmin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
||||||
|
|
||||||
newUser, err := user.Create(tests.Context(), auth.Claims{}, test.MasterDB, mockUserCreateRequest(), time.Now().UTC().AddDate(-1, -1, -1))
|
newUser, err := appCtx.UserRepo.Create(tests.Context(), auth.Claims{}, mockUserCreateRequest(), time.Now().UTC().AddDate(-1, -1, -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("\t%s\tCreate new user failed.", tests.Failed)
|
t.Fatalf("\t%s\tCreate new user failed.", tests.Failed)
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,12 @@ func mockUserCreateRequest() user.UserCreateRequest {
|
|||||||
// mockUser creates a new user for testing and associates it with the supplied account ID.
|
// mockUser creates a new user for testing and associates it with the supplied account ID.
|
||||||
func newMockUser(accountID string, role user_account.UserAccountRole) mockUser {
|
func newMockUser(accountID string, role user_account.UserAccountRole) mockUser {
|
||||||
req := mockUserCreateRequest()
|
req := mockUserCreateRequest()
|
||||||
u, err := user.Create(tests.Context(), auth.Claims{}, test.MasterDB, req, time.Now().UTC().AddDate(-1, -1, -1))
|
u, err := appCtx.UserRepo.Create(tests.Context(), auth.Claims{}, req, time.Now().UTC().AddDate(-1, -1, -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = user_account.Create(tests.Context(), auth.Claims{}, test.MasterDB, user_account.UserAccountCreateRequest{
|
_, err = appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
|
||||||
UserID: u.ID,
|
UserID: u.ID,
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Roles: []user_account.UserAccountRole{role},
|
Roles: []user_account.UserAccountRole{role},
|
||||||
@ -126,7 +126,7 @@ func TestUserCRUDAdmin(t *testing.T) {
|
|||||||
t.Logf("\t%s\tReceived expected result.", tests.Success)
|
t.Logf("\t%s\tReceived expected result.", tests.Success)
|
||||||
|
|
||||||
// Only for user creation do we need to do this.
|
// Only for user creation do we need to do this.
|
||||||
_, err := user_account.Create(tests.Context(), auth.Claims{}, test.MasterDB, user_account.UserAccountCreateRequest{
|
_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
|
||||||
UserID: actual.ID,
|
UserID: actual.ID,
|
||||||
AccountID: tr.Account.ID,
|
AccountID: tr.Account.ID,
|
||||||
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
||||||
@ -401,7 +401,7 @@ func TestUserCRUDAdmin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
||||||
|
|
||||||
_, err := user_account.Create(tests.Context(), auth.Claims{}, test.MasterDB, user_account.UserAccountCreateRequest{
|
_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
|
||||||
UserID: tr.User.ID,
|
UserID: tr.User.ID,
|
||||||
AccountID: newAccount.ID,
|
AccountID: newAccount.ID,
|
||||||
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
||||||
@ -805,7 +805,7 @@ func TestUserCRUDUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
t.Logf("\tTest: %s - %s %s", rt.name, rt.method, rt.url)
|
||||||
|
|
||||||
_, err := user_account.Create(tests.Context(), auth.Claims{}, test.MasterDB, user_account.UserAccountCreateRequest{
|
_, err := appCtx.UserAccountRepo.Create(tests.Context(), auth.Claims{}, user_account.UserAccountCreateRequest{
|
||||||
UserID: tr.User.ID,
|
UserID: tr.User.ID,
|
||||||
AccountID: newAccount.ID,
|
AccountID: newAccount.ID,
|
||||||
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"expvar"
|
"expvar"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project_route"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -88,12 +89,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
Service struct {
|
Service struct {
|
||||||
Name string `default:"web-app" envconfig:"NAME"`
|
Name string `default:"web-app" envconfig:"NAME"`
|
||||||
Project string `default:"" envconfig:"PROJECT"`
|
|
||||||
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://example.saasstartupkit.com"`
|
BaseUrl string `default:"" envconfig:"BASE_URL" example:"http://example.saasstartupkit.com"`
|
||||||
HostNames []string `envconfig:"HOST_NAMES" example:"www.example.saasstartupkit.com"`
|
HostNames []string `envconfig:"HOST_NAMES" example:"www.example.saasstartupkit.com"`
|
||||||
EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"`
|
EnableHTTPS bool `default:"false" envconfig:"ENABLE_HTTPS"`
|
||||||
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
TemplateDir string `default:"./templates" envconfig:"TEMPLATE_DIR"`
|
||||||
SharedTemplateDir string `default:"../../resources/templates/shared" envconfig:"SHARED_TEMPLATE_DIR"`
|
|
||||||
StaticFiles struct {
|
StaticFiles struct {
|
||||||
Dir string `default:"./static" envconfig:"STATIC_DIR"`
|
Dir string `default:"./static" envconfig:"STATIC_DIR"`
|
||||||
S3Enabled bool `envconfig:"S3_ENABLED"`
|
S3Enabled bool `envconfig:"S3_ENABLED"`
|
||||||
@ -101,13 +100,17 @@ func main() {
|
|||||||
CloudFrontEnabled bool `envconfig:"CLOUDFRONT_ENABLED"`
|
CloudFrontEnabled bool `envconfig:"CLOUDFRONT_ENABLED"`
|
||||||
ImgResizeEnabled bool `envconfig:"IMG_RESIZE_ENABLED"`
|
ImgResizeEnabled bool `envconfig:"IMG_RESIZE_ENABLED"`
|
||||||
}
|
}
|
||||||
WebApiBaseUrl string `default:"http://127.0.0.1:3001" envconfig:"WEB_API_BASE_URL" example:"http://api.example.saasstartupkit.com"`
|
|
||||||
SessionKey string `default:"" envconfig:"SESSION_KEY"`
|
|
||||||
SessionName string `default:"" envconfig:"SESSION_NAME"`
|
SessionName string `default:"" envconfig:"SESSION_NAME"`
|
||||||
EmailSender string `default:"test@example.saasstartupkit.com" envconfig:"EMAIL_SENDER"`
|
|
||||||
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
|
||||||
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
|
||||||
}
|
}
|
||||||
|
Project struct {
|
||||||
|
Name string `default:"" envconfig:"PROJECT"`
|
||||||
|
SharedTemplateDir string `default:"../../resources/templates/shared" envconfig:"SHARED_TEMPLATE_DIR"`
|
||||||
|
SharedSecretKey string `default:"" envconfig:"SHARED_SECRET_KEY"`
|
||||||
|
EmailSender string `default:"test@example.saasstartupkit.com" envconfig:"EMAIL_SENDER"`
|
||||||
|
WebApiBaseUrl string `default:"http://127.0.0.1:3001" envconfig:"WEB_API_BASE_URL" example:"http://api.example.saasstartupkit.com"`
|
||||||
|
}
|
||||||
Redis struct {
|
Redis struct {
|
||||||
Host string `default:":6379" envconfig:"HOST"`
|
Host string `default:":6379" envconfig:"HOST"`
|
||||||
DB int `default:"1" envconfig:"DB"`
|
DB int `default:"1" envconfig:"DB"`
|
||||||
@ -145,12 +148,6 @@ func main() {
|
|||||||
UseAwsSecretManager bool `default:"false" envconfig:"USE_AWS_SECRET_MANAGER"`
|
UseAwsSecretManager bool `default:"false" envconfig:"USE_AWS_SECRET_MANAGER"`
|
||||||
KeyExpiration time.Duration `default:"3600s" envconfig:"KEY_EXPIRATION"`
|
KeyExpiration time.Duration `default:"3600s" envconfig:"KEY_EXPIRATION"`
|
||||||
}
|
}
|
||||||
STMP struct {
|
|
||||||
Host string `default:"localhost" envconfig:"HOST"`
|
|
||||||
Port int `default:"25" envconfig:"PORT"`
|
|
||||||
User string `default:"" envconfig:"USER"`
|
|
||||||
Pass string `default:"" envconfig:"PASS" json:"-"` // don't print
|
|
||||||
}
|
|
||||||
BuildInfo struct {
|
BuildInfo struct {
|
||||||
CiCommitRefName string `envconfig:"CI_COMMIT_REF_NAME"`
|
CiCommitRefName string `envconfig:"CI_COMMIT_REF_NAME"`
|
||||||
CiCommitShortSha string `envconfig:"CI_COMMIT_SHORT_SHA"`
|
CiCommitShortSha string `envconfig:"CI_COMMIT_SHORT_SHA"`
|
||||||
@ -202,8 +199,8 @@ func main() {
|
|||||||
// deployments and distributed to each instance of the service running.
|
// deployments and distributed to each instance of the service running.
|
||||||
if cfg.Aws.SecretsManagerConfigPrefix == "" {
|
if cfg.Aws.SecretsManagerConfigPrefix == "" {
|
||||||
var pts []string
|
var pts []string
|
||||||
if cfg.Service.Project != "" {
|
if cfg.Project.Name != "" {
|
||||||
pts = append(pts, cfg.Service.Project)
|
pts = append(pts, cfg.Project.Name)
|
||||||
}
|
}
|
||||||
pts = append(pts, cfg.Env, cfg.Service.Name)
|
pts = append(pts, cfg.Env, cfg.Service.Name)
|
||||||
|
|
||||||
@ -293,6 +290,37 @@ func main() {
|
|||||||
awsSession = awstrace.WrapSession(awsSession)
|
awsSession = awstrace.WrapSession(awsSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Shared Secret Key used for encrypting sessions and links.
|
||||||
|
|
||||||
|
// Set the secret key if not provided in the config.
|
||||||
|
if cfg.Project.SharedSecretKey == "" {
|
||||||
|
|
||||||
|
// AWS secrets manager ID for storing the session key. This is optional and only will be used
|
||||||
|
// if a valid AWS session is provided.
|
||||||
|
secretID := filepath.Join(cfg.Aws.SecretsManagerConfigPrefix, "sharedSecretKey")
|
||||||
|
|
||||||
|
// If AWS is enabled, check the Secrets Manager for the session key.
|
||||||
|
if awsSession != nil {
|
||||||
|
cfg.Project.SharedSecretKey, err = devops.SecretManagerGetString(awsSession, secretID)
|
||||||
|
if err != nil && errors.Cause(err) != devops.ErrSecreteNotFound {
|
||||||
|
log.Fatalf("main : Session : %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the session key is still empty, generate a new key.
|
||||||
|
if cfg.Project.SharedSecretKey == "" {
|
||||||
|
cfg.Project.SharedSecretKey = string(securecookie.GenerateRandomKey(32))
|
||||||
|
|
||||||
|
if awsSession != nil {
|
||||||
|
err = devops.SecretManagerPutString(awsSession, secretID, cfg.Service.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("main : Session : %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Start Redis
|
// Start Redis
|
||||||
// Ensure the eviction policy on the redis cluster is set correctly.
|
// Ensure the eviction policy on the redis cluster is set correctly.
|
||||||
@ -367,6 +395,7 @@ func main() {
|
|||||||
// Notify Email
|
// Notify Email
|
||||||
var notifyEmail notify.Email
|
var notifyEmail notify.Email
|
||||||
if awsSession != nil {
|
if awsSession != nil {
|
||||||
|
// Send emails with AWS SES. Alternative to use SMTP with notify.NewEmailSmtp.
|
||||||
notifyEmail, err = notify.NewEmailAws(awsSession, cfg.Service.SharedTemplateDir, cfg.Service.EmailSender)
|
notifyEmail, err = notify.NewEmailAws(awsSession, cfg.Service.SharedTemplateDir, cfg.Service.EmailSender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("main : Notify Email : %+v", err)
|
log.Fatalf("main : Notify Email : %+v", err)
|
||||||
@ -384,15 +413,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
d := gomail.Dialer{
|
notifyEmail = notify.NewEmailDisabled()
|
||||||
Host: cfg.STMP.Host,
|
|
||||||
Port: cfg.STMP.Port,
|
|
||||||
Username: cfg.STMP.User,
|
|
||||||
Password: cfg.STMP.Pass}
|
|
||||||
notifyEmail, err = notify.NewEmailSmtp(d, cfg.Service.SharedTemplateDir, cfg.Service.EmailSender)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("main : Notify Email : %+v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@ -433,46 +454,18 @@ func main() {
|
|||||||
serviceMiddlewares = append(serviceMiddlewares, redirect)
|
serviceMiddlewares = append(serviceMiddlewares, redirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the new session store and append it to the global list of middlewares.
|
||||||
|
|
||||||
// Init session store
|
// Init session store
|
||||||
if cfg.Service.SessionName == "" {
|
if cfg.Service.SessionName == "" {
|
||||||
cfg.Service.SessionName = fmt.Sprintf("%s-session", cfg.Service.Name)
|
cfg.Service.SessionName = fmt.Sprintf("%s-session", cfg.Service.Name)
|
||||||
}
|
}
|
||||||
|
sessionStore := sessions.NewCookieStore([]byte(cfg.Service.SecretKey))
|
||||||
// Set the session key if not provided in the config.
|
|
||||||
if cfg.Service.SessionKey == "" {
|
|
||||||
|
|
||||||
// AWS secrets manager ID for storing the session key. This is optional and only will be used
|
|
||||||
// if a valid AWS session is provided.
|
|
||||||
secretID := filepath.Join(cfg.Aws.SecretsManagerConfigPrefix, "session")
|
|
||||||
|
|
||||||
// If AWS is enabled, check the Secrets Manager for the session key.
|
|
||||||
if awsSession != nil {
|
|
||||||
cfg.Service.SessionKey, err = devops.SecretManagerGetString(awsSession, secretID)
|
|
||||||
if err != nil && errors.Cause(err) != devops.ErrSecreteNotFound {
|
|
||||||
log.Fatalf("main : Session : %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the session key is still empty, generate a new key.
|
|
||||||
if cfg.Service.SessionKey == "" {
|
|
||||||
cfg.Service.SessionKey = string(securecookie.GenerateRandomKey(32))
|
|
||||||
|
|
||||||
if awsSession != nil {
|
|
||||||
err = devops.SecretManagerPutString(awsSession, secretID, cfg.Service.SessionKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("main : Session : %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the new session store and append it to the global list of middlewares.
|
|
||||||
sessionStore := sessions.NewCookieStore([]byte(cfg.Service.SessionKey))
|
|
||||||
serviceMiddlewares = append(serviceMiddlewares, mid.Session(sessionStore, cfg.Service.SessionName))
|
serviceMiddlewares = append(serviceMiddlewares, mid.Session(sessionStore, cfg.Service.SessionName))
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// URL Formatter
|
// URL Formatter
|
||||||
projectRoutes, err := project_routes.New(cfg.Service.WebApiBaseUrl, cfg.Service.BaseUrl)
|
projectRoutes, err := project_route.New(cfg.Service.WebApiBaseUrl, cfg.Service.BaseUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("main : project routes : %+v", cfg.Service.BaseUrl, err)
|
log.Fatalf("main : project routes : %+v", cfg.Service.BaseUrl, err)
|
||||||
}
|
}
|
||||||
@ -926,7 +919,7 @@ func main() {
|
|||||||
if cfg.HTTP.Host != "" {
|
if cfg.HTTP.Host != "" {
|
||||||
api := http.Server{
|
api := http.Server{
|
||||||
Addr: cfg.HTTP.Host,
|
Addr: cfg.HTTP.Host,
|
||||||
Handler: handlers.APP(shutdown, log, cfg.Env, cfg.Service.StaticFiles.Dir, cfg.Service.TemplateDir, masterDb, redisClient, authenticator, projectRoutes, cfg.Service.SessionKey, notifyEmail, renderer, serviceMiddlewares...),
|
Handler: handlers.APP(shutdown, log, cfg.Env, cfg.Service.StaticFiles.Dir, cfg.Service.TemplateDir, masterDb, redisClient, authenticator, projectRoutes, cfg.Service.SecretKey, notifyEmail, renderer, serviceMiddlewares...),
|
||||||
ReadTimeout: cfg.HTTP.ReadTimeout,
|
ReadTimeout: cfg.HTTP.ReadTimeout,
|
||||||
WriteTimeout: cfg.HTTP.WriteTimeout,
|
WriteTimeout: cfg.HTTP.WriteTimeout,
|
||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
@ -943,7 +936,7 @@ func main() {
|
|||||||
if cfg.HTTPS.Host != "" {
|
if cfg.HTTPS.Host != "" {
|
||||||
api := http.Server{
|
api := http.Server{
|
||||||
Addr: cfg.HTTPS.Host,
|
Addr: cfg.HTTPS.Host,
|
||||||
Handler: handlers.APP(shutdown, log, cfg.Env, cfg.Service.StaticFiles.Dir, cfg.Service.TemplateDir, masterDb, redisClient, authenticator, projectRoutes, cfg.Service.SessionKey, notifyEmail, renderer, serviceMiddlewares...),
|
Handler: handlers.APP(shutdown, log, cfg.Env, cfg.Service.StaticFiles.Dir, cfg.Service.TemplateDir, masterDb, redisClient, authenticator, projectRoutes, cfg.Service.SecretKey, notifyEmail, renderer, serviceMiddlewares...),
|
||||||
ReadTimeout: cfg.HTTPS.ReadTimeout,
|
ReadTimeout: cfg.HTTPS.ReadTimeout,
|
||||||
WriteTimeout: cfg.HTTPS.WriteTimeout,
|
WriteTimeout: cfg.HTTPS.WriteTimeout,
|
||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
|
21
internal/platform/notify/email_disabled.go
Normal file
21
internal/platform/notify/email_disabled.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// DisableEmail defines an implementation of the email interface that doesn't send any email.
|
||||||
|
type DisableEmail struct{}
|
||||||
|
|
||||||
|
// NewEmailDisabled disables sending any emails with an empty implementation of the email interface.
|
||||||
|
func NewEmailDisabled() *DisableEmail {
|
||||||
|
return &DisableEmail{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send does nothing.
|
||||||
|
func (n *DisableEmail) Send(ctx context.Context, toEmail, subject, templateName string, data map[string]interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify does nothing.
|
||||||
|
func (n *DisableEmail) Verify() error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,5 +1,27 @@
|
|||||||
package notify
|
package notify
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Alternative to use AWS SES with SMTP
|
||||||
|
import "gopkg.in/gomail.v2"
|
||||||
|
|
||||||
|
var cfg struct {
|
||||||
|
...
|
||||||
|
SMTP struct {
|
||||||
|
Host string `default:"localhost" envconfig:"HOST"`
|
||||||
|
Port int `default:"25" envconfig:"PORT"`
|
||||||
|
User string `default:"" envconfig:"USER"`
|
||||||
|
Pass string `default:"" envconfig:"PASS" json:"-"` // don't print
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
d := gomail.Dialer{
|
||||||
|
Host: cfg.SMTP.Host,
|
||||||
|
Port: cfg.SMTP.Port,
|
||||||
|
Username: cfg.SMTP.User,
|
||||||
|
Password: cfg.SMTP.Pass}
|
||||||
|
notifyEmail, err = notify.NewEmailSmtp(d, cfg.Service.SharedTemplateDir, cfg.Service.EmailSender)
|
||||||
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -414,7 +414,7 @@ func (repo *Repository) Archive(ctx context.Context, claims auth.Claims, req Pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes an project from the database.
|
// Delete removes an project from the database.
|
||||||
func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, dbConn *sqlx.DB, req ProjectDeleteRequest) error {
|
func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, req ProjectDeleteRequest) error {
|
||||||
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Delete")
|
span, ctx := tracer.StartSpanFromContext(ctx, "internal.project.Delete")
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
|
|
||||||
@ -437,8 +437,8 @@ func (repo *Repository) Delete(ctx context.Context, claims auth.Claims, dbConn *
|
|||||||
query.Where(query.Equal("id", req.ID))
|
query.Where(query.Equal("id", req.ID))
|
||||||
// Execute the query with the provided context.
|
// Execute the query with the provided context.
|
||||||
sql, args := query.Build()
|
sql, args := query.Build()
|
||||||
sql = dbConn.Rebind(sql)
|
sql = repo.DbConn.Rebind(sql)
|
||||||
_, err = dbConn.ExecContext(ctx, sql, args...)
|
_, err = repo.DbConn.ExecContext(ctx, sql, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrapf(err, "query - %s", query.String())
|
err = errors.Wrapf(err, "query - %s", query.String())
|
||||||
err = errors.WithMessagef(err, "delete project %s failed", req.ID)
|
err = errors.WithMessagef(err, "delete project %s failed", req.ID)
|
||||||
|
Reference in New Issue
Block a user