You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-08-08 22:36:41 +02:00
updates from review, WIP
This commit is contained in:
7
build/README.md
Normal file
7
build/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# `/build`
|
||||||
|
|
||||||
|
Packaging and Continuous Integration.
|
||||||
|
|
||||||
|
Put your cloud (AMI), container (Docker), OS (deb, rpm, pkg) package configurations and scripts in the /build/package
|
||||||
|
directory.
|
||||||
|
|
@ -200,6 +200,13 @@ If you have a lot of migrations, it can be a pain to run all them. For example,
|
|||||||
the app into a clean database. To prevent this, use the initSchema function that will run as-if no migration was run
|
the app into a clean database. To prevent this, use the initSchema function that will run as-if no migration was run
|
||||||
before (in a new clean database).
|
before (in a new clean database).
|
||||||
|
|
||||||
|
|
||||||
|
**Migrations should be backwards compatible with the existing deployed code.** Refrain from `drop table`. Instead of
|
||||||
|
renaming columns, add a new column and copy the data from the old column using an `update`.
|
||||||
|
|
||||||
|
Ideally migrations should be idempotent to avoid possible data loss since data could have been generated between
|
||||||
|
migration runs.
|
||||||
|
|
||||||
Another bonus with the globally defined schema is that it enables your testing package the ability to dynamically [spin
|
Another bonus with the globally defined schema is that it enables your testing package the ability to dynamically [spin
|
||||||
up database containers](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/blob/issue8/datadog-lambda-func/internal/platform/tests/main.go#L127)
|
up database containers](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/blob/issue8/datadog-lambda-func/internal/platform/tests/main.go#L127)
|
||||||
on-demand and automatically include all the migrations. This allows the testing package to
|
on-demand and automatically include all the migrations. This allows the testing package to
|
||||||
|
12
cmd/README.md
Normal file
12
cmd/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# `/cmd`
|
||||||
|
|
||||||
|
Main applications for this project.
|
||||||
|
|
||||||
|
The directory name for each application should match the name of the executable you want to have (e.g., `/cmd/myapp`).
|
||||||
|
|
||||||
|
Don't put a lot of code in the application directory. If you think the code can be imported and used in other projects,
|
||||||
|
then it should live in the `/pkg` directory. If the code is not reusable or if you don't want others to reuse it,
|
||||||
|
put that code in the `/internal` directory. You'll be surprised what others will do, so be explicit about your intentions!
|
||||||
|
|
||||||
|
It's common to have a small `main` function that imports and invokes the code from the `/internal` and `/pkg`
|
||||||
|
directories and nothing else.
|
@ -567,11 +567,13 @@ func main() {
|
|||||||
// Need defined functions below since they require config values, able to add additional functions
|
// Need defined functions below since they require config values, able to add additional functions
|
||||||
// here to extend functionality.
|
// here to extend functionality.
|
||||||
tmplFuncs := template.FuncMap{
|
tmplFuncs := template.FuncMap{
|
||||||
|
// BuildInfo returns the specific key from BuildInfo set in the current config.
|
||||||
"BuildInfo": func(k string) string {
|
"BuildInfo": func(k string) string {
|
||||||
r := reflect.ValueOf(cfg.BuildInfo)
|
r := reflect.ValueOf(cfg.BuildInfo)
|
||||||
f := reflect.Indirect(r).FieldByName(k)
|
f := reflect.Indirect(r).FieldByName(k)
|
||||||
return f.String()
|
return f.String()
|
||||||
},
|
},
|
||||||
|
// SiteBaseUrl returns the absolute URL for a relative path using the base site URL set in the config.
|
||||||
"SiteBaseUrl": func(p string) string {
|
"SiteBaseUrl": func(p string) string {
|
||||||
u, err := url.Parse(cfg.Service.BaseUrl)
|
u, err := url.Parse(cfg.Service.BaseUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -580,21 +582,10 @@ func main() {
|
|||||||
u.Path = p
|
u.Path = p
|
||||||
return u.String()
|
return u.String()
|
||||||
},
|
},
|
||||||
"AssetUrl": func(p string) string {
|
// SiteAssetUrl returns the absolute URL for a relative path that either served locally, from the public S3
|
||||||
var u string
|
// bucket or from Cloudfront depending on the current config settings for StaticFiles. The defined static asset
|
||||||
if staticUrlFormatter != nil {
|
// prefix to the path defined in the current config settings for StaticFiles. The response URL includes a cache
|
||||||
u = staticUrlFormatter(p)
|
// busting query param based on the current commit.
|
||||||
} else {
|
|
||||||
if !strings.HasPrefix(p, "/") {
|
|
||||||
p = "/" + p
|
|
||||||
}
|
|
||||||
u = p
|
|
||||||
}
|
|
||||||
|
|
||||||
u = browserCacheBusterFunc(u)
|
|
||||||
|
|
||||||
return u
|
|
||||||
},
|
|
||||||
"SiteAssetUrl": func(p string) string {
|
"SiteAssetUrl": func(p string) string {
|
||||||
var u string
|
var u string
|
||||||
if staticUrlFormatter != nil {
|
if staticUrlFormatter != nil {
|
||||||
@ -610,6 +601,8 @@ func main() {
|
|||||||
|
|
||||||
return u
|
return u
|
||||||
},
|
},
|
||||||
|
// SiteS3Url returns the URL that for an S3 Key path that has been uploaded to S3 to the specific public s3 key
|
||||||
|
// prefix that returns the absolute S3 URL or the CloudFront equivalent if enabled.
|
||||||
"SiteS3Url": func(p string) string {
|
"SiteS3Url": func(p string) string {
|
||||||
var u string
|
var u string
|
||||||
if staticUrlFormatter != nil {
|
if staticUrlFormatter != nil {
|
||||||
@ -619,6 +612,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
return u
|
return u
|
||||||
},
|
},
|
||||||
|
// S3Url returns the URL that for an S3 Key path that has been uploaded to S3 to the public s3 key
|
||||||
|
// prefix that returns the absolute S3 URL or the CloudFront equivalent if enabled.
|
||||||
"S3Url": func(p string) string {
|
"S3Url": func(p string) string {
|
||||||
var u string
|
var u string
|
||||||
if staticUrlFormatter != nil {
|
if staticUrlFormatter != nil {
|
||||||
@ -628,6 +623,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return u
|
return u
|
||||||
},
|
},
|
||||||
|
// ValidationErrorHasField checks if the error is a validation error and has an error for the specified field.
|
||||||
"ValidationErrorHasField": func(err interface{}, fieldName string) bool {
|
"ValidationErrorHasField": func(err interface{}, fieldName string) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
@ -643,6 +639,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
// ValidationFieldErrors returns the list of validation field errors.
|
||||||
"ValidationFieldErrors": func(err interface{}, fieldName string) []weberror.FieldError {
|
"ValidationFieldErrors": func(err interface{}, fieldName string) []weberror.FieldError {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return []weberror.FieldError{}
|
return []weberror.FieldError{}
|
||||||
@ -659,6 +656,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
},
|
},
|
||||||
|
// ValidationFieldClass returns a CSS class for validation if the field exists in the validation errors.
|
||||||
"ValidationFieldClass": func(err interface{}, fieldName string) string {
|
"ValidationFieldClass": func(err interface{}, fieldName string) string {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return ""
|
return ""
|
||||||
@ -675,6 +673,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return "is-valid"
|
return "is-valid"
|
||||||
},
|
},
|
||||||
|
// ErrorMessage returns the error message that is formatted as a response for end users to consume.
|
||||||
"ErrorMessage": func(ctx context.Context, err error) string {
|
"ErrorMessage": func(ctx context.Context, err error) string {
|
||||||
werr, ok := err.(*weberror.Error)
|
werr, ok := err.(*weberror.Error)
|
||||||
if ok {
|
if ok {
|
||||||
@ -685,6 +684,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s", err)
|
return fmt.Sprintf("%s", err)
|
||||||
},
|
},
|
||||||
|
// ErrorDetails returns the full error for dev and stage envs to help with debugging.
|
||||||
"ErrorDetails": func(ctx context.Context, err error) string {
|
"ErrorDetails": func(ctx context.Context, err error) string {
|
||||||
var displayFullError bool
|
var displayFullError bool
|
||||||
switch webcontext.ContextEnv(ctx) {
|
switch webcontext.ContextEnv(ctx) {
|
||||||
@ -707,8 +707,7 @@ func main() {
|
|||||||
|
|
||||||
return fmt.Sprintf("%+v", err)
|
return fmt.Sprintf("%+v", err)
|
||||||
},
|
},
|
||||||
// Returns the current user from the session.
|
// ContextUser returns the current user and caches the result.
|
||||||
// @TODO: Need to add logging for the errors.
|
|
||||||
"ContextUser": func(ctx context.Context) *user.UserResponse {
|
"ContextUser": func(ctx context.Context) *user.UserResponse {
|
||||||
sess := webcontext.ContextSession(ctx)
|
sess := webcontext.ContextSession(ctx)
|
||||||
|
|
||||||
@ -716,6 +715,7 @@ func main() {
|
|||||||
|
|
||||||
u := &user.UserResponse{}
|
u := &user.UserResponse{}
|
||||||
if err := redisClient.Get(cacheKey).Scan(u); err != nil && err != redis.Nil {
|
if err := redisClient.Get(cacheKey).Scan(u); err != nil && err != redis.Nil {
|
||||||
|
log.Printf("main : ContextUser : Redis Get failed - %+v ", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,13 +737,13 @@ func main() {
|
|||||||
|
|
||||||
err = redisClient.Set(cacheKey, u, time.Hour).Err()
|
err = redisClient.Set(cacheKey, u, time.Hour).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("main : ContextAccount : Redis Set failed - %+v ", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return u
|
return u
|
||||||
},
|
},
|
||||||
// Returns the current account from the session.
|
// ContextAccount returns the account for current context user with auth is using and caches the result.
|
||||||
// @TODO: Need to add logging for the errors.
|
|
||||||
"ContextAccount": func(ctx context.Context) *account.AccountResponse {
|
"ContextAccount": func(ctx context.Context) *account.AccountResponse {
|
||||||
sess := webcontext.ContextSession(ctx)
|
sess := webcontext.ContextSession(ctx)
|
||||||
|
|
||||||
@ -751,6 +751,7 @@ func main() {
|
|||||||
|
|
||||||
a := &account.AccountResponse{}
|
a := &account.AccountResponse{}
|
||||||
if err := redisClient.Get(cacheKey).Scan(a); err != nil && err != redis.Nil {
|
if err := redisClient.Get(cacheKey).Scan(a); err != nil && err != redis.Nil {
|
||||||
|
log.Printf("main : ContextAccount : Redis Get failed - %+v ", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,11 +773,13 @@ func main() {
|
|||||||
|
|
||||||
err = redisClient.Set(cacheKey, a, time.Hour).Err()
|
err = redisClient.Set(cacheKey, a, time.Hour).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("main : ContextAccount : Redis Set failed - %+v ", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return a
|
return a
|
||||||
},
|
},
|
||||||
|
// ContextCanSwitchAccount returns if the current context user has multiple accounts.
|
||||||
"ContextCanSwitchAccount": func(ctx context.Context) bool {
|
"ContextCanSwitchAccount": func(ctx context.Context) bool {
|
||||||
claims, err := auth.ClaimsFromContext(ctx)
|
claims, err := auth.ClaimsFromContext(ctx)
|
||||||
if err != nil || len(claims.AccountIDs) < 2 {
|
if err != nil || len(claims.AccountIDs) < 2 {
|
||||||
@ -784,6 +787,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
// ContextIsVirtualSession returns true if the current context user with auth is in a virtual session.
|
||||||
"ContextIsVirtualSession": func(ctx context.Context) bool {
|
"ContextIsVirtualSession": func(ctx context.Context) bool {
|
||||||
claims, err := auth.ClaimsFromContext(ctx)
|
claims, err := auth.ClaimsFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -797,6 +801,63 @@ func main() {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
// T creates the translation for the locale given the 'key' and params passed in.
|
||||||
|
"T": func(ctx context.Context, key string, params ...string) string {
|
||||||
|
trns := webcontext.ContextTranslator(ctx)
|
||||||
|
if trns == nil {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := trns.T(key, params...)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("main : Translate.T : Failed to translate %s with '%s' - %+v", trns.Locale(), key, err.Error())
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
|
||||||
|
"C": func(ctx context.Context, key string, num float64, digits uint64, param string) string {
|
||||||
|
trns := webcontext.ContextTranslator(ctx)
|
||||||
|
if trns == nil {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := trns.C(key, num, digits, param)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("main : Translate.C : Failed to translate %s with '%s' - %+v", trns.Locale(), key, err.Error())
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in
|
||||||
|
"O": func(ctx context.Context, key string, num float64, digits uint64, param string) string {
|
||||||
|
trns := webcontext.ContextTranslator(ctx)
|
||||||
|
if trns == nil {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := trns.O(key, num, digits, param)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("main : Translate.O : Failed to translate %s with '%s' - %+v", trns.Locale(), key, err.Error())
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2'
|
||||||
|
// arguments and 'param1' and 'param2' passed in
|
||||||
|
"R": func(ctx context.Context, key string, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) string {
|
||||||
|
trns := webcontext.ContextTranslator(ctx)
|
||||||
|
if trns == nil {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := trns.R(key, num1, digits1, num2, digits2, param1, param2)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("main : Translate.R : Failed to translate %s with '%s' - %+v", trns.Locale(), key, err.Error())
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
imgUrlFormatter := staticUrlFormatter
|
imgUrlFormatter := staticUrlFormatter
|
||||||
|
3
configs/README.md
Normal file
3
configs/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# `/configs`
|
||||||
|
|
||||||
|
Configuration file templates or default configs.
|
4
deployments/README.md
Normal file
4
deployments/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# `/deployments`
|
||||||
|
|
||||||
|
IaaS, PaaS, system and container orchestration deployment configurations and templates (docker-compose, kubernetes/helm,
|
||||||
|
mesos, terraform, bosh).
|
@ -38,7 +38,7 @@ services:
|
|||||||
datadog:
|
datadog:
|
||||||
image: example-project/datadog:latest
|
image: example-project/datadog:latest
|
||||||
build:
|
build:
|
||||||
context: docker/datadog-agent
|
context: build/docker/datadog-agent
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
ports:
|
ports:
|
||||||
- 8125:8125 # metrics
|
- 8125:8125 # metrics
|
||||||
|
2
go.mod
2
go.mod
@ -41,7 +41,7 @@ require (
|
|||||||
github.com/tinylib/msgp v1.1.0 // indirect
|
github.com/tinylib/msgp v1.1.0 // indirect
|
||||||
github.com/urfave/cli v1.21.0
|
github.com/urfave/cli v1.21.0
|
||||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
|
||||||
gitlab.com/geeks-accelerator/oss/devops v1.0.3
|
gitlab.com/geeks-accelerator/oss/devops v1.0.7
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||||
golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de // indirect
|
golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de // indirect
|
||||||
|
9
go.sum
9
go.sum
@ -1,6 +1,7 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
@ -78,10 +79,14 @@ github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
|||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
|
||||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||||
|
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
|
||||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||||
|
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||||
|
github.com/gobuffalo/packr/v2 v2.5.2 h1:4EvjeIpQLZuRIljwnidYgbRXbr1yIzVRrESiLjqKj6s=
|
||||||
github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08=
|
github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@ -117,6 +122,7 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
|
|||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
|
||||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||||
@ -170,6 +176,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 h1:X9XMOYjxEfAYSy3xK1DzO5dMkkWhs9E9UCcS1IERx2k=
|
github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 h1:X9XMOYjxEfAYSy3xK1DzO5dMkkWhs9E9UCcS1IERx2k=
|
||||||
@ -208,6 +215,8 @@ github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryB
|
|||||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
|
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
|
||||||
gitlab.com/geeks-accelerator/oss/devops v1.0.3 h1:SE2ZD4Csvmm3t/50RoJkVLjDcwXKHayQYawSkpOSqIw=
|
gitlab.com/geeks-accelerator/oss/devops v1.0.3 h1:SE2ZD4Csvmm3t/50RoJkVLjDcwXKHayQYawSkpOSqIw=
|
||||||
gitlab.com/geeks-accelerator/oss/devops v1.0.3/go.mod h1:rvI71qNJyNiO99ZgGnv/PmJCVrjJjupsXBmfYFXdjGM=
|
gitlab.com/geeks-accelerator/oss/devops v1.0.3/go.mod h1:rvI71qNJyNiO99ZgGnv/PmJCVrjJjupsXBmfYFXdjGM=
|
||||||
|
gitlab.com/geeks-accelerator/oss/devops v1.0.7 h1:ZlQufuVnRN3DwJ0I5c5KA5edhQs7OstXc0uUZ9V0ixI=
|
||||||
|
gitlab.com/geeks-accelerator/oss/devops v1.0.7/go.mod h1:JEl0T87/zftowrIzY1D+rhDMhG0AxnghuZB+VzEWuqM=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
5
internal/README.md
Normal file
5
internal/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# `/internal`
|
||||||
|
|
||||||
|
Private application and library code. This is the code you don't want others importing in their applications or libraries.
|
||||||
|
|
||||||
|
You can optionally add a bit of extra structure to your internal packages to separate your shared and non-shared internal code. It's not required, but it's nice to have visual clues showing the intended package use. Your actual application code can go in the /internal/app directory (e.g., /internal/app/myapp) and the code shared by those apps in the /internal/pkg directory (e.g., /internal/pkg/myprivlib).
|
@ -373,8 +373,6 @@ func (r *TemplateRenderer) Render(ctx context.Context, w http.ResponseWriter, re
|
|||||||
data["error"] = terr
|
data["error"] = terr
|
||||||
}
|
}
|
||||||
|
|
||||||
data["trans"] = webcontext.ContextTranslator(ctx)
|
|
||||||
|
|
||||||
// Append request data map to render data last so any previous value can be overwritten.
|
// Append request data map to render data last so any previous value can be overwritten.
|
||||||
if data != nil {
|
if data != nil {
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -65,8 +66,24 @@ func init() {
|
|||||||
// Provide one or more arguments for additional supported locales.
|
// Provide one or more arguments for additional supported locales.
|
||||||
uniTrans = ut.New(en, en, fr, id, ja, nl, zh)
|
uniTrans = ut.New(en, en, fr, id, ja, nl, zh)
|
||||||
|
|
||||||
if _, err := os.Stat("templates/content/translations"); !os.IsNotExist(err) {
|
// Try to load the Template directory from an environment variable for release images else it's local development.
|
||||||
err := uniTrans.Import(ut.FormatJSON, "templates/content/translations")
|
var transDir string
|
||||||
|
if ev := os.Getenv("TRANSLATIONS_DIR"); ev != "" {
|
||||||
|
transDir = ev
|
||||||
|
} else {
|
||||||
|
if ev := os.Getenv("SHARED_TEMPLATE_DIR"); ev != "" {
|
||||||
|
transDir = ev
|
||||||
|
} else if ev := os.Getenv("TEMPLATE_DIR"); ev != "" {
|
||||||
|
transDir = ev
|
||||||
|
} else {
|
||||||
|
transDir = "./templates"
|
||||||
|
}
|
||||||
|
|
||||||
|
transDir = filepath.Join(transDir, "translations")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(transDir); !os.IsNotExist(err) {
|
||||||
|
err := uniTrans.Import(ut.FormatJSON, transDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
10
tools/README.md
Normal file
10
tools/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# `/tools`
|
||||||
|
|
||||||
|
Supporting tools for this project. Note that these tools can import code from the /pkg and /internal directories.
|
||||||
|
|
||||||
|
Current Tools:
|
||||||
|
* [schema](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/tree/master/tools/schema) - A command line tool for
|
||||||
|
local development that executes database migrations.
|
||||||
|
|
||||||
|
* [text-translator](https://gitlab.com/geeks-accelerator/oss/saas-starter-kit/tree/master/tools/text-translator) - A
|
||||||
|
command line tool for supporting internazionalied go-templates.
|
Reference in New Issue
Block a user