From b462f69a6074a07e29385c2d20ffb352e6052e4c Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 2 Apr 2018 00:13:47 -0700 Subject: [PATCH 01/48] version bump to v0.11.0-edge --- aah/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aah/version.go b/aah/version.go index 70409ee..bd216b5 100644 --- a/aah/version.go +++ b/aah/version.go @@ -5,4 +5,4 @@ package main // Version no. of aah framework CLI tool -const Version = "0.10.2" +const Version = "0.11.0-edge" From 854975b399067cd68a4dce69f6e7fc682b81b4a6 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 2 Apr 2018 00:14:46 -0700 Subject: [PATCH 02/48] app template and log message update --- aah/aah.go | 4 ++-- aah/app-template/aah.project.atmpl | 2 +- aah/app-template/app/init.go.atmpl | 2 +- aah/compile.go | 11 +++++++++++ 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index 1352b26..d52cfd9 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -123,8 +123,8 @@ func main() { //___________________________________ func printHeader(c *cli.Context) error { - hdr := fmt.Sprintf("aah framework v%s - https://aahframework.org", aah.Version) - improveRpt := "# Report improvements/bugs at https://github.com/go-aah/aah/issues #" + hdr := fmt.Sprintf("aah framework v%s", aah.Version) + improveRpt := "# Report improvements/bugs at https://aahframework.org/issues #" cnt := len(improveRpt) sp := (cnt - len(hdr)) / 2 diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index 791d7bb..44964c1 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -50,7 +50,7 @@ log { } # Hot-Reload is development purpose to help developer. -# Read more about implementation here - https://github.com/go-aah/aah/issues/4 +# Read more about implementation here - https://aahframework.org/issues/4 # # NOTE: Do not use hot-reload feature for production purpose, it's not recommended. hot_reload { diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index c8b83c6..640692a 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -102,7 +102,7 @@ func init() { // Doc: https://godoc.org/gopkg.in/go-playground/validator.v9 //__________________________________________________________________________ // Obtain aah validator instance, then add yours - // validator := valpar.Validator() + // validator := aah.Validator() // // // Add your validation funcs diff --git a/aah/compile.go b/aah/compile.go index c01781b..3f38bab 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -290,6 +290,7 @@ const aahMainTemplate = `// GENERATED CODE - DO NOT EDIT package main import ( + "bytes" "flag" "fmt" "os" @@ -298,6 +299,7 @@ import ( "syscall" "aahframework.org/aah.v0" + "aahframework.org/aruntime.v0" "aahframework.org/config.v0" "aahframework.org/essentials.v0" "aahframework.org/log.v0"{{ range $k, $v := $.AppImportPaths }} @@ -337,6 +339,15 @@ func setAppProxyPort(e *aah.Event) { {{- end }} func main() { + defer func() { + if r := recover(); r != nil { + st := aruntime.NewStacktrace(r, aah.AppConfig()) + buf := new(bytes.Buffer) + st.Print(buf) + log.Error(buf.String()) + } + }() + log.Infof("aah framework v%s, requires ≥ go1.8", aah.Version) flag.Parse() From 0804c8f960614b9b3e7cacd1c77259b91bc87e81 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 3 Apr 2018 19:50:48 -0700 Subject: [PATCH 03/48] added enable config int secure header on template --- aah/app-template/config/security.conf.atmpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index f8a4dba..6133dae 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -354,6 +354,10 @@ security { # Tip: Quick way to verify secure headers - https://securityheaders.io # --------------------------------------------------------------------------- http_header { + # Enabling HTTP secure headers. + # Default value is `true`. + enable = true + # X-XSS-Protection # Designed to enable the cross-site scripting (XSS) filter built into modern # web browsers. This is usually enabled by default, but using this header From e218a58857ef2429ac1de20a459945d6f5b6d8e6 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 4 Apr 2018 19:46:15 -0700 Subject: [PATCH 04/48] travis build config update --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 23e7f9e..ecef445 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,9 @@ go: go_import_path: aahframework.org/tools.v0/aah +before_install: + - bash <(curl -s https://aahframework.org/base-before-install) + install: - go get -t -v ./... From 369177dfa7825e5b9ec942f09d98737933c165ce Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 12 Apr 2018 00:27:08 -0700 Subject: [PATCH 05/48] go-aah/aah#157 adapting to aah edge improvements --- aah/compile.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 3f38bab..54f65c0 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -361,7 +361,7 @@ func main() { // display application information if *version { - fmt.Printf("%-12s: %s\n", "Binary Name", aah.AppBuildInfo().BinaryName) + fmt.Printf("\n%-12s: %s\n", "Binary Name", aah.AppBuildInfo().BinaryName) fmt.Printf("%-12s: %s\n", "Version", aah.AppBuildInfo().Version) fmt.Printf("%-12s: %s\n", "Build Date", aah.AppBuildInfo().Date) return @@ -377,7 +377,9 @@ func main() { aah.OnInit(setAppEnvProfile) } - aah.Init("{{ .AppImportPath }}") + if err := aah.Init("{{ .AppImportPath }}"); err != nil { + log.Fatal(err) + } // Adding all the application controllers which refers 'aah.Context' directly // or indirectly from app/controllers/** {{ range $i, $c := .AppControllers }} @@ -400,19 +402,19 @@ func main() { {{ if $v.Authenticator -}} log.Debugf("Calling authenticator Init for auth scheme '%s'", "{{ $k }}") if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { - log.Fatal(err) + aah.AppLog().Fatal(err) } {{ end -}} {{ if $v.Authorizer -}} log.Debugf("Calling authorizer Init for auth scheme '%s'", "{{ $k }}") if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthorizer(&{{ $v.Authorizer }}{}); err != nil { - log.Fatal(err) + aah.AppLog().Fatal(err) } {{ end -}} {{ end -}} {{ end }} - log.Info("aah application initialized successfully") + aah.AppLog().Info("aah application initialized successfully") {{ if eq .AppTargetCmd "RunCmd" -}} {{ if .AppProxyPort -}} @@ -435,7 +437,7 @@ func main() { // Call aah shutdown aah.Shutdown() - log.Info("aah application shutdown successful") + aah.AppLog().Info("aah application shutdown successful") // bye bye, see you later. os.Exit(0) From d87057af36dbe99b9c68b7694597ea9a5cb3a8f6 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 14 Apr 2018 11:48:05 -0700 Subject: [PATCH 06/48] go-aah/aah#158 added secure json config --- aah/app-template/config/aah.conf.atmpl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index 09c1ee1..c5861ce 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -354,6 +354,16 @@ render { # Default value is `false`. #pretty = true + # To prevent Cross Site Script Inclusion (XSSI) attacks on JSON response + # payload aka JSON vulnerability. XSSI attack is only successful if the + # returned JSON response is executable as JavaScript. + # + # aah prefixes JSON response to make them non-executable. + # Default value is `)]}',\n` + #secure_json { + # prefix = ")]}',\n" + #} + # Gzip compression configuration for HTTP response. gzip { # By default Gzip compression is enabled in aah framework, however From 71f732d5aa42f213d1ee7249b7fd7a55e3c6be71 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sun, 15 Apr 2018 11:22:38 -0700 Subject: [PATCH 07/48] adapted to newly extracted ainsp lib --- aah/ast.go | 621 ------------------------------------------------- aah/compile.go | 3 +- aah/util.go | 5 + 3 files changed, 7 insertions(+), 622 deletions(-) delete mode 100644 aah/ast.go diff --git a/aah/ast.go b/aah/ast.go deleted file mode 100644 index 18c834b..0000000 --- a/aah/ast.go +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright (c) Jeevanandam M (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style -// license that can be found in the LICENSE file. - -package main - -import ( - "errors" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/scanner" - "go/token" - "os" - "path/filepath" - "strings" - - "aahframework.org/essentials.v0" -) - -var ( - buildImportCache = map[string]string{} - - // Reference: https://golang.org/pkg/builtin/ - builtInDataTypes = map[string]bool{ - "bool": true, - "byte": true, - "complex128": true, - "complex64": true, - "error": true, - "float32": true, - "float64": true, - "int": true, - "int16": true, - "int32": true, - "int64": true, - "int8": true, - "rune": true, - "string": true, - "uint": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uint8": true, - "uintptr": true, - } - - errInvalidActionParam = errors.New("aah: invalid action parameter") - errInterfaceActionParam = errors.New("aah: 'interface{}' is not supported in the action parameter") - errMapActionParam = errors.New("aah: 'map' is not supported in the action parameter") -) - -type ( - // Program holds all details loaded from the Go source code for given Path. - program struct { - Path string - Packages []*packageInfo - RegisteredActions map[string]map[string]uint8 - } - - // PackageInfo holds the single paackge information. - packageInfo struct { - Fset *token.FileSet - Pkg *ast.Package - Types map[string]*typeInfo - ImportPath string - FilePath string - Files []string - } - - // TypeInfo holds the information about Controller Name, Methods, - // Embedded types etc. - typeInfo struct { - Name string - ImportPath string - Methods []*methodInfo - EmbeddedTypes []*typeInfo - } - - // MethodInfo holds the information of single method and it's Parameters. - methodInfo struct { - Name string - StructName string - Parameters []*parameterInfo - } - - // ParameterInfo holds the information of single Parameter in the method. - parameterInfo struct { - Name string - ImportPath string - Type *typeExpr - } - - // TypeExpr holds the information of single parameter data type. - typeExpr struct { - Expr string - IsBuiltIn bool - PackageName string - ImportPath string - PackageIndex uint8 - Valid bool - } -) - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// Package methods -//___________________________________ - -// LoadProgram method loads the Go source code for the given directory. -func loadProgram(path string, excludes ess.Excludes, registeredActions map[string]map[string]uint8) (*program, []error) { - if err := validateInput(path); err != nil { - return nil, append([]error{}, err) - } - - prg := &program{ - Path: path, - Packages: []*packageInfo{}, - RegisteredActions: registeredActions, - } - - var ( - pkgs map[string]*ast.Package - errs []error - ) - - err := ess.Walk(path, func(srcPath string, info os.FileInfo, err error) error { - if err != nil { - errs = append(errs, err) - } - - // Excludes - if excludes.Match(filepath.Base(srcPath)) { - if info.IsDir() { - return filepath.SkipDir - } - - return nil - } - - if !info.IsDir() { - return nil - } - - if info.IsDir() && ess.IsDirEmpty(srcPath) { - // skip directory if it's empty - return filepath.SkipDir - } - - pfset := token.NewFileSet() - pkgs, err = parser.ParseDir(pfset, srcPath, func(f os.FileInfo) bool { - return !f.IsDir() && !excludes.Match(f.Name()) - }, 0) - - if err != nil { - if errList, ok := err.(scanner.ErrorList); ok { - // TODO parsing error list - fmt.Println(errList) - } - - errs = append(errs, fmt.Errorf("error parsing dir[%s]: %s", srcPath, err)) - return nil - } - - pkg, err := validateAndGetPkg(pkgs, srcPath) - if err != nil { - errs = append(errs, err) - return nil - } - - if pkg != nil { - pkg.Fset = pfset - pkg.FilePath = srcPath - pkg.ImportPath = stripGoPath(srcPath) - prg.Packages = append(prg.Packages, pkg) - } - - return nil - }) - - if err != nil { - errs = append(errs, err) - } - - return prg, errs -} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// Program methods -//___________________________________ - -// Process method processes all packages in the program for `Type`, -// `Embedded Type`, `Method`, etc. -func (prg *program) Process() { - for _, pkgInfo := range prg.Packages { - pkgInfo.Types = map[string]*typeInfo{} - - // Each source file - for name, file := range pkgInfo.Pkg.Files { - pkgInfo.Files = append(pkgInfo.Files, filepath.Base(name)) - fileImports := make(map[string]string) - - for _, decl := range file.Decls { - // Processing imports - pkgInfo.processImports(decl, fileImports) - - // Processing types - pkgInfo.processTypes(decl, fileImports) - - // Processing methods - processMethods(pkgInfo, prg.RegisteredActions, decl, fileImports) - } - } - } -} - -// FindTypeByEmbeddedType method returns all the typeInfo that has directly or -// indirectly embedded by given type name. Type name must be fully qualified -// type name. E.g.: aahframework.org/aah.Controller -func (prg *program) FindTypeByEmbeddedType(qualifiedTypeName string) []*typeInfo { - var ( - queue = []string{qualifiedTypeName} - processed []string - result []*typeInfo - ) - - for len(queue) > 0 { - typeName := queue[0] - queue = queue[1:] - processed = append(processed, typeName) - - // search within all packages in the program - for _, p := range prg.Packages { - // search within all struct type in the package - for _, t := range p.Types { - // If this one has been processed or is already in queue, then move on. - if ess.IsSliceContainsString(processed, t.FullyQualifiedName()) || - ess.IsSliceContainsString(queue, t.FullyQualifiedName()) { - continue - } - - // search through the embedded types to see if the current type is among them. - for _, et := range t.EmbeddedTypes { - // If so, add this type's FullyQualifiedName into queue, - // and it's typeInfo into result. - if typeName == et.FullyQualifiedName() { - queue = append(queue, t.FullyQualifiedName()) - result = append(result, t) - break - } - } - } - } - } - - return result -} - -// CreateImportPaths method returns unique package alias with import path. -func (prg *program) CreateImportPaths(types []*typeInfo) map[string]string { - importPaths := map[string]string{} - for _, t := range types { - createAlias(t.PackageName(), t.ImportPath, importPaths) - for _, m := range t.Methods { - for _, p := range m.Parameters { - if !p.Type.IsBuiltIn { - createAlias(p.Type.PackageName, p.ImportPath, importPaths) - } - } - } - } - - return importPaths -} - -func createAlias(packageName, importPath string, importPaths map[string]string) { - importPath = filepath.ToSlash(importPath) - if _, found := importPaths[importPath]; !found { - cnt := 0 - pkgAlias := packageName - - for isPkgAliasExists(importPaths, pkgAlias) { - pkgAlias = fmt.Sprintf("%s%d", packageName, cnt) - cnt++ - } - - if !ess.IsStrEmpty(pkgAlias) && !ess.IsStrEmpty(importPath) { - importPaths[importPath] = pkgAlias - } - } -} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// PackageInfo methods -//___________________________________ - -// Name method return package name -func (p *packageInfo) Name() string { - return filepath.Base(p.ImportPath) -} - -func (p *packageInfo) processTypes(decl ast.Decl, imports map[string]string) { - genDecl, ok := decl.(*ast.GenDecl) - if !ok || !isTypeTok(genDecl) || len(genDecl.Specs) == 0 { - return - } - - spec := genDecl.Specs[0].(*ast.TypeSpec) - st, ok := spec.Type.(*ast.StructType) - if !ok { - // Not a struct type - return - } - - typeName := spec.Name.Name - ty := &typeInfo{ - Name: typeName, - ImportPath: filepath.ToSlash(p.ImportPath), - Methods: make([]*methodInfo, 0), - EmbeddedTypes: make([]*typeInfo, 0), - } - - for _, field := range st.Fields.List { - // If field.Names is set, it's not an embedded type. - if field.Names != nil && len(field.Names) > 0 { - continue - } - - fPkgName, fTypeName := parseStructFieldExpr(field.Type) - if ess.IsStrEmpty(fTypeName) { - continue - } - - // Find the import path for embedded type. If it was referenced without - // a package name, use the current package import path otherwise - // get the import path by package name. - var eTypeImportPath string - if ess.IsStrEmpty(fPkgName) { - eTypeImportPath = ty.ImportPath - } else { - var found bool - if eTypeImportPath, found = imports[fPkgName]; !found { - logErrorf("AST: Unable to find import path for %s.%s", fPkgName, fTypeName) - continue - } - } - - ty.EmbeddedTypes = append(ty.EmbeddedTypes, &typeInfo{Name: fTypeName, ImportPath: eTypeImportPath}) - } - - p.Types[typeName] = ty -} - -func (p *packageInfo) processImports(decl ast.Decl, imports map[string]string) { - genDecl, ok := decl.(*ast.GenDecl) - if !ok || !isImportTok(genDecl) { - return - } - - for _, dspec := range genDecl.Specs { - spec := dspec.(*ast.ImportSpec) - var pkgAlias string - if spec.Name != nil { - if spec.Name.Name == "_" { - continue - } - - pkgAlias = spec.Name.Name - } - - importPath := spec.Path.Value[1 : len(spec.Path.Value)-1] - if ess.IsStrEmpty(pkgAlias) { - if alias, found := buildImportCache[importPath]; found { - pkgAlias = alias - } else { // build cache - pkg, err := build.Import(importPath, p.FilePath, 0) - if err != nil { - logErrorf("AST: Unable to find import path: %s", importPath) - continue - } - pkgAlias = pkg.Name - buildImportCache[importPath] = pkg.Name - } - } - - imports[pkgAlias] = importPath - } -} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// TypeInfo methods -//___________________________________ - -// FullyQualifiedName method returns the fully qualified type name. -func (t *typeInfo) FullyQualifiedName() string { - return fmt.Sprintf("%s.%s", t.ImportPath, t.Name) -} - -// PackageName method returns types package name from import path. -func (t *typeInfo) PackageName() string { - return filepath.Base(t.ImportPath) -} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// TypeExpr methods -//___________________________________ - -// Name method returns type name for expression. -func (te *typeExpr) Name() string { - if te.IsBuiltIn || ess.IsStrEmpty(te.PackageName) { - return te.Expr - } - - return fmt.Sprintf("%s%s.%s", te.Expr[:te.PackageIndex], te.PackageName, te.Expr[te.PackageIndex:]) -} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// Unexported methods -//___________________________________ - -func validateInput(path string) error { - if ess.IsStrEmpty(path) { - return errors.New("path is required input") - } - - if !ess.IsFileExists(path) { - return fmt.Errorf("path is does not exists: %s", path) - } - - return nil -} - -func validateAndGetPkg(pkgs map[string]*ast.Package, path string) (*packageInfo, error) { - pkgCnt := len(pkgs) - - // no source code found in the directory - if pkgCnt == 0 { - return nil, nil - } - - // not permitted by Go lang spec - if pkgCnt > 1 { - var names []string - for k := range pkgs { - names = append(names, k) - } - return nil, fmt.Errorf("more than one package name [%s] found in single"+ - " directory: %s", strings.Join(names, ", "), path) - } - - pkg := &packageInfo{} - for _, v := range pkgs { - pkg.Pkg = v - } - - return pkg, nil -} - -func isImportTok(decl *ast.GenDecl) bool { - return token.IMPORT == decl.Tok -} - -func isTypeTok(decl *ast.GenDecl) bool { - return token.TYPE == decl.Tok -} - -func stripGoPath(pkgFilePath string) string { - idx := strings.Index(pkgFilePath, "src") - return filepath.Clean(pkgFilePath[idx+4:]) -} - -func isPkgAliasExists(importPaths map[string]string, pkgAlias string) bool { - _, found := importPaths[pkgAlias] - return found -} - -func processMethods(pkg *packageInfo, routeMethods map[string]map[string]uint8, decl ast.Decl, imports map[string]string) { - fn, ok := decl.(*ast.FuncDecl) - - // Do not process if these met: - // 1. does not have receiver, it means package function/method - // 2. method is not exported - // 3. method returns result - if !ok || fn.Recv == nil || !fn.Name.IsExported() || - fn.Type.Results != nil { - return - } - - actionName := fn.Name.Name - if isInterceptorActioName(actionName) { - return - } - - controllerName := getName(fn.Recv.List[0].Type) - method := &methodInfo{Name: actionName, StructName: controllerName, Parameters: []*parameterInfo{}} - - // processed so set to level 2, used to display unimplemented action details - // TODO for controller check too - for k, v := range routeMethods { - if strings.HasSuffix(k, controllerName) { - if _, found := v[actionName]; found { - v[actionName] = 2 - } - } - } - - // processing method parameters - for _, field := range fn.Type.Params.List { - for _, fieldName := range field.Names { - te, err := parseParamFieldExpr(pkg.Name(), field.Type) - if err != nil { - logErrorf("AST: %s, please fix the parameter '%s' on action '%s.%s'; "+ - "otherwise your action may not work properly", err, fieldName.Name, controllerName, actionName) - continue - } - - var importPath string - if !ess.IsStrEmpty(te.PackageName) { - var found bool - if importPath, found = imports[te.PackageName]; !found { - importPath = pkg.ImportPath - } - } - - method.Parameters = append(method.Parameters, ¶meterInfo{ - Name: fieldName.Name, - ImportPath: importPath, - Type: te, - }) - } - } - - if ty := pkg.Types[controllerName]; ty == nil { - pos := pkg.Fset.Position(decl.Pos()) - filename := stripGoPath(pos.Filename) - logErrorf("AST: Method '%s' has incorrect struct recevier '%s' on file [%s] at line #%d", - actionName, controllerName, filename, pos.Line) - } else { - ty.Methods = append(ty.Methods, method) - } -} - -func isInterceptorActioName(actionName string) bool { - return (strings.HasPrefix(actionName, "Before") || strings.HasPrefix(actionName, "After") || - strings.HasPrefix(actionName, "Panic") || strings.HasPrefix(actionName, "Finally")) -} - -func getName(expr ast.Expr) string { - switch t := expr.(type) { - case *ast.Ident: - return t.Name - case *ast.SelectorExpr: - return getName(t.X) - case *ast.StarExpr: - return getName(t.X) - default: - return "" - } -} - -func isBuiltInDataType(typeName string) bool { - _, found := builtInDataTypes[typeName] - return found -} - -// parseStructFieldExpr method to find a direct "embedded|sub-type". -// Struct ast.Field as follows: -// Ident { "type-name" } e.g. UserController -// SelectorExpr { "package-name", "type-name" } e.g. aah.Controller -// StarExpr { "*", "package-name", "type-name"} e.g. *aah.Controller -func parseStructFieldExpr(fieldType ast.Expr) (string, string) { - for { - if starExpr, ok := fieldType.(*ast.StarExpr); ok { - fieldType = starExpr.X - continue - } - break - } - - // type it's in the same package, it's an ast.Ident. - if ident, ok := fieldType.(*ast.Ident); ok { - return "", ident.Name - } - - // type it's in the different package, it's an ast.SelectorExpr. - if selectorExpr, ok := fieldType.(*ast.SelectorExpr); ok { - if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok { - return pkgIdent.Name, selectorExpr.Sel.Name - } - } - - return "", "" -} - -func parseParamFieldExpr(pkgName string, expr ast.Expr) (*typeExpr, error) { - switch t := expr.(type) { - case *ast.Ident: - if isBuiltInDataType(t.Name) { - return &typeExpr{Expr: t.Name, IsBuiltIn: true}, nil - } - return &typeExpr{Expr: t.Name, PackageName: pkgName}, nil - case *ast.SelectorExpr: - e, err := parseParamFieldExpr(pkgName, t.X) - return &typeExpr{Expr: t.Sel.Name, PackageName: e.Expr}, err - case *ast.StarExpr: - e, err := parseParamFieldExpr(pkgName, t.X) - return &typeExpr{Expr: "*" + e.Expr, PackageName: e.PackageName, PackageIndex: e.PackageIndex + uint8(1)}, err - case *ast.ArrayType: - e, err := parseParamFieldExpr(pkgName, t.Elt) - return &typeExpr{Expr: "[]" + e.Expr, PackageName: e.PackageName, PackageIndex: e.PackageIndex + uint8(2)}, err - case *ast.Ellipsis: - e, err := parseParamFieldExpr(pkgName, t.Elt) - return &typeExpr{Expr: "[]" + e.Expr, PackageName: e.PackageName, PackageIndex: e.PackageIndex + uint8(2)}, err - case *ast.InterfaceType: - return nil, errInterfaceActionParam - case *ast.MapType: - return nil, errMapActionParam - } - - return nil, errInvalidActionParam -} diff --git a/aah/compile.go b/aah/compile.go index 54f65c0..829d944 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -14,6 +14,7 @@ import ( "strings" "aahframework.org/aah.v0" + "aahframework.org/ainsp.v0" "aahframework.org/config.v0" "aahframework.org/essentials.v0" "aahframework.org/router.v0" @@ -52,7 +53,7 @@ func compileApp(args *compileArgs) (string, error) { registeredActions := aah.AppRouter().RegisteredActions() // Go AST processing for Controllers - prg, errs := loadProgram(appControllersPath, ess.Excludes(excludes), registeredActions) + prg, errs := ainsp.Inspect(appControllersPath, ess.Excludes(excludes), registeredActions) if len(errs) > 0 { errMsgs := []string{} for _, e := range errs { diff --git a/aah/util.go b/aah/util.go index 16d212a..823fa4c 100644 --- a/aah/util.go +++ b/aah/util.go @@ -377,3 +377,8 @@ func logError(v ...interface{}) { func logErrorf(format string, v ...interface{}) { cliLog.Errorf("ERROR "+format, v...) } + +func stripGoPath(pkgFilePath string) string { + idx := strings.Index(pkgFilePath, "src") + return filepath.Clean(pkgFilePath[idx+4:]) +} From 29109b49f889e16e0ff804f997d82517c68e706e Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 23 Apr 2018 07:35:46 -0700 Subject: [PATCH 08/48] app templete improvements, websocket addition, aah new command improvements with websocket, etc --- aah/aah.go | 4 +- aah/app-template/.gitignore | 2 +- aah/app-template/aah.project.atmpl | 6 +- aah/app-template/app/controllers/app.go.atmpl | 10 +- .../app/controllers/v1/value.go.atmpl | 104 ++++ aah/app-template/app/init.go.atmpl | 43 +- aah/app-template/app/models/value.go | 7 + .../app/security/authentication_provider.go | 10 +- .../app/websockets/sample.go.atmpl | 80 +++ aah/app-template/config/aah.conf.atmpl | 60 ++- aah/app-template/config/env/dev.conf.atmpl | 10 +- aah/app-template/config/env/prod.conf.atmpl | 12 + aah/app-template/config/routes.conf.atmpl | 74 ++- aah/app-template/config/security.conf.atmpl | 56 +- aah/app-template/i18n/messages.en.atmpl | 2 +- aah/app_tmpl.go | 95 ++++ aah/build.go | 5 +- aah/compile.go | 131 +++-- aah/new.go | 477 ++++++++++-------- aah/run.go | 67 ++- aah/util.go | 2 +- 21 files changed, 922 insertions(+), 335 deletions(-) create mode 100644 aah/app-template/app/controllers/v1/value.go.atmpl create mode 100644 aah/app-template/app/models/value.go create mode 100644 aah/app-template/app/websockets/sample.go.atmpl create mode 100644 aah/app_tmpl.go diff --git a/aah/aah.go b/aah/aah.go index d52cfd9..0de4ab9 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -43,8 +43,8 @@ var ( gocmd string gosrcDir string - libNames = []string{"tools", "aah", "ahttp", "aruntime", "config", "essentials", "forge", "i18n", - "log", "router", "security", "test", "valpar", "view"} + libNames = []string{"tools", "ainsp", "aah", "ahttp", "aruntime", "config", "essentials", "forge", "i18n", + "log", "router", "security", "test", "valpar", "view", "ws"} // abstract it, so we can do unit test fatal = log.Fatal diff --git a/aah/app-template/.gitignore b/aah/app-template/.gitignore index 0d8386d..19f55cb 100644 --- a/aah/app-template/.gitignore +++ b/aah/app-template/.gitignore @@ -1,4 +1,4 @@ -# aah framework application - .gitignore +# aah application - .gitignore aah.go *.pid diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index 44964c1..b845227 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -1,5 +1,5 @@ ############################################################## -# {{ .AppName }} - aah framework project +# {{ .App.Name }} - aah framework project # # Note: Add it to version control ############################################################## @@ -8,7 +8,7 @@ build { # Application binary name # Default value is `name` attribute value from `aah.conf` - #binary_name = "{{ .AppName }}" + #binary_name = "{{ .App.Name }}" # Used as fallback if # - `git commit sha` or @@ -18,7 +18,7 @@ build { # If application is missing any dependencies in `build import path` # during a compile and build, aah CLI will try to get dependencies # using 'go get '. - # Default value is `false`. + # Default value is `true`. #dep_get = true flags = ["-i"] diff --git a/aah/app-template/app/controllers/app.go.atmpl b/aah/app-template/app/controllers/app.go.atmpl index d1b6ede..4ca002b 100644 --- a/aah/app-template/app/controllers/app.go.atmpl +++ b/aah/app-template/app/controllers/app.go.atmpl @@ -2,7 +2,8 @@ package controllers import ( "aahframework.org/aah.v0" - "{{.AppImportPath}}/app/models" + + "{{ .App.ImportPath }}/app/models" ) // AppController struct application controller @@ -10,9 +11,12 @@ type AppController struct { *aah.Context } -// Index method is application {{ if eq .AppType "web" -}}home page.{{ else }}root API endpoint.{{- end }} +// Index method is application {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} +// +// Route: / +// Method: GET func (a *AppController) Index() { - {{- if eq .AppType "web" }} + {{- if .App.IsWebApp }} data := aah.Data{ "Greet": models.Greet{ Message: "Welcome to aah framework - Web Application", diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl new file mode 100644 index 0000000..b329f91 --- /dev/null +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -0,0 +1,104 @@ +package v1 + +import ( + "aahframework.org/aah.v0" + + "{{ .App.ImportPath }}/app/models" +) + +var values = make(map[string]*models.Value) + +// ValueController is kickstart sample for API implementation. +type ValueController struct { + *aah.Context +} + +// List method returns all the values. +// +// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values +// Method: GET +func (v *ValueController) List() { + v.Reply().Ok().JSON(values) +} + +// Index method returns value for given key. +// If key not found then returns 404 NotFound error. +// +// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key +// Method: GET +func (v *ValueController) Index(key string) { + if val, found := values[key]; found { + v.Reply().Ok().JSON(val) + return + } + + v.Reply().NotFound().JSON(aah.Data{ + "message": "Value not exists", + }) +} + +// Create method creates new entry in the values map with given payload. +// If key already exists then returns 409 Conflict error. +// +// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values +// Method: POST +// Request Body: +// { +// "key": "key1" +// "value": 3437476 // any valid type, since value is type interface{} +// } +func (v *ValueController) Create(val *models.Value) { + if _, found := values[val.Key]; found { + v.Reply().Conflict().JSON(aah.Data{ + "message": "Key already exists", + }) + return + } + + // Add it to values map + values[val.Key] = val + v.Reply().Created() +} + +// Update method updates value entry on map for given key and Payload. +// If key not exists then returns 400 BadRequest error. +// +// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key +// Method: PUT +// Request Body: +// { +// "value": 3437476 // any valid type, since value is type interface{} +// } +func (v *ValueController) Update(key string, val *models.Value) { + if c, found := values[key]; found { + c.Value = val.Value + values[key] = c + v.Reply().Ok().JSON(aah.Data{ + "message": "Value updated successfully", + }) + return + } + + v.Reply().BadRequest().JSON(aah.Data{ + "message": "Invalid input", + }) +} + +// Delete method deletes value for given key. +// If key not exists then returns 400 BadRequest error. +// +// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key +// Method: DELETE +func (v *ValueController) Delete(key string) { + if _, found := values[key]; found { + delete(values, key) + v.Reply().Ok().JSON(aah.Data{ + "message": "Value deleted successfully", + }) + return + } + + v.Reply().BadRequest().JSON(aah.Data{ + "message": "Invalid input", + }) +} diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index 640692a..f5b858b 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -3,11 +3,13 @@ package main -import ( - "aahframework.org/aah.v0"{{ if eq .AppType "web" }} +import ({{ if .App.IsSecurityEnabled }} + "{{ .App.ImportPath }}/app/security"{{ end }} + + "aahframework.org/aah.v0"{{ if .App.IsWebApp }} // Registering HTML minifier for web application - _ "github.com/aah-cb/minify"{{ end }}{{ if eq .AppViewEngine "pug" }} + _ "github.com/aah-cb/minify"{{ end }}{{ if eq .App.ViewEngine "pug" }} // Registering Pug View Engine (formerly known as Jade) _ "github.com/aah-cb/ve-pug"{{ end }} @@ -46,7 +48,34 @@ func init() { // aah.OnShutdown(cache.Flush) // aah.OnShutdown(db.Disconnect) - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + {{ if .App.IsSecurityEnabled }}// Event: OnPostAuth + // Published right after the Authentication successful + aah.OnPostAuth(security.PostAuthEvent){{ end }} + + {{ if .App.IsWebSocketApp -}} + // WebSocket Events + // Doc: https://docs.aahframework.org/websocket.html#events + // + // Add your WebSocket events on app start. + aah.OnStart(func(e *aah.Event) { + // Event: OnPreConnect + // aah.OnWSPreConnect(websocket.PreConnect) + + // Event: OnPostConnect + // aah.OnWSPostConnect(websocket.PostConnect) + + // Event: AuthCallback + // aah.SetWSAuthCallback(websocket.DoAuth) + + // Event: OnPostDisconnect + // aah.OnWSPostDisconnect(websocket.PostDisconnect) + + // Event: OnError + // aah.OnWSError(websocket.Error) + }) + {{ end -}} + + {{ if not .App.IsWebSocketApp -}}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Middleware's // Doc: https://docs.aahframework.org/middleware.html // @@ -56,7 +85,7 @@ func init() { aah.Middlewares( aah.RouteMiddleware, aah.CORSMiddleware, - aah.BindMiddleware,{{ if eq .AppType "web" }} + aah.BindMiddleware,{{ if .App.IsWebApp }} aah.AntiCSRFMiddleware,{{ end }} aah.AuthcAuthzMiddleware, @@ -71,9 +100,9 @@ func init() { // Add Application Error Handler // Doc: https://docs.aahframework.org/error-handling.html //__________________________________________________________________________ - // aah.SetErrorHandler(AppErrorHandler) + // aah.SetErrorHandler(AppErrorHandler){{ end }} - {{- if eq .AppType "web" }} + {{- if .App.IsWebApp }} //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Add Custom Template Functions diff --git a/aah/app-template/app/models/value.go b/aah/app-template/app/models/value.go new file mode 100644 index 0000000..aa817d9 --- /dev/null +++ b/aah/app-template/app/models/value.go @@ -0,0 +1,7 @@ +package models + +// Value model is used by ValueController +type Value struct { + Key string `json:"key" validate:"required"` + Value interface{} `json:"value" validate:"required"` +} diff --git a/aah/app-template/app/security/authentication_provider.go b/aah/app-template/app/security/authentication_provider.go index 7c23ad2..0740e05 100644 --- a/aah/app-template/app/security/authentication_provider.go +++ b/aah/app-template/app/security/authentication_provider.go @@ -56,13 +56,11 @@ func (a *AuthenticationProvider) GetAuthenticationInfo(authcToken *authc.Authent return authcInfo, nil } -func postAuthEvent(e *aah.Event) { +// PostAuthEvent method used for activities after authentication successful. +func PostAuthEvent(e *aah.Event) { ctx := e.Data.(*aah.Context) - // Do post successful authentication actions... - _ = ctx -} + ctx.Log().Info("Called method security.PostAuthEvent") -func init() { - aah.OnPostAuth(postAuthEvent) + // Do post successful authentication actions... } diff --git a/aah/app-template/app/websockets/sample.go.atmpl b/aah/app-template/app/websockets/sample.go.atmpl new file mode 100644 index 0000000..1e55cd9 --- /dev/null +++ b/aah/app-template/app/websockets/sample.go.atmpl @@ -0,0 +1,80 @@ +package websockets + +import ( + "strings" + + "aahframework.org/aah.v0" + "aahframework.org/ws.v0" + + "{{ .App.ImportPath }}/app/models" +) + +// SampleWebSocket struct is sample WebSocket implementation. +type SampleWebSocket struct { + *ws.Context +} + +// Handle method is sample WebSocket action implementation. +// +// It handles Text and JSON encoding based on Path param value. +// +// Route: /ws/:mode +// Method: WS +func (s *SampleWebSocket) Handle(mode string) { + switch strings.ToLower(mode) { + case "text": + s.handleTextMode() + case "json": + s.handleJSONMode() + } +} + +// handleTextMode method is used to communicate in Text +func (s *SampleWebSocket) handleTextMode() { + s.Log().Info("Handling mode: text") + + for { + str, err := s.ReadText() + if err != nil { + if ws.IsDisconnected(err) { + // WebSocket client is gone, exit here + return + } + + s.Log().Error(err) + continue // we are moving on to next WS frame/msg + } + + // if no error, echo back Text to WebSocket client + if err = s.ReplyText(str); err != nil { + s.Log().Error(err) + } + } +} + +// handleJSONMode method is used to communicate in JSON +func (s *SampleWebSocket) handleJSONMode() { + s.Log().Infof("Handling mode: json") + + for { + var greet models.Greet + if err := s.ReadJSON(&greet); err != nil { + if ws.IsDisconnected(err) { + // WebSocket client is gone, exit here + return + } + + s.Log().Error(err) + + // Its a JSON read error + _ = s.ReplyJSON(aah.Data{"message": "invalid JSON"}) + + continue // we are moving on to next WS frame/msg + } + + // if no error, echo back JSON to WebSocket client + if err := s.ReplyJSON(greet); err != nil { + s.Log().Error(err) + } + } +} diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index c5861ce..c5cad2a 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -1,19 +1,19 @@ ################################################### -# {{ .AppName }} - aah framework application +# {{ .App.Name }} - aah Application # # Complete configuration reference: -# https://docs.aahframework.org/app-config.html +# Doc: https://docs.aahframework.org/app-config.html ################################################### # Application name (non-whitespace) # Default value is `basename` of import path. -name = "{{ .AppName }}" +name = "{{ .App.Name }}" # Friendly description of application -desc = "aah framework {{ .AppType }} application" +desc = "aah {{ .App.Type }} application" # Application type, typically either Web or API. -type = "{{ .AppType }}" +type = "{{ .App.Type }}" # Application instance name is used when you're running aah application cluster. # This value is used in the context based logging, it distinguishes your instance @@ -76,6 +76,31 @@ server { # Default value is `true`. #keep_alive = true + {{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket -}} + # -------------------------------------------------------------------------- + # WebSocket configurations + # Doc: https://docs.aahframework.org/websocket.html#configuration + # -------------------------------------------------------------------------- + websocket { + # Default value is `false`. + enable = true + + # Origin configuration + origin { + # When origin check is enabled, aah does check against HTTP Header + # `Origin` value. + # Default value is `false`. + check = false + + # Origin whitelist + # No default values. + #whitelist = [ + # "https://websocket.org", + #] + } + } + {{ end }} + ssl { # Default value is `false`. #enable = false @@ -149,6 +174,7 @@ server { } } + {{ if not .App.IsWebSocketApp -}} # -------------------------------------------------------------------------- # To manage aah server effectively it is necessary to know details about the # request, response, processing time, client IP address, etc. aah framework @@ -270,9 +296,9 @@ request { # Tag Name is used for bind values to struct exported fields. # Default value is `bind`. #tag_name = "bind" - } + }{{ end }} } -{{ if eq .AppType "web" -}} +{{ if .App.IsWebApp -}} # --------------------------------------------------------------- # i18n configuration # Doc: https://docs.aahframework.org/app-config.html#section-i18n @@ -334,6 +360,7 @@ runtime { } } +{{ if not .App.IsWebSocketApp -}} # ----------------------------------------------------------------- # Render configuration # Doc: https://docs.aahframework.org/app-config.html#section-render @@ -347,12 +374,7 @@ render { # - Based `render.default` value supported types are `html`, `json`, `xml` and `text` # - Finally aah framework uses `http.DetectContentType` API # Default value is `empty` string. - default = "{{ if eq .AppType "web" }}html{{ else }}json{{ end }}" - - # Pretty print option is helpful in `dev` environment profile. - # It is only applicable to JSON and XML. - # Default value is `false`. - #pretty = true + default = "{{ if .App.IsWebApp }}html{{ else }}json{{ end }}" # To prevent Cross Site Script Inclusion (XSSI) attacks on JSON response # payload aka JSON vulnerability. XSSI attack is only successful if the @@ -382,7 +404,7 @@ render { #level = 4 } } -{{ if eq .AppType "web" -}} +{{ if .App.IsWebApp -}} # ------------------------------------------------------------------ # Cache configuration # Doc: https://docs.aahframework.org/static-files.html#cache-control @@ -420,12 +442,12 @@ view { # Choosing view engine for application. You could implement # on your own with simple interface `view.Enginer`. # Default value is `go`. - engine = "{{ .AppViewEngine }}" + engine = "{{ .App.ViewEngine }}" # Choose your own view file extension. # Default value is chosen based on `view.engine`, # while creating a new app using command `aah new`. - ext = "{{ .AppViewFileExt }}" + ext = "{{ .App.ViewFileExt }}" # Choose whether you need a case sensitive view file resolve or not. # For e.g.: "/views/pages/app/login.tmpl" == "/views/pages/App/Login.tmpl" @@ -433,8 +455,8 @@ view { #case_sensitive = false # To use custom Go template delimiters for view files. - # Default value is `{{ .TmplDemils }}`. - #delimiters = "{{ .TmplDemils }}" + # Default value is `{{ .App.TmplDemils }}`. + #delimiters = "{{ .App.TmplDemils }}" # Framework chooses the default app layout as `master.html` if you do not # provide one. However you may have pages or template without layout too. @@ -447,7 +469,7 @@ view { # Application Security # Doc: https://docs.aahframework.org/security-config.html # -------------------------------------------------------------- -include "./security.conf" +include "./security.conf"{{ end }} # -------------------------------------------------------------- # Environment Profiles e.g.: dev, qa, prod diff --git a/aah/app-template/config/env/dev.conf.atmpl b/aah/app-template/config/env/dev.conf.atmpl index 43598ab..326b737 100644 --- a/aah/app-template/config/env/dev.conf.atmpl +++ b/aah/app-template/config/env/dev.conf.atmpl @@ -17,7 +17,7 @@ dev { # Level indicates the logging levels like `ERROR`, `WARN`, `INFO`, `DEBUG`, # `TRACE`, FATAL and PANIC. Config value can be in lowercase or uppercase. # Default value is `debug`. - level = "info" + #level = "debug" # Format to define log entry output format. Supported formats are `text` and `json`. # Default value is `text`. @@ -34,12 +34,4 @@ dev { #color = false } - # ------------------------- - # Render configuration - # ------------------------- - render { - # pretty is only applicable to JSON & XML rendering - pretty = true - } - } diff --git a/aah/app-template/config/env/prod.conf.atmpl b/aah/app-template/config/env/prod.conf.atmpl index bc14244..905e65a 100644 --- a/aah/app-template/config/env/prod.conf.atmpl +++ b/aah/app-template/config/env/prod.conf.atmpl @@ -4,6 +4,18 @@ prod { + # ------------------------------------------------------------------ + # Runtime configuration + # Doc: https://docs.aahframework.org/app-config.html#section-runtime + # ------------------------------------------------------------------ + runtime { + debug { + # Whether to strip source `src` base path from file path. + # Default value is `false`. + strip_src_base = true + } + } + # -------------------------------------------------- # Log Configuration # Doc: https://docs.aahframework.org/logging.html diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index 8679517..ecac552 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -1,8 +1,8 @@ #################################################### -# {{ .AppName }} - Application Routes Configuration +# {{ .App.Name }} - Application Routes Configuration # # Complete routes configuration reference: -# https://docs.aahframework.org/routes-config.html +# Doc: https://docs.aahframework.org/routes-config.html #################################################### #------------------------------------------------------------------------ @@ -13,14 +13,15 @@ domains { # Pick your choice of an `unique keyname` to define your domain section # in the routes configuration. # For e.g.: Domain name/ip address with port no - localhost { - name = "{{ .AppName }} routes" + {{ .App.Name }} { + name = "{{ .App.Name }} routes" # aah supports multi-domain routes configuration out-of-the-box. # `host` used to determine domain routes for the incoming request. # For e.g: example.org host = "localhost" + {{ if not .App.IsWebSocketApp -}} # Redirect trailing slash is to enable automatic redirection if the current # route can't be matched but a `route` for the path with (without) # the trailing slash exists. @@ -30,21 +31,21 @@ domains { # aah supports out-of-the-box `405 MethodNotAllowed` status with `Allow` # header as per `RFC7231`. Perfect for RESTful APIs. # Default value is `true`. - {{ if eq .AppType "web" }}#{{ end }}method_not_allowed = true + {{ if .App.IsWebApp }}#{{ end }}method_not_allowed = true # aah framework supports out-of-the-box `OPTIONS` request replies. # User defined `OPTIONS` routes take priority over the automatic replies. # Perfect for RESTful APIs. # Default value is `true`. - {{ if eq .AppType "web" }}#{{ end }}auto_options = true + {{ if .App.IsWebApp }}#{{ end }}auto_options = true # Default auth is used when route does not have attribute `auth` defined. # If you don't define attribute `auth` then framework treats that route as # `anonymous` auth scheme. # Default value is empty string. - #default_auth = "" + #default_auth = ""{{- end }} - {{ if .AppCORSEnable -}} + {{ if .App.CORSEnable -}} #---------------------------------------------------------------------------- # CORS (Cross-Origin Resource Sharing) # Doc: https://docs.aahframework.org/cors.html @@ -89,7 +90,7 @@ domains { #allow_credentials = true }{{ end }} - {{ if eq .AppType "web" -}} + {{ if .App.IsWebApp -}} #---------------------------------------------------------------------------- # Static Routes Configuration # To serve static files, it can be directory or individual file. @@ -140,15 +141,18 @@ domains { }{{ end }} #----------------------------------------------------------------------------- - # Application routes + # Application routes definitions + # # Doc: https://docs.aahframework.org/routes-config.html#section-routes # Doc: https://docs.aahframework.org/routes-config.html#namespace-group-routes #----------------------------------------------------------------------------- routes { - #------------------------------------------------------ + {{ if not .App.IsWebSocketApp -}}#------------------------------------------------------ # Pick an unique name, it's called `route name`, # used for reverse URL. + # + # Pro Tip: https://docs.aahframework.org/routes-config.html#pro-tips-for-nested-namespace-routes #------------------------------------------------------ index { # path is used to match incoming requests @@ -194,18 +198,60 @@ domains { # If this value is not provided then global `request.max_body_size` config # from `aah.conf` is applied. So use it for specific cases. # No default value, global value is applied. - #max_body_size = "5mb" + #max_body_size = "5mb"{{ if .App.IsWebApp }} # Optionally you can disable Anti-CSRF check for particular route. # There are cases you might need this option. In-general don't disable the check. # Default value is `true`. - #anti_csrf_check = false + #anti_csrf_check = false{{ end }} } - {{ if eq .AppAuthScheme "form" -}}login_submit { + {{ if eq .App.AuthScheme "form" -}}login_submit { path ="/login" method = "POST" controller = "VirtualFormController" + }{{ end }}{{ end -}}{{ if or .App.IsAPIApp .App.IsSubTypeAPI }} + + # Below routes definition demonstrates part of aah routes configuration + # capabilities + api_v1 { + path = "{{ if .App.IsWebApp }}/api{{ end }}/v1" + auth = "anonymous" + {{ if .App.IsWebApp }}anti_csrf_check = false{{ end }} + + routes { + all_values { + path = "/values" + controller = "{{ if .App.IsWebApp }}/api{{ end }}v1/ValueController" + action = "List" + + routes { + value_get { # /v1/values/:key + path = "/:key" + } + value_create { # /v1/values + method = "POST" + } + value_update { # /v1/values/:key + path = "/:key" + method = "PUT" + } + value_delete { # /v1/values/:key + path = "/:key" + method = "DELETE" + } + } + } # end - all_values + } + } # end - api_v1 + + {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }}# WebSocket routes definitions + # Sample implementation handles Text and JSON mode + ws_sample { + path = "/ws/:mode" + websocket = "SampleWebSocket" + method = "WS" + action = "Handle" }{{ end }} } # end - routes diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index 6133dae..1aade04 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -1,8 +1,8 @@ ###################################################### -# {{ .AppName }} - Application Security Configuration +# {{ .App.Name }} - Application Security Configuration # # Complete routes configuration reference: -# https://docs.aahframework.org/security-config.html +# Doc: https://docs.aahframework.org/security-config.html ###################################################### security { @@ -11,7 +11,7 @@ security { # Doc: https://docs.aahframework.org/security-design.html # ------------------------------------------------------- auth_schemes { - {{ if eq .AppAuthScheme "form" -}} + {{ if eq .App.AuthScheme "form" -}} # HTTP Form Auth Scheme # It is custom defined name, this is used in the routes `auth` attribute. form_auth { @@ -35,7 +35,7 @@ security { # it with application provide credential. # Doc: https://docs.aahframework.org/password-encoders.html # Default value is `bcrypt`. - password_encoder = "{{ .AppPasswordEncoder }}" + password_encoder = "{{ .App.PasswordEncoderAlgo }}" # Field names are used to extract `AuthenticationToken` from request. field { @@ -73,7 +73,7 @@ security { } }{{ end -}} - {{ if eq .AppAuthScheme "basic" -}} + {{ if eq .App.AuthScheme "basic" -}} # HTTP Basic Auth Scheme # It is custom defined name, this is used in the routes `auth` attribute. basic_auth { @@ -87,11 +87,11 @@ security { # However aah framework does its due diligence. realm_name = "Protected" - {{ if eq .AppBasicAuthMode "file-realm" -}} + {{ if eq .App.BasicAuthMode "file-realm" -}} # Basic auth realm file path. You can use absolute path or # environment variable to provide path. # It is required value, no default. - file_realm = "{{ .AppBasicAuthFileRealmPath }}"{{ else -}} + file_realm = "{{ .App.BasicAuthFileRealmPath }}"{{ else -}} # Framework calls `Authenticator` to get the Subject's authentication # information. Then framework validates the credential using password # encoder. @@ -107,10 +107,10 @@ security { # it with application provide credential. # Doc: https://docs.aahframework.org/password-encoders.html # Default value is `bcrypt`. - password_encoder = "{{ .AppPasswordEncoder }}" + password_encoder = "{{ .App.PasswordEncoderAlgo }}" }{{ end -}} - {{ if eq .AppAuthScheme "generic" -}} + {{ if eq .App.AuthScheme "generic" -}} # Generic Auth Scheme # It is custom defined name, this is used in the routes `auth` attribute. generic_auth { @@ -148,7 +148,7 @@ security { # aah supports `bcrypt`, `scrypt`, `pbkdf2` password algorithm # Doc: https://docs.aahframework.org/password-encoders.html # ------------------------------------------------------------ - password_encoder { {{ if eq .AppPasswordEncoder "bcrypt" }} + password_encoder { {{ if eq .App.PasswordEncoderAlgo "bcrypt" }} # bcrypt algorithm # # Learn more: @@ -163,7 +163,7 @@ security { # Default value is `12`. cost = 12 }{{ end }} - {{ if eq .AppPasswordEncoder "scrypt" }} + {{ if eq .App.PasswordEncoderAlgo "scrypt" }} # scrypt algorithm # # Learn more: @@ -191,7 +191,7 @@ security { # Default value is `24` #salt_length = 24 }{{ end }} - {{ if eq .AppPasswordEncoder "pbkdf2" }} + {{ if eq .App.PasswordEncoderAlgo "pbkdf2" }} # pbkdf2 algorithm # # Learn more: @@ -219,7 +219,7 @@ security { }{{ end }} } - {{ if eq .AppType "web" }}{{ if or (eq .AppAuthScheme "form") (eq .AppAuthScheme "basic") -}} + {{ if .App.IsWebApp }}{{ if or (eq .App.AuthScheme "form") (eq .App.AuthScheme "basic") -}} # ----------------------------------------------------------------------- # Session configuration # HTTP state management across multiple requests. @@ -230,7 +230,7 @@ security { # destroyed at the end of the request. Supported values are `stateless` # and `stateful`. # Default value is `stateless` for API and `stateful` for Web app. - mode = "{{ if eq .AppAuthScheme "" }}stateless{{ else }}stateful{{ end }}" + mode = "{{ if eq .App.AuthScheme "" }}stateless{{ else }}stateful{{ end }}" # Session store is to choose where session value should be persisted. store { @@ -238,12 +238,12 @@ security { # Also framework provide extensible `session.Storer` interface to # add custom session store. # Default value is `cookie`. - type = "{{ .AppSessionStore }}" - {{ if eq .AppSessionStore "file" -}} + type = "{{ .App.SessionStore }}" + {{ if eq .App.SessionStore "file" -}} # Filepath is used for file store to store session file in the file system. # This is only applicable for `type = "file"`, make sure application has # Read/Write access to the directory. Provide absolute path. - filepath = "{{ .AppSessionFileStorePath }}"{{ end }} + filepath = "{{ .App.SessionFileStorePath }}"{{ end }} } # Session ID length @@ -257,7 +257,7 @@ security { # Session cookie name prefix. # Default value is `aah_` For e.g.: `aah_myapp_session` - prefix = "aah_{{ .AppName }}" + prefix = "aah_{{ .App.Name }}" # Default value is `empty` string. #domain = "" @@ -280,13 +280,13 @@ security { # should be same in all instance. For HMAC sign & verify it recommend to use # key size is `32` or `64` bytes. # Default value is `64` bytes (`aah new` generates strong one). - sign_key = "{{ .AppSessionSignKey }}" + sign_key = "{{ securerandomstring 64 }}" # HTTP session cookie value encryption and decryption using `AES`. For server # farm this should be same in all instance. AES algorithm is used, valid # lengths are `16`, `24`, or `32` bytes to select `AES-128`, `AES-192`, or `AES-256`. # Default value is `32` bytes (`aah new` generates strong one). - enc_key = "{{ .AppSessionEncKey }}" + enc_key = "{{ securerandomstring 32 }}" # Cleanup Interval is used to clean the expired session objects from store. # This is only applicable for non-cookie store type. @@ -294,9 +294,9 @@ security { # `m -> minutes`, `h -> hours`. # Default value is `30m`. #cleanup_interval = "30m" - }{{ end }}{{ end -}} + }{{ end }}{{ end }} - {{ if eq .AppType "web" -}} + {{ if .App.IsWebApp }} # ------------------------------------------------------------ # Anti-CSRF Protection # Doc: https://docs.aahframework.org/anti-csrf-protection.html @@ -337,13 +337,13 @@ security { # should be same in all instance. For HMAC sign & verify it recommend to use # key size is `32` or `64` bytes. # Default value is `64` bytes (`aah new` generates strong one). - sign_key = "{{ .AppAntiCSRFSignKey }}" + sign_key = "{{ securerandomstring 64 }}" # Anti-CSRF cookie value encryption and decryption using `AES`. For server # farm this should be same in all instance. AES algorithm is used, valid # lengths are `16`, `24`, or `32` bytes to select `AES-128`, `AES-192`, or `AES-256`. # Default value is `32` bytes (`aah new` generates strong one). - enc_key = "{{ .AppAntiCSRFEncKey }}" + enc_key = "{{ securerandomstring 32 }}" }{{ end }} # --------------------------------------------------------------------------- @@ -370,7 +370,7 @@ security { # Encouraged to make use of header `Content-Security-Policy` with enhanced # policy to reduce XSS risk along with header `X-XSS-Protection`. # Default values is `1; mode=block`. - {{ if eq .AppType "web" -}}#xxssp = "1; mode=block"{{ else }}xxssp = ""{{ end }} + {{ if .App.IsWebApp -}}#xxssp = "1; mode=block"{{ else }}xxssp = ""{{ end }} # X-Content-Type-Options # Prevent Content Sniffing or MIME sniffing. @@ -388,7 +388,7 @@ security { # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xfo # https://www.keycdn.com/blog/x-frame-options/ # Default value is `SAMEORIGIN`. - {{ if eq .AppType "web" -}}#xfo = "SAMEORIGIN"{{ else }}xfo = "DENY"{{ end }} + {{ if .App.IsWebApp -}}#xfo = "SAMEORIGIN"{{ else }}xfo = "DENY"{{ end }} # Referrer-Policy # This header governs which referrer information, sent in the Referer header, should @@ -400,7 +400,7 @@ security { # https://scotthelme.co.uk/a-new-security-header-referrer-policy/ # https://www.w3.org/TR/referrer-policy/ # Default value is `no-referrer-when-downgrade`. - {{ if eq .AppType "web" -}}#rp = "no-referrer-when-downgrade"{{ else }}rp = ""{{ end }} + {{ if .App.IsWebApp -}}#rp = "no-referrer-when-downgrade"{{ else }}rp = ""{{ end }} # Strict-Transport-Security (STS, aka HSTS) # STS header that lets a web site tell browsers that it should only be communicated @@ -508,6 +508,6 @@ security { # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xpcdp # https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html # Default value is `master-only`. - {{ if eq .AppType "web" -}}#xpcdp = "master-only"{{ else }}xpcdp = ""{{ end }} + {{ if .App.IsWebApp -}}#xpcdp = "master-only"{{ else }}xpcdp = ""{{ end }} } } diff --git a/aah/app-template/i18n/messages.en.atmpl b/aah/app-template/i18n/messages.en.atmpl index 6a82d11..6357dd8 100644 --- a/aah/app-template/i18n/messages.en.atmpl +++ b/aah/app-template/i18n/messages.en.atmpl @@ -1,5 +1,5 @@ ############################################# -# i18n messages for {{.AppName}} application +# i18n messages for {{ .App.Name }} application # # Complete configuration reference: # https://docs.aahframework.org/i18n.html diff --git a/aah/app_tmpl.go b/aah/app_tmpl.go new file mode 100644 index 0000000..45e3b16 --- /dev/null +++ b/aah/app_tmpl.go @@ -0,0 +1,95 @@ +package main + +import ( + "text/template" + + "aahframework.org/essentials.v0" +) + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// appTmplData and its methods +//______________________________________________________________________________ + +const ( + typeWeb = "web" + typeAPI = "api" + typeWebSocket = "websocket" + storeCookie = "cookie" + storeFile = "file" + aahTmplExt = ".atmpl" + authForm = "form" + authBasic = "basic" + authGeneric = "generic" + authNone = "none" + basicFileRealm = "file-realm" +) + +// appTmplData struct holds inputs collected from user for new aah creation +type appTmplData struct { + Name string + Type string + ImportPath string + BaseDir string + ViewEngine string + ViewFileExt string + AuthScheme string + BasicAuthMode string + PasswordEncoderAlgo string + SessionStore string + SessionFileStorePath string + BasicAuthFileRealmPath string + CORSEnable bool + TmplDemils string + SubTypes []string +} + +func (a *appTmplData) IsWebApp() bool { + return a.Type == typeWeb +} + +func (a *appTmplData) IsAPIApp() bool { + return a.Type == typeAPI +} + +func (a *appTmplData) IsWebSocketApp() bool { + return a.Type == typeWebSocket +} + +func (a *appTmplData) IsAuthSchemeForWeb() bool { + return a.Type == typeWeb && (a.AuthScheme == authForm || a.AuthScheme == authBasic) +} + +func (a *appTmplData) IsAuthSchemeForAPI() bool { + return a.Type == typeAPI && (a.AuthScheme == authGeneric || a.AuthScheme == authBasic) +} + +func (a *appTmplData) IsSecurityEnabled() bool { + return !ess.IsStrEmpty(a.AuthScheme) +} + +func (a *appTmplData) IsSubTypeAPI() bool { + return a.checkSubType(typeAPI) +} + +func (a *appTmplData) IsSubTypeWebSocket() bool { + return a.checkSubType(typeWebSocket) +} + +func (a *appTmplData) checkSubType(t string) bool { + for _, v := range a.SubTypes { + if v == t { + return true + } + } + return false +} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Template funcs +//______________________________________________________________________________ + +var appTemplateFuncs = template.FuncMap{ + "securerandomstring": func(length int) string { + return ess.SecureRandomString(length) + }, +} diff --git a/aah/build.go b/aah/build.go index e5b9732..7f083d8 100644 --- a/aah/build.go +++ b/aah/build.go @@ -54,7 +54,10 @@ var buildCmd = cli.Command{ func buildAction(c *cli.Context) error { importPath := getAppImportPath(c) - aah.Init(importPath) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } + appBaseDir := aah.AppBaseDir() projectCfg := aahProjectCfg(appBaseDir) cliLog = initCLILogger(projectCfg) diff --git a/aah/compile.go b/aah/compile.go index 829d944..226cdca 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -41,6 +41,7 @@ func compileApp(args *compileArgs) (string, error) { appImportPath := aah.AppImportPath() appCodeDir := filepath.Join(appBaseDir, "app") appControllersPath := filepath.Join(appCodeDir, "controllers") + appWebSocketsPath := filepath.Join(appCodeDir, "websockets") appBuildDir := filepath.Join(appBaseDir, "build") appName := projectCfg.StringDefault("name", aah.AppName()) @@ -53,37 +54,80 @@ func compileApp(args *compileArgs) (string, error) { registeredActions := aah.AppRouter().RegisteredActions() // Go AST processing for Controllers - prg, errs := ainsp.Inspect(appControllersPath, ess.Excludes(excludes), registeredActions) - if len(errs) > 0 { - errMsgs := []string{} - for _, e := range errs { - errMsgs = append(errMsgs, e.Error()) + acntlr, errs := ainsp.Inspect(appControllersPath, ess.Excludes(excludes), registeredActions) + if len(acntlr.Packages) > 0 { + if len(errs) > 0 { + errMsgs := []string{} + for _, e := range errs { + errMsgs = append(errMsgs, e.Error()) + } + return "", errors.New(strings.Join(errMsgs, "\n")) } - return "", errors.New(strings.Join(errMsgs, "\n")) - } - // call the process - prg.Process() + // call the process + acntlr.Process() - // Print router configuration missing/error details - missingActions := []string{} - for c, m := range prg.RegisteredActions { - for a, v := range m { - if v == 1 && !router.IsDefaultAction(a) { - missingActions = append(missingActions, fmt.Sprintf("%s.%s", c, a)) + // Print router configuration missing/error details + missingActions := []string{} + for c, m := range acntlr.RegisteredActions { + for a, v := range m { + if v == 1 && !router.IsDefaultAction(a) { + missingActions = append(missingActions, fmt.Sprintf("%s.%s", c, a)) + } } } - } - if len(missingActions) > 0 { - logError("Following actions are configured in 'routes.conf', however not implemented in Controller:\n\t", - strings.Join(missingActions, "\n\t")) + if len(missingActions) > 0 { + logError("Following actions are configured in 'routes.conf', however not implemented in Controller:\n\t", + strings.Join(missingActions, "\n\t")) + } } // get all the types info referred aah framework context embedded - appControllers := prg.FindTypeByEmbeddedType(fmt.Sprintf("%s.Context", libImportPath("aah"))) - appImportPaths := prg.CreateImportPaths(appControllers) + appControllers := acntlr.FindTypeByEmbeddedType(fmt.Sprintf("%s.Context", libImportPath("aah"))) + appImportPaths := acntlr.CreateImportPaths(appControllers, map[string]string{}) appSecurity := appSecurity(aah.AppConfig(), appImportPaths) + // Go AST processing for WebSockets + registeredWSActions := aah.AppRouter().RegisteredWSActions() + wsc, errs := ainsp.Inspect(appWebSocketsPath, ess.Excludes(excludes), registeredWSActions) + if len(wsc.Packages) > 0 { + if len(errs) > 0 { + errMsgs := []string{} + for _, e := range errs { + errMsgs = append(errMsgs, e.Error()) + } + return "", errors.New(strings.Join(errMsgs, "\n")) + } + + // call the process + wsc.Process() + + // Print router configuration missing/error details + missingWSActions := []string{} + for c, m := range wsc.RegisteredActions { + for a, v := range m { + if v == 1 && !router.IsDefaultAction(a) { + missingWSActions = append(missingWSActions, fmt.Sprintf("%s.%s", c, a)) + } + } + } + if len(missingWSActions) > 0 { + logError("Following WebSocket actions are configured in 'routes.conf', however not implemented in WebSocket:\n\t", + strings.Join(missingWSActions, "\n\t")) + } + } + + appWebSockets := wsc.FindTypeByEmbeddedType(fmt.Sprintf("%s.Context", libImportPath("ws"))) + appImportPaths = wsc.CreateImportPaths(appWebSockets, appImportPaths) + + if len(appControllers) == 0 && len(appWebSockets) == 0 { + return "", fmt.Errorf("It seems your application have zero controller or websocket") + } + + if len(appControllers) > 0 || len(appWebSockets) > 0 { + appImportPaths[libImportPath("ainsp")] = "ainsp" + } + // prepare aah application version and build date appVersion := getAppVersion(appBaseDir, projectCfg) appBuildDate := getBuildDate() @@ -125,6 +169,7 @@ func compileApp(args *compileArgs) (string, error) { "AppBuildDate": appBuildDate, "AppBinaryName": appBinaryName, "AppControllers": appControllers, + "AppWebSockets": appWebSockets, "AppImportPaths": appImportPaths, "AppSecurity": appSecurity, "AppIsPackaged": args.AppPack, @@ -197,7 +242,7 @@ func checkAndGetAppDeps(appImportPath string, cfg *config.Config) error { notExistsPkgs = append(notExistsPkgs, pkg) } - if cfg.BoolDefault("build.dep_get", false) && len(notExistsPkgs) > 0 { + if cfg.BoolDefault("build.dep_get", true) && len(notExistsPkgs) > 0 { cliLog.Info("Getting application dependencies ...") if err := goGet(notExistsPkgs...); err != nil { return err @@ -309,7 +354,7 @@ import ( var ( // Defining flags - version = flag.Bool("version", false, "Display application name, version and build date.") + version = flag.Bool("version", false, "Display aah application name, version and build date.") configPath = flag.String("config", "", "Absolute path of external config file.") profile = flag.String("profile", "", "Environment profile name to activate. e.g: dev, qa, prod.") _ = reflect.Invalid @@ -382,32 +427,38 @@ func main() { log.Fatal(err) } + {{ if gt (len .AppControllers) 0 -}} // Adding all the application controllers which refers 'aah.Context' directly // or indirectly from app/controllers/** {{ range $i, $c := .AppControllers }} - aah.AddController( - (*{{ index $.AppImportPaths .ImportPath }}.{{ .Name }})(nil), - []*aah.MethodInfo{ - {{ range .Methods }}&aah.MethodInfo{ - Name: "{{ .Name }}", - Parameters: []*aah.ParameterInfo{ {{ range .Parameters -}} - &aah.ParameterInfo{Name: "{{ .Name }}", Type: reflect.TypeOf((*{{ .Type.Name }})(nil))},{{- end }} - }, - },{{ end }} - }, - ){{- end }} - - {{ if .AppSecurity -}} + aah.AddController((*{{ index $.AppImportPaths .ImportPath }}.{{ .Name }})(nil), []*ainsp.Method{ {{ range .Methods }} + {Name: "{{ .Name }}"{{ if gt (len .Parameters) 0 }}, Parameters: []*ainsp.Parameter{ {{ range .Parameters }} + {Name: "{{ .Name }}", Type: reflect.TypeOf((*{{ .Type.Name }})(nil))},{{- end }} + }{{ end }}},{{ end }} + }){{- end }} + {{ end -}} + + {{ if gt (len .AppWebSockets) 0 -}} + // Adding all the application websockets which refers 'ws.Context' directly + // or indirectly from app/websockets/** {{ range $i, $c := .AppWebSockets }} + aah.AddWebSocket((*{{ index $.AppImportPaths .ImportPath }}.{{ .Name }})(nil), []*ainsp.Method{ {{ range .Methods }} + {Name: "{{ .Name }}"{{ if gt (len .Parameters) 0 }}, Parameters: []*ainsp.Parameter{ {{ range .Parameters }} + {Name: "{{ .Name }}", Type: reflect.TypeOf((*{{ .Type.Name }})(nil))},{{- end }} + }{{ end }}},{{ end }} + }){{- end }} + {{ end -}} + + {{ if .AppSecurity }} // Initialize application security auth schemes - Authenticator & Authorizer secMgr := aah.AppSecurityManager() {{- range $k, $v := $.AppSecurity }} {{ if $v.Authenticator -}} - log.Debugf("Calling authenticator Init for auth scheme '%s'", "{{ $k }}") + aah.AppLog().Debugf("Calling authenticator Init for auth scheme '%s'", "{{ $k }}") if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { aah.AppLog().Fatal(err) } {{ end -}} {{ if $v.Authorizer -}} - log.Debugf("Calling authorizer Init for auth scheme '%s'", "{{ $k }}") + aah.AppLog().Debugf("Calling authorizer Init for auth scheme '%s'", "{{ $k }}") if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthorizer(&{{ $v.Authorizer }}{}); err != nil { aah.AppLog().Fatal(err) } @@ -431,9 +482,9 @@ func main() { sig := <-sc switch sig { case os.Interrupt: - log.Warn("Interrupt signal (SIGINT) received") + aah.AppLog().Warn("Interrupt signal (SIGINT) received") case syscall.SIGTERM: - log.Warn("Termination signal (SIGTERM) received") + aah.AppLog().Warn("Termination signal (SIGTERM) received") } // Call aah shutdown diff --git a/aah/new.go b/aah/new.go index cffcdf8..fc4c575 100644 --- a/aah/new.go +++ b/aah/new.go @@ -17,28 +17,15 @@ import ( "gopkg.in/urfave/cli.v1" - aah "aahframework.org/aah.v0" + "aahframework.org/aah.v0" "aahframework.org/essentials.v0" ) -const ( - typeWeb = "web" - typeAPI = "api" - storeCookie = "cookie" - storeFile = "file" - aahTmplExt = ".atmpl" - authForm = "form" - authBasic = "basic" - authGeneric = "generic" - authNone = "none" - basicFileRealm = "file-realm" -) - var ( newCmd = cli.Command{ Name: "new", Aliases: []string{"n"}, - Usage: "Create new aah 'web' or 'api' application (interactive)", + Usage: "Create new aah 'web', 'api' or 'websocket' application (interactive)", Description: `aah new command is an interactive program to assist you to quick start aah application. Just provide your inputs based on your use case to generate base structure to kick @@ -58,54 +45,45 @@ func newAction(c *cli.Context) error { fmt.Println() fmt.Println("Based on your inputs, aah CLI tool generates the aah application structure for you.") - // Collect data - importPath := getImportPath(reader) - appType := getAppType(reader) - viewEngineInfo := getViewEngine(reader, appType) - authScheme := getAuthScheme(reader, appType) - basicAuthMode := getBasicAuthMode(reader, authScheme) - passwordEncoder := getPasswordHashAlgorithm(reader, authScheme) - sessionStore := getSessionInfo(reader, appType, authScheme) - cors := getCORSInfo(reader) + // Collect inputs for aah app creation + importPath := collectImportPath(reader) + appType := collectAppType(reader) + + // Depends on application type choice, collect subsequent inputs + app := &appTmplData{ + ImportPath: importPath, + Type: appType, + } + + switch appType { + case typeWeb: + collectInputsForWebApp(app) + case typeAPI: + collectInputsForAPIApp(app) + } // Process it - appDir := filepath.Join(gosrcDir, filepath.FromSlash(importPath)) - appName := filepath.Base(appDir) - appSessionFilepath := filepath.ToSlash(filepath.Join(appDir, "sessions")) - data := map[string]interface{}{ - "AppName": appName, - "AppType": appType, - "AppImportPath": importPath, - "AppViewEngine": viewEngineInfo[0], - "AppViewFileExt": viewEngineInfo[1], - "AppAuthScheme": authScheme, - "AppBasicAuthMode": basicAuthMode, - "AppPasswordEncoder": passwordEncoder, - "AppSessionStore": sessionStore, - "AppSessionFileStorePath": appSessionFilepath, - "AppSessionSignKey": ess.SecureRandomString(64), - "AppSessionEncKey": ess.SecureRandomString(32), - "AppAntiCSRFSignKey": ess.SecureRandomString(64), - "AppAntiCSRFEncKey": ess.SecureRandomString(32), - "AppCORSEnable": cors, - "TmplDemils": "{{.}}", - } - - if basicAuthMode == basicFileRealm { - data["AppBasicAuthFileRealmPath"] = filepath.Join(appDir, "config", "basic-realm.conf") + app.BaseDir = filepath.Join(gosrcDir, filepath.FromSlash(importPath)) + app.Name = filepath.Base(app.BaseDir) + app.SessionFileStorePath = filepath.ToSlash(filepath.Join(app.BaseDir, "sessions")) + + if app.BasicAuthMode == basicFileRealm { + app.BasicAuthFileRealmPath = filepath.Join(app.BaseDir, "config", "basic-realm.conf") } else { - data["AppBasicAuthFileRealmPath"] = "/path/to/basic-realm.conf" + app.BasicAuthFileRealmPath = "/path/to/basic-realm.conf" } - if err := createAahApp(appDir, data); err != nil { + if err := createAahApp(app.BaseDir, map[string]interface{}{ + "App": app, + }); err != nil { logFatal(err) } - fmt.Printf("\nYour aah %s application was created successfully at '%s'\n", appType, appDir) - fmt.Printf("You shall run your application via the command: 'aah run --importpath %s'\n", importPath) + fmt.Printf("\nYour aah %s application was created successfully at '%s'\n", app.Type, app.BaseDir) + fmt.Printf("You shall run your application via the command: 'aah run --importpath %s'\n", app.ImportPath) fmt.Println("\nGo to https://docs.aahframework.org to learn more and customize your aah application.") - if basicAuthMode == basicFileRealm { + if app.BasicAuthMode == basicFileRealm { fmt.Println("\nNext step:") fmt.Println("\tCreate basic auth realm file per your application requirements.") fmt.Println("\tRefer to 'https://docs.aahframework.org/authentication.html#basic-auth-file-realm-format' to create basic auth realm file.") @@ -124,7 +102,7 @@ func readInput(reader *bufio.Reader, prompt string) string { return strings.TrimSpace(input) } -func getImportPath(reader *bufio.Reader) string { +func collectImportPath(reader *bufio.Reader) string { var importPath string for { importPath = filepath.ToSlash(readInput(reader, "\nEnter your application import path: ")) @@ -140,14 +118,14 @@ func getImportPath(reader *bufio.Reader) string { return strings.Replace(importPath, " ", "-", -1) } -func getAppType(reader *bufio.Reader) string { +func collectAppType(reader *bufio.Reader) string { var appType string for { - appType = readInput(reader, "\nChoose your application type (web or api), default is 'web': ") - if ess.IsStrEmpty(appType) || appType == typeWeb || appType == typeAPI { + appType = strings.ToLower(readInput(reader, "\nChoose your application type (web, api or websocket), default is 'web': ")) + if ess.IsStrEmpty(appType) || appType == typeWeb || appType == typeAPI || appType == typeWebSocket { break } else { - logError("Unsupported new aah application type, choose either 'web or 'api'") + logError("Unsupported new aah application type, choose either 'web', 'api' or 'websocket'") appType = "" } } @@ -157,11 +135,63 @@ func getAppType(reader *bufio.Reader) string { return appType } -func getViewEngine(reader *bufio.Reader, appType string) []string { - if appType != typeWeb { - return []string{"go", ".html"} +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Collecting inputs for Web App +//______________________________________________________________________________ + +func collectInputsForWebApp(app *appTmplData) { + app.TmplDemils = "{{.}}" + viewEngine(reader, app) + + authScheme(reader, app) + + if app.AuthScheme == authBasic { + basicAuthMode(reader, app) + } + + passwordHashAlgorithm(reader, app) + + sessionInfo(reader, app) + + // In the web application user may like to have API also WebSocket within it. + collectAppSubTypesChoice(reader, app) + + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o), default is 'N'") +} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Collecting inputs for API App +//______________________________________________________________________________ + +func collectInputsForAPIApp(app *appTmplData) { + authScheme(reader, app) + + if app.AuthScheme == authBasic { + basicAuthMode(reader, app) + } + + passwordHashAlgorithm(reader, app) + + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o), default is 'N'") +} + +func collectAppSubTypesChoice(reader *bufio.Reader, app *appTmplData) { + app.SubTypes = make([]string, 0) + + // API choice + choice := collectYesOrNo(reader, "Would you like to add API [/api/v1/*] within your Web App ([Y]es or [N]o), default is 'N'") + if choice { + app.SubTypes = append(app.SubTypes, typeAPI) } + // WebSocket choice + choice = collectYesOrNo(reader, "Would you like to add WebSocket [/ws/*] within your Web App ([Y]es or [N]o), default is 'N'") + if choice { + app.SubTypes = append(app.SubTypes, typeWebSocket) + } +} + +func viewEngine(reader *bufio.Reader, app *appTmplData) { builtInViewEngines := []string{"go", "pug"} var engine string for { @@ -170,127 +200,108 @@ func getViewEngine(reader *bufio.Reader, appType string) []string { if ess.IsStrEmpty(engine) || ess.IsSliceContainsString(builtInViewEngines, engine) { break } else { - logErrorf("Unsupported View Engine, choose either %s", strings.Join(builtInViewEngines, " or ")) + logErrorf("Unsupported View Engine") engine = "" } } switch engine { case "pug": - return []string{"pug", ".pug"} + app.ViewEngine = "pug" + app.ViewFileExt = ".pug" default: - return []string{"go", ".html"} + app.ViewEngine = "go" + app.ViewFileExt = ".html" } } -func getAuthScheme(reader *bufio.Reader, appType string) string { - var ( - authScheme string - schemeNames string - ) +func authScheme(reader *bufio.Reader, app *appTmplData) { + var schemeNames string - if appType == typeWeb { + if app.IsWebApp() { schemeNames = "form, basic" - } else if appType == typeAPI { + } else if app.IsAPIApp() { schemeNames = "basic, generic" } for { - authScheme = readInput(reader, fmt.Sprintf("\nChoose your application Auth Scheme (%v), default is 'none': ", schemeNames)) - if isAuthSchemeSupported(authScheme) { - if ess.IsStrEmpty(authScheme) || authScheme == authNone || - (appType == typeWeb && (authScheme == authForm || authScheme == authBasic)) || - (appType == typeAPI && (authScheme == authGeneric || authScheme == authBasic)) { + app.AuthScheme = strings.ToLower(readInput(reader, fmt.Sprintf("\nChoose your application Auth Scheme (%v), default is 'none': ", schemeNames))) + if isAuthSchemeSupported(app.AuthScheme) { + if ess.IsStrEmpty(app.AuthScheme) || app.AuthScheme == authNone || + app.IsAuthSchemeForWeb() || app.IsAuthSchemeForAPI() { break } else { - logErrorf("Application type '%v' is not applicable with auth scheme '%v'", appType, authScheme) - authScheme = "" + logErrorf("Application type '%v' is not applicable with auth scheme '%v'", app.Type, app.AuthScheme) + app.AuthScheme = "" } } else { - logErrorf("Unsupported Auth Scheme, choose either %v or 'none'", schemeNames) - authScheme = "" + logErrorf("Unsupported Auth Scheme") + app.AuthScheme = "" } } - if authScheme == authNone { - authScheme = "" + if app.AuthScheme == authNone { + app.AuthScheme = "" } - - return authScheme } -func getBasicAuthMode(reader *bufio.Reader, authScheme string) string { - var basicAuthMode string - if authScheme == authBasic { - for { - basicAuthMode = readInput(reader, "\nChoose your basic auth mode (file-realm, dynamic), default is 'file-realm': ") - if ess.IsStrEmpty(basicAuthMode) || basicAuthMode == "dynamic" { - break - } else { - logError("Unsupported Basic auth mode") - basicAuthMode = "" - } - } - - if ess.IsStrEmpty(basicAuthMode) { - basicAuthMode = basicFileRealm +func basicAuthMode(reader *bufio.Reader, app *appTmplData) { + for { + app.BasicAuthMode = strings.ToLower(readInput(reader, "\nChoose your basic auth mode (file-realm, dynamic), default is 'file-realm': ")) + if ess.IsStrEmpty(app.BasicAuthMode) || app.BasicAuthMode == "dynamic" { + break + } else { + logError("Unsupported Basic auth mode") + app.BasicAuthMode = "" } } - return basicAuthMode + if ess.IsStrEmpty(app.BasicAuthMode) { + app.BasicAuthMode = basicFileRealm + } } -func getPasswordHashAlgorithm(reader *bufio.Reader, authScheme string) string { - var authPasswordAlgorithm string - if authScheme == authForm || authScheme == authBasic { +func passwordHashAlgorithm(reader *bufio.Reader, app *appTmplData) { + if app.AuthScheme == authForm || app.AuthScheme == authBasic { for { - authPasswordAlgorithm = readInput(reader, "\nChoose your password hash algorithm (bcrypt, scrypt, pbkdf2), default is 'bcrypt': ") - - if ess.IsStrEmpty(authPasswordAlgorithm) || authPasswordAlgorithm == "bcrypt" || - authPasswordAlgorithm == "scrypt" || authPasswordAlgorithm == "pbkdf2" { + app.PasswordEncoderAlgo = strings.ToLower(readInput(reader, "\nChoose your password hash algorithm (bcrypt, scrypt, pbkdf2), default is 'bcrypt': ")) + if ess.IsStrEmpty(app.PasswordEncoderAlgo) || app.PasswordEncoderAlgo == "bcrypt" || + app.PasswordEncoderAlgo == "scrypt" || app.PasswordEncoderAlgo == "pbkdf2" { break } else { logError("Unsupported Password hash algorithm") - authPasswordAlgorithm = "" + app.PasswordEncoderAlgo = "" } } - if ess.IsStrEmpty(authPasswordAlgorithm) { - authPasswordAlgorithm = "bcrypt" + if ess.IsStrEmpty(app.PasswordEncoderAlgo) { + app.PasswordEncoderAlgo = "bcrypt" } } - return authPasswordAlgorithm } -func getSessionInfo(reader *bufio.Reader, appType, authScheme string) string { - sessionStore := storeCookie - - if appType == typeWeb && (authScheme == authForm || authScheme == authBasic) { - // Session Store +func sessionInfo(reader *bufio.Reader, app *appTmplData) { + if app.IsAuthSchemeForWeb() { for { - sessionStore = readInput(reader, "\nChoose your session store (cookie or file), default is 'cookie': ") - if ess.IsStrEmpty(sessionStore) || sessionStore == storeCookie || sessionStore == storeFile { + app.SessionStore = strings.ToLower(readInput(reader, "\nChoose your session store (cookie or file), default is 'cookie': ")) + if ess.IsStrEmpty(app.SessionStore) || app.SessionStore == storeCookie || app.SessionStore == storeFile { break } else { - logError("Unsupported session store type, choose either 'cookie or 'file") - sessionStore = "" + logError("Unsupported session store type") + app.SessionStore = "" } } - if ess.IsStrEmpty(sessionStore) { - sessionStore = storeCookie + if ess.IsStrEmpty(app.SessionStore) { + app.SessionStore = storeCookie } } - - return sessionStore } -func getCORSInfo(reader *bufio.Reader) bool { - enable := false +func collectYesOrNo(reader *bufio.Reader, msg string) bool { var input string for { - input = readInput(reader, "\nWould you like to enable CORS ([Y]es or [N]o), default is 'N': ") - input = strings.ToLower(strings.TrimSpace(input)) + input = strings.ToLower(readInput(reader, "\n"+msg+": ")) if ess.IsStrEmpty(input) { input = "n" } @@ -302,116 +313,181 @@ func getCORSInfo(reader *bufio.Reader) bool { input = "" } } + return input == "y" +} - if input == "yes" { - enable = true - } - - return enable +type file struct { + src, dst string } func createAahApp(appDir string, data map[string]interface{}) error { - appType := data["AppType"].(string) - viewEngine := data["AppViewEngine"].(string) - aahToolsPath := getAahToolsPath() + app := data["App"].(*appTmplData) + aahToolsPath := aahToolsPath() appTemplatePath := filepath.Join(aahToolsPath.Dir, "app-template") + appTmplBaseDir := appTemplatePath + appBaseDir := app.BaseDir // app directory creation if err := ess.MkDirAll(appDir, permRWXRXRX); err != nil { logFatal(err) } + files := make([]file, 0) + // aah.project - processFile(appDir, appTemplatePath, filepath.Join(appTemplatePath, "aah.project.atmpl"), data) + files = append(files, file{ + src: filepath.Join(appTmplBaseDir, "aah.project.atmpl"), + dst: filepath.Join(appBaseDir, "aah.project.atmpl"), + }) - // gitignore - processFile(appDir, appTemplatePath, filepath.Join(appTemplatePath, ".gitignore"), data) + // .gitignore + files = append(files, file{ + src: filepath.Join(appTmplBaseDir, ".gitignore"), + dst: filepath.Join(appBaseDir, ".gitignore"), + }) // source - processSection(appDir, appTemplatePath, "app", data) + files = append(files, sourceTmplFiles(app, appTmplBaseDir, appBaseDir)...) // config - processSection(appDir, appTemplatePath, "config", data) + files = append(files, configTmplFiles(app.Type, appTmplBaseDir, appBaseDir)...) - if typeWeb == appType { + if app.IsWebApp() { // i18n - processSection(appDir, appTemplatePath, "i18n", data) + files = append(files, tmplFiles(filepath.Join(appTmplBaseDir, "i18n"), appTmplBaseDir, appBaseDir, true)...) // static - processSection(appDir, appTemplatePath, "static", data) + files = append(files, tmplFiles(filepath.Join(appTmplBaseDir, "static"), appTmplBaseDir, appBaseDir, true)...) // views - switch viewEngine { - case "pug": - processSection(appDir, appTemplatePath, filepath.Join("views", "pug"), data) - default: // go - processSection(appDir, appTemplatePath, filepath.Join("views", "go"), data) - } + files = append(files, viewTmplFiles(app.ViewEngine, appTmplBaseDir, appBaseDir)...) + } + + // processing app template files + for _, f := range files { + processFile(appBaseDir, f, data) } return nil } -func processSection(destDir, srcDir, dir string, data map[string]interface{}) { - files, _ := ess.FilesPath(filepath.Join(srcDir, dir), true) - for _, v := range files { - if strings.Contains(v, "/app/security/") { - authScheme := data["AppAuthScheme"].(string) - if !ess.IsStrEmpty(authScheme) && authScheme != authNone { - if authScheme == authBasic { - basicAuthMode := data["AppBasicAuthMode"].(string) - if basicAuthMode == "dynamic" { - processFile(destDir, srcDir, v, data) - } - } else { - processFile(destDir, srcDir, v, data) - } - } - } else { - processFile(destDir, srcDir, v, data) +func configTmplFiles(appType, appTmplBaseDir, appBaseDir string) []file { + srcDir := filepath.Join(appTmplBaseDir, "config") + flist, _ := ess.FilesPath(srcDir, true) + files := []file{} + for _, f := range flist { + if appType == typeWebSocket && strings.HasSuffix(f, "security.conf.atmpl") { + continue } + files = append(files, file{src: f, dst: filepath.Join(appBaseDir, f[len(appTmplBaseDir):])}) } + return files } -func processFile(destDir, srcDir, f string, data map[string]interface{}) { - dfPath := getDestPath(destDir, srcDir, f) - dfDir := filepath.Dir(dfPath) - if !ess.IsFileExists(dfDir) { - _ = ess.MkDirAll(dfDir, permRWXRXRX) +func sourceTmplFiles(app *appTmplData, appTmplBaseDir, appBaseDir string) []file { + files := []file{} + + fn := func(srcDir string, recur bool) { + flist, _ := ess.FilesPath(srcDir, recur) + for _, f := range flist { + files = append(files, file{src: f, dst: filepath.Join(appBaseDir, f[len(appTmplBaseDir):])}) + } } - sf, _ := os.Open(f) - df, _ := os.Create(dfPath) + // /app + fn(filepath.Join(appTmplBaseDir, "app"), false) + + // /app/controllers + if app.IsWebApp() || app.IsAPIApp() { + fn(filepath.Join(appTmplBaseDir, "app", "controllers"), false) - if strings.HasSuffix(f, aahTmplExt) { - sfbytes, _ := ioutil.ReadAll(sf) - if err := renderTmpl(df, string(sfbytes), data); err != nil { - logFatalf("Unable to process file '%s': %s", dfPath, err) - } - } else { - _, _ = io.Copy(df, sf) } - _ = ess.ApplyFileMode(dfPath, permRWRWRW) - ess.CloseQuietly(sf, df) + if app.IsAPIApp() { + fn(filepath.Join(appTmplBaseDir, "app", "controllers", "v1"), false) + } + + if app.IsSubTypeAPI() { + files = append(files, file{ + src: filepath.Join(appTmplBaseDir, filepath.FromSlash("app/controllers/v1/value.go.atmpl")), + dst: filepath.Join(appBaseDir, filepath.FromSlash("app/controllers/api/v1/value.go")), + }) + } + + // /app/websockets + if app.IsWebSocketApp() || app.IsSubTypeWebSocket() { + fn(filepath.Join(appTmplBaseDir, "app", "websockets"), true) + } + + // /app/models + files = append(files, file{ + src: filepath.Join(appTmplBaseDir, filepath.FromSlash("app/models/greet.go")), + dst: filepath.Join(appBaseDir, filepath.FromSlash("app/models/greet.go")), + }) + if app.IsAPIApp() || app.IsSubTypeAPI() { + files = append(files, file{ + src: filepath.Join(appTmplBaseDir, filepath.FromSlash("app/models/value.go")), + dst: filepath.Join(appBaseDir, filepath.FromSlash("app/models/value.go")), + }) + } + + // /app/security + if app.IsSecurityEnabled() && app.BasicAuthMode != basicFileRealm { + fn(filepath.Join(appTmplBaseDir, "app", "security"), true) + } + + return files } -func getDestPath(destDir, srcDir, v string) string { - dpath := v[len(srcDir):] +func viewTmplFiles(engName, appTmplBaseDir, appBaseDir string) []file { + srcDir := filepath.Join(appTmplBaseDir, "views", engName) + flist, _ := ess.FilesPath(srcDir, true) + files := []file{} + for _, f := range flist { + files = append(files, file{src: f, dst: filepath.Join(appBaseDir, "views", f[len(srcDir):])}) + } + return files +} - // Handle specific - views files for multiple engine - if strings.HasPrefix(dpath[1:], "views") { - r := strings.SplitAfterN(dpath, string(filepath.Separator), 4) - dpath = filepath.Join(r[1], r[3]) +func tmplFiles(srcDir, appTmplBaseDir, appBaseDir string, recur bool) []file { + flist, _ := ess.FilesPath(srcDir, recur) + files := []file{} + for _, f := range flist { + files = append(files, file{src: f, dst: filepath.Join(appBaseDir, f[len(appTmplBaseDir):])}) } + return files +} - dpath = filepath.Join(destDir, dpath) +func processFile(appBaseDir string, f file, data map[string]interface{}) { + dst := f.dst - if strings.HasSuffix(v, aahTmplExt) { - dpath = dpath[:len(dpath)-len(aahTmplExt)] + // remove .atmpl suffix if exists + if strings.HasSuffix(dst, aahTmplExt) { + dst = dst[:len(dst)-len(aahTmplExt)] } - return dpath + // create dst dir if not exists + dstDir := filepath.Dir(dst) + if !ess.IsFileExists(dstDir) { + _ = ess.MkDirAll(dstDir, permRWXRXRX) + } + + // open src and create dst + sf, _ := os.Open(f.src) + df, _ := os.Create(dst) + + // render or write it directly + if strings.HasSuffix(f.src, aahTmplExt) { + sfbytes, _ := ioutil.ReadAll(sf) + if err := renderTmpl(df, string(sfbytes), data); err != nil { + logFatalf("Unable to process file '%s': %s", dst, err) + } + } else { + _, _ = io.Copy(df, sf) + } + + _ = ess.ApplyFileMode(dst, permRWRWRW) + ess.CloseQuietly(sf, df) } func isAuthSchemeSupported(authScheme string) bool { @@ -425,22 +501,27 @@ func checkAndGenerateInitgoFile(importPath, baseDir string) { cliLog.Warn("***** In v0.10 'init.go' file introduced to evolve aah framework." + " Since its not found, generating 'init.go' file. Please add it to your version control. *****\n") - aahToolsPath := getAahToolsPath() + aahToolsPath := aahToolsPath() appTemplatePath := filepath.Join(aahToolsPath.Dir, "app-template") appType := typeAPI if ess.IsFileExists(filepath.Join(baseDir, "views")) { appType = typeWeb } data := map[string]interface{}{ - "AppType": appType, - "AppViewEngine": aah.AppConfig().StringDefault("view.engine", "go"), + "App": &appTmplData{ + Type: appType, + ViewEngine: aah.AppConfig().StringDefault("view.engine", "go"), + }, } - processFile(baseDir, appTemplatePath, filepath.Join(appTemplatePath, "app", "init.go"+aahTmplExt), data) + processFile(baseDir, file{ + src: filepath.Join(appTemplatePath, "app", "init.go.atmpl"), + dst: filepath.Join(baseDir, "app", "init.go"), + }, data) } } -func getAahToolsPath() *build.Package { +func aahToolsPath() *build.Package { aahToolsPath, err := build.Import(path.Join(libImportPath("tools"), "aah"), "", build.FindOnly) if err != nil { logFatal(err) diff --git a/aah/run.go b/aah/run.go index 666113b..472d7a9 100644 --- a/aah/run.go +++ b/aah/run.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httputil" "net/url" @@ -111,7 +112,9 @@ func runAction(c *cli.Context) error { appStartArgs = append(appStartArgs, "-profile", envProfile) } - aah.Init(importPath) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) @@ -267,7 +270,67 @@ func (hr *hotReload) ServeHTTP(w http.ResponseWriter, r *http.Request) { } waitForConnReady(hr.ProxyPort) } - hr.Proxy.ServeHTTP(w, r) + hr.ProxyServe(w, r) +} + +// Typically for HTTP method: CONNECT and WebSocket needs tunneling, we cannot +// use `httputil.ReverseProxy` since it handles Hop-By-Hop headers on proxy +// connection - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#hbh +func (hr *hotReload) needTunneling(r *http.Request) bool { + return r.Method == http.MethodConnect || + strings.EqualFold(strings.ToLower(r.Header.Get("Upgrade")), "websocket") +} + +func (hr *hotReload) ProxyServe(w http.ResponseWriter, r *http.Request) { + if hr.needTunneling(r) { + hr.tunnel(w, r) + } else { + hr.Proxy.ServeHTTP(w, r) + } +} + +func (hr *hotReload) tunnel(w http.ResponseWriter, r *http.Request) { + var peer net.Conn + var err error + address := fmt.Sprintf("%s:%s", hr.Addr, hr.ProxyPort) + if hr.IsSSL { + peer, err = tls.Dial("tcp", address, &tls.Config{InsecureSkipVerify: true}) + } else { + peer, err = net.DialTimeout("tcp", address, 10*time.Second) + } + + if err != nil { + http.Error(w, "Error tunneling with peer", http.StatusBadGateway) + return + } + + hj, ok := w.(http.Hijacker) + if !ok { + http.Error(w, "Error hijacking is not supported", http.StatusInternalServerError) + return + } + + conn, _, err := hj.Hijack() + if err != nil { + http.Error(w, err.Error(), http.StatusServiceUnavailable) + return + } + + if err = r.Write(peer); err != nil { + logErrorf("Error tunneling data to peer: %s", err) + return + } + + go func() { + defer peer.Close() + defer conn.Close() + io.Copy(peer, conn) + }() + go func() { + defer peer.Close() + defer conn.Close() + io.Copy(conn, peer) + }() } func startWatcher(projectCfg *config.Config, baseDir string, w *watcher.Watcher, watch chan<- bool) { diff --git a/aah/util.go b/aah/util.go index 823fa4c..97c5f83 100644 --- a/aah/util.go +++ b/aah/util.go @@ -159,7 +159,7 @@ func execCmd(cmdName string, args []string, stdout bool) (string, error) { } func renderTmpl(w io.Writer, text string, data interface{}) error { - tmpl := template.Must(template.New("").Parse(text)) + tmpl := template.Must(template.New("").Funcs(appTemplateFuncs).Parse(text)) return tmpl.Execute(w, data) } From d21f2a6d4ef26b8031735e95450c72de46541ee6 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 23 Apr 2018 20:00:57 -0700 Subject: [PATCH 09/48] app template update --- aah/app-template/app/controllers/v1/value.go.atmpl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index b329f91..09c4390 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -2,7 +2,8 @@ package v1 import ( "aahframework.org/aah.v0" - + "aahframework.org/ahttp.v0" + "{{ .App.ImportPath }}/app/models" ) @@ -57,7 +58,11 @@ func (v *ValueController) Create(val *models.Value) { // Add it to values map values[val.Key] = val - v.Reply().Created() + v.Reply().Created(). + Header(ahttp.HeaderLocation, fmt.Sprintf("%s:%s", v.Req.Scheme, v.ReverseURL("value_get", val.Key))). + JSON(aah.Data{ + "key": val.Key, + }) } // Update method updates value entry on map for given key and Payload. From b887c9450f6d75b535f8a7887b46fd0c52b80224 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 25 Apr 2018 00:22:36 -0700 Subject: [PATCH 10/48] cmd switch gets smart to handle edge version better --- aah/aah.go | 10 ++---- aah/switch.go | 11 ++++--- aah/util.go | 88 +++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index 0de4ab9..ae0c44f 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -32,10 +32,9 @@ import ( ) const ( - permRWXRXRX = 0755 - permRWRWRW = 0666 - versionSeries = "v0" - importPrefix = "aahframework.org" + permRWXRXRX = 0755 + permRWRWRW = 0666 + importPrefix = "aahframework.org" ) var ( @@ -43,9 +42,6 @@ var ( gocmd string gosrcDir string - libNames = []string{"tools", "ainsp", "aah", "ahttp", "aruntime", "config", "essentials", "forge", "i18n", - "log", "router", "security", "test", "valpar", "view", "ws"} - // abstract it, so we can do unit test fatal = log.Fatal fatalf = log.Fatalf diff --git a/aah/switch.go b/aah/switch.go index a36fc21..c8bd46b 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -80,7 +80,7 @@ func whoami(branchName string) error { func doRefresh(branchName string) error { fname := friendlyName(branchName) if branchName == releaseBranchName { - cliLog.Infof("Refresh is only applicable to edge version, currently you're on '%s' version.\n", fname) + cliLog.Infof("Refresh option is for 'edge' version only, currently you're on '%s' version.\n", fname) cliLog.Infof("Use 'aah update' command to update your aah to the latest release version on your GOPATH.\n") return nil } @@ -88,7 +88,7 @@ func doRefresh(branchName string) error { cliLog.Infof("Refreshing aah '%s' version ...\n", fname) // Refresh to latest edge codebase - refreshCodebase(libNames...) + refreshCodebase(aahLibraryDirs()) // Refresh dependencies in grace mode fetchAahDeps() @@ -118,15 +118,16 @@ func doSwitch(branchName, target string) error { cliLog.Infof("Switching aah version to '%s' ...\n", friendlyName(toBranch)) // Checkout the branch - for _, lib := range libNames { - if err := gitCheckout(libDir(lib), toBranch); err != nil { + aahLibDirs := aahLibraryDirs() + for _, d := range aahLibDirs { + if err := gitCheckout(d, toBranch); err != nil { logFatalf("Error occurred which switching aah version: %s", err) } } if toBranch == edgeBranchName { cliLog.Infof("Refreshing aah version to latest '%s' ...\n", friendlyName(toBranch)) - refreshCodebase(libNames...) + refreshCodebase(aahLibDirs) } // Refresh dependencies in grace mode diff --git a/aah/util.go b/aah/util.go index 97c5f83..7aedc59 100644 --- a/aah/util.go +++ b/aah/util.go @@ -10,7 +10,6 @@ import ( "net" "os" "os/exec" - "path" "path/filepath" "runtime" "strconv" @@ -267,7 +266,7 @@ func gitCheckout(dir, branch string) error { } func libImportPath(name string) string { - return fmt.Sprintf("%s/%s.%s", importPrefix, name, versionSeries) + return fmt.Sprintf("%s/%s.%s", importPrefix, name, inferVersionSeries()) } func libDir(name string) string { @@ -325,22 +324,35 @@ func waitForConnReady(port string) { } func installAahCLI() { - args := []string{"install", path.Join(importPrefix, "tools.v0", "aah")} + verser := inferVersionSeries() + args := []string{"install", fmt.Sprintf("%s/tools.%s/aah", importPrefix, verser)} if _, err := execCmd(gocmd, args, false); err != nil { logFatalf("Unable to compile CLI tool: %s", err) } } func fetchAahDeps() { - if err := goGet(path.Join(importPrefix, "tools.v0", "aah", "...")); err != nil { - logFatalf("Unable to refresh dependencies: %s", err) + // depList would have duplicate import paths + // since we collect from more than one lib + // TODO: improve efficiency + var depList []string + for _, i := range aahImportPaths() { + depList = append(depList, libDependencyImports(i)...) + } + + // infer not exists libraries on GOPATH using importpath + notEixstsList := inferNotExistsDeps(depList) + if len(notEixstsList) > 0 { + if err := goGet(notEixstsList...); err != nil { + logFatalf("Error during go get: %s", err) + } } } -func refreshCodebase(names ...string) { - for _, lib := range names { - if err := gitPull(libDir(lib)); err != nil { - logFatalf("Unable to refresh library: %s.%s", lib, versionSeries) +func refreshCodebase(libDirs []string) { + for _, ld := range libDirs { + if err := gitPull(ld); err != nil { + logFatalf("Unable to refresh library: %s", filepath.Base(ld)) } } } @@ -382,3 +394,61 @@ func stripGoPath(pkgFilePath string) string { idx := strings.Index(pkgFilePath, "src") return filepath.Clean(pkgFilePath[idx+4:]) } + +func inferVersionSeries() string { + verser := "v0" + for _, d := range aahLibraryDirs() { + baseName := filepath.Base(d) + if strings.HasPrefix(baseName, "aah") { + return strings.Split(baseName, ".")[1] + } + } + return verser +} + +func aahLibraryDirs() []string { + dirs, err := ess.DirsPath(filepath.Join(gosrcDir, importPrefix), false) + if err != nil { + return []string{} + } + return dirs +} + +func aahImportPaths() []string { + var importPaths []string + gsLen := len(gosrcDir) + for _, d := range aahLibraryDirs() { + p := d[gsLen+1:] + if strings.Contains(p, "tools") { + p += "/aah" // Note: this import path so always forward slash + } + importPaths = append(importPaths, p) + } + return importPaths +} + +func libDependencyImports(importPath string) []string { + var depList []string + str, err := execCmd(gocmd, []string{"list", "-f", "{{.Imports}}", importPath}, false) + if err != nil { + logErrorf("Unable to infer dependency imports for %s", importPath) + return []string{} + } + + str = strings.TrimSpace(str) + for _, i := range strings.Fields(str[1 : len(str)-1]) { + depList = append(depList, strings.TrimSpace(i)) + } + + return depList +} + +func inferNotExistsDeps(depList []string) []string { + var notExistsList []string + for _, d := range depList { + if !ess.IsImportPathExists(d) && !ess.IsSliceContainsString(notExistsList, d) { + notExistsList = append(notExistsList, d) + } + } + return notExistsList +} From 0d58422cc2591269808dd6fe6ac801f0b1242d0d Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 26 Apr 2018 01:31:20 -0700 Subject: [PATCH 11/48] code quality and app template update --- aah/aah.go | 3 +-- aah/app-template/config/routes.conf.atmpl | 25 ++++++++++++------- aah/app_tmpl.go | 5 ++++ aah/clean.go | 4 +++- aah/generate.go | 8 +++++-- aah/new.go | 7 +----- aah/run.go | 29 +++++++++++++---------- aah/util.go | 6 ++--- 8 files changed, 51 insertions(+), 36 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index ae0c44f..b3657cb 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -78,8 +78,7 @@ func main() { // if panic happens, recover and abort nicely :) defer func() { if r := recover(); r != nil { - cfg, _ := config.ParseString(``) - strace := aruntime.NewStacktrace(r, cfg) + strace := aruntime.NewStacktrace(r, config.NewEmptyConfig()) strace.Print(os.Stdout) exit(2) } diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index ecac552..0293861 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -13,15 +13,14 @@ domains { # Pick your choice of an `unique keyname` to define your domain section # in the routes configuration. # For e.g.: Domain name/ip address with port no - {{ .App.Name }} { + {{ .App.DomainNameKey }} { name = "{{ .App.Name }} routes" # aah supports multi-domain routes configuration out-of-the-box. # `host` used to determine domain routes for the incoming request. # For e.g: example.org - host = "localhost" + host = "localhost"{{ if not .App.IsWebSocketApp }} - {{ if not .App.IsWebSocketApp -}} # Redirect trailing slash is to enable automatic redirection if the current # route can't be matched but a `route` for the path with (without) # the trailing slash exists. @@ -43,9 +42,16 @@ domains { # If you don't define attribute `auth` then framework treats that route as # `anonymous` auth scheme. # Default value is empty string. - #default_auth = ""{{- end }} + #default_auth = "" + + # Anti-CSRF protection. + # Typically if you have `
` usage in your application, + # don't disable the check. `aah new` set to false for API app. + # Default value is `true`. + anti_csrf_check = {{ if .App.IsAPIApp }}false{{ else }}true{{ end }} + + {{ end }}{{ if .App.CORSEnable }} - {{ if .App.CORSEnable -}} #---------------------------------------------------------------------------- # CORS (Cross-Origin Resource Sharing) # Doc: https://docs.aahframework.org/cors.html @@ -88,9 +94,10 @@ domains { # Specify `Access-Control-Allow-Credentials` header value. # Default value is `false`. #allow_credentials = true - }{{ end }} + } + + {{ end }}{{ if .App.IsWebApp }} - {{ if .App.IsWebApp -}} #---------------------------------------------------------------------------- # Static Routes Configuration # To serve static files, it can be directory or individual file. @@ -138,9 +145,9 @@ domains { path = "/robots.txt" file = "robots.txt" } - }{{ end }} + } - #----------------------------------------------------------------------------- + {{ end }}#----------------------------------------------------------------------------- # Application routes definitions # # Doc: https://docs.aahframework.org/routes-config.html#section-routes diff --git a/aah/app_tmpl.go b/aah/app_tmpl.go index 45e3b16..5e98ed7 100644 --- a/aah/app_tmpl.go +++ b/aah/app_tmpl.go @@ -1,6 +1,7 @@ package main import ( + "strings" "text/template" "aahframework.org/essentials.v0" @@ -55,6 +56,10 @@ func (a *appTmplData) IsWebSocketApp() bool { return a.Type == typeWebSocket } +func (a *appTmplData) DomainNameKey() string { + return strings.Replace(strings.Replace(a.Name, " ", "_", -1), "-", "_", -1) +} + func (a *appTmplData) IsAuthSchemeForWeb() bool { return a.Type == typeWeb && (a.AuthScheme == authForm || a.AuthScheme == authBasic) } diff --git a/aah/clean.go b/aah/clean.go index 771e0e2..2c6107f 100644 --- a/aah/clean.go +++ b/aah/clean.go @@ -37,7 +37,9 @@ var cleanCmd = cli.Command{ func cleanAction(c *cli.Context) error { importPath := getAppImportPath(c) - aah.Init(importPath) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) diff --git a/aah/generate.go b/aah/generate.go index 32e1daa..0b641f5 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -95,7 +95,9 @@ func generateScriptsAction(c *cli.Context) error { func generateSystemdScript(c *cli.Context) error { importPath := getAppImportPath(c) - aah.Init(importPath) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) @@ -131,7 +133,9 @@ func generateSystemdScript(c *cli.Context) error { func generateDockerScript(c *cli.Context) error { importPath := getAppImportPath(c) - aah.Init(importPath) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) diff --git a/aah/new.go b/aah/new.go index fc4c575..0893ed4 100644 --- a/aah/new.go +++ b/aah/new.go @@ -459,12 +459,7 @@ func tmplFiles(srcDir, appTmplBaseDir, appBaseDir string, recur bool) []file { } func processFile(appBaseDir string, f file, data map[string]interface{}) { - dst := f.dst - - // remove .atmpl suffix if exists - if strings.HasSuffix(dst, aahTmplExt) { - dst = dst[:len(dst)-len(aahTmplExt)] - } + dst := strings.TrimSuffix(f.dst, aahTmplExt) // create dst dir if not exists dstDir := filepath.Dir(dst) diff --git a/aah/run.go b/aah/run.go index 472d7a9..a61a6dc 100644 --- a/aah/run.go +++ b/aah/run.go @@ -70,19 +70,19 @@ var runCmd = cli.Command{ type ( hotReload struct { - ProxyURL *url.URL + ChangedOrError bool + IsSSL bool ProxyPort string BaseDir string Addr string Port string - IsSSL bool SSLCert string SSLKey string Args []string + ProxyURL *url.URL Proxy *httputil.ReverseProxy Process *process ProjectConfig *config.Config - ChangedOrError bool Watcher *watcher.Watcher } @@ -192,6 +192,7 @@ func (hr *hotReload) Start() { server.ErrorLog = hr.Proxy.ErrorLog if hr.IsSSL { + /* #nosec Its required for development activity */ hr.Proxy.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} err = server.ListenAndServeTLS(hr.SSLCert, hr.SSLKey) } else { @@ -224,6 +225,7 @@ func (hr *hotReload) CompileAndStart() error { } hr.Process = &process{ + // #nosec cmd: exec.Command(appBinary, hr.Args...), nw: ¬ifyWriter{ w: os.Stdout, @@ -260,7 +262,7 @@ func (hr *hotReload) ServeHTTP(w http.ResponseWriter, r *http.Request) { if hr.ChangedOrError { cliLog.Info("Application file change(s) detected") hr.ChangedOrError = false - hr.Watcher.Close() + ess.CloseQuietly(hr.Watcher) hr.Stop() if err := hr.CompileAndStart(); err != nil { logError(err) @@ -294,6 +296,7 @@ func (hr *hotReload) tunnel(w http.ResponseWriter, r *http.Request) { var err error address := fmt.Sprintf("%s:%s", hr.Addr, hr.ProxyPort) if hr.IsSSL { + /* #nosec Its required for development activity */ peer, err = tls.Dial("tcp", address, &tls.Config{InsecureSkipVerify: true}) } else { peer, err = net.DialTimeout("tcp", address, 10*time.Second) @@ -322,14 +325,14 @@ func (hr *hotReload) tunnel(w http.ResponseWriter, r *http.Request) { } go func() { - defer peer.Close() - defer conn.Close() - io.Copy(peer, conn) + defer ess.CloseQuietly(peer) + defer ess.CloseQuietly(conn) + _, _ = io.Copy(peer, conn) }() go func() { - defer peer.Close() - defer conn.Close() - io.Copy(conn, peer) + defer ess.CloseQuietly(conn) + defer ess.CloseQuietly(peer) + _, _ = io.Copy(conn, peer) }() } @@ -348,7 +351,9 @@ func startWatcher(projectCfg *config.Config, baseDir string, w *watcher.Watcher, if e.Op == watcher.Create { _ = w.Add(e.Path) } - watch <- true + if !e.IsDir() { + watch <- true + } case err := <-w.Error: if err == watcher.ErrWatchedFileDeleted { // treat as trace information, not an error @@ -395,8 +400,6 @@ func loadWatchFiles(projectCfg *config.Config, baseDir string, w *watcher.Watche dirExcludes = append(dirExcludes, "build", "static", "vendor", "tests", "logs") dirs, _ := ess.DirsPathExcludes(baseDir, true, dirExcludes) - // dirs = excludeAndCreateSlice(dirs, baseDir) - // dirs = excludeAndCreateSlice(dirs, filepath.Join(baseDir, "app")) for _, d := range dirs { if err := w.Add(d); err != nil { logErrorf("Unable add watch for '%v'", d) diff --git a/aah/util.go b/aah/util.go index 7aedc59..71a4aaf 100644 --- a/aah/util.go +++ b/aah/util.go @@ -25,7 +25,7 @@ import ( ) func importPathRelwd() string { - pwd, _ := os.Getwd() + pwd, _ := os.Getwd() // #nosec var importPath string if idx := strings.Index(pwd, "src"); idx > 0 { @@ -136,7 +136,7 @@ func getBuildDate() string { } func execCmd(cmdName string, args []string, stdout bool) (string, error) { - cmd := exec.Command(cmdName, args...) + cmd := exec.Command(cmdName, args...) // #nosec cliLog.Trace("Executing ", strings.Join(cmd.Args, " ")) if stdout { @@ -219,7 +219,7 @@ func isAahProject(file string) bool { } func findAvailablePort() string { - lstn, err := net.Listen("tcp", ":0") + lstn, err := net.Listen("tcp", ":0") // #nosec if err != nil { logError(err) return "0" From 5cade9a46d72c7d0a07c09dd5c1d1d7b1aa3156e Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 26 Apr 2018 19:51:14 -0700 Subject: [PATCH 12/48] app template update init.go --- aah/app-template/app/init.go.atmpl | 40 ++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index f5b858b..e236914 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -48,30 +48,48 @@ func init() { // aah.OnShutdown(cache.Flush) // aah.OnShutdown(db.Disconnect) - {{ if .App.IsSecurityEnabled }}// Event: OnPostAuth - // Published right after the Authentication successful - aah.OnPostAuth(security.PostAuthEvent){{ end }} + // HTTP Events + // + // Add your HTTP events on app start. + aah.OnStart(func(_ *aah.Event) { + // he := aah.AppHTTPEngine() + + // Event: OnRequest + // he.OnRequest(myserverext.OnRequest) + + // Event: OnPreReply + // he.OnPreReply(myserverext.OnPreReply) + + // Event: OnPostReply + // he.OnPostReply(myserverext.OnPostReply) + + // Event: OnPreAuth + // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} + + // Event: OnPostAuth + // Published right after the successful Authentication + aah.OnPostAuth(security.PostAuthEvent){{ end }} + }) {{ if .App.IsWebSocketApp -}} // WebSocket Events // Doc: https://docs.aahframework.org/websocket.html#events // // Add your WebSocket events on app start. - aah.OnStart(func(e *aah.Event) { + aah.OnStart(func(_ *aah.Event) { + // wse := aah.AppWSEngine() + // Event: OnPreConnect - // aah.OnWSPreConnect(websocket.PreConnect) + // wse.OnPreConnect(mywebsocket.PreConnect) // Event: OnPostConnect - // aah.OnWSPostConnect(websocket.PostConnect) - - // Event: AuthCallback - // aah.SetWSAuthCallback(websocket.DoAuth) + // wse.OnPostConnect(mywebsocket.PostConnect) // Event: OnPostDisconnect - // aah.OnWSPostDisconnect(websocket.PostDisconnect) + // wse.OnPostDisconnect(mywebsocket.PostDisconnect) // Event: OnError - // aah.OnWSError(websocket.Error) + // wse.OnError(mywebsocket.Error) }) {{ end -}} From ca18c83f7a2988bd43d14f16fac1b1fe4cbbabbb Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 27 Apr 2018 21:15:19 -0700 Subject: [PATCH 13/48] version printer, cmd description, cmd switch and app template update --- aah/aah.go | 69 +++++-------------- aah/app-template/app/controllers/app.go.atmpl | 3 - .../app/controllers/v1/value.go.atmpl | 24 ------- aah/app-template/app/init.go.atmpl | 14 ++-- .../app/websockets/sample.go.atmpl | 3 - aah/build.go | 10 +-- aah/clean.go | 2 +- aah/generate.go | 4 +- aah/list.go | 4 +- aah/new.go | 2 +- aah/run.go | 20 +++--- aah/switch.go | 18 +++-- aah/update.go | 9 +-- aah/util.go | 22 +++++- aah/version.go | 49 +++++++++++++ 15 files changed, 133 insertions(+), 120 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index b3657cb..7ddc6ac 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -7,28 +7,17 @@ package main import ( "errors" "fmt" - "io" "os" "os/exec" "path/filepath" - "runtime" "sort" - "strings" "gopkg.in/urfave/cli.v1" - "aahframework.org/aah.v0" - "aahframework.org/ahttp.v0" "aahframework.org/aruntime.v0" "aahframework.org/config.v0" "aahframework.org/essentials.v0" - "aahframework.org/i18n.v0" "aahframework.org/log.v0" - "aahframework.org/router.v0" - "aahframework.org/security.v0" - "aahframework.org/test.v0" - "aahframework.org/valpar.v0" - "aahframework.org/view.v0" ) const ( @@ -41,6 +30,7 @@ var ( gopath string gocmd string gosrcDir string + aahVer string // abstract it, so we can do unit test fatal = log.Fatal @@ -70,6 +60,10 @@ func checkPrerequisites() error { gosrcDir = filepath.Join(gopath, "src") + if aahVer, err = aahVersion(); err == errVersionNotExists { + return errors.New("aah framework is not installed, its easy to install. Run 'go get aahframework.org/tools.v0/aah'") + } + return nil } @@ -118,66 +112,39 @@ func main() { //___________________________________ func printHeader(c *cli.Context) error { - hdr := fmt.Sprintf("aah framework v%s", aah.Version) + hdrCont := fmt.Sprintf("aah framework v%s", aahVer) improveRpt := "# Report improvements/bugs at https://aahframework.org/issues #" cnt := len(improveRpt) - sp := (cnt - len(hdr)) / 2 - - if !isWindowsOS() { - fmt.Fprintf(c.App.Writer, "\033[1;32m") - } + sp := (cnt - len(hdrCont)) / 2 - printChr(c.App.Writer, "‾", cnt) - fmt.Fprintf(c.App.Writer, "\n") - printChr(c.App.Writer, " ", sp) - fmt.Fprintf(c.App.Writer, hdr) - printChr(c.App.Writer, " ", sp) - fmt.Fprintf(c.App.Writer, "\n") - printChr(c.App.Writer, "_", cnt) - fmt.Fprintf(c.App.Writer, "\n") - - if !isWindowsOS() { - fmt.Fprintf(c.App.Writer, "\033[0m") - } + fmt.Println(chrtostr("=", cnt)) + fmt.Println(chrtostr(" ", sp) + hdrCont) + fmt.Println(chrtostr("=", cnt)) + fmt.Printf(improveRpt + "\n\n") - fmt.Fprintf(c.App.Writer, improveRpt+"\n\n") return nil } -func printChr(w io.Writer, chr string, cnt int) { +func chrtostr(chr string, cnt int) string { + var str string for idx := 0; idx < cnt; idx++ { - fmt.Fprintf(w, chr) + str += chr } + return str } func init() { cli.HelpFlag = cli.BoolFlag{ Name: "h, help", - Usage: "show help", + Usage: "Shows help", } cli.VersionFlag = cli.BoolFlag{ Name: "v, version", - Usage: "print aah framework version and go version", + Usage: "Prints cli, aah, go and aah libraries version", } - cli.VersionPrinter = func(c *cli.Context) { - _ = printHeader(c) - fmt.Fprint(c.App.Writer, "Version Info:\n") - fmt.Fprintf(c.App.Writer, "\t%-17s v%s\n", "aah framework", aah.Version) - fmt.Fprintf(c.App.Writer, "\t%-17s v%s\n", "aah cli tool", Version) - fmt.Fprintf(c.App.Writer, "\t%-17s %s\n", "Modules: ", strings.Join( - []string{ - "ahttp v" + ahttp.Version, "aruntime v" + aruntime.Version, "config v" + config.Version, - "essentials v" + ess.Version, "i18n v" + i18n.Version, "log v" + log.Version}, ", ")) - fmt.Fprintf(c.App.Writer, "\t%-17s %s\n", "", strings.Join( - []string{"router v" + router.Version, "security v" + security.Version, - "test v" + test.Version, "valpar v" + valpar.Version, "view v" + view.Version}, ", ")) - fmt.Println() - fmt.Fprintf(c.App.Writer, "\t%-17s %s\n", fmt.Sprintf("go[%s/%s]", - runtime.GOOS, runtime.GOARCH), runtime.Version()[2:]) - fmt.Println() - } + cli.VersionPrinter = VersionPrinter cli.AppHelpTemplate = `Usage: {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} diff --git a/aah/app-template/app/controllers/app.go.atmpl b/aah/app-template/app/controllers/app.go.atmpl index 4ca002b..ddb1f76 100644 --- a/aah/app-template/app/controllers/app.go.atmpl +++ b/aah/app-template/app/controllers/app.go.atmpl @@ -12,9 +12,6 @@ type AppController struct { } // Index method is application {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} -// -// Route: / -// Method: GET func (a *AppController) Index() { {{- if .App.IsWebApp }} data := aah.Data{ diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index 09c4390..1a388a8 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -15,18 +15,12 @@ type ValueController struct { } // List method returns all the values. -// -// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values -// Method: GET func (v *ValueController) List() { v.Reply().Ok().JSON(values) } // Index method returns value for given key. // If key not found then returns 404 NotFound error. -// -// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key -// Method: GET func (v *ValueController) Index(key string) { if val, found := values[key]; found { v.Reply().Ok().JSON(val) @@ -40,14 +34,6 @@ func (v *ValueController) Index(key string) { // Create method creates new entry in the values map with given payload. // If key already exists then returns 409 Conflict error. -// -// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values -// Method: POST -// Request Body: -// { -// "key": "key1" -// "value": 3437476 // any valid type, since value is type interface{} -// } func (v *ValueController) Create(val *models.Value) { if _, found := values[val.Key]; found { v.Reply().Conflict().JSON(aah.Data{ @@ -67,13 +53,6 @@ func (v *ValueController) Create(val *models.Value) { // Update method updates value entry on map for given key and Payload. // If key not exists then returns 400 BadRequest error. -// -// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key -// Method: PUT -// Request Body: -// { -// "value": 3437476 // any valid type, since value is type interface{} -// } func (v *ValueController) Update(key string, val *models.Value) { if c, found := values[key]; found { c.Value = val.Value @@ -91,9 +70,6 @@ func (v *ValueController) Update(key string, val *models.Value) { // Delete method deletes value for given key. // If key not exists then returns 400 BadRequest error. -// -// Route: {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key -// Method: DELETE func (v *ValueController) Delete(key string) { if _, found := values[key]; found { delete(values, key) diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index e236914..aa042d5 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -69,9 +69,8 @@ func init() { // Event: OnPostAuth // Published right after the successful Authentication aah.OnPostAuth(security.PostAuthEvent){{ end }} - }) + }){{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - {{ if .App.IsWebSocketApp -}} // WebSocket Events // Doc: https://docs.aahframework.org/websocket.html#events // @@ -91,9 +90,8 @@ func init() { // Event: OnError // wse.OnError(mywebsocket.Error) }) - {{ end -}} - {{ if not .App.IsWebSocketApp -}}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + {{ end }}{{ if not .App.IsWebSocketApp }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Middleware's // Doc: https://docs.aahframework.org/middleware.html // @@ -118,9 +116,7 @@ func init() { // Add Application Error Handler // Doc: https://docs.aahframework.org/error-handling.html //__________________________________________________________________________ - // aah.SetErrorHandler(AppErrorHandler){{ end }} - - {{- if .App.IsWebApp }} + // aah.SetErrorHandler(AppErrorHandler){{ end }}{{ if .App.IsWebApp }} //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Add Custom Template Functions @@ -134,9 +130,9 @@ func init() { // Add Custom Session Store // Doc: https://docs.aahframework.org/session.html //__________________________________________________________________________ - // aah.AddSessionStore("redis", &RedisSessionStore{}){{ end }} + // aah.AddSessionStore("redis", &RedisSessionStore{}) - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + {{ end }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Add Custom value Parser // Doc: https://docs.aahframework.org/request-parameters-auto-bind.html //__________________________________________________________________________ diff --git a/aah/app-template/app/websockets/sample.go.atmpl b/aah/app-template/app/websockets/sample.go.atmpl index 1e55cd9..31f87e8 100644 --- a/aah/app-template/app/websockets/sample.go.atmpl +++ b/aah/app-template/app/websockets/sample.go.atmpl @@ -17,9 +17,6 @@ type SampleWebSocket struct { // Handle method is sample WebSocket action implementation. // // It handles Text and JSON encoding based on Path param value. -// -// Route: /ws/:mode -// Method: WS func (s *SampleWebSocket) Handle(mode string) { switch strings.ToLower(mode) { case "text": diff --git a/aah/build.go b/aah/build.go index 7f083d8..8c7ec12 100644 --- a/aah/build.go +++ b/aah/build.go @@ -22,10 +22,10 @@ import ( var buildCmd = cli.Command{ Name: "build", Aliases: []string{"b"}, - Usage: "Build aah application for deployment", - Description: `Build aah application by import path. + Usage: "Builds aah application for deployment", + Description: `Builds aah application for deployment. - Artifact naming convention: ---.zip + Artifact naming convention: ---.zip For e.g.: aahwebsite-381eaa8-darwin-amd64.zip Examples of short and long flags: @@ -42,11 +42,11 @@ var buildCmd = cli.Command{ }, cli.StringFlag{ Name: "e, envprofile", - Usage: "Environment profile name to activate. e.g: dev, qa, prod", + Usage: "Environment profile name to activate (e.g: dev, qa, prod)", }, cli.StringFlag{ Name: "o, output", - Usage: "Output of aah application build artifact. Default is '/build/---.zip'", + Usage: "Output of aah application build artifact; the default is '/build/---.zip'", }, }, } diff --git a/aah/clean.go b/aah/clean.go index 2c6107f..4133d98 100644 --- a/aah/clean.go +++ b/aah/clean.go @@ -17,7 +17,7 @@ var cleanCmd = cli.Command{ Name: "clean", Aliases: []string{"c"}, Usage: "Cleans the aah generated files and build directory", - Description: `aah clean command does cleanup of generated files and build directory. + Description: `Cleans the aah generated files and build directory. Such as aah.go and /build directory. diff --git a/aah/generate.go b/aah/generate.go index 0b641f5..98fb88c 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -23,8 +23,8 @@ var generateCmd = cli.Command{ Name: "generate", Aliases: []string{"g"}, Usage: "Generates boilerplate code, configurations, complement scripts (systemd, docker), etc.", - Description: `Generate command increases productivity and helps developer on tedious tasks during application development. - It generates boilerplate code, configuration files, complement scripts (systemd, docker), etc. + Description: `Command generate increases productivity and helps developer on tedious tasks during application development. + Such as boilerplate code, configuration files, complement scripts (systemd, docker), etc. To know more about available 'generate' sub commands: aah h g diff --git a/aah/list.go b/aah/list.go index ebc1cff..30aa52e 100644 --- a/aah/list.go +++ b/aah/list.go @@ -20,8 +20,8 @@ const aahProjectIdentifier = "aah.project" var listCmd = cli.Command{ Name: "list", Aliases: []string{"l"}, - Usage: "List all aah projects in GOPATH", - Description: `List command allows you to view all projects that are making use of aah in your GOPATH. + Usage: "Lists all the aah projects on your GOPATH", + Description: `Command 'list' helps you to view all the aah application projects on your GOPATH. `, Action: listAction, } diff --git a/aah/new.go b/aah/new.go index 0893ed4..9bc6fd8 100644 --- a/aah/new.go +++ b/aah/new.go @@ -25,7 +25,7 @@ var ( newCmd = cli.Command{ Name: "new", Aliases: []string{"n"}, - Usage: "Create new aah 'web', 'api' or 'websocket' application (interactive)", + Usage: "Creates new aah 'web', 'api' or 'websocket' application (interactive)", Description: `aah new command is an interactive program to assist you to quick start aah application. Just provide your inputs based on your use case to generate base structure to kick diff --git a/aah/run.go b/aah/run.go index a61a6dc..1db331c 100644 --- a/aah/run.go +++ b/aah/run.go @@ -34,9 +34,9 @@ import ( var runCmd = cli.Command{ Name: "run", Aliases: []string{"r"}, - Usage: "Run aah framework application (supports hot-reload)", - Description: `Run the aah framework web/api application. It supports hot-reload, just code and refresh the browser - to see your updates. + Usage: "Runs aah application (supports hot-reload)", + Description: `Runs aah application. It supports hot-reload (just code and refresh the browser + to see your updates). Examples of short and long flags: aah run @@ -46,12 +46,12 @@ var runCmd = cli.Command{ aah run -i github.com/user/appname -e qa aah run -i github.com/user/appname -e qa -c /path/to/config/external.conf - aah run --importpath github.com/username/name - aah run --importpath github.com/username/name --envprofile qa - aah run --importpath github.com/username/name --envprofile qa --config /path/to/config/external.conf + aah run --importpath github.com/user/appname + aah run --importpath github.com/user/appname --envprofile qa + aah run --importpath github.com/user/appname --envprofile qa --config /path/to/config/external.conf - Note: It is recommended to use build and deploy approach instead of - using 'aah run' for production use.`, + Note: For production use, it is recommended to follow build and deploy approach instead of + using 'aah run'.`, Flags: []cli.Flag{ cli.StringFlag{ Name: "i, importpath", @@ -59,7 +59,7 @@ var runCmd = cli.Command{ }, cli.StringFlag{ Name: "e, envprofile", - Usage: "Environment profile name to activate. e.g: dev, qa, prod"}, + Usage: "Environment profile name to activate (e.g: dev, qa, prod)"}, cli.StringFlag{ Name: "c, config", Usage: "External config file for overriding aah.conf values", @@ -368,7 +368,7 @@ func startWatcher(projectCfg *config.Config, baseDir string, w *watcher.Watcher, if cliLog.IsLevelTrace() { var fileList []string for path := range w.WatchedFiles() { - fileList = append(fileList, stripGoPath(path)) + fileList = append(fileList, stripGoSrcPath(path)) } cliLog.Trace("Watched files:\n\t", strings.Join(fileList, "\n\t")) } diff --git a/aah/switch.go b/aah/switch.go index c8bd46b..87c32b4 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -5,6 +5,7 @@ package main import ( + "fmt" "strings" "gopkg.in/urfave/cli.v1" @@ -18,8 +19,8 @@ const ( var switchCmd = cli.Command{ Name: "switch", Aliases: []string{"s"}, - Usage: "Switch between aah release and edge version", - Description: `Provides an ability to switch between aah release (currently on your GOPATH) and latest edge version. + Usage: "To switch between aah release and edge version", + Description: `Provides an ability to switch between aah release and latest edge version. Examples of short and long flags: aah s @@ -34,7 +35,7 @@ var switchCmd = cli.Command{ aah switch --refresh Note: - - Currently it works with only GOPATH. Gradually I will add vendorize support too. + - Currently it works with only GOPATH. - It always operates on latest edge version and current release version on your GOPATH, specific version is not supported.`, Flags: []cli.Flag{ cli.StringFlag{ @@ -104,7 +105,16 @@ func doSwitch(branchName, target string) error { fname := friendlyName(branchName) if target == fname { cliLog.Infof("You're already on '%s' version.\n", fname) - cliLog.Infof("To switch to latest release version. Run 'aah switch -v release'\n") + cliLog.Infof("To switch to release version. Run 'aah s -v release'\n") + + if fname == "edge" { + ans := collectYesOrNo(reader, "Would you like to refresh 'edge' to latest? ([Y]es or [N]o), default is 'N'") + fmt.Println() + if ans { + doRefresh(branchName) + } + } + return nil } diff --git a/aah/update.go b/aah/update.go index 78fe81e..a22a6d7 100644 --- a/aah/update.go +++ b/aah/update.go @@ -14,15 +14,16 @@ import ( var updateCmd = cli.Command{ Name: "update", Aliases: []string{"u"}, - Usage: "Update your aah to the latest release version on your GOPATH", - Description: `Provides an easy and convenient way to update your aah to the latest release version on your GOPATH. + Usage: "Updates aah to the latest release version on your GOPATH", + Description: `Provides an easy and convenient way to update your aah framework version +to the latest release version on your GOPATH. Examples of short and long flags: aah u aah update Note: - - Currently it works with only GOPATH. Gradually I will add vendorize support too. + - Currently it works with only GOPATH. - It always operates on aah latest release version, specific version is not supported. `, Action: updateAction, @@ -33,7 +34,7 @@ func updateAction(c *cli.Context) error { branchName := gitBranchName(libDir("aah")) if branchName != releaseBranchName { fmt.Printf("Update command only applicable to aah release version.\n") - fmt.Printf("Currently you're on aah 'edge' version, use 'aah switch --refresh' command to get latest edge version.\n\n") + fmt.Printf("Currently you're on aah 'edge' version, use 'aah s -r' command to refresh edge version.\n\n") return nil } diff --git a/aah/util.go b/aah/util.go index 71a4aaf..3aac94b 100644 --- a/aah/util.go +++ b/aah/util.go @@ -7,6 +7,7 @@ package main import ( "fmt" "io" + "io/ioutil" "net" "os" "os/exec" @@ -390,7 +391,7 @@ func logErrorf(format string, v ...interface{}) { cliLog.Errorf("ERROR "+format, v...) } -func stripGoPath(pkgFilePath string) string { +func stripGoSrcPath(pkgFilePath string) string { idx := strings.Index(pkgFilePath, "src") return filepath.Clean(pkgFilePath[idx+4:]) } @@ -452,3 +453,22 @@ func inferNotExistsDeps(depList []string) []string { } return notExistsList } + +func readVersionNo(baseDir string) (string, error) { + versionFile := filepath.Join(baseDir, "version.go") + if !ess.IsFileExists(versionFile) { + return "", errVersionNotExists + } + + bytes, err := ioutil.ReadFile(versionFile) + if err != nil { + return "", err + } + + result := verRegex.FindStringSubmatch(string(bytes)) + if len(result) >= 2 { + return result[1], nil + } + + return "Unknown", nil +} diff --git a/aah/version.go b/aah/version.go index bd216b5..38d3a35 100644 --- a/aah/version.go +++ b/aah/version.go @@ -4,5 +4,54 @@ package main +import ( + "errors" + "fmt" + "path/filepath" + "regexp" + "strings" + + "gopkg.in/urfave/cli.v1" +) + // Version no. of aah framework CLI tool const Version = "0.11.0-edge" + +var ( + errVersionNotExists = errors.New("version not exists") + verRegex = regexp.MustCompile(`Version = "([\d.]+(\-edge)?)"`) +) + +// VersionPrinter method prints the versions info. +func VersionPrinter(c *cli.Context) { + cliLog = initCLILogger(nil) + fmt.Println("Version:") + fmt.Printf(" %-4s v%s\n", "cli", Version) + fmt.Printf(" %-4s v%s\n", "aah", aahVer) + if goVer := goVersion(); len(goVer) > 0 { + fmt.Printf(" %-4s v%s\n", "go", goVer) + } + + fmt.Printf("\nLibraries:\n") + for _, bd := range aahLibraryDirs() { + bn := filepath.Base(bd) + if strings.HasPrefix(bn, "aah") || strings.HasPrefix(bn, "tools") { + continue + } + if verNo, err := readVersionNo(bd); err == nil { + fmt.Printf(" %s v%s\n", bn[:len(bn)-3], verNo) + } + } + fmt.Println() +} + +func aahVersion() (string, error) { + return readVersionNo(libDir("aah")) +} + +func goVersion() string { + if ver, err := execCmd(gocmd, []string{"version"}, false); err == nil { + return strings.TrimLeft(strings.Fields(ver)[2], "go") + } + return "" +} From fa188a5c9b2e78ecb685bc4cff5f8c4a2ea29900 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 28 Apr 2018 19:41:25 -0700 Subject: [PATCH 14/48] made cmd switch faster than before --- aah/aah.go | 8 +++++ aah/build.go | 2 +- aah/clean.go | 2 +- aah/generate.go | 4 +-- aah/run.go | 2 +- aah/switch.go | 47 ++++++++++++++++----------- aah/util.go | 85 ++++++++++++++++++++++++++----------------------- 7 files changed, 87 insertions(+), 63 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index 7ddc6ac..2be9784 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -30,6 +30,7 @@ var ( gopath string gocmd string gosrcDir string + gitcmd string aahVer string // abstract it, so we can do unit test @@ -54,12 +55,19 @@ func checkPrerequisites() error { return err } + // Go executable if gocmd, err = exec.LookPath("go"); err != nil { return err } gosrcDir = filepath.Join(gopath, "src") + // git + if gitcmd, err = exec.LookPath("git"); err != nil { + return err + } + + // aah if aahVer, err = aahVersion(); err == errVersionNotExists { return errors.New("aah framework is not installed, its easy to install. Run 'go get aahframework.org/tools.v0/aah'") } diff --git a/aah/build.go b/aah/build.go index 8c7ec12..b3989dc 100644 --- a/aah/build.go +++ b/aah/build.go @@ -52,7 +52,7 @@ var buildCmd = cli.Command{ } func buildAction(c *cli.Context) error { - importPath := getAppImportPath(c) + importPath := appImportPath(c) if err := aah.Init(importPath); err != nil { logFatal(err) diff --git a/aah/clean.go b/aah/clean.go index 4133d98..f20814c 100644 --- a/aah/clean.go +++ b/aah/clean.go @@ -35,7 +35,7 @@ var cleanCmd = cli.Command{ } func cleanAction(c *cli.Context) error { - importPath := getAppImportPath(c) + importPath := appImportPath(c) if err := aah.Init(importPath); err != nil { logFatal(err) diff --git a/aah/generate.go b/aah/generate.go index 98fb88c..4c0cdd8 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -93,7 +93,7 @@ func generateScriptsAction(c *cli.Context) error { //___________________________________ func generateSystemdScript(c *cli.Context) error { - importPath := getAppImportPath(c) + importPath := appImportPath(c) if err := aah.Init(importPath); err != nil { logFatal(err) @@ -131,7 +131,7 @@ func generateSystemdScript(c *cli.Context) error { } func generateDockerScript(c *cli.Context) error { - importPath := getAppImportPath(c) + importPath := appImportPath(c) if err := aah.Init(importPath); err != nil { logFatal(err) diff --git a/aah/run.go b/aah/run.go index 1db331c..95d9336 100644 --- a/aah/run.go +++ b/aah/run.go @@ -99,7 +99,7 @@ type ( ) func runAction(c *cli.Context) error { - importPath := getAppImportPath(c) + importPath := appImportPath(c) appStartArgs := []string{} configPath := getNonEmptyAbsPath(c.String("c"), c.String("config")) diff --git a/aah/switch.go b/aah/switch.go index 87c32b4..35a63da 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -6,7 +6,9 @@ package main import ( "fmt" + "path/filepath" "strings" + "sync" "gopkg.in/urfave/cli.v1" ) @@ -14,6 +16,7 @@ import ( const ( releaseBranchName = "master" edgeBranchName = "v0-edge" + emojiThumpsUp = `👍` ) var switchCmd = cli.Command{ @@ -89,13 +92,13 @@ func doRefresh(branchName string) error { cliLog.Infof("Refreshing aah '%s' version ...\n", fname) // Refresh to latest edge codebase - refreshCodebase(aahLibraryDirs()) + refreshLibCode(aahLibraryDirs()) // Refresh dependencies in grace mode - fetchAahDeps() + fetchLibDeps() - // Install aah CLI for the currently version - installAahCLI() + // Install aah CLI for current version + installCLI() cliLog.Infof("You have successfully refreshed aah '%s' version.\n", fname) return nil @@ -104,11 +107,11 @@ func doRefresh(branchName string) error { func doSwitch(branchName, target string) error { fname := friendlyName(branchName) if target == fname { - cliLog.Infof("You're already on '%s' version.\n", fname) + cliLog.Infof("Currently you're on aah '%s' version.\n", fname) cliLog.Infof("To switch to release version. Run 'aah s -v release'\n") if fname == "edge" { - ans := collectYesOrNo(reader, "Would you like to refresh 'edge' to latest? ([Y]es or [N]o), default is 'N'") + ans := collectYesOrNo(reader, "Would you like to refresh 'edge' to latest updates? ([Y]es or [N]o), default is 'N'") fmt.Println() if ans { doRefresh(branchName) @@ -125,31 +128,39 @@ func doSwitch(branchName, target string) error { toBranch = releaseBranchName } - cliLog.Infof("Switching aah version to '%s' ...\n", friendlyName(toBranch)) + cliLog.Infof("Switching aah to '%s' version ...\n", friendlyName(toBranch)) // Checkout the branch aahLibDirs := aahLibraryDirs() - for _, d := range aahLibDirs { - if err := gitCheckout(d, toBranch); err != nil { - logFatalf("Error occurred which switching aah version: %s", err) - } + var wg sync.WaitGroup + for _, dir := range aahLibDirs { + wg.Add(1) + go func(d string) { + defer wg.Done() + baseName := filepath.Base(d) + if err := gitCheckout(d, toBranch); err != nil { + logErrorf("Unable to switch library version, possibliy you may have local changes[%s]: %s", baseName, err) + } + cliLog.Tracef("Library '%s' have been switched to '%s' successfully", baseName, toBranch) + }(dir) } + wg.Wait() if toBranch == edgeBranchName { - cliLog.Infof("Refreshing aah version to latest '%s' ...\n", friendlyName(toBranch)) - refreshCodebase(aahLibDirs) + cliLog.Infof("Refreshing aah to latest '%s' updates ...\n", friendlyName(toBranch)) + refreshLibCode(aahLibDirs) } // Refresh dependencies in grace mode - fetchAahDeps() + fetchLibDeps() - // Install aah CLI for the currently version - installAahCLI() + // Install aah CLI for current version + installCLI() if toBranch == releaseBranchName { - cliLog.Infof("You have successfully switched to aah 'release' version.\n") + cliLog.Infof("You have successfully switched %s.\n", emojiThumpsUp) } else { - cliLog.Infof("You have successfully switched to aah 'edge' version, your feedback is appreciated.\n") + cliLog.Infof("You have successfully switched %s, your feedback is appreciated.\n", emojiThumpsUp) } return nil } diff --git a/aah/util.go b/aah/util.go index 3aac94b..57a80de 100644 --- a/aah/util.go +++ b/aah/util.go @@ -15,6 +15,7 @@ import ( "runtime" "strconv" "strings" + "sync" "text/template" "time" @@ -103,21 +104,17 @@ func getAppVersion(appBaseDir string, cfg *config.Config) string { version := cfg.StringDefault("build.version", "") // git describe - if gitcmd, err := exec.LookPath("git"); err == nil { - if !ess.IsFileExists(filepath.Join(appBaseDir, ".git")) { - return version - } - - gitArgs := []string{"-C", appBaseDir, "describe", "--always", "--dirty"} - output, err := execCmd(gitcmd, gitArgs, false) - if err != nil { - return version - } + if !ess.IsFileExists(filepath.Join(appBaseDir, ".git")) { + return version + } - version = strings.TrimSpace(output) + gitArgs := []string{"-C", appBaseDir, "describe", "--always", "--dirty"} + output, err := execCmd(gitcmd, gitArgs, false) + if err != nil { + return version } - return version + return strings.TrimSpace(output) } // getBuildDate method returns application build date, which used to display @@ -258,9 +255,8 @@ func initCLILogger(cfg *config.Config) *log.Logger { } func gitCheckout(dir, branch string) error { - if gitcmd, err := exec.LookPath("git"); err == nil { - gitArgs := []string{"-C", dir, "checkout", branch} - _, err := execCmd(gitcmd, gitArgs, false) + if ess.IsFileExists(filepath.Join(dir, ".git")) { + _, err := execCmd(gitcmd, []string{"-C", dir, "checkout", branch}, false) return err } return nil @@ -281,18 +277,18 @@ func gitBranchName(dir string) string { return "" } - if gitcmd, err := exec.LookPath("git"); err == nil { - gitArgs := []string{"-C", dir, "rev-parse", "--abbrev-ref", "HEAD"} - output, _ := execCmd(gitcmd, gitArgs, false) - return strings.TrimSpace(output) + if !ess.IsFileExists(filepath.Join(dir, ".git")) { + return "" } - return "" + + gitArgs := []string{"-C", dir, "rev-parse", "--abbrev-ref", "HEAD"} + output, _ := execCmd(gitcmd, gitArgs, false) + return strings.TrimSpace(output) } func gitPull(dir string) error { - if gitcmd, err := exec.LookPath("git"); err == nil { - gitArgs := []string{"-C", dir, "pull"} - _, err := execCmd(gitcmd, gitArgs, false) + if ess.IsFileExists(filepath.Join(dir, ".git")) { + _, err := execCmd(gitcmd, []string{"-C", dir, "pull"}, false) return err } return nil @@ -300,8 +296,7 @@ func gitPull(dir string) error { func goGet(pkgs ...string) error { for _, pkg := range pkgs { - args := []string{"get", pkg} - if _, err := execCmd(gocmd, args, false); err != nil { + if _, err := execCmd(gocmd, []string{"get", pkg}, false); err != nil { return err } } @@ -324,25 +319,29 @@ func waitForConnReady(port string) { } } -func installAahCLI() { +func installCLI() { verser := inferVersionSeries() args := []string{"install", fmt.Sprintf("%s/tools.%s/aah", importPrefix, verser)} if _, err := execCmd(gocmd, args, false); err != nil { - logFatalf("Unable to compile CLI tool: %s", err) + logFatalf("Unable to compile aah CLI: %s", err) } } -func fetchAahDeps() { - // depList would have duplicate import paths - // since we collect from more than one lib - // TODO: improve efficiency - var depList []string +func fetchLibDeps() { + var notEixstsList []string + var wg sync.WaitGroup for _, i := range aahImportPaths() { - depList = append(depList, libDependencyImports(i)...) + wg.Add(1) + go func(p string) { + defer wg.Done() + if neList := inferNotExistsDeps(libDependencyImports(p)); len(neList) > 0 { + notEixstsList = append(notEixstsList, neList...) + } + }(i) } + wg.Wait() // infer not exists libraries on GOPATH using importpath - notEixstsList := inferNotExistsDeps(depList) if len(notEixstsList) > 0 { if err := goGet(notEixstsList...); err != nil { logFatalf("Error during go get: %s", err) @@ -350,15 +349,21 @@ func fetchAahDeps() { } } -func refreshCodebase(libDirs []string) { - for _, ld := range libDirs { - if err := gitPull(ld); err != nil { - logFatalf("Unable to refresh library: %s", filepath.Base(ld)) - } +func refreshLibCode(libDirs []string) { + var wg sync.WaitGroup + for _, dir := range libDirs { + wg.Add(1) + go func(d string) { + defer wg.Done() + if err := gitPull(d); err != nil { + logErrorf("Unable to refresh library, possibliy you may have local changes: %s", filepath.Base(d)) + } + }(dir) } + wg.Wait() } -func getAppImportPath(c *cli.Context) string { +func appImportPath(c *cli.Context) string { importPath := firstNonEmpty(c.String("i"), c.String("importpath")) if ess.IsStrEmpty(importPath) { importPath = importPathRelwd() From 3ecc0e68267fdb235bb72ce5174d5c055abad76f Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sun, 29 Apr 2018 13:54:13 -0700 Subject: [PATCH 15/48] cmd and template updates --- .../app/controllers/v1/value.go.atmpl | 2 + aah/app-template/app/init.go.atmpl | 125 +++++++++++------- aah/switch.go | 2 +- aah/test.cfg | 60 --------- aah/version.go | 7 +- 5 files changed, 85 insertions(+), 111 deletions(-) delete mode 100644 aah/test.cfg diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index 1a388a8..a532272 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -1,6 +1,8 @@ package v1 import ( + "fmt" + "aahframework.org/aah.v0" "aahframework.org/ahttp.v0" diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index aa042d5..ad9d1e0 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -32,66 +32,28 @@ func init() { //__________________________________________________________________________ // Event: OnInit + // // Published right after the `aah.AppConfig()` is loaded. // // aah.OnInit(config.LoadRemote) // Event: OnStart - // Published right before the start of aah go Server. + // + // Published right before the start of aah server. // // aah.OnStart(db.Connect) - // aah.OnStart(cache.Load) + // aah.OnStart(cache.Load){{ if or .App.IsWebApp .App.IsAPIApp }} + aah.OnStart(SubscribeHTTPEvents){{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} + aah.OnStart(SubscribeWebSocketEvents){{ end }} // Event: OnShutdown + // // Published on receiving OS Signals `SIGINT` or `SIGTERM`. // // aah.OnShutdown(cache.Flush) // aah.OnShutdown(db.Disconnect) - // HTTP Events - // - // Add your HTTP events on app start. - aah.OnStart(func(_ *aah.Event) { - // he := aah.AppHTTPEngine() - - // Event: OnRequest - // he.OnRequest(myserverext.OnRequest) - - // Event: OnPreReply - // he.OnPreReply(myserverext.OnPreReply) - - // Event: OnPostReply - // he.OnPostReply(myserverext.OnPostReply) - - // Event: OnPreAuth - // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} - - // Event: OnPostAuth - // Published right after the successful Authentication - aah.OnPostAuth(security.PostAuthEvent){{ end }} - }){{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - - // WebSocket Events - // Doc: https://docs.aahframework.org/websocket.html#events - // - // Add your WebSocket events on app start. - aah.OnStart(func(_ *aah.Event) { - // wse := aah.AppWSEngine() - - // Event: OnPreConnect - // wse.OnPreConnect(mywebsocket.PreConnect) - - // Event: OnPostConnect - // wse.OnPostConnect(mywebsocket.PostConnect) - - // Event: OnPostDisconnect - // wse.OnPostDisconnect(mywebsocket.PostDisconnect) - - // Event: OnError - // wse.OnError(mywebsocket.Error) - }) - - {{ end }}{{ if not .App.IsWebSocketApp }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + {{ if not .App.IsWebSocketApp }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Middleware's // Doc: https://docs.aahframework.org/middleware.html // @@ -149,4 +111,75 @@ func init() { // // // Add your validation funcs +}{{ if or .App.IsWebApp .App.IsAPIApp }} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// HTTP Events +// +// Subscribing HTTP events on app start. +//__________________________________________________________________________ + +func SubscribeHTTPEvents(_ *aah.Event) { + {{ if not .App.IsSecurityEnabled }}//{{ end }} he := aah.AppHTTPEngine() + + // Event: OnRequest + // he.OnRequest(myserverext.OnRequest) + + // Event: OnPreReply + // he.OnPreReply(myserverext.OnPreReply) + + // Event: OnPostReply + // he.OnPostReply(myserverext.OnPostReply) + + // Event: OnPreAuth + // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} + + // Event: OnPostAuth + // Published right after the successful Authentication + he.OnPostAuth(security.PostAuthEvent){{ else }} + + // Event: OnPostAuth + // Published right after the successful Authentication + // he.OnPostAuth(myserverext.PostAuthEvent){{ end }} } +{{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// WebSocket Events +// +// Doc: https://docs.aahframework.org/websocket.html#events +// +// Subscribing WebSocket events on app start. +//__________________________________________________________________________ + +func SubscribeWebSocketEvents(_ *aah.Event) { + // wse := aah.AppWSEngine() + + // Event: OnPreConnect + // + // Published before connection gets upgraded to WebSocket. + // It provides a control of accepting incoming request or reject it + // using ctx.Abort(errorCode) + // wse.OnPreConnect(mywebsockets.HandleEvents) + + // Event: OnPostConnect + // + // Published right after the successful WebSocket connection which is + // established with the aah server. + // wse.OnPostConnect(mywebsockets.HandleEvents) + + // Event: OnPostDisconnect + // + // Published right after the WebSocket client got disconnected. + // It could have occurred due to graceful disconnect, network related error, etc. + // wse.OnPostDisconnect(mywebsockets.HandleEvents) + + // Event: OnError + // + // Published whenever error occurs in the lifecycle such as Origin Check failed, + // WebSocket/WebSocket Action not found, WebSocket Action parameter parse error, + // and WebSocket upgrade fails. + // + //`ctx.ErrorReason()` method can be called to know the reason for the error. + // wse.OnError(mywebsockets.HandleEvents) +}{{ end }} diff --git a/aah/switch.go b/aah/switch.go index 35a63da..31fa2ff 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -22,7 +22,7 @@ const ( var switchCmd = cli.Command{ Name: "switch", Aliases: []string{"s"}, - Usage: "To switch between aah release and edge version", + Usage: "Switches between aah release and edge version", Description: `Provides an ability to switch between aah release and latest edge version. Examples of short and long flags: diff --git a/aah/test.cfg b/aah/test.cfg deleted file mode 100644 index 500c45a..0000000 --- a/aah/test.cfg +++ /dev/null @@ -1,60 +0,0 @@ -env { - #active = "prod" - - dev { - http { - port = 5000 - } - } - - - # -------------------------------- - # Production Configuration Section - # -------------------------------- - prod { - - # -------------------- - # Logger configuration - # -------------------- - log { - receiver = "file" - file = "testing-name.log" - } - - # ------------------------- - # Render/View configuration - # ------------------------- - render { - default = "html" - pretty = false - gzip = true - - multipart { - enable = true - size = "32mb" - } - } - - # ------------------------ - # DataSource configuration - # ------------------------ - datasource { - default = "mydb1" - - mydb1 { - driver = "" - url = "" - max_idle_conns = 5 - max_open_conns = 10 - } - - mydb2 { - driver = "" - url = "" - max_idle_conns = 5 - max_open_conns = 10 - } - } - - } -} diff --git a/aah/version.go b/aah/version.go index 38d3a35..2f4c4be 100644 --- a/aah/version.go +++ b/aah/version.go @@ -25,11 +25,10 @@ var ( // VersionPrinter method prints the versions info. func VersionPrinter(c *cli.Context) { cliLog = initCLILogger(nil) - fmt.Println("Version:") - fmt.Printf(" %-4s v%s\n", "cli", Version) - fmt.Printf(" %-4s v%s\n", "aah", aahVer) + fmt.Printf("%-4s v%s\n", "cli", Version) + fmt.Printf("%-4s v%s\n", "aah", aahVer) if goVer := goVersion(); len(goVer) > 0 { - fmt.Printf(" %-4s v%s\n", "go", goVer) + fmt.Printf("%-4s v%s\n", "go", goVer) } fmt.Printf("\nLibraries:\n") From 67bf7084a8f86efca333b64a503b71c0f75be118 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 5 May 2018 19:15:03 -0700 Subject: [PATCH 16/48] header char update and generated code hint to compiler --- aah/aah.go | 8 ++++---- aah/compile.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index 2be9784..fc27437 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -125,15 +125,15 @@ func printHeader(c *cli.Context) error { cnt := len(improveRpt) sp := (cnt - len(hdrCont)) / 2 - fmt.Println(chrtostr("=", cnt)) - fmt.Println(chrtostr(" ", sp) + hdrCont) - fmt.Println(chrtostr("=", cnt)) + fmt.Println(chr2str("-", cnt)) + fmt.Println(chr2str(" ", sp) + hdrCont) + fmt.Println(chr2str("-", cnt)) fmt.Printf(improveRpt + "\n\n") return nil } -func chrtostr(chr string, cnt int) string { +func chr2str(chr string, cnt int) string { var str string for idx := 0; idx < cnt; idx++ { str += chr diff --git a/aah/compile.go b/aah/compile.go index 226cdca..78492c2 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -327,7 +327,7 @@ func prepareAuthAlias(keyAuthAlias, auth, importPathPrefix string, appImportPath // Generate Templates //___________________________________ -const aahMainTemplate = `// GENERATED CODE - DO NOT EDIT +const aahMainTemplate = `// Code generated by aah CLI, DO NOT EDIT // // aah framework v{{.AahVersion}} - https://aahframework.org // FILE: aah.go From ada5bedfcd5ba62746a4f7c82131e74a9116c9de Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 7 May 2018 19:19:31 -0700 Subject: [PATCH 17/48] app template key correction --- aah/app-template/config/aah.conf.atmpl | 4 ++-- aah/app-template/config/env/prod.conf.atmpl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index c5cad2a..f2b8543 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -188,7 +188,7 @@ server { # Absolute path to access log file or relative path. # Default location is application logs directory - #file = "{{ .AppName }}-access.log" + #file = "{{ .App.Name }}-access.log" # Default server access log pattern #pattern = "%clientip %custom:- %reqtime %reqmethod %requrl %reqproto %resstatus %ressize %restime %reqhdr:referer" @@ -214,7 +214,7 @@ server { # Absolute path to dump log file or relative path. # Default location is application logs directory - #file = "{{ .AppName }}-dump.log" + #file = "{{ .App.Name }}-dump.log" # Log Request body into dump log. aah dumps body for JSON, XML, Form # HTML and Plain Text content types. diff --git a/aah/app-template/config/env/prod.conf.atmpl b/aah/app-template/config/env/prod.conf.atmpl index 905e65a..0a41c1d 100644 --- a/aah/app-template/config/env/prod.conf.atmpl +++ b/aah/app-template/config/env/prod.conf.atmpl @@ -43,7 +43,7 @@ prod { # File config attribute is applicable only to `file` receiver type. # Default value is `aah-log-file.log`. - file = "{{ .AppName }}.log" + file = "{{ .App.Name }}.log" # Rotate config section is applicable only to `file` receiver type. # Default rotation is 'daily'. From 6b790a51670015bbf654093e8388beb3a6186e48 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 9 May 2018 10:18:07 -0700 Subject: [PATCH 18/48] process is handled in ainsp --- aah/compile.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 78492c2..e8e695e 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -64,9 +64,6 @@ func compileApp(args *compileArgs) (string, error) { return "", errors.New(strings.Join(errMsgs, "\n")) } - // call the process - acntlr.Process() - // Print router configuration missing/error details missingActions := []string{} for c, m := range acntlr.RegisteredActions { @@ -99,9 +96,6 @@ func compileApp(args *compileArgs) (string, error) { return "", errors.New(strings.Join(errMsgs, "\n")) } - // call the process - wsc.Process() - // Print router configuration missing/error details missingWSActions := []string{} for c, m := range wsc.RegisteredActions { From 26727badeb26cdd6ff51245c211c1321b5926763 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 16 May 2018 00:10:32 -0700 Subject: [PATCH 19/48] go-aah/aah#156 integrated vfs into CLI and aah binary build support added --- .travis.yml | 4 +- aah/aah.go | 2 +- aah/app-template/aah.project.atmpl | 42 ++++- aah/app-template/app/init.go.atmpl | 19 ++- aah/build.go | 125 ++++++++++++--- aah/clean.go | 10 +- aah/compile.go | 29 ++-- aah/embed.go | 245 +++++++++++++++++++++++++++++ aah/util.go | 34 +++- 9 files changed, 450 insertions(+), 60 deletions(-) create mode 100644 aah/embed.go diff --git a/.travis.yml b/.travis.yml index ecef445..a3c8671 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,8 @@ branches: - /^v[0-9.]+$/ go: - - 1.9 - - "1.10" + - 1.9.x + - 1.x - tip go_import_path: aahframework.org/tools.v0/aah diff --git a/aah/aah.go b/aah/aah.go index fc27437..c127d38 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -80,7 +80,7 @@ func main() { // if panic happens, recover and abort nicely :) defer func() { if r := recover(); r != nil { - strace := aruntime.NewStacktrace(r, config.NewEmptyConfig()) + strace := aruntime.NewStacktrace(r, config.NewEmpty()) strace.Print(os.Stdout) exit(2) } diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index b845227..cf8f4ea 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -28,14 +28,20 @@ build { tags = "" # AST excludes is used for `aah.Context` inspection and generating aah - # application main Go file. Valid exclude patterns - # refer: https://golang.org/pkg/path/filepath/#Match + # application main Go file. + # + # Valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match ast_excludes = ["*_test.go", ".*", "*.bak", "*.tmp", "vendor"] # Packing excludes is used to exclude file/directory during aah application - # build archive. Valid exclude patterns - # refer: https://golang.org/pkg/path/filepath/#Match - excludes = ["*.go", "*_test.go", ".*", "*.bak", "*.tmp", "vendor", "app", "build", "tests", "logs"] + # build archive. + # + # Note: It is applicable to aah single binary build. This list excluded from + # embedding. + # + # Valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match + excludes = ["*.go", ".*", "*.bak", "*.tmp", "*.pid", + "vendor", "app", "build", "tests", "logs"] } # Logger configuration for aah CLI tool. @@ -66,3 +72,29 @@ hot_reload { file_excludes = [".*", "_test.go", "LICENSE", "README.md"] } } + +# Virtual FileSystem (VFS) configuration +vfs { + # Adding custom mount points. Configured mount points directory sub-tree + # gets embedded into aah binary during a build. + # + # Note: Its applicable for aah single binary build. + mount { + # Choose an unique key name + #my_mount { + # # Mount point (path separator is '/'). + # mount_path = "/my_mount" + # + # # Physical FileSystem path. + # # Note: Only absolute path accepted. + # physical_path = "/Users/jeeva/path/to/physical" + #} + } + + # This config is speed up the single binary build process. + # Do not spend time to gzip for already compress content. + # + # Even without this config aah does best decision on what to gzip. + no_gzip = [".png", ".jpeg", ".jpg", ".gif", ".bmp", ".tiff", ".tif" + ".woff", ".woff2"] +} diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index ad9d1e0..70a6cd1 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -60,7 +60,7 @@ func init() { // Executed in the order they are defined. It is recommended; NOT to change // the order of pre-defined aah framework middleware's. //__________________________________________________________________________ - aah.Middlewares( + aah.AppHTTPEngine().Middlewares( aah.RouteMiddleware, aah.CORSMiddleware, aah.BindMiddleware,{{ if .App.IsWebApp }} @@ -123,23 +123,31 @@ func SubscribeHTTPEvents(_ *aah.Event) { {{ if not .App.IsSecurityEnabled }}//{{ end }} he := aah.AppHTTPEngine() // Event: OnRequest + // + // Published for each incoming request to the aah go server. // he.OnRequest(myserverext.OnRequest) // Event: OnPreReply + // + // Published right before writing an reply on the wire. // he.OnPreReply(myserverext.OnPreReply) // Event: OnPostReply + // + // Published right after the reply is written on the wire. // he.OnPostReply(myserverext.OnPostReply) // Event: OnPreAuth + // + // Published right before the Authentication by security manager. // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} // Event: OnPostAuth - // Published right after the successful Authentication + // Published right after the successful Authentication by security manager. he.OnPostAuth(security.PostAuthEvent){{ else }} // Event: OnPostAuth - // Published right after the successful Authentication + // Published right after the successful Authentication by security manager. // he.OnPostAuth(myserverext.PostAuthEvent){{ end }} } {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} @@ -155,6 +163,9 @@ func SubscribeHTTPEvents(_ *aah.Event) { func SubscribeWebSocketEvents(_ *aah.Event) { // wse := aah.AppWSEngine() + // Custom ID Generator + // wse.SetIDGenerator(websockets.MyCustomIDGenerator) + // Event: OnPreConnect // // Published before connection gets upgraded to WebSocket. @@ -180,6 +191,6 @@ func SubscribeWebSocketEvents(_ *aah.Event) { // WebSocket/WebSocket Action not found, WebSocket Action parameter parse error, // and WebSocket upgrade fails. // - //`ctx.ErrorReason()` method can be called to know the reason for the error. + // `ctx.ErrorReason()` method can be called to know the reason for the error. // wse.OnError(mywebsockets.HandleEvents) }{{ end }} diff --git a/aah/build.go b/aah/build.go index b3989dc..0a9bdb4 100644 --- a/aah/build.go +++ b/aah/build.go @@ -22,14 +22,16 @@ import ( var buildCmd = cli.Command{ Name: "build", Aliases: []string{"b"}, - Usage: "Builds aah application for deployment", - Description: `Builds aah application for deployment. + Usage: "Builds aah application for deployment (single or non-single)", + Description: `Builds aah application for deployment. It supports single and non-single + binary. Its a trade-off learn more https://docs.aahframework.org/build-packaging.html Artifact naming convention: ---.zip For e.g.: aahwebsite-381eaa8-darwin-amd64.zip Examples of short and long flags: aah build + aah build --single aah build -e dev aah build -i github.com/user/appname -o /Users/jeeva -e qa aah build -i github.com/user/appname -o /Users/jeeva/aahwebsite.zip @@ -48,24 +50,39 @@ var buildCmd = cli.Command{ Name: "o, output", Usage: "Output of aah application build artifact; the default is '/build/---.zip'", }, + cli.BoolFlag{ + Name: "s, single", + Usage: "Creates aah single application binary", + }, }, } func buildAction(c *cli.Context) error { importPath := appImportPath(c) - if err := aah.Init(importPath); err != nil { logFatal(err) } - appBaseDir := aah.AppBaseDir() - projectCfg := aahProjectCfg(appBaseDir) + projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) - cliLog.Infof("Loaded aah project file: %s", filepath.Join(appBaseDir, aahProjectIdentifier)) + cliLog.Infof("Loaded aah project file: %s", filepath.Join(aah.AppBaseDir(), aahProjectIdentifier)) cliLog.Infof("Build starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) - appBinay, err := compileApp(&compileArgs{ + if c.Bool("s") || c.Bool("single") { + buildSingleBinary(c, projectCfg) + } else { + buildBinary(c, projectCfg) + } + + return nil +} + +func buildBinary(c *cli.Context, projectCfg *config.Config) { + appBaseDir := aah.AppBaseDir() + cleanupAutoGenVFSFiles(appBaseDir) + + appBinary, err := compileApp(&compileArgs{ Cmd: "BuildCmd", ProjectCfg: projectCfg, AppPack: true, @@ -75,41 +92,73 @@ func buildAction(c *cli.Context) error { } appProfile := firstNonEmpty(c.String("e"), c.String("envprofile"), "prod") - buildBaseDir, err := copyFilesToWorkingDir(projectCfg, appBaseDir, appBinay, appProfile) + buildBaseDir, err := copyFilesToWorkingDir(projectCfg, appBaseDir, appBinary, appProfile) if err != nil { logFatal(err) } - outputFile := firstNonEmpty(c.String("o"), c.String("output")) - archiveName := ess.StripExt(filepath.Base(appBinay)) + "-" + getAppVersion(appBaseDir, projectCfg) - archiveName = addTargetBuildInfo(archiveName) + destArchiveFile := createZipArchiveName(c, projectCfg, appBaseDir, appBinary) - var destArchiveFile string - if ess.IsStrEmpty(outputFile) { - destArchiveFile = filepath.Join(appBaseDir, "build", archiveName) - } else { - destArchiveFile, err = filepath.Abs(outputFile) - if err != nil { - logFatal(err) + // Creating app archive + if err = createZipArchive(buildBaseDir, destArchiveFile); err != nil { + logFatal(err) + } + + cliLog.Infof("Build successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + cliLog.Infof("Application artifact is here: %s\n", destArchiveFile) +} + +func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { + cliLog.Infof("Embed starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + appBaseDir := aah.AppBaseDir() + defer cleanupAutoGenVFSFiles(appBaseDir) + + excludes, _ := projectCfg.StringList("build.excludes") + noGzipList, _ := projectCfg.StringList("vfs.no_gzip") + + // Default mount point + if err := processMount(appBaseDir, "/app", appBaseDir, ess.Excludes(excludes), noGzipList); err != nil { + logFatal(err) + } + + // Custom mount points + mountKeys := projectCfg.KeysByPath("vfs.mount") + for _, key := range mountKeys { + vroot := projectCfg.StringDefault("vfs.mount."+key+".mount_path", "") + proot := projectCfg.StringDefault("vfs.mount."+key+".physical_path", "") + + if !filepath.IsAbs(proot) { + logErrorf("vfs %s: physical_path is not absolute path, skip mount: %s", proot, vroot) + continue } - if !strings.HasSuffix(destArchiveFile, ".zip") { - destArchiveFile = filepath.Join(destArchiveFile, archiveName) + if !ess.IsStrEmpty(vroot) && !ess.IsStrEmpty(proot) { + cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) + if err := processMount(appBaseDir, vroot, proot, ess.Excludes(excludes), noGzipList); err != nil { + logFatal(err) + } } } + cliLog.Infof("Embed successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) - if !strings.HasSuffix(destArchiveFile, ".zip") { - destArchiveFile = destArchiveFile + ".zip" + appBinary, err := compileApp(&compileArgs{ + Cmd: "BuildCmd", + ProjectCfg: projectCfg, + AppPack: true, + AppEmbed: true, + }) + if err != nil { + logFatal(err) } // Creating app archive - if err = createZipArchive(buildBaseDir, destArchiveFile); err != nil { + destArchiveFile := createZipArchiveName(c, projectCfg, appBaseDir, appBinary) + if err = createZipArchive(appBinary, destArchiveFile); err != nil { logFatal(err) } cliLog.Infof("Build successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) - cliLog.Infof("Your application artifact is here: %s\n", destArchiveFile) - return nil + cliLog.Infof("Application artifact is here: %s\n", destArchiveFile) } func copyFilesToWorkingDir(projectCfg *config.Config, appBaseDir, appBinary, appProfile string) (string, error) { @@ -190,6 +239,32 @@ func createZipArchive(buildBaseDir, destArchiveFile string) error { return ess.Zip(destArchiveFile, buildBaseDir) } +func createZipArchiveName(c *cli.Context, projectCfg *config.Config, appBaseDir, appBinary string) string { + var err error + outputFile := firstNonEmpty(c.String("o"), c.String("output")) + archiveName := ess.StripExt(filepath.Base(appBinary)) + "-" + getAppVersion(appBaseDir, projectCfg) + archiveName = addTargetBuildInfo(archiveName) + + var destArchiveFile string + if ess.IsStrEmpty(outputFile) { + destArchiveFile = filepath.Join(appBaseDir, "build", archiveName) + } else { + destArchiveFile, err = filepath.Abs(outputFile) + if err != nil { + logFatal(err) + } + + if !strings.HasSuffix(destArchiveFile, ".zip") { + destArchiveFile = filepath.Join(destArchiveFile, archiveName) + } + } + + if !strings.HasSuffix(destArchiveFile, ".zip") { + destArchiveFile = destArchiveFile + ".zip" + } + return destArchiveFile +} + const aahBashStartupTemplate = `#!/usr/bin/env bash # The MIT License (MIT) diff --git a/aah/clean.go b/aah/clean.go index f20814c..568a47c 100644 --- a/aah/clean.go +++ b/aah/clean.go @@ -5,12 +5,9 @@ package main import ( - "path/filepath" - "gopkg.in/urfave/cli.v1" "aahframework.org/aah.v0" - "aahframework.org/essentials.v0" ) var cleanCmd = cli.Command{ @@ -43,11 +40,8 @@ func cleanAction(c *cli.Context) error { projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) - ess.DeleteFiles( - filepath.Join(aah.AppBaseDir(), "app", "aah.go"), - filepath.Join(aah.AppBaseDir(), "build"), - filepath.Join(aah.AppBaseDir(), aah.AppName()+".pid"), - ) + cleanupAutoGenFiles(aah.AppBaseDir()) + cleanupAutoGenVFSFiles(aah.AppBaseDir()) cliLog.Infof("Import Path '%v' clean successful.\n", importPath) diff --git a/aah/compile.go b/aah/compile.go index e8e695e..e2f0cd3 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -8,6 +8,7 @@ import ( "bytes" "errors" "fmt" + "go/format" "io/ioutil" "path" "path/filepath" @@ -25,6 +26,7 @@ type compileArgs struct { ProxyPort string ProjectCfg *config.Config AppPack bool + AppEmbed bool } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ @@ -148,11 +150,8 @@ func compileApp(args *compileArgs) (string, error) { // main.go location e.g. path/to/import/app buildArgs = append(buildArgs, path.Join(appImportPath, "app")) - // clean previous main.go and binary file up before we start the build - appMainGoFile := filepath.Join(appCodeDir, "aah.go") - cliLog.Debugf("Cleaning %s", appMainGoFile) - cliLog.Debugf("Cleaning build directory %s", appBuildDir) - ess.DeleteFiles(appMainGoFile, appBuildDir) + // clean previously auto generated files + cleanupAutoGenFiles(appBaseDir) if err := generateSource(appCodeDir, "aah.go", aahMainTemplate, map[string]interface{}{ "AppTargetCmd": args.Cmd, @@ -167,6 +166,7 @@ func compileApp(args *compileArgs) (string, error) { "AppImportPaths": appImportPaths, "AppSecurity": appSecurity, "AppIsPackaged": args.AppPack, + "AppIsEmbedded": args.AppEmbed, }); err != nil { return "", err } @@ -195,11 +195,19 @@ func generateSource(dir, filename, templateSource string, templateArgs map[strin file := filepath.Join(dir, filename) buf := &bytes.Buffer{} - if err := renderTmpl(buf, templateSource, templateArgs); err != nil { + err := renderTmpl(buf, templateSource, templateArgs) + if err != nil { return err } - if err := ioutil.WriteFile(file, buf.Bytes(), permRWXRXRX); err != nil { + b := buf.Bytes() + if strings.HasSuffix(filename, ".go") { + if b, err = format.Source(b); err != nil { + return fmt.Errorf("aah '%s' file format source error: %s", filename, err) + } + } + + if err := ioutil.WriteFile(file, b, permRWXRXRX); err != nil { return fmt.Errorf("aah '%s' file write error: %s", filename, err) } return nil @@ -355,7 +363,7 @@ var ( ) func mergeExternalConfig(e *aah.Event) { - externalConfig, err := config.LoadFile(*configPath) + externalConfig, err := config.VFSLoadFile(aah.AppVFS(), *configPath) if err != nil { log.Fatalf("Unable to load external config: %s", *configPath) } @@ -397,7 +405,7 @@ func main() { Date: "{{ .AppBuildDate }}", }) - aah.SetAppPackaged({{ .AppIsPackaged }}) + {{ if .AppIsPackaged }}aah.SetAppPackaged({{ .AppIsPackaged }}){{ end }} // display application information if *version { @@ -417,6 +425,9 @@ func main() { aah.OnInit(setAppEnvProfile) } + {{ if .AppIsEmbedded }}// Set application into VFS embedded mode + aah.AppSetEmbeddedMode(){{ end }} + if err := aah.Init("{{ .AppImportPath }}"); err != nil { log.Fatal(err) } diff --git a/aah/embed.go b/aah/embed.go new file mode 100644 index 0000000..f9e8728 --- /dev/null +++ b/aah/embed.go @@ -0,0 +1,245 @@ +// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) +// aahframework.org/tools/aah source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "go/format" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "text/template" + "time" + + "aahframework.org/aah.v0" + "aahframework.org/essentials.v0" + "aahframework.org/vfs.v0" +) + +// Standard frame type MTU size is 1500 bytes so 1400 bytes would make sense +// to Gzip by default. Read: https://en.wikipedia.org/wiki/Maximum_transmission_unit +var defaultGzipMinSize int64 = 1400 + +var vfsTmpl = template.Must(template.New("vfs").Funcs(vfsTmplFuncMap).Parse(vfsTmplStr)) + +func processMount(appBaseDir, vroot, proot string, skipList ess.Excludes, noGzipList []string) error { + b, err := generateVFSSource(vroot, proot, skipList, noGzipList) + if err != nil { + return err + } + + // destination file + filename := fmt.Sprintf("aah%s_vfs.go", strings.Replace(vroot, "/", "_", -1)) + return ioutil.WriteFile(filepath.Join(appBaseDir, "app", filename), b, permRWXRXRX) +} + +// generateVFSSource method creates Virtual FileSystem (VFS) code +// to add files and directories within binary for configured Mount points +// on file aah.project. +func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList []string) ([]byte, error) { + err := skipList.Validate() + if err != nil { + return nil, err + } + + buf := &bytes.Buffer{} + if err = vfsTmpl.ExecuteTemplate(buf, "vfs_start_embed", aah.Data{ + "MountPath": vroot, + "PhysicalPath": proot, + }); err != nil { + return nil, err + } + + files := make(map[string]os.FileInfo) + if err := ess.Walk(proot, func(fpath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if skipList.Match(filepath.Base(fpath)) { + if info.IsDir() { + return filepath.SkipDir // skip directory + } + return nil // skip file + } + + if info.IsDir() { + mp := filepath.ToSlash(filepath.Join(vroot, strings.TrimPrefix(fpath, proot))) + + if err = vfsTmpl.ExecuteTemplate(buf, "vfs_dir", aah.Data{ + "Node": &vfs.NodeInfo{Dir: info.IsDir(), Path: mp, Time: info.ModTime()}, + }); err != nil { + return err + } + } else { + files[fpath] = info + } + + return nil + }); err != nil { + return nil, err + } + + _s(fmt.Fprintf(buf, "\n// Adding files into VFS\n")) + for fname, info := range files { + f, err := os.Open(fname) + if err != nil { + logError(err) + continue + } + + cliLog.Debugf(" |--- Processing file: %s", fname) + mp := filepath.ToSlash(filepath.Join(vroot, strings.TrimPrefix(fname, proot))) + + if err = vfsTmpl.ExecuteTemplate(buf, "vfs_file", aah.Data{ + "Node": &vfs.NodeInfo{DataSize: info.Size(), Path: mp, Time: info.ModTime()}, + }); err != nil { + logError(err) + return nil, err + } + + if info.Size() > 0 { + if err = convertFile(buf, f, info, noGzip(noGzipList, info.Name())); err != nil { + logError(err) + return nil, err + } + } + _s(fmt.Fprint(buf, "\"))\n\n")) + ess.CloseQuietly(f) + } + + _s(fmt.Fprint(buf, "}")) + return format.Source(buf.Bytes()) +} + +func convertFile(buf *bytes.Buffer, r io.ReadSeeker, fi os.FileInfo, noGzip bool) error { + restorePoint := buf.Len() + w := &stringWriter{w: buf} + + // if its already less then MTU size or gzip not required + if fi.Size() <= defaultGzipMinSize || noGzip { + _, err := io.Copy(w, r) + return err + } + + gw := gzip.NewWriter(w) + _, err := io.Copy(gw, r) + if err != nil { + return err + } + + if err = gw.Close(); err != nil { + return err + } + + if int64(w.size) >= fi.Size() { + if _, err = r.Seek(0, io.SeekStart); err != nil { + return err + } + + buf.Truncate(restorePoint) + if _, err = io.Copy(w, r); err != nil { + return err + } + } + + return nil +} + +const lowerHex = "0123456789abcdef" + +// https://github.com/go-bindata/go-bindata/blob/master/stringwriter.go +type stringWriter struct { + w io.Writer + size int +} + +func (s *stringWriter) Write(p []byte) (n int, err error) { + buf := []byte(`\x00`) + for _, b := range p { + buf[2], buf[3] = lowerHex[b/16], lowerHex[b%16] + if _, err = s.w.Write(buf); err != nil { + return + } + n++ + s.size++ + } + return +} + +func timeStr(t time.Time) string { + if t.IsZero() { + return "time.Time{}" + } + t = t.UTC() // always go with UTC + return fmt.Sprintf("time.Date(%d, %d, %d, %d, %d, %d, %d, time.UTC)", + t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond()) +} + +func _s(_ ...interface{}) {} + +func noGzip(noGzipList []string, name string) bool { + for _, t := range noGzipList { + if strings.HasSuffix(name, t) { + return true + } + } + return false +} + +var vfsTmplFuncMap = template.FuncMap{ + "timestr": timeStr, +} + +const vfsTmplStr = `{{ define "vfs_start_mount"}} // Code generated by aah framework - VFS, DO NOT EDIT. + +package main + +import ( + "time" + + "aahframework.org/aah.v0" + "aahframework.org/log.v0" + "aahframework.org/vfs.v0" +) + +func init() { + if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { + log.Fatal(err) + } +{{ end }} + +{{ define "vfs_start_embed" }}{{ template "vfs_start_mount" . }} + + // Find Mount point + m, err := aah.AppVFS().FindMount("{{ .MountPath }}") + if err != nil { + log.Fatal(err) + } + + // Adding directories into VFS +{{- end -}} + +{{ define "vfs_dir" }} + m.AddDir(&vfs.NodeInfo{ + Dir: {{ .Node.Dir }}, + Path: "{{ .Node.Path }}", + Time: {{ .Node.Time | timestr }}, + }) +{{ end }} + +{{ define "vfs_file" }} + m.AddFile(&vfs.NodeInfo{ + DataSize: {{ .Node.DataSize }}, + Path: "{{ .Node.Path }}", + Time: {{ .Node.Time | timestr }}, + }, + []byte(" +{{- end }} +` diff --git a/aah/util.go b/aah/util.go index 57a80de..379b0d6 100644 --- a/aah/util.go +++ b/aah/util.go @@ -377,15 +377,23 @@ func appImportPath(c *cli.Context) string { } func logFatal(v ...interface{}) { - _ = log.SetPattern("%level %message") - fatal(v...) - _ = log.SetPattern(log.DefaultPattern) + if cliLog == nil { + _ = log.SetPattern("%level %message") + fatal(v...) + _ = log.SetPattern(log.DefaultPattern) + } else { + cliLog.Fatal(append([]interface{}{"FATAL"}, v...)) + } } func logFatalf(format string, v ...interface{}) { - _ = log.SetPattern("%level %message") - fatalf(format, v...) - _ = log.SetPattern(log.DefaultPattern) + if cliLog == nil { + _ = log.SetPattern("%level %message") + fatalf(format, v...) + _ = log.SetPattern(log.DefaultPattern) + } else { + cliLog.Fatalf("FATAL "+format, v...) + } } func logError(v ...interface{}) { @@ -477,3 +485,17 @@ func readVersionNo(baseDir string) (string, error) { return "Unknown", nil } + +func cleanupAutoGenFiles(appBaseDir string) { + appMainGoFile := filepath.Join(appBaseDir, "app", "aah.go") + appBuildDir := filepath.Join(appBaseDir, "build") + cliLog.Debugf("Cleaning %s", appMainGoFile) + cliLog.Debugf("Cleaning build directory %s", appBuildDir) + ess.DeleteFiles(appMainGoFile, appBuildDir) +} + +func cleanupAutoGenVFSFiles(appBaseDir string) { + vfsFiles, _ := filepath.Glob(filepath.Join(appBaseDir, "app", "aah_*_vfs.go")) + cliLog.Debugf("Cleaning embed files %s", strings.Join(vfsFiles, "\n\t")) + ess.DeleteFiles(vfsFiles...) +} From db2b95ca6462c3da06ca4dddfb28de2bddc5aa30 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 16 May 2018 04:00:07 -0700 Subject: [PATCH 20/48] cleanup update in autogen, additional informative debug log on embed, cmd switch update --- aah/build.go | 5 ++--- aah/compile.go | 6 +++--- aah/embed.go | 20 ++++++++++++++++++-- aah/switch.go | 26 +++++++++----------------- aah/util.go | 22 ++++++++++++++++++++-- 5 files changed, 52 insertions(+), 27 deletions(-) diff --git a/aah/build.go b/aah/build.go index 0a9bdb4..2e720d7 100644 --- a/aah/build.go +++ b/aah/build.go @@ -111,7 +111,7 @@ func buildBinary(c *cli.Context, projectCfg *config.Config) { func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { cliLog.Infof("Embed starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) appBaseDir := aah.AppBaseDir() - defer cleanupAutoGenVFSFiles(appBaseDir) + cleanupAutoGenVFSFiles(appBaseDir) excludes, _ := projectCfg.StringList("build.excludes") noGzipList, _ := projectCfg.StringList("vfs.no_gzip") @@ -133,9 +133,8 @@ func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { } if !ess.IsStrEmpty(vroot) && !ess.IsStrEmpty(proot) { - cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) if err := processMount(appBaseDir, vroot, proot, ess.Excludes(excludes), noGzipList); err != nil { - logFatal(err) + logError(err) } } } diff --git a/aah/compile.go b/aah/compile.go index e2f0cd3..488a200 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -152,6 +152,9 @@ func compileApp(args *compileArgs) (string, error) { // clean previously auto generated files cleanupAutoGenFiles(appBaseDir) + if !args.AppEmbed { + cleanupAutoGenVFSFiles(appBaseDir) + } if err := generateSource(appCodeDir, "aah.go", aahMainTemplate, map[string]interface{}{ "AppTargetCmd": args.Cmd, @@ -425,9 +428,6 @@ func main() { aah.OnInit(setAppEnvProfile) } - {{ if .AppIsEmbedded }}// Set application into VFS embedded mode - aah.AppSetEmbeddedMode(){{ end }} - if err := aah.Init("{{ .AppImportPath }}"); err != nil { log.Fatal(err) } diff --git a/aah/embed.go b/aah/embed.go index f9e8728..08a9004 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -29,6 +29,11 @@ var defaultGzipMinSize int64 = 1400 var vfsTmpl = template.Must(template.New("vfs").Funcs(vfsTmplFuncMap).Parse(vfsTmplStr)) func processMount(appBaseDir, vroot, proot string, skipList ess.Excludes, noGzipList []string) error { + if !ess.IsFileExists(proot) { + return &os.PathError{Op: "open", Path: proot, Err: os.ErrNotExist} + } + + cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) b, err := generateVFSSource(vroot, proot, skipList, noGzipList) if err != nil { return err @@ -62,12 +67,20 @@ func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList [] return err } - if skipList.Match(filepath.Base(fpath)) { + fname := filepath.Base(fpath) + if skipList.Match(fname) { + if fname == "app" && strings.Contains(fpath, "/pages/") { + goto sc + } + + cliLog.Debugf(" |--- Skipping: %s", fpath) if info.IsDir() { return filepath.SkipDir // skip directory } return nil // skip file + // } } + sc: if info.IsDir() { mp := filepath.ToSlash(filepath.Join(vroot, strings.TrimPrefix(fpath, proot))) @@ -94,7 +107,7 @@ func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList [] continue } - cliLog.Debugf(" |--- Processing file: %s", fname) + cliLog.Debugf(" |--- Processing: %s", fname) mp := filepath.ToSlash(filepath.Join(vroot, strings.TrimPrefix(fname, proot))) if err = vfsTmpl.ExecuteTemplate(buf, "vfs_file", aah.Data{ @@ -210,6 +223,9 @@ import ( ) func init() { + // Set app vfs into embedded mode + aah.AppSetEmbeddedMode() + if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { log.Fatal(err) } diff --git a/aah/switch.go b/aah/switch.go index 31fa2ff..7bcd7da 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -6,9 +6,7 @@ package main import ( "fmt" - "path/filepath" "strings" - "sync" "gopkg.in/urfave/cli.v1" ) @@ -91,12 +89,16 @@ func doRefresh(branchName string) error { cliLog.Infof("Refreshing aah '%s' version ...\n", fname) + aahLibDirs := aahLibraryDirs() + // Refresh to latest edge codebase - refreshLibCode(aahLibraryDirs()) + refreshLibCode(aahLibDirs) // Refresh dependencies in grace mode fetchLibDeps() + checkoutBranch(aahLibDirs, edgeBranchName) + // Install aah CLI for current version installCLI() @@ -130,21 +132,9 @@ func doSwitch(branchName, target string) error { cliLog.Infof("Switching aah to '%s' version ...\n", friendlyName(toBranch)) - // Checkout the branch + // // Checkout the branch aahLibDirs := aahLibraryDirs() - var wg sync.WaitGroup - for _, dir := range aahLibDirs { - wg.Add(1) - go func(d string) { - defer wg.Done() - baseName := filepath.Base(d) - if err := gitCheckout(d, toBranch); err != nil { - logErrorf("Unable to switch library version, possibliy you may have local changes[%s]: %s", baseName, err) - } - cliLog.Tracef("Library '%s' have been switched to '%s' successfully", baseName, toBranch) - }(dir) - } - wg.Wait() + checkoutBranch(aahLibDirs, toBranch) if toBranch == edgeBranchName { cliLog.Infof("Refreshing aah to latest '%s' updates ...\n", friendlyName(toBranch)) @@ -154,6 +144,8 @@ func doSwitch(branchName, target string) error { // Refresh dependencies in grace mode fetchLibDeps() + checkoutBranch(aahLibDirs, toBranch) + // Install aah CLI for current version installCLI() diff --git a/aah/util.go b/aah/util.go index 379b0d6..caa6ce6 100644 --- a/aah/util.go +++ b/aah/util.go @@ -441,6 +441,22 @@ func aahImportPaths() []string { return importPaths } +func checkoutBranch(aahLibDirs []string, branchName string) { + var wg sync.WaitGroup + for _, dir := range aahLibDirs { + wg.Add(1) + go func(d string) { + defer wg.Done() + baseName := filepath.Base(d) + if err := gitCheckout(d, branchName); err != nil { + logErrorf("Unable to switch library version, possibliy you may have local changes[%s]: %s", baseName, err) + } + cliLog.Tracef("Library '%s' have been switched to '%s' successfully", baseName, branchName) + }(dir) + } + wg.Wait() +} + func libDependencyImports(importPath string) []string { var depList []string str, err := execCmd(gocmd, []string{"list", "-f", "{{.Imports}}", importPath}, false) @@ -496,6 +512,8 @@ func cleanupAutoGenFiles(appBaseDir string) { func cleanupAutoGenVFSFiles(appBaseDir string) { vfsFiles, _ := filepath.Glob(filepath.Join(appBaseDir, "app", "aah_*_vfs.go")) - cliLog.Debugf("Cleaning embed files %s", strings.Join(vfsFiles, "\n\t")) - ess.DeleteFiles(vfsFiles...) + if len(vfsFiles) > 0 { + cliLog.Debugf("Cleaning embed files %s", strings.Join(vfsFiles, "\n\t")) + ess.DeleteFiles(vfsFiles...) + } } From 2405cb03cd1fb6d994b60eaeab35476702232e64 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 16 May 2018 05:19:49 -0700 Subject: [PATCH 21/48] go-aah/aah#156 handle windows path as forward slash --- aah/embed.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aah/embed.go b/aah/embed.go index 08a9004..7443364 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -12,6 +12,7 @@ import ( "io" "io/ioutil" "os" + "path" "path/filepath" "strings" "text/template" @@ -29,6 +30,7 @@ var defaultGzipMinSize int64 = 1400 var vfsTmpl = template.Must(template.New("vfs").Funcs(vfsTmplFuncMap).Parse(vfsTmplStr)) func processMount(appBaseDir, vroot, proot string, skipList ess.Excludes, noGzipList []string) error { + proot = filepath.ToSlash(proot) if !ess.IsFileExists(proot) { return &os.PathError{Op: "open", Path: proot, Err: os.ErrNotExist} } @@ -67,7 +69,8 @@ func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList [] return err } - fname := filepath.Base(fpath) + fpath = filepath.ToSlash(fpath) + fname := path.Base(fpath) if skipList.Match(fname) { if fname == "app" && strings.Contains(fpath, "/pages/") { goto sc @@ -78,7 +81,6 @@ func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList [] return filepath.SkipDir // skip directory } return nil // skip file - // } } sc: From 199e6459ada9c59ed5effd7fdf0d029953926a3a Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 17 May 2018 08:31:57 -0700 Subject: [PATCH 22/48] go-aah/aah#178 brings same experience with single and non-single binary --- aah/app-template/aah.project.atmpl | 4 +- aah/build.go | 270 +---------------------------- 2 files changed, 9 insertions(+), 265 deletions(-) diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index cf8f4ea..01a321f 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -76,9 +76,7 @@ hot_reload { # Virtual FileSystem (VFS) configuration vfs { # Adding custom mount points. Configured mount points directory sub-tree - # gets embedded into aah binary during a build. - # - # Note: Its applicable for aah single binary build. + # gets embedded into aah binary during a single binary build. mount { # Choose an unique key name #my_mount { diff --git a/aah/build.go b/aah/build.go index 2e720d7..cb4bc2a 100644 --- a/aah/build.go +++ b/aah/build.go @@ -1,11 +1,10 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/tools/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main import ( - "bytes" "fmt" "io/ioutil" "path/filepath" @@ -24,28 +23,22 @@ var buildCmd = cli.Command{ Aliases: []string{"b"}, Usage: "Builds aah application for deployment (single or non-single)", Description: `Builds aah application for deployment. It supports single and non-single - binary. Its a trade-off learn more https://docs.aahframework.org/build-packaging.html + binary. It is a trade-off learn more https://docs.aahframework.org/build-packaging.html Artifact naming convention: ---.zip For e.g.: aahwebsite-381eaa8-darwin-amd64.zip Examples of short and long flags: - aah build - aah build --single - aah build -e dev - aah build -i github.com/user/appname -o /Users/jeeva -e qa - aah build -i github.com/user/appname -o /Users/jeeva/aahwebsite.zip - aah build --importpath github.com/user/appname --output /Users/jeeva --envprofile qa`, + aah build OR aah b + aah build --single OR aah b -s + aah build -i github.com/user/appname -o /Users/jeeva + aah build -i github.com/user/appname -o /Users/jeeva/aahwebsite.zip`, Action: buildAction, Flags: []cli.Flag{ cli.StringFlag{ Name: "i, importpath", Usage: "Import path of aah application", }, - cli.StringFlag{ - Name: "e, envprofile", - Usage: "Environment profile name to activate (e.g: dev, qa, prod)", - }, cli.StringFlag{ Name: "o, output", Usage: "Output of aah application build artifact; the default is '/build/---.zip'", @@ -91,8 +84,7 @@ func buildBinary(c *cli.Context, projectCfg *config.Config) { logFatal(err) } - appProfile := firstNonEmpty(c.String("e"), c.String("envprofile"), "prod") - buildBaseDir, err := copyFilesToWorkingDir(projectCfg, appBaseDir, appBinary, appProfile) + buildBaseDir, err := copyFilesToWorkingDir(projectCfg, appBaseDir, appBinary) if err != nil { logFatal(err) } @@ -160,7 +152,7 @@ func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { cliLog.Infof("Application artifact is here: %s\n", destArchiveFile) } -func copyFilesToWorkingDir(projectCfg *config.Config, appBaseDir, appBinary, appProfile string) (string, error) { +func copyFilesToWorkingDir(projectCfg *config.Config, appBaseDir, appBinary string) (string, error) { appBinaryName := filepath.Base(appBinary) tmpDir, err := ioutil.TempDir("", appBinaryName) if err != nil { @@ -205,26 +197,6 @@ func copyFilesToWorkingDir(projectCfg *config.Config, appBaseDir, appBinary, app } } - // startup files - data := map[string]string{ - "AppName": ess.StripExt(appBinaryName), - "AppProfile": appProfile, - "Backtick": "`", - } - buf := &bytes.Buffer{} - if err = renderTmpl(buf, aahBashStartupTemplate, data); err != nil { - return "", err - } - if err = ioutil.WriteFile(filepath.Join(buildBaseDir, "aah.sh"), buf.Bytes(), permRWXRXRX); err != nil { - return "", err - } - - buf.Reset() - if err = renderTmpl(buf, aahCmdStartupTemplate, data); err != nil { - return "", err - } - err = ioutil.WriteFile(filepath.Join(buildBaseDir, "aah.cmd"), buf.Bytes(), permRWXRXRX) - return buildBaseDir, err } @@ -263,229 +235,3 @@ func createZipArchiveName(c *cli.Context, projectCfg *config.Config, appBaseDir, } return destArchiveFile } - -const aahBashStartupTemplate = `#!/usr/bin/env bash - -# The MIT License (MIT) -# -# Copyright (c) Jeevanandam M., https://myjeeva.com -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -########################################### -# Start and Stop script for aah application -########################################### - -APP_NAME="{{ .AppName }}" -APP_ENV_PROFILE="{{ .AppProfile }}" -APP_EXT_CONFIG="" - -if [ ! -z "$2" ]; then - APP_ENV_PROFILE=$2 -fi - -if [ ! -z "$3" ]; then - APP_EXT_CONFIG="-config=$3" -fi - -# resolve links - $0 may be a softlink -PRG="$0" -while [ -h "$PRG" ] ; do - ls={{ .Backtick }}ls -ld "$PRG"{{ .Backtick }} - link={{ .Backtick }}expr "$ls" : '.*-> \(.*\)$'{{ .Backtick }} - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG={{ .Backtick }}dirname "$PRG"{{ .Backtick }}/"$link" - fi -done - -# resolve APP_DIR and set executable -APP_DIR=$(cd "$(dirname $PRG)"; pwd) -APP_EXECUTABLE="$APP_DIR"/bin/"$APP_NAME" -APP_PID="$APP_DIR"/"$APP_NAME".pid - -if [ ! -x "$APP_EXECUTABLE" ]; then - echo "Cannot find aah application executable: $APP_EXECUTABLE" - exit 1 -fi - -# go to application base directory -cd "$APP_DIR" - -start() { - if [ ! -z "$APP_PID" ]; then # not empty - if [ -f "$APP_PID" ]; then # exists and regular file - if [ -s "$APP_PID" ]; then # not-empty - echo "Existing PID file found during start." - if [ -r "$APP_PID" ]; then - PID={{ .Backtick }}cat "$APP_PID"{{ .Backtick }} - ps -p $PID >/dev/null 2>&1 - if [ $? -eq 0 ] ; then - echo "$APP_NAME appears to still be running with PID $PID. Start aborted." - ps -f -p $PID - exit 1 - fi - fi - fi - fi - fi - - nohup "$APP_EXECUTABLE" -profile="$APP_ENV_PROFILE" "$APP_EXT_CONFIG" > appstart.log 2>&1 & - echo "$APP_NAME started." -} - -stop() { - if [ ! -z "$APP_PID" ]; then # not empty - if [ -f "$APP_PID" ]; then # exists and regular file - if [ -s "$APP_PID" ]; then # not-empty - PID={{ .Backtick }}cat "$APP_PID"{{ .Backtick }} - kill -15 "$PID" >/dev/null 2>&1 - if [ $? -gt 0 ]; then - echo "$APP_PID file found but no matching process was found. Stop aborted." - exit 1 - else - rm -f "$APP_PID" >/dev/null 2>&1 - echo "$APP_NAME stopped." - fi - else - echo "$APP_PID file is empty and has been ignored." - fi - else - echo "$APP_PID file does not exists. Stop aborted." - exit 1 - fi - fi -} - -version() { - "$APP_EXECUTABLE" -version - echo "" -} - -case "$1" in -start) - start - ;; -stop) - stop - ;; -restart) - stop - sleep 2 - start - ;; -version) - version - ;; -*) - echo "Usage: $0 {start|stop|restart|version}" - echo "" - exit 1 -esac - -exit 0 -` - -const aahCmdStartupTemplate = `@ECHO OFF - -REM The MIT License (MIT) -REM -REM Copyright (c) Jeevanandam M., https://myjeeva.com -REM -REM Permission is hereby granted, free of charge, to any person obtaining a copy -REM of this software and associated documentation files (the "Software"), to deal -REM in the Software without restriction, including without limitation the rights -REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -REM copies of the Software, and to permit persons to whom the Software is -REM furnished to do so, subject to the following conditions: -REM -REM The above copyright notice and this permission notice shall be included in all -REM copies or substantial portions of the Software. -REM -REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -REM SOFTWARE. - -REM ########################################## -REM Start and Stop script for aah application -REM ########################################## - -SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION - -SET APP_NAME={{ .AppName }} -SET APP_ENV_PROFILE={{ .AppProfile }} -SET APP_EXT_CONFIG="" - -IF NOT "%2" == "" ( - SET APP_ENV_PROFILE="%2" -) - -IF NOT "%3" == "" ( - SET APP_EXT_CONFIG="-config %3" -) - -REM resolve APP_DIR and set executable -SET APP_DIR=%~dp0 -SET APP_EXECUTABLE=%APP_DIR%bin\%APP_NAME%.exe -SET APP_PID=%APP_DIR%%APP_NAME%.pid - -REM change directory -cd %APP_DIR% - -if ""%1"" == """" GOTO :cmdUsage -if ""%1"" == ""start"" GOTO :doStart -if ""%1"" == ""stop"" GOTO :doStop -if ""%1"" == ""version"" GOTO :doVersion - -:doStart -REM check app is running already -tasklist /FI "IMAGENAME eq %APP_NAME%.exe" 2>NUL | find /I /N "%APP_NAME%.exe">NUL -IF "%ERRORLEVEL%" == "0" ( - ECHO %APP_NAME% appears to still be running. Start aborted. - GOTO :end -) - -START "" /B "%APP_EXECUTABLE%" -profile "%APP_ENV_PROFILE%" "%APP_EXT_CONFIG%" > appstart.log 2>&1 -ECHO {{ .AppName }} started. -GOTO :end - -:doStop -SET /P PID= < %APP_PID% -IF NOT %PID% == "" ( - taskkill /pid %PID% /f - ECHO {{ .AppName }} stopped. -) -GOTO :end - -:doVersion -%APP_EXECUTABLE% -version -GOTO :end - -:cmdUsage -echo Usage: %0 {start or stop or version} -GOTO :end - -:end -ENDLOCAL -` From c81e483e3bb6ff5acf1d9fe0930d6538306c8e26 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 17 May 2018 09:05:19 -0700 Subject: [PATCH 23/48] go-aah/aah#179 added a -list flag in aah binary to support find option --- aah/compile.go | 60 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 488a200..6029790 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -346,6 +346,7 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "reflect" "syscall" @@ -358,14 +359,15 @@ import ( ) var ( - // Defining flags - version = flag.Bool("version", false, "Display aah application name, version and build date.") + // Define aah application binary flags configPath = flag.String("config", "", "Absolute path of external config file.") - profile = flag.String("profile", "", "Environment profile name to activate. e.g: dev, qa, prod.") + list = flag.String("list", "", "Prints the embedded file/directory path that matches the given pattern.") + profile = flag.String("profile", "", "Environment profile name to activate. For e.g.: dev, qa, prod, etc.") + version = flag.Bool("version", false, "Prints the aah application binary name, version and build timestamp.") _ = reflect.Invalid ) -func mergeExternalConfig(e *aah.Event) { +func MergeSuppliedConfig(e *aah.Event) { externalConfig, err := config.VFSLoadFile(aah.AppVFS(), *configPath) if err != nil { log.Fatalf("Unable to load external config: %s", *configPath) @@ -377,13 +379,37 @@ func mergeExternalConfig(e *aah.Event) { } } -func setAppEnvProfile(e *aah.Event) { +func ActivateAppEnvProfile(e *aah.Event) { aah.AppConfig().SetString("env.active", *profile) } +func PrintFilepath(pattern string) { + if !aah.AppVFS().IsEmbeddedMode() { + fmt.Println("aah application binary is not in Embedded Mode, refer to 'aah help build'") + return + } + + if err := aah.AppVFS().Walk(aah.AppVirtualBaseDir(), + func(fpath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if match, err := filepath.Match(pattern, info.Name()); match { + fmt.Println(fpath) + } else if err != nil { + fmt.Println("ERROR", err) + } + + return nil + }); err != nil { + fmt.Println("ERROR", err) + } +} + {{ if eq .AppTargetCmd "RunCmd" -}} {{ if .AppProxyPort -}} -func setAppProxyPort(e *aah.Event) { +func RunCmdSetAppProxyPort(e *aah.Event) { aah.AppConfig().SetString("server.proxyport", "{{ .AppProxyPort }}") } {{- end }} @@ -399,7 +425,6 @@ func main() { } }() - log.Infof("aah framework v%s, requires ≥ go1.8", aah.Version) flag.Parse() aah.SetAppBuildInfo(&aah.BuildInfo{ @@ -412,22 +437,29 @@ func main() { // display application information if *version { - fmt.Printf("\n%-12s: %s\n", "Binary Name", aah.AppBuildInfo().BinaryName) - fmt.Printf("%-12s: %s\n", "Version", aah.AppBuildInfo().Version) - fmt.Printf("%-12s: %s\n", "Build Date", aah.AppBuildInfo().Date) + fmt.Printf("%-16s: %s\n", "Binary Name", aah.AppBuildInfo().BinaryName) + fmt.Printf("%-16s: %s\n", "Version", aah.AppBuildInfo().Version) + fmt.Printf("%-16s: %s\n", "Build Timestamp", aah.AppBuildInfo().Date) + return + } + + if !ess.IsStrEmpty(*list) { + PrintFilepath(*list) return } // Apply supplied external config file if !ess.IsStrEmpty(*configPath) { - aah.OnInit(mergeExternalConfig) + aah.OnInit(MergeSuppliedConfig) } - // Apply environment profile + // Activate environment profile if !ess.IsStrEmpty(*profile) { - aah.OnInit(setAppEnvProfile) + aah.OnInit(ActivateAppEnvProfile) } + log.Infof("aah framework v%s, requires ≥ go1.8", aah.Version) + if err := aah.Init("{{ .AppImportPath }}"); err != nil { log.Fatal(err) } @@ -475,7 +507,7 @@ func main() { {{ if eq .AppTargetCmd "RunCmd" -}} {{ if .AppProxyPort -}} - aah.OnStart(setAppProxyPort) + aah.OnStart(RunCmdSetAppProxyPort) {{- end }} {{- end }} From e7b26b84afb150fbcaaa0c7566731358d3a658f1 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 17 May 2018 20:15:58 -0700 Subject: [PATCH 24/48] go-aah/aah#183 systemd service script is updated --- aah/compile.go | 8 ++++---- aah/generate.go | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 6029790..693a699 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -367,7 +367,7 @@ var ( _ = reflect.Invalid ) -func MergeSuppliedConfig(e *aah.Event) { +func MergeSuppliedConfig(_ *aah.Event) { externalConfig, err := config.VFSLoadFile(aah.AppVFS(), *configPath) if err != nil { log.Fatalf("Unable to load external config: %s", *configPath) @@ -379,7 +379,7 @@ func MergeSuppliedConfig(e *aah.Event) { } } -func ActivateAppEnvProfile(e *aah.Event) { +func ActivateAppEnvProfile(_ *aah.Event) { aah.AppConfig().SetString("env.active", *profile) } @@ -390,12 +390,12 @@ func PrintFilepath(pattern string) { } if err := aah.AppVFS().Walk(aah.AppVirtualBaseDir(), - func(fpath string, info os.FileInfo, err error) error { + func(fpath string, _ os.FileInfo, err error) error { if err != nil { return err } - if match, err := filepath.Match(pattern, info.Name()); match { + if match, err := filepath.Match(pattern, fpath); match { fmt.Println(fpath) } else if err != nil { fmt.Println("ERROR", err) diff --git a/aah/generate.go b/aah/generate.go index 4c0cdd8..77056cd 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -227,22 +227,20 @@ func checkAndConfirmOverwrite(destFile string) bool { // Script Templates //___________________________________ -const aahSystemdScriptTemplate = `// GENERATED BY aah CLI - Feel free to customization it. -// FILE: {{ .FileName }} -// DATE: {{ .CreateDate }} -// DESC: aah application systemd service file +const aahSystemdScriptTemplate = `# GENERATED BY aah CLI - Feel free to customization it. +# FILE: {{ .FileName }} +# DATE: {{ .CreateDate }} +# DESC: aah application systemd service file [Unit] Description={{ .Desc }} After=network.target [Service] +#User=aah +#Group=aah EnvironmentFile=/home/aah/{{ .AppName }}_env_values -User=aah -Group=aah -Type=forking -ExecStart=/home/aah/{{ .AppName }}/aah.sh start -ExecStop=/home/aah/{{ .AppName }}/aah.sh stop +ExecStart=/home/aah/{{ .AppName }}/bin/{{ .AppName }} -profile prod ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure From 86114fde03e1814b569c07adc56478b14f3a0af3 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 17 May 2018 23:53:48 -0700 Subject: [PATCH 25/48] go-aah/aah#179 updated pattern syntax to regex --- aah/compile.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 693a699..ebe248b 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -346,8 +346,8 @@ import ( "fmt" "os" "os/signal" - "path/filepath" "reflect" + "regexp" "syscall" "aahframework.org/aah.v0" @@ -361,7 +361,7 @@ import ( var ( // Define aah application binary flags configPath = flag.String("config", "", "Absolute path of external config file.") - list = flag.String("list", "", "Prints the embedded file/directory path that matches the given pattern.") + list = flag.String("list", "", "Prints the embedded file/directory path that matches the given regex pattern.") profile = flag.String("profile", "", "Environment profile name to activate. For e.g.: dev, qa, prod, etc.") version = flag.Bool("version", false, "Prints the aah application binary name, version and build timestamp.") _ = reflect.Invalid @@ -385,7 +385,13 @@ func ActivateAppEnvProfile(_ *aah.Event) { func PrintFilepath(pattern string) { if !aah.AppVFS().IsEmbeddedMode() { - fmt.Println("aah application binary is not in Embedded Mode, refer to 'aah help build'") + fmt.Println("'"+aah.AppBuildInfo().BinaryName + "' binary does not have embedded files.") + return + } + + regex, err := regexp.Compile(pattern) + if err != nil { + fmt.Println("ERROR", err) return } @@ -395,10 +401,8 @@ func PrintFilepath(pattern string) { return err } - if match, err := filepath.Match(pattern, fpath); match { + if regex.MatchString(fpath) { fmt.Println(fpath) - } else if err != nil { - fmt.Println("ERROR", err) } return nil From 0c932b38ce00c1c8fa8d5f91bbcbf4bc94c8a66f Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 18 May 2018 21:18:42 -0700 Subject: [PATCH 26/48] go-aah/aah#184 route config tmpl update [ci skip] --- aah/app-template/config/routes.conf.atmpl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index 0293861..8d00d9e 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -213,11 +213,7 @@ domains { #anti_csrf_check = false{{ end }} } - {{ if eq .App.AuthScheme "form" -}}login_submit { - path ="/login" - method = "POST" - controller = "VirtualFormController" - }{{ end }}{{ end -}}{{ if or .App.IsAPIApp .App.IsSubTypeAPI }} + {{ end -}}{{ if or .App.IsAPIApp .App.IsSubTypeAPI }} # Below routes definition demonstrates part of aah routes configuration # capabilities From c940610be4221125fc3522fd8fbe3d9fc403afd5 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 21 May 2018 22:58:39 -0700 Subject: [PATCH 27/48] vfs embed processing updates and app template config updates --- aah/app-template/aah.project.atmpl | 22 ++-- aah/app-template/config/aah.conf.atmpl | 61 +++++----- aah/app-template/config/routes.conf.atmpl | 4 +- aah/app-template/config/security.conf.atmpl | 116 ++++++++++++-------- aah/build.go | 50 +++++---- aah/compile.go | 6 +- aah/embed.go | 36 +++--- 7 files changed, 174 insertions(+), 121 deletions(-) diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index 01a321f..2e4cb9a 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -73,26 +73,34 @@ hot_reload { } } +#------------------------------------------------------------------------ # Virtual FileSystem (VFS) configuration +# +# Doc: https://docs.aahframework.org/vfs.html +#------------------------------------------------------------------------ vfs { - # Adding custom mount points. Configured mount points directory sub-tree - # gets embedded into aah binary during a single binary build. + # Adding custom mount points. + # + # Configured mount points directories and sub-directories + # are embedded into the aah binary during the single binary build. + # + # Note: Embed packaging uses same exclusion list from config `build.excludes`. mount { - # Choose an unique key name + # Choose a unique key name #my_mount { # # Mount point (path separator is '/'). # mount_path = "/my_mount" # # # Physical FileSystem path. - # # Note: Only absolute path accepted. + # # Note: Only absolute path is accepted. # physical_path = "/Users/jeeva/path/to/physical" #} } - # This config is speed up the single binary build process. - # Do not spend time to gzip for already compress content. + # This config speeds up the single binary build process. + # No need to spend time in gzipping for compressed content type. # - # Even without this config aah does best decision on what to gzip. + # Even without this config, aah decides best on what to gzip. no_gzip = [".png", ".jpeg", ".jpg", ".gif", ".bmp", ".tiff", ".tif" ".woff", ".woff2"] } diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index f2b8543..3acdbaf 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -1,9 +1,9 @@ -################################################### +# --------------------------------------------------------------------------- # {{ .App.Name }} - aah Application # # Complete configuration reference: # Doc: https://docs.aahframework.org/app-config.html -################################################### +# --------------------------------------------------------------------------- # Application name (non-whitespace) # Default value is `basename` of import path. @@ -15,16 +15,17 @@ desc = "aah {{ .App.Type }} application" # Application type, typically either Web or API. type = "{{ .App.Type }}" -# Application instance name is used when you're running aah application cluster. -# This value is used in the context based logging, it distinguishes your instance -# log from other instances. +# Application instance name is used for traceability when running in cluster mode. +# It provides context based logging and distinguishes app instance +# across cluster. # -# Typically you can to pass `instance_name` value via aah external config -# support or Environment variable. +# Typically pass value of `instance_name` via aah external config or +# Environment variable. instance_name = $AAH_INSTANCE_NAME # Configure file path of application PID file to be written. # Ensure application has appropriate permission and directory exists. +# # Default value is `/.pid` #pid_file = "/path/to/pidfile.pid" @@ -42,7 +43,7 @@ server { #port = "" # Header value written as `Server` HTTP header. - # If you do not want to include `Server` header, comment it out. + # To exclude header `Server` from writing, simply comment it out. header = "aah-go-server" # Valid time units are "s = seconds", "m = minutes" @@ -53,6 +54,7 @@ server { # Because ReadTimeout does not let Handlers make per-request decisions on # each request body's acceptable deadline or upload rate, most users will # prefer to use ReadHeaderTimeout. It is valid to use them both. + # # Default value is `90s`. #read = "90s" @@ -60,6 +62,7 @@ server { # out writes of the response. It is reset whenever a new request's header is # read. Like ReadTimeout, it does not let Handlers make decisions on a # per-request basis. + # # Default value is `90s`. #write = "90s" @@ -240,17 +243,18 @@ request { # Default value is `true`. enable = true - # Default value is `X-Request-Id`, change it if you have different one. + # Default value is `X-Request-Id`, customize it per use case. #header = "X-Request-Id" } # Max request body size for all incoming HTTP requests except `MultipartForm`. - # Also you can override this size for individual route on specific cases - # in `routes.conf` if need be. + # Also it could be overridden at individual route on specific cases + # in `routes.conf`. + # # Default value is `5mb`. #max_body_size = "5mb" - # Default value is `32mb`, choose your value based on your use case + # Default value is `32mb`, choose value based on app use case #multipart_size = "32mb" # aah provides `Content Negotiation` feature for the incoming HTTP request. @@ -264,12 +268,14 @@ request { # For example: Client sends Content-Type header as `application/xml`. # However server only supports JSON payload as request body. # Then server responds with 415 Unsupported Media Type. + # # Default value is empty list and disabled. #accepted = ["application/json", "text/json"] # For example: Client sends Accept header as `application/xml`. # However server only supports serving JSON i.e. `application/json`. # Then server responds with 406 Not Acceptable. + # # Default value is empty list and disabled. #offered = ["application/json", "text/json"] } @@ -282,10 +288,10 @@ request { # F -> Form Parameter # Q -> Query Parameter # - # For example: Let's say you have a controller action named `OrderInfo` and its has - # parameter called `orderId`. So framework tries to parse and bind based - # on the priority. The `orderId` present in `Path` and `Form`, framework - # binds the value from `Path`. Typically recommended to have unique names + # For example: Let's say we have a controller action named `OrderInfo` and it has + # parameter called `orderId`. So aah tries to parse and bind based + # on the priority. The `orderId` present in `Path` and `Form`, aah + # binds the value from `Path`. Typically it is recommended to have unique names # in the request parameter though :) # Path -> Form -> Query # If not found then it returns with default Go zero value. @@ -346,7 +352,7 @@ format { runtime { debug { # Choose an appropriate buffer size for collecting all goroutines stack trace - # dump based on your case. + # dump based on need. # Default value is `2mb`. #stack_buffer_size = "2mb" @@ -392,8 +398,8 @@ render { # framework ensures HTTP client accepts Gzip response otherwise # it won't use Gzip compression. # - # Tips: If you have nginx or apache web server enabled with gzip in-front - # of aah server the set this value to `false`. + # Tip: If there is a web server in-front of aah server then set this + # value to `false`. # # Default value is `true`. #enable = true @@ -439,17 +445,16 @@ cache { # Doc: https://docs.aahframework.org/app-config.html#section-view # --------------------------------------------------------------- view { - # Choosing view engine for application. You could implement - # on your own with simple interface `view.Enginer`. + # Application view engine. # Default value is `go`. engine = "{{ .App.ViewEngine }}" - # Choose your own view file extension. - # Default value is chosen based on `view.engine`, - # while creating a new app using command `aah new`. + # View file extension. + # Default value is chosen based on `view.engine`, during a new app creation + # via `aah new` ext = "{{ .App.ViewFileExt }}" - # Choose whether you need a case sensitive view file resolve or not. + # View file name and path resolve case sensitive or not. # For e.g.: "/views/pages/app/login.tmpl" == "/views/pages/App/Login.tmpl" # Default value is `false`. #case_sensitive = false @@ -458,9 +463,9 @@ view { # Default value is `{{ .App.TmplDemils }}`. #delimiters = "{{ .App.TmplDemils }}" - # Framework chooses the default app layout as `master.html` if you do not - # provide one. However you may have pages or template without layout too. - # So option to disable the default layout for HTML. + # aah chooses the default app layout as `master.html`. Also aah supports + # non-layout too. + # Option to disable the default layout for HTML. # Default value is `true`. Available since v0.6 #default_layout = false }{{ end }} diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index 8d00d9e..8faef2d 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -1,9 +1,9 @@ -#################################################### +# --------------------------------------------------------------------------- # {{ .App.Name }} - Application Routes Configuration # # Complete routes configuration reference: # Doc: https://docs.aahframework.org/routes-config.html -#################################################### +# --------------------------------------------------------------------------- #------------------------------------------------------------------------ # Domain and sub-domain configuration goes into section `domains { ... }` diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index 1aade04..2baf3f9 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -1,13 +1,14 @@ -###################################################### +# --------------------------------------------------------------------------- # {{ .App.Name }} - Application Security Configuration # # Complete routes configuration reference: # Doc: https://docs.aahframework.org/security-config.html -###################################################### +# --------------------------------------------------------------------------- security { # ------------------------------------------------------- # Authentication & Authorization configuration + # # Doc: https://docs.aahframework.org/security-design.html # ------------------------------------------------------- auth_schemes { @@ -17,27 +18,32 @@ security { form_auth { # Auth scheme name. # Currently supported values are `form`, `basic` and `generic`. + # # It is required value, no default. scheme = "form" - # Framework calls `Authenticator` to get the Subject's authentication - # information. Then framework validates the credential using password + # aah calls `Authenticator` to get the Subject's authentication + # information. Then aah validates the credential using password # encoder. + # # It is required value, no default. authenticator = "security/AuthenticationProvider" - # Framework calls `Authorizer` to get Subject's authorization information, - # such as Roles, Permissions. Then it populates the Subject instance. + # aah calls `Authorizer` only after successful authentication; to get + # Subject's authorization information such as Roles, Permissions. + # # It is required value, no default. authorizer = "security/AuthorizationProvider" # Password encoder is used to encode the given credential and then compares - # it with application provide credential. + # it with application provided credential. + # # Doc: https://docs.aahframework.org/password-encoders.html + # # Default value is `bcrypt`. password_encoder = "{{ .App.PasswordEncoderAlgo }}" - # Field names are used to extract `AuthenticationToken` from request. + # Field names are used to extract `AuthenticationToken` from the HTTP request. field { # Default value is `username` #identity = "username" @@ -48,26 +54,32 @@ security { # URLs is used when appropriate. url { - # Login page URL, implement your login and configure here. + # Login page URL, implement app login page and configure here. + # # Default value is `/login.html`. #login = "/login.html" # Login submit URL, used to submit login credential for authentication. - # Basically login form submits its values to this URL as POST request. + # Basically login form submit values to this URL as POST request. + # + # This URL can be accessed via `{{ rurl . aah_login_submit }}` on view file. + # # Default value is `/login`. #login_submit = "/login" - # Login failure, any failure during authentication process. Framework + # Login failure, any failure during authentication process. aah # sends user to this URL and logs detailed information in the log. #login_failure = "/login.html?error=true" - # Default page URL after the successful authentication. By default framework + # Default page URL after the successful authentication. By default aah # redirects the user to requested page after authentication. Otherwise it # sends to this URL. + # # Default value is `/`. #default_target = "/" - # Always redirect to default URL, regardless of the request page. + # Always redirect to default URL, regardless of the requested page/URL. + # # Default value is `false`. #always_to_default = false } @@ -83,35 +95,41 @@ security { scheme = "basic" # Realm name is used for `Www-Authenticate` HTTP header. - # Note: Nowadays, modern browsers are not respecting this values. + # + # Note: Modern browsers are not respecting this values now a days. # However aah framework does its due diligence. realm_name = "Protected" - {{ if eq .App.BasicAuthMode "file-realm" -}} - # Basic auth realm file path. You can use absolute path or + {{ if eq .App.BasicAuthMode "file-realm" }}# Basic auth realm file path. Use absolute path or # environment variable to provide path. + # # It is required value, no default. - file_realm = "{{ .App.BasicAuthFileRealmPath }}"{{ else -}} - # Framework calls `Authenticator` to get the Subject's authentication + file_realm = "{{ .App.BasicAuthFileRealmPath }}"{{ else }} + # aah calls `Authenticator` to get the Subject's authentication # information. Then framework validates the credential using password # encoder. - # It is required value when `file_realm` not configured, no default. + # + # It is required value when `file_realm` attribute is not configured, + #no default. authenticator = "security/AuthenticationProvider" - # Framework calls `Authorizer` to get Subject's authorization information, - # such as Roles and Permissions. Then it populates the Subject instance. - # It is required value when `file_realm` not configured, no default. + # aah calls `Authorizer` only after successful authentication to get + # Subject's authorization information such as Roles and Permissions. + # + # It is required value when `file_realm` attribute is not configured, + # no default. authorizer = "security/AuthorizationProvider"{{ end }} # Password encoder is used to encode the given credential and then compares # it with application provide credential. + # # Doc: https://docs.aahframework.org/password-encoders.html + # # Default value is `bcrypt`. password_encoder = "{{ .App.PasswordEncoderAlgo }}" - }{{ end -}} + } - {{ if eq .App.AuthScheme "generic" -}} - # Generic Auth Scheme + {{ end }}{{ if eq .App.AuthScheme "generic" }}# Generic Auth Scheme # It is custom defined name, this is used in the routes `auth` attribute. generic_auth { # Auth scheme name. @@ -119,25 +137,28 @@ security { # It is required value, no default. scheme = "generic" - # Framework calls `Authenticator` to get the Subject's authentication - # information. The credential validation is not done by framework, it is - # left to interface implementation. + # aah calls `Authenticator` to get the Subject's authentication + # information. The credential validation is not done by framework. + # It is interface implementation responsibility. + # # It is required value, no default. authenticator = "security/AuthenticationProvider" - # Framework calls `Authorizer` to get Subject's authorization information, - # such as Roles and Permissions. Then it populates the Subject instance. + # aah calls `Authorizer` only after successful authentication to get + # Subject's authorization information such as Roles and Permissions. + # # It is required value, no default. authorizer = "security/AuthorizationProvider" - # Header names are used to extract `AuthenticationToken` from request. + # Header names are used to extract `AuthenticationToken` from the HTTP request. header { # Default value is 'Authorization' #identity = "Authorization" # Optional credential header - # Typically it's not used, however in the industry people do use it - # Default value is empty string + # Typically it's not used, however in the industry people do use it. + # + # Default value is empty string. #credential = "X-AuthPass" } }{{ end }} @@ -146,6 +167,7 @@ security { # ------------------------------------------------------------ # Password Encoders Configuration # aah supports `bcrypt`, `scrypt`, `pbkdf2` password algorithm + # # Doc: https://docs.aahframework.org/password-encoders.html # ------------------------------------------------------------ password_encoder { {{ if eq .App.PasswordEncoderAlgo "bcrypt" }} @@ -223,6 +245,7 @@ security { # ----------------------------------------------------------------------- # Session configuration # HTTP state management across multiple requests. + # # Doc: https://docs.aahframework.org/security-config.html#section-session # ----------------------------------------------------------------------- session { @@ -299,6 +322,7 @@ security { {{ if .App.IsWebApp }} # ------------------------------------------------------------ # Anti-CSRF Protection + # # Doc: https://docs.aahframework.org/anti-csrf-protection.html # ------------------------------------------------------------ anti_csrf { @@ -437,10 +461,10 @@ security { # https://developers.google.com/web/fundamentals/security/csp/ # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#csp # - # Read above references and define your policy. + # Read above references and define app policy. # - # Note: It is highly recommended to verify your policy directives in report - # only mode before enabling this header. Since its highly controls how your + # Note: It is highly recommended to verify policy directives in report + # only mode before enabling this header. Since its highly controls how # page is rendered. # # No default values, you have to provide it. @@ -452,8 +476,10 @@ security { # you need to specify the report-uri policy directive. report_uri = "" - # Puts your `Content-Security-Policy` in report only mode, so that you can verify - # and then set `csp_report_only` value to false. + # By default aah puts `Content-Security-Policy` in report only mode, + # so that could be verified before making it live mode + # (set `csp_report_only` to false). + # # Don't forget to set the `report-uri` for validation. report_only = true } @@ -464,15 +490,14 @@ security { # Learn more: # https://scotthelme.co.uk/hpkp-http-public-key-pinning/ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning - # Read above references and define your keys. + # Read above references and define app keys. # # Note: # - HPKP has the potential to lock out users for a long time if used incorrectly! # The use of backup certificates and/or pinning the CA certificate is recommended. - # - It is highly recommended to verify your policy directives in report only mode - # before enabling this header - # - It is highly recommended to verify your PKP in report only mode before enabling this header. - # No default values, you have to provide it. + # - It is highly recommended to verify app PKP in report only mode before enabling this header. + # + # No default values, aah user have to provide it. pkp { # The Base64 encoded Subject Public Key Information (SPKI) fingerprint. # These values gets added as `pin-sha256=; ...`. @@ -494,8 +519,9 @@ security { # failure reporting, you need to specify the report-uri. report_uri = "" - # Puts your `Public-Key-Pins` in report only mode, so that you can verify - # and then set `pkp_report_only` value to false. + # By default aah puts `Public-Key-Pins` in report only mode, so that it + # could be verified before making it live (set `pkp_report_only` to false). + # # Don't forget to set the `report-uri` for validation. report_only = true } diff --git a/aah/build.go b/aah/build.go index cb4bc2a..c0be65c 100644 --- a/aah/build.go +++ b/aah/build.go @@ -73,7 +73,7 @@ func buildAction(c *cli.Context) error { func buildBinary(c *cli.Context, projectCfg *config.Config) { appBaseDir := aah.AppBaseDir() - cleanupAutoGenVFSFiles(appBaseDir) + processVFSConfig(projectCfg, false) appBinary, err := compileApp(&compileArgs{ Cmd: "BuildCmd", @@ -102,6 +102,30 @@ func buildBinary(c *cli.Context, projectCfg *config.Config) { func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { cliLog.Infof("Embed starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + processVFSConfig(projectCfg, true) + cliLog.Infof("Embed successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + + appBinary, err := compileApp(&compileArgs{ + Cmd: "BuildCmd", + ProjectCfg: projectCfg, + AppPack: true, + AppEmbed: true, + }) + if err != nil { + logFatal(err) + } + + // Creating app archive + destArchiveFile := createZipArchiveName(c, projectCfg, aah.AppBaseDir(), appBinary) + if err = createZipArchive(appBinary, destArchiveFile); err != nil { + logFatal(err) + } + + cliLog.Infof("Build successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + cliLog.Infof("Application artifact is here: %s\n", destArchiveFile) +} + +func processVFSConfig(projectCfg *config.Config, mode bool) { appBaseDir := aah.AppBaseDir() cleanupAutoGenVFSFiles(appBaseDir) @@ -109,7 +133,7 @@ func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { noGzipList, _ := projectCfg.StringList("vfs.no_gzip") // Default mount point - if err := processMount(appBaseDir, "/app", appBaseDir, ess.Excludes(excludes), noGzipList); err != nil { + if err := processMount(mode, appBaseDir, "/app", appBaseDir, ess.Excludes(excludes), noGzipList); err != nil { logFatal(err) } @@ -125,31 +149,11 @@ func buildSingleBinary(c *cli.Context, projectCfg *config.Config) { } if !ess.IsStrEmpty(vroot) && !ess.IsStrEmpty(proot) { - if err := processMount(appBaseDir, vroot, proot, ess.Excludes(excludes), noGzipList); err != nil { + if err := processMount(mode, appBaseDir, vroot, proot, ess.Excludes(excludes), noGzipList); err != nil { logError(err) } } } - cliLog.Infof("Embed successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) - - appBinary, err := compileApp(&compileArgs{ - Cmd: "BuildCmd", - ProjectCfg: projectCfg, - AppPack: true, - AppEmbed: true, - }) - if err != nil { - logFatal(err) - } - - // Creating app archive - destArchiveFile := createZipArchiveName(c, projectCfg, appBaseDir, appBinary) - if err = createZipArchive(appBinary, destArchiveFile); err != nil { - logFatal(err) - } - - cliLog.Infof("Build successful for '%s' [%s]", aah.AppName(), aah.AppImportPath()) - cliLog.Infof("Application artifact is here: %s\n", destArchiveFile) } func copyFilesToWorkingDir(projectCfg *config.Config, appBaseDir, appBinary string) (string, error) { diff --git a/aah/compile.go b/aah/compile.go index ebe248b..8badf26 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -152,9 +152,6 @@ func compileApp(args *compileArgs) (string, error) { // clean previously auto generated files cleanupAutoGenFiles(appBaseDir) - if !args.AppEmbed { - cleanupAutoGenVFSFiles(appBaseDir) - } if err := generateSource(appCodeDir, "aah.go", aahMainTemplate, map[string]interface{}{ "AppTargetCmd": args.Cmd, @@ -438,6 +435,9 @@ func main() { }) {{ if .AppIsPackaged }}aah.SetAppPackaged({{ .AppIsPackaged }}){{ end }} + {{ if .AppIsEmbedded }} + // Set app vfs into embedded mode + aah.AppVFS().SetEmbeddedMode(){{ end }} // display application information if *version { diff --git a/aah/embed.go b/aah/embed.go index 7443364..3d92ecc 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -29,14 +29,16 @@ var defaultGzipMinSize int64 = 1400 var vfsTmpl = template.Must(template.New("vfs").Funcs(vfsTmplFuncMap).Parse(vfsTmplStr)) -func processMount(appBaseDir, vroot, proot string, skipList ess.Excludes, noGzipList []string) error { +func processMount(mode bool, appBaseDir, vroot, proot string, skipList ess.Excludes, noGzipList []string) error { proot = filepath.ToSlash(proot) if !ess.IsFileExists(proot) { return &os.PathError{Op: "open", Path: proot, Err: os.ErrNotExist} } - cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) - b, err := generateVFSSource(vroot, proot, skipList, noGzipList) + if mode { + cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) + } + b, err := generateVFSSource(mode, vroot, proot, skipList, noGzipList) if err != nil { return err } @@ -49,20 +51,31 @@ func processMount(appBaseDir, vroot, proot string, skipList ess.Excludes, noGzip // generateVFSSource method creates Virtual FileSystem (VFS) code // to add files and directories within binary for configured Mount points // on file aah.project. -func generateVFSSource(vroot, proot string, skipList ess.Excludes, noGzipList []string) ([]byte, error) { +func generateVFSSource(mode bool, vroot, proot string, skipList ess.Excludes, noGzipList []string) ([]byte, error) { err := skipList.Validate() if err != nil { return nil, err } buf := &bytes.Buffer{} - if err = vfsTmpl.ExecuteTemplate(buf, "vfs_start_embed", aah.Data{ + startTmpl := "vfs_start_embed" + if !mode { + startTmpl = "vfs_start_mount" + } + if err = vfsTmpl.ExecuteTemplate(buf, startTmpl, aah.Data{ + "Mode": mode, "MountPath": vroot, "PhysicalPath": proot, }); err != nil { return nil, err } + // non-single binary mode, exit here + if !mode { + _s(fmt.Fprint(buf, "\n}")) + return format.Source(buf.Bytes()) + } + files := make(map[string]os.FileInfo) if err := ess.Walk(proot, func(fpath string, info os.FileInfo, err error) error { if err != nil { @@ -212,22 +225,19 @@ var vfsTmplFuncMap = template.FuncMap{ "timestr": timeStr, } -const vfsTmplStr = `{{ define "vfs_start_mount"}} // Code generated by aah framework - VFS, DO NOT EDIT. +const vfsTmplStr = `{{ define "vfs_start_mount"}} // Code generated by aah CLI - VFS, DO NOT EDIT. package main -import ( - "time" +import ({{ if .Mode }} + "time"{{ end }} "aahframework.org/aah.v0" - "aahframework.org/log.v0" - "aahframework.org/vfs.v0" + "aahframework.org/log.v0"{{ if .Mode }} + "aahframework.org/vfs.v0"{{ end }} ) func init() { - // Set app vfs into embedded mode - aah.AppSetEmbeddedMode() - if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { log.Fatal(err) } From 50af96c174e82fd277d039023ff89a3cab495a66 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 22 May 2018 01:24:44 -0700 Subject: [PATCH 28/48] aah new cmd and app template update --- aah/app-template/.gitignore | 4 +++- aah/app-template/config/aah.conf.atmpl | 4 ++-- aah/app-template/config/security.conf.atmpl | 2 +- aah/app_tmpl.go | 3 ++- aah/new.go | 7 ++++--- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/aah/app-template/.gitignore b/aah/app-template/.gitignore index 19f55cb..f9e9b01 100644 --- a/aah/app-template/.gitignore +++ b/aah/app-template/.gitignore @@ -1,6 +1,8 @@ -# aah application - .gitignore +# .gitignore config +# aah application aah.go +aah_*_vfs.go *.pid build/ vendor/*/ diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index 3acdbaf..c52f3af 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -460,8 +460,8 @@ view { #case_sensitive = false # To use custom Go template delimiters for view files. - # Default value is `{{ .App.TmplDemils }}`. - #delimiters = "{{ .App.TmplDemils }}" + # Default value is `{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}`. + #delimiters = "{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}" # aah chooses the default app layout as `master.html`. Also aah supports # non-layout too. diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index 2baf3f9..d198ba3 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -62,7 +62,7 @@ security { # Login submit URL, used to submit login credential for authentication. # Basically login form submit values to this URL as POST request. # - # This URL can be accessed via `{{ rurl . aah_login_submit }}` on view file. + # This URL can be accessed via `{{ .App.TmplDelimLeft }} rurl . aah_login_submit {{ .App.TmplDelimRight }}` on view file. # # Default value is `/login`. #login_submit = "/login" diff --git a/aah/app_tmpl.go b/aah/app_tmpl.go index 5e98ed7..a7fa4ce 100644 --- a/aah/app_tmpl.go +++ b/aah/app_tmpl.go @@ -40,7 +40,8 @@ type appTmplData struct { SessionFileStorePath string BasicAuthFileRealmPath string CORSEnable bool - TmplDemils string + TmplDelimLeft string + TmplDelimRight string SubTypes []string } diff --git a/aah/new.go b/aah/new.go index 9bc6fd8..1880ea5 100644 --- a/aah/new.go +++ b/aah/new.go @@ -51,8 +51,10 @@ func newAction(c *cli.Context) error { // Depends on application type choice, collect subsequent inputs app := &appTmplData{ - ImportPath: importPath, - Type: appType, + ImportPath: importPath, + Type: appType, + TmplDelimLeft: "{{", + TmplDelimRight: "}}", } switch appType { @@ -140,7 +142,6 @@ func collectAppType(reader *bufio.Reader) string { //______________________________________________________________________________ func collectInputsForWebApp(app *appTmplData) { - app.TmplDemils = "{{.}}" viewEngine(reader, app) authScheme(reader, app) From 1439de272cbc9217ed04ac2a3060deb25f1dacc4 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 25 May 2018 21:35:56 -0700 Subject: [PATCH 29/48] go-aah/aah#116 added migrate code command --- aah/aah.go | 1 + aah/embed.go | 6 +- aah/generate.go | 4 +- aah/migrate.go | 145 ++++++++++++++++++++++++++++++++++++++++++++ aah/migrate.grammar | 38 ++++++++++++ 5 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 aah/migrate.go create mode 100644 aah/migrate.grammar diff --git a/aah/aah.go b/aah/aah.go index c127d38..2547379 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -109,6 +109,7 @@ func main() { switchCmd, updateCmd, generateCmd, + migrateCmd, } sort.Sort(cli.FlagsByName(app.Flags)) diff --git a/aah/embed.go b/aah/embed.go index 3d92ecc..74d137f 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -36,7 +36,7 @@ func processMount(mode bool, appBaseDir, vroot, proot string, skipList ess.Exclu } if mode { - cliLog.Infof("|--- Processing mount: '%s' <== '%s'", vroot, proot) + cliLog.Infof("|-- Processing mount: '%s' <== '%s'", vroot, proot) } b, err := generateVFSSource(mode, vroot, proot, skipList, noGzipList) if err != nil { @@ -89,7 +89,7 @@ func generateVFSSource(mode bool, vroot, proot string, skipList ess.Excludes, no goto sc } - cliLog.Debugf(" |--- Skipping: %s", fpath) + cliLog.Debugf(" |-- Skipping: %s", fpath) if info.IsDir() { return filepath.SkipDir // skip directory } @@ -122,7 +122,7 @@ func generateVFSSource(mode bool, vroot, proot string, skipList ess.Excludes, no continue } - cliLog.Debugf(" |--- Processing: %s", fname) + cliLog.Debugf(" |-- Processing: %s", fname) mp := filepath.ToSlash(filepath.Join(vroot, strings.TrimPrefix(fname, proot))) if err = vfsTmpl.ExecuteTemplate(buf, "vfs_file", aah.Data{ diff --git a/aah/generate.go b/aah/generate.go index 77056cd..f744207 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -30,7 +30,7 @@ var generateCmd = cli.Command{ aah h g aah help generate - To know more about individual sub command details: + To know more about individual sub-commands details: aah g h s aah generate help script `, @@ -94,10 +94,10 @@ func generateScriptsAction(c *cli.Context) error { func generateSystemdScript(c *cli.Context) error { importPath := appImportPath(c) - if err := aah.Init(importPath); err != nil { logFatal(err) } + projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) diff --git a/aah/migrate.go b/aah/migrate.go new file mode 100644 index 0000000..7b44b20 --- /dev/null +++ b/aah/migrate.go @@ -0,0 +1,145 @@ +// Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) +// aahframework.org/tools/aah source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "go/format" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "aahframework.org/aah.v0" + "aahframework.org/config.v0" + "aahframework.org/essentials.v0" + "gopkg.in/urfave/cli.v1" +) + +const aahGrammarIdentifier = "migrate.grammar" + +var migrateCmd = cli.Command{ + Name: "migrate", + Aliases: []string{"m"}, + Usage: "Migrates application codebase to current version of aah (currently beta)", + Description: `Command migrate is to house migration related sub-commands of aah. + Currently it supports Go source code migrate. + + To know more about available 'migrate' sub commands: + aah h m + aah help migrate + + To know more about individual sub-commands details: + aah m h c + aah migrate help code +`, + Subcommands: []cli.Command{ + cli.Command{ + Name: "code", + Aliases: []string{"c"}, + Usage: "Migrates Go source code by making it compatible with current version of aah", + Description: `Command code is to fix/upgrade aah's breaking changes and deprecated elements + in Go source file to the current version of aah. + + The goal 'Code' command is to keep aah users always up-to-date with latest of aah; to take + advantage of aah features and its capabilities. + + Example of script command: + aah m c -i github.com/user/appname + aah migrate code --importpath github.com/user/appname + `, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "i, importpath", + Usage: "Import path of aah application", + }, + }, + Action: migrateCodeAction, + }, + }, +} + +// TODO use golang.org/x/tools/imports + +func migrateCodeAction(c *cli.Context) error { + importPath := appImportPath(c) + if err := aah.Init(importPath); err != nil { + logFatal(err) + } + + projectCfg := aahProjectCfg(aah.AppBaseDir()) + cliLog = initCLILogger(projectCfg) + grammarFile := filepath.Join(aahToolsPath().Dir, aahGrammarIdentifier) + grammarCfg, err := config.LoadFile(grammarFile) + if err != nil { + logFatal(err) + } + + cliLog.Info("Note:") + cliLog.Info("-----") + cliLog.Info("Command operates based on grammer file. If you identify a new grammar entry, \n" + + "create an issue here https://aahframework.org/issues, to include in the grammar file.\n") + + cliLog.Infof("Loaded grammar file: %s", grammarFile) + cliLog.Infof("Loaded aah project file: %s", filepath.Join(aah.AppBaseDir(), aahProjectIdentifier)) + cliLog.Infof("Migrate starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) + + // Go Source files + if migrateGoSrcFiles(projectCfg, grammarCfg) == 0 { + cliLog.Info("It seems application codebase 'app/**' is up-to-date") + } + + cliLog.Infof("Migrate successful for '%s' [%s]\n", aah.AppName(), aah.AppImportPath()) + return nil +} + +func migrateGoSrcFiles(projectCfg, grammarCfg *config.Config) int { + grammar, found := grammarCfg.StringList("file.go.upgrades_replacer") + if !found { + logFatalf("Config 'file.go.upgrades_replacer' not found in grammar file") + } + fixer := strings.NewReplacer(grammar...) + excludes, _ := projectCfg.StringList("build.ast_excludes") + files, _ := ess.FilesPathExcludes(filepath.Join(aah.AppBaseDir(), "app"), true, ess.Excludes(excludes)) + count := 0 + for _, f := range files { + df := strings.TrimPrefix(filepath.ToSlash(stripGoSrcPath(f)), aah.AppImportPath()+"/") + fileBytes, err := ioutil.ReadFile(f) + if err != nil { + logError(err) + cliLog.Infof(" |-- skipped: %s", df) + continue + } + + modFileBytes := []byte(fixer.Replace(string(fileBytes))) + if bytes.Equal(fileBytes, modFileBytes) { + // not modified + continue + } + + // file modified + fmtFileBytes, err := format.Source(modFileBytes) + if err != nil { + logErrorf("While formating: %s", err) + cliLog.Infof(" |-- skipped: %s", df) + continue + } + + if err = os.Truncate(f, 0); err != nil { + logErrorf("While truncate: %s", err) + cliLog.Infof(" |-- skipped: %s", df) + continue + } + + if err = ioutil.WriteFile(f, []byte(fmtFileBytes), permRWRWRW); err != nil { + logError(err) + } + + cliLog.Infof(" |-- processed: %s", df) + count++ + } + + return count +} diff --git a/aah/migrate.grammar b/aah/migrate.grammar new file mode 100644 index 0000000..69b2635 --- /dev/null +++ b/aah/migrate.grammar @@ -0,0 +1,38 @@ +# ------------------------------------------------------------ +# aah migrate grammar file +# +# Syntax format is same as aah config format +# ------------------------------------------------------------ + +file { + + # Grammar for Go source file (.go) + # Each line have "from" and "to" replacement + # + # Format: "oldsignature", "newsignature" + go { + upgrades_replacer = [ + "aah.AddServerTLSConfig", "aah.SetTLSConfig", + "aah.SubscribeEventf", "aah.SubscribeEventFunc", + "aah.UnsubscribeEventf", "aah.UnsubscribeEventFunc", + "aah.EventOnAfterReply", "aah.EventOnPostReply", + "aah.OnRequest", "aah.AppHTTPEngine().OnRequest", + "aah.OnPreReply", "aah.AppHTTPEngine().OnPreReply", + "aah.OnPostReply", "aah.AppHTTPEngine().OnPostReply", + "aah.OnAfterReply", "aah.AppHTTPEngine().OnPostReply", + "aah.OnPreAuth", "aah.AppHTTPEngine().OnPreAuth", + "aah.OnPostAuth", "aah.AppHTTPEngine().OnPostAuth", + "aah.Middlewares", "aah.AppHTTPEngine().Middlewares", + "Req.AcceptContentType", "Req.AcceptContentType()", + "Req.AcceptEncoding", "Req.AcceptEncoding()", + "Req.ClientIP", "Req.ClientIP()", + "Req.ContentType", "Req.ContentType()", + "Req.Locale", "Req.Locale()", + ".Readfrom(", ".FromReader(", + ".RedirectSts(", ".RedirectWithStatus(", + ".FindDomain(", ".Lookup(" + ] + } + + # upcoming elements +} From 1cf15258b4ec64ba708f1d54c0f26e4af7f2f669 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 26 May 2018 20:52:55 -0700 Subject: [PATCH 30/48] migrate grammar update --- aah/migrate.grammar | 1 + 1 file changed, 1 insertion(+) diff --git a/aah/migrate.grammar b/aah/migrate.grammar index 69b2635..7569d7d 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -16,6 +16,7 @@ file { "aah.SubscribeEventf", "aah.SubscribeEventFunc", "aah.UnsubscribeEventf", "aah.UnsubscribeEventFunc", "aah.EventOnAfterReply", "aah.EventOnPostReply", + "aah.OnShutdown", "aah.OnPostShutdown", "aah.OnRequest", "aah.AppHTTPEngine().OnRequest", "aah.OnPreReply", "aah.AppHTTPEngine().OnPreReply", "aah.OnPostReply", "aah.AppHTTPEngine().OnPostReply", From fec324be1648685d566a7f773a992792c2676604 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sun, 27 May 2018 15:21:20 -0700 Subject: [PATCH 31/48] migrate grammer update and desc --- aah/app-template/app/init.go.atmpl | 53 ++++++++++++++---------------- aah/compile.go | 1 + aah/migrate.go | 8 +++-- aah/migrate.grammar | 13 +++++--- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index 70a6cd1..f72cb28 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -32,26 +32,28 @@ func init() { //__________________________________________________________________________ // Event: OnInit - // - // Published right after the `aah.AppConfig()` is loaded. + // Doc: https://docs.aahframework.org/server-extension.html#event-oninit // // aah.OnInit(config.LoadRemote) // Event: OnStart - // - // Published right before the start of aah server. + // Doc: https://docs.aahframework.org/server-extension.html#event-onstart // // aah.OnStart(db.Connect) // aah.OnStart(cache.Load){{ if or .App.IsWebApp .App.IsAPIApp }} aah.OnStart(SubscribeHTTPEvents){{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} aah.OnStart(SubscribeWebSocketEvents){{ end }} - // Event: OnShutdown + // Event: OnPreShutdown + // Doc: https://docs.aahframework.org/server-extension.html#event-onpreshutdown // - // Published on receiving OS Signals `SIGINT` or `SIGTERM`. + // aah.OnPreShutdown(notify.AnnounceImGonnaShutdown) + + // Event: OnPostShutdown + // Doc: https://docs.aahframework.org/server-extension.html#event-onpostshutdown // - // aah.OnShutdown(cache.Flush) - // aah.OnShutdown(db.Disconnect) + // aah.OnPostShutdown(cache.Flush) + // aah.OnPostShutdown(db.Disconnect) {{ if not .App.IsWebSocketApp }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Middleware's @@ -123,38 +125,44 @@ func SubscribeHTTPEvents(_ *aah.Event) { {{ if not .App.IsSecurityEnabled }}//{{ end }} he := aah.AppHTTPEngine() // Event: OnRequest + // Doc: https://docs.aahframework.org/server-extension.html#event-onrequest // - // Published for each incoming request to the aah go server. // he.OnRequest(myserverext.OnRequest) // Event: OnPreReply + // Doc: https://docs.aahframework.org/server-extension.html#event-onprereply // - // Published right before writing an reply on the wire. // he.OnPreReply(myserverext.OnPreReply) + // Event: OnHeaderReply + // Doc: https://docs.aahframework.org/server-extension.html#event-onheaderreply + // + // he.OnHeaderReply(myserverext.OnHeaderReply) + // Event: OnPostReply + // Doc: https://docs.aahframework.org/server-extension.html#event-onpostreply // - // Published right after the reply is written on the wire. // he.OnPostReply(myserverext.OnPostReply) // Event: OnPreAuth + // Doc: https://docs.aahframework.org/server-extension.html#event-onpreauth // - // Published right before the Authentication by security manager. // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} // Event: OnPostAuth - // Published right after the successful Authentication by security manager. + // Doc: https://docs.aahframework.org/server-extension.html#event-onpostauth + // he.OnPostAuth(security.PostAuthEvent){{ else }} // Event: OnPostAuth - // Published right after the successful Authentication by security manager. + // Doc: https://docs.aahframework.org/server-extension.html#event-onpostauth + // // he.OnPostAuth(myserverext.PostAuthEvent){{ end }} } {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // WebSocket Events -// // Doc: https://docs.aahframework.org/websocket.html#events // // Subscribing WebSocket events on app start. @@ -164,33 +172,22 @@ func SubscribeWebSocketEvents(_ *aah.Event) { // wse := aah.AppWSEngine() // Custom ID Generator + // // wse.SetIDGenerator(websockets.MyCustomIDGenerator) // Event: OnPreConnect - // - // Published before connection gets upgraded to WebSocket. - // It provides a control of accepting incoming request or reject it - // using ctx.Abort(errorCode) + // // wse.OnPreConnect(mywebsockets.HandleEvents) // Event: OnPostConnect // - // Published right after the successful WebSocket connection which is - // established with the aah server. // wse.OnPostConnect(mywebsockets.HandleEvents) // Event: OnPostDisconnect // - // Published right after the WebSocket client got disconnected. - // It could have occurred due to graceful disconnect, network related error, etc. // wse.OnPostDisconnect(mywebsockets.HandleEvents) // Event: OnError // - // Published whenever error occurs in the lifecycle such as Origin Check failed, - // WebSocket/WebSocket Action not found, WebSocket Action parameter parse error, - // and WebSocket upgrade fails. - // - // `ctx.ErrorReason()` method can be called to know the reason for the error. // wse.OnError(mywebsockets.HandleEvents) }{{ end }} diff --git a/aah/compile.go b/aah/compile.go index 8badf26..2d5768c 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -444,6 +444,7 @@ func main() { fmt.Printf("%-16s: %s\n", "Binary Name", aah.AppBuildInfo().BinaryName) fmt.Printf("%-16s: %s\n", "Version", aah.AppBuildInfo().Version) fmt.Printf("%-16s: %s\n", "Build Timestamp", aah.AppBuildInfo().Date) + fmt.Printf("%-16s: %s\n", "aah Version", aah.Version) return } diff --git a/aah/migrate.go b/aah/migrate.go index 7b44b20..b28de7c 100644 --- a/aah/migrate.go +++ b/aah/migrate.go @@ -43,8 +43,9 @@ var migrateCmd = cli.Command{ Description: `Command code is to fix/upgrade aah's breaking changes and deprecated elements in Go source file to the current version of aah. - The goal 'Code' command is to keep aah users always up-to-date with latest of aah; to take - advantage of aah features and its capabilities. + The goal of 'Code' command is to keep aah users always up-to-date with latest version of aah. + + Note: It does not take file backup, assumes aah application uses version control. Example of script command: aah m c -i github.com/user/appname @@ -96,10 +97,11 @@ func migrateCodeAction(c *cli.Context) error { } func migrateGoSrcFiles(projectCfg, grammarCfg *config.Config) int { - grammar, found := grammarCfg.StringList("file.go.upgrades_replacer") + grammar, found := grammarCfg.StringList("file.go.upgrade_replacer") if !found { logFatalf("Config 'file.go.upgrades_replacer' not found in grammar file") } + fixer := strings.NewReplacer(grammar...) excludes, _ := projectCfg.StringList("build.ast_excludes") files, _ := ess.FilesPathExcludes(filepath.Join(aah.AppBaseDir(), "app"), true, ess.Excludes(excludes)) diff --git a/aah/migrate.grammar b/aah/migrate.grammar index 7569d7d..d40619e 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -11,10 +11,8 @@ file { # # Format: "oldsignature", "newsignature" go { - upgrades_replacer = [ + upgrade_replacer = [ "aah.AddServerTLSConfig", "aah.SetTLSConfig", - "aah.SubscribeEventf", "aah.SubscribeEventFunc", - "aah.UnsubscribeEventf", "aah.UnsubscribeEventFunc", "aah.EventOnAfterReply", "aah.EventOnPostReply", "aah.OnShutdown", "aah.OnPostShutdown", "aah.OnRequest", "aah.AppHTTPEngine().OnRequest", @@ -23,7 +21,14 @@ file { "aah.OnAfterReply", "aah.AppHTTPEngine().OnPostReply", "aah.OnPreAuth", "aah.AppHTTPEngine().OnPreAuth", "aah.OnPostAuth", "aah.AppHTTPEngine().OnPostAuth", - "aah.Middlewares", "aah.AppHTTPEngine().Middlewares", + "aah.Middlewares", "aah.AppHTTPEngine().Middlewares", + "SubscribeEventf", "SubscribeEventFunc", + "UnsubscribeEventf", "UnsubscribeEventFunc", + "ahttp.GetResponseWriter", "ahttp.AcquireResponseWriter", + "ahttp.PutResponseWriter", "ahttp.ReleaseResponseWriter", + "ahttp.GetGzipResponseWriter", "ahttp.WrapGzipWriter", + "ahttp.PutGzipResponseWiriter", "ahttp.ReleaseResponseWriter", + "Req.Raw", "Req.Unwrap()", "Req.AcceptContentType", "Req.AcceptContentType()", "Req.AcceptEncoding", "Req.AcceptEncoding()", "Req.ClientIP", "Req.ClientIP()", From 1196ad802072ef2ec2b0246929218d4363b201fb Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 28 May 2018 22:54:45 -0700 Subject: [PATCH 32/48] app template and migrate grammar update --- aah/app-template/aah.project.atmpl | 53 +++++++++++++++++++----------- aah/app-template/app/init.go.atmpl | 2 +- aah/migrate.grammar | 30 ++++++++--------- 3 files changed, 49 insertions(+), 36 deletions(-) diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index 2e4cb9a..0811e7c 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -1,10 +1,14 @@ -############################################################## +# --------------------------------------------------------------------------- # {{ .App.Name }} - aah framework project # # Note: Add it to version control -############################################################## +# --------------------------------------------------------------------------- -# Build section is used during aah application compile and build command. +# --------------------------------------------------------------------------- +# Build section is used during aah application run and build command. +# +# Doc: https://docs.aahframework.org/aah-project-file.html#section-build +# --------------------------------------------------------------------------- build { # Application binary name # Default value is `name` attribute value from `aah.conf` @@ -15,36 +19,40 @@ build { # - `AAH_APP_VERSION` environment value is not available. version = "0.0.1" - # If application is missing any dependencies in `build import path` - # during a compile and build, aah CLI will try to get dependencies - # using 'go get '. - # Default value is `true`. + # If application misses any dependencies in `build import path` + # during compile and build, aah CLI will try to get the missing + # dependencies using 'go get '. + # + # Default value is `true` since v0.11.0 #dep_get = true + # `-i` may not be required for go1.10 and above, however no impact. flags = ["-i"] ldflags = "" tags = "" - # AST excludes is used for `aah.Context` inspection and generating aah + # AST excludes is used for `aah.Context` inspection and for generating aah # application main Go file. # - # Valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match + # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match ast_excludes = ["*_test.go", ".*", "*.bak", "*.tmp", "vendor"] # Packing excludes is used to exclude file/directory during aah application # build archive. # - # Note: It is applicable to aah single binary build. This list excluded from - # embedding. + # Note: It is also applicable to aah single binary build. This list is excluded + # from embedding. # - # Valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match + # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match excludes = ["*.go", ".*", "*.bak", "*.tmp", "*.pid", "vendor", "app", "build", "tests", "logs"] } -# Logger configuration for aah CLI tool. +# --------------------------------------------------------------------------- +# Logger configuration for aah CLI tool +# --------------------------------------------------------------------------- log { # Log level # Default value is `info`. @@ -55,18 +63,23 @@ log { #color = false } -# Hot-Reload is development purpose to help developer. +# --------------------------------------------------------------------------- +# Hot-Reload helps developer for rapid development. # Read more about implementation here - https://aahframework.org/issues/4 # -# NOTE: Do not use hot-reload feature for production purpose, it's not recommended. +# NOTE: Do not use hot-reload for production purposes, it is not recommended. +# Use app binary reload capability - `kill -HUP ` instead. +# --------------------------------------------------------------------------- hot_reload { # Default value is `true`. enable = true # Watch configuration - files/directories exclusion list. + # + # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match watch { - # Note: static directory not required to be monitored, since server delivers - # up-to-date file on environment profile `dev`. + # Note: static directory is not required to be monitored since aah server + # delivers up-to-date file on environment profile `dev`. dir_excludes = [".*"] file_excludes = [".*", "_test.go", "LICENSE", "README.md"] @@ -81,10 +94,10 @@ hot_reload { vfs { # Adding custom mount points. # - # Configured mount points directories and sub-directories - # are embedded into the aah binary during the single binary build. + # Configured mount point directories and sub-directories + # are embedded into the aah app binary during single binary build. # - # Note: Embed packaging uses same exclusion list from config `build.excludes`. + # Note: Embed packaging uses the exclusion list from config `build.excludes`. mount { # Choose a unique key name #my_mount { diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index f72cb28..b3512fa 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -97,7 +97,7 @@ func init() { // aah.AddSessionStore("redis", &RedisSessionStore{}) {{ end }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Custom value Parser + // Add Custom Value Parser // Doc: https://docs.aahframework.org/request-parameters-auto-bind.html //__________________________________________________________________________ // if err := aah.AddValueParser(reflect.TypeOf(CustomType{}), customParser); err != nil { diff --git a/aah/migrate.grammar b/aah/migrate.grammar index d40619e..d0a523b 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -12,22 +12,22 @@ file { # Format: "oldsignature", "newsignature" go { upgrade_replacer = [ - "aah.AddServerTLSConfig", "aah.SetTLSConfig", + "aah.AddServerTLSConfig(", "aah.SetTLSConfig(", "aah.EventOnAfterReply", "aah.EventOnPostReply", - "aah.OnShutdown", "aah.OnPostShutdown", - "aah.OnRequest", "aah.AppHTTPEngine().OnRequest", - "aah.OnPreReply", "aah.AppHTTPEngine().OnPreReply", - "aah.OnPostReply", "aah.AppHTTPEngine().OnPostReply", - "aah.OnAfterReply", "aah.AppHTTPEngine().OnPostReply", - "aah.OnPreAuth", "aah.AppHTTPEngine().OnPreAuth", - "aah.OnPostAuth", "aah.AppHTTPEngine().OnPostAuth", - "aah.Middlewares", "aah.AppHTTPEngine().Middlewares", - "SubscribeEventf", "SubscribeEventFunc", - "UnsubscribeEventf", "UnsubscribeEventFunc", - "ahttp.GetResponseWriter", "ahttp.AcquireResponseWriter", - "ahttp.PutResponseWriter", "ahttp.ReleaseResponseWriter", - "ahttp.GetGzipResponseWriter", "ahttp.WrapGzipWriter", - "ahttp.PutGzipResponseWiriter", "ahttp.ReleaseResponseWriter", + "aah.OnShutdown(", "aah.OnPostShutdown(", + "aah.OnRequest(", "aah.AppHTTPEngine().OnRequest(", + "aah.OnPreReply(", "aah.AppHTTPEngine().OnPreReply(", + "aah.OnPostReply(", "aah.AppHTTPEngine().OnPostReply(", + "aah.OnAfterReply(", "aah.AppHTTPEngine().OnPostReply(", + "aah.OnPreAuth(", "aah.AppHTTPEngine().OnPreAuth(", + "aah.OnPostAuth(", "aah.AppHTTPEngine().OnPostAuth(", + "aah.Middlewares(", "aah.AppHTTPEngine().Middlewares(", + ".SubscribeEventf(", ".SubscribeEventFunc(", + ".UnsubscribeEventf(", ".UnsubscribeEventFunc(", + "ahttp.GetResponseWriter(", "ahttp.AcquireResponseWriter(", + "ahttp.PutResponseWriter(", "ahttp.ReleaseResponseWriter(", + "ahttp.GetGzipResponseWriter(", "ahttp.WrapGzipWriter(", + "ahttp.PutGzipResponseWiriter(", "ahttp.ReleaseResponseWriter(", "Req.Raw", "Req.Unwrap()", "Req.AcceptContentType", "Req.AcceptContentType()", "Req.AcceptEncoding", "Req.AcceptEncoding()", From 2ce00a5bc3652498bf1a03e2a1a60aa1ee1a864c Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 29 May 2018 21:03:10 -0700 Subject: [PATCH 33/48] minor update on vfs load --- aah/compile.go | 3 --- aah/embed.go | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 2d5768c..855fb84 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -435,9 +435,6 @@ func main() { }) {{ if .AppIsPackaged }}aah.SetAppPackaged({{ .AppIsPackaged }}){{ end }} - {{ if .AppIsEmbedded }} - // Set app vfs into embedded mode - aah.AppVFS().SetEmbeddedMode(){{ end }} // display application information if *version { diff --git a/aah/embed.go b/aah/embed.go index 74d137f..24edfdc 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -238,6 +238,8 @@ import ({{ if .Mode }} ) func init() { + aah.AppVFS().SetEmbeddedMode() + if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { log.Fatal(err) } From d2abcfed3323af4191b45efb4afb62996668bf60 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 29 May 2018 21:45:48 -0700 Subject: [PATCH 34/48] embed and compile upadtes --- aah/compile.go | 16 +++++++++++----- aah/embed.go | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/aah/compile.go b/aah/compile.go index 855fb84..77add4d 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -340,6 +340,7 @@ package main import ( "bytes" "flag" + "path/filepath" "fmt" "os" "os/signal" @@ -365,12 +366,17 @@ var ( ) func MergeSuppliedConfig(_ *aah.Event) { - externalConfig, err := config.VFSLoadFile(aah.AppVFS(), *configPath) + cpath, err := filepath.Abs(*configPath) + if err != nil { + log.Errorf("Unable to resolve external config: %s", *configPath) + } + + externalConfig, err := config.LoadFile(cpath) if err != nil { - log.Fatalf("Unable to load external config: %s", *configPath) + log.Errorf("Unable to load external config: %s", cpath) } - log.Debug("Merging external config into aah application config") + log.Infof("Merging external config[%s] into aah application[%s]", cpath, aah.AppName()) if err := aah.AppConfig().Merge(externalConfig); err != nil { log.Errorf("Unable to merge external config into aah application[%s]: %s", aah.AppName(), err) } @@ -388,7 +394,7 @@ func PrintFilepath(pattern string) { regex, err := regexp.Compile(pattern) if err != nil { - fmt.Println("ERROR", err) + log.Error(err) return } @@ -404,7 +410,7 @@ func PrintFilepath(pattern string) { return nil }); err != nil { - fmt.Println("ERROR", err) + log.Error(err) } } diff --git a/aah/embed.go b/aah/embed.go index 24edfdc..7a1a0ab 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -238,7 +238,7 @@ import ({{ if .Mode }} ) func init() { - aah.AppVFS().SetEmbeddedMode() + {{ if .Mode }}aah.AppVFS().SetEmbeddedMode(){{ end }} if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { log.Fatal(err) From 642d5cc78f69719e000ead713417b8bde2b4b749 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 5 Jun 2018 17:21:57 -0700 Subject: [PATCH 35/48] go-aah/aah#186 subject principal provider. compile, migrate, embed and app template upadtes --- aah/app-template/config/aah.conf.atmpl | 435 ++++------------------ aah/app-template/config/routes.conf.atmpl | 35 +- aah/build.go | 10 +- aah/compile.go | 57 ++- aah/embed.go | 4 +- aah/migrate.go | 121 ++++-- aah/migrate.grammar | 16 +- aah/new.go | 8 +- 8 files changed, 245 insertions(+), 441 deletions(-) diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index c52f3af..63aa871 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -1,9 +1,11 @@ -# --------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # {{ .App.Name }} - aah Application # -# Complete configuration reference: +# NOTE: Since v0.11.0, app config file has minimal configuration. +# To explore all the supported configurations, refer documentation. +# # Doc: https://docs.aahframework.org/app-config.html -# --------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Application name (non-whitespace) # Default value is `basename` of import path. @@ -12,74 +14,31 @@ name = "{{ .App.Name }}" # Friendly description of application desc = "aah {{ .App.Type }} application" -# Application type, typically either Web or API. +# Application type, typically these `web`, `api`, `websocket` type = "{{ .App.Type }}" -# Application instance name is used for traceability when running in cluster mode. -# It provides context based logging and distinguishes app instance -# across cluster. -# -# Typically pass value of `instance_name` via aah external config or -# Environment variable. -instance_name = $AAH_INSTANCE_NAME - -# Configure file path of application PID file to be written. -# Ensure application has appropriate permission and directory exists. -# -# Default value is `/.pid` -#pid_file = "/path/to/pidfile.pid" - # ----------------------------------------------------------------- -# Server configuration - HTTP +# Server configuration # Doc: https://docs.aahframework.org/app-config.html#section-server # ----------------------------------------------------------------- server { + # Address is used to bind against host address, IP address, UNIX socket. + # # For unix socket: unix:/tmp/aahframework.sock # Default value is `empty` string. #address = "" + # Port is used to bind server listener on particular port. + # # For standard port `80` and `443`, put empty string or a value # Default value is 8080. #port = "" - # Header value written as `Server` HTTP header. + # Header value is written as HTTP header `Server: aah-go-server`. + # # To exclude header `Server` from writing, simply comment it out. - header = "aah-go-server" - - # Valid time units are "s = seconds", "m = minutes" - timeout { - # Mapped to `http.Server.ReadTimeout`, is the maximum duration for reading - # the entire request, including the body. - # - # Because ReadTimeout does not let Handlers make per-request decisions on - # each request body's acceptable deadline or upload rate, most users will - # prefer to use ReadHeaderTimeout. It is valid to use them both. - # - # Default value is `90s`. - #read = "90s" - - # Mapped to `http.Server.WriteTimeout`, is the maximum duration before timing - # out writes of the response. It is reset whenever a new request's header is - # read. Like ReadTimeout, it does not let Handlers make decisions on a - # per-request basis. - # - # Default value is `90s`. - #write = "90s" - - # aah server graceful shutdown timeout - # Default value is `60s`. - #grace_shutdown = "60s" - } + header = "aah-go-server"{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - # Mapped to `http.Server.MaxHeaderBytes`. - # Default value is `1mb`. - #max_header_bytes = "1mb" - - # HTTP server keep alive option. - # Default value is `true`. - #keep_alive = true - - {{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket -}} # -------------------------------------------------------------------------- # WebSocket configurations # Doc: https://docs.aahframework.org/websocket.html#configuration @@ -101,134 +60,7 @@ server { # "https://websocket.org", #] } - } - {{ end }} - - ssl { - # Default value is `false`. - #enable = false - - # Default value is `empty` string. - #cert = "" - - # Default value is `empty` string. - #key = "" - - # Disabling HTTP/2 set it true. - # Default value is `false`. - #disable_http2 = true - - # Redirect HTTP => HTTPS functionality does protocol switch, so it works - # with domain and subdomains. - # For example: - # http://aahframework.org => https://aahframework.org - # http://www.aahframework.org => https://www.aahframework.org - # http://docs.aahframework.org => https://docs.aahframework.org - redirect_http { - # Enabling HTTP => HTTPS redirects. - # Default is value is `false`. - #enable = true - - # Port no. of HTTP requests to listen. - # For standard port `80` put empty string or a value. - # It is required value, no default. - port = "8080" - - # Redirect code - # Default value is `307`. - #code = 307 - } - - lets_encrypt { - # To get SSL certificate from Let's Encrypt CA, enable it. - # Don't forget to enable `server.ssl.enable=true`. - # Default value is `false`. - #enable = false - - # Host policy controls which domains the autocert will attempt - # to retrieve new certificates for. It does not affect cached certs. - # It is required, no default value. - #host_policy = ["example.org", "docs.example.org"] - - # Renew before optionally specifies how early certificates should - # be renewed before they expire. - # Default value is `10` days. - #renew_before = 10 - - # Email optionally specifies a contact email address. This is used by CAs, - # such as Let's Encrypt, to notify about problems with issued certificates. - # If the Client's account key is already registered, Email is not used. - #email = "jeeva@myjeeva.com" - - # Force RSA makes the autocert generate certificates with 2048-bit RSA keys. - # If false, a default is used. Currently the default is - # EC-based keys using the P-256 curve. - #force_rsa = false - - # Cache optionally stores and retrieves previously-obtained certificates - # autocert manager. By default certs will only be cached for the lifetime - # of the autocert manager. - # - # autocert manager passes the Cache certificates data encoded in PEM, - # with private/public parts combined in a single Cache.Put call, - # private key first. - # Default value is `empty` string. - #cache_dir = "/Users/jeeva/autocert" - } - } - - {{ if not .App.IsWebSocketApp -}} - # -------------------------------------------------------------------------- - # To manage aah server effectively it is necessary to know details about the - # request, response, processing time, client IP address, etc. aah framework - # provides the flexible and configurable access log capabilities. - # Doc: https://docs.aahframework.org/server-access-log.html - # -------------------------------------------------------------------------- - access_log { - # Enabling server access log - # Default value is `false`. - #enable = true - - # Absolute path to access log file or relative path. - # Default location is application logs directory - #file = "{{ .App.Name }}-access.log" - - # Default server access log pattern - #pattern = "%clientip %custom:- %reqtime %reqmethod %requrl %reqproto %resstatus %ressize %restime %reqhdr:referer" - - # Access Log channel buffer size - # Default value is `500`. - #channel_buffer_size = 500 - - # Include static files access log too. - # Default value is `true`. - #static_file = false - } - - # ------------------------------------------------------- - # Dump Request & Response Details - # Such as URL, Proto, Headers, Body, etc. - # Note: Dump is not applicable for Static Files delivery. - # Doc: https://docs.aahframework.org/server-dump-log.html - # ------------------------------------------------------- - dump_log { - # Default value is `false`. - #enable = true - - # Absolute path to dump log file or relative path. - # Default location is application logs directory - #file = "{{ .App.Name }}-dump.log" - - # Log Request body into dump log. aah dumps body for JSON, XML, Form - # HTML and Plain Text content types. - # Default value is `false`. - #request_body = true - - # Log Request body into dump log. aah dumps body for JSON, XML, Form - # HTML, and Plain Text content types. - # Default value is `false`. - #response_body = true - } + }{{ end }} } # ------------------------------------------------------------------ @@ -236,186 +68,59 @@ server { # Doc: https://docs.aahframework.org/app-config.html#section-request # ------------------------------------------------------------------ request { - # aah framework encourages to have unique `Request Id` for each incoming - # request, it helps in traceability. If request has already `sX-Request-Id` - # HTTP header then it does not generate one. + # ----------------------------------------------------------------------------- + # aah framework encourages to have a unique `Request Id` for each incoming + # request. It helps in traceability. If a request already has `X-Request-Id` + # HTTP header, then aah does not generate one. + # + # GUID is generated using MangoDB ObjectId algorithm. + # + # Doc: https://docs.aahframework.org/app-config.html#section-request-id + # ----------------------------------------------------------------------------- id { + # To enable/disable request ID generation. # Default value is `true`. enable = true + # HTTP header name for Request ID. If a request already has HTTP header, then + # aah does not generate one. + # # Default value is `X-Request-Id`, customize it per use case. #header = "X-Request-Id" } +}{{ if not .App.IsWebSocketApp }} - # Max request body size for all incoming HTTP requests except `MultipartForm`. - # Also it could be overridden at individual route on specific cases - # in `routes.conf`. - # - # Default value is `5mb`. - #max_body_size = "5mb" - - # Default value is `32mb`, choose value based on app use case - #multipart_size = "32mb" - - # aah provides `Content Negotiation` feature for the incoming HTTP request. - # Read more about implementation and RFC details here GitHub #75. - # Perfect for REST API, also can be used for web application too if needed. - content_negotiation { - # To enable Content Negotiation for the application. - # Default value is `false`. - #enable = true - - # For example: Client sends Content-Type header as `application/xml`. - # However server only supports JSON payload as request body. - # Then server responds with 415 Unsupported Media Type. - # - # Default value is empty list and disabled. - #accepted = ["application/json", "text/json"] - - # For example: Client sends Accept header as `application/xml`. - # However server only supports serving JSON i.e. `application/json`. - # Then server responds with 406 Not Acceptable. - # - # Default value is empty list and disabled. - #offered = ["application/json", "text/json"] - } - - # Auto Bind configuration used to bind request parameters to controller - # action parameters. - auto_bind { - # Priority is used to select the bind source priority. - # P -> Path Parameter - # F -> Form Parameter - # Q -> Query Parameter - # - # For example: Let's say we have a controller action named `OrderInfo` and it has - # parameter called `orderId`. So aah tries to parse and bind based - # on the priority. The `orderId` present in `Path` and `Form`, aah - # binds the value from `Path`. Typically it is recommended to have unique names - # in the request parameter though :) - # Path -> Form -> Query - # If not found then it returns with default Go zero value. - # - # Default value is `PFQ`. - #priority = "PFQ" - - # Tag Name is used for bind values to struct exported fields. - # Default value is `bind`. - #tag_name = "bind" - }{{ end }} -} -{{ if .App.IsWebApp -}} -# --------------------------------------------------------------- -# i18n configuration -# Doc: https://docs.aahframework.org/app-config.html#section-i18n -# --------------------------------------------------------------- -i18n { - # It is used as fallback if framework is unable to determine the - # locale from HTTP Request. - # Default value is `en`. - #default = "en" - - # Overriding Request Locale `Accept-Language` header value via URL Path - # parameter or URL Query parameter. - param_name { - # Specify URL Path Param name i.e. `/:lang/home.html`, `/:lang/aboutus.html`, etc. - # For e.g.: `/en/home.html`, `/en/aboutus.html`, `/zh-CN/home.html`, `/zh-CN/aboutus.html` etc. - # Default value is `lang`. - #path = "locale" - - # Specify URL Query Param name i.e `?lang=en`, `?lang=zh-CN`, etc. - # Default value is `lang`. - #query = "locale" - } -}{{ end }} - -# ----------------------------------------------------------------- -# Format configuration -# Doc: https://docs.aahframework.org/app-config.html#section-format -# ----------------------------------------------------------------- -format { - # Time format for auto parse and bind. aah tries to parse the - # time value in the order they defined till it gets success - # otherwise returns the error. - time = [ - "2006-01-02T15:04:05Z07:00", - "2006-01-02T15:04:05Z", - "2006-01-02 15:04:05", - "2006-01-02" - ] -} - -# ------------------------------------------------------------------ -# Runtime configuration -# Doc: https://docs.aahframework.org/app-config.html#section-runtime -# ------------------------------------------------------------------ -runtime { - debug { - # Choose an appropriate buffer size for collecting all goroutines stack trace - # dump based on need. - # Default value is `2mb`. - #stack_buffer_size = "2mb" - - # Whether to collect all the Go routines details or not. - # Default value is `false`. - #all_goroutines = true - - # Whether to strip source `src` base path from file path. - # Default value is `false`. - #strip_src_base = true - } -} - -{{ if not .App.IsWebSocketApp -}} # ----------------------------------------------------------------- # Render configuration # Doc: https://docs.aahframework.org/app-config.html#section-render # ----------------------------------------------------------------- render { - # aah framework chooses the `Content-Type` value automatically based on - # configuration if `aah.Reply()` builder value is not set. It selects in - # the order of: - # - Based on URL file extension, supported `.html`, `.htm`, `.json`, `.js`, `.xml` and `.txt` - # - Request Accept Header - Most Qualified one as per RFC7321 - # - Based `render.default` value supported types are `html`, `json`, `xml` and `text` - # - Finally aah framework uses `http.DetectContentType` API + # aah infers the `Content-Type` value automatically + # if `Reply()` builder does not have value. + # + # It infers in the order of: + # - URL file extension - supports `.html`, `.htm`, `.json`, `.js`, `.xml` + # and `.txt` + # + # - Request Accept Header - Most Qualified one as per RFC 7321 + # * Supports vendor type as per RFC 4288 + # + # - Fallback to `render.default` value - supports `html`, `json`, `xml` and `text` + # # Default value is `empty` string. default = "{{ if .App.IsWebApp }}html{{ else }}json{{ end }}" +}{{ if .App.IsWebApp }} - # To prevent Cross Site Script Inclusion (XSSI) attacks on JSON response - # payload aka JSON vulnerability. XSSI attack is only successful if the - # returned JSON response is executable as JavaScript. - # - # aah prefixes JSON response to make them non-executable. - # Default value is `)]}',\n` - #secure_json { - # prefix = ")]}',\n" - #} - - # Gzip compression configuration for HTTP response. - gzip { - # By default Gzip compression is enabled in aah framework, however - # framework ensures HTTP client accepts Gzip response otherwise - # it won't use Gzip compression. - # - # Tip: If there is a web server in-front of aah server then set this - # value to `false`. - # - # Default value is `true`. - #enable = true - - # Used to control Gzip compression levels. Valid levels are - # 1 = BestSpeed to 9 = BestCompression. - # Default value is `4`. - #level = 4 - } -} -{{ if .App.IsWebApp -}} -# ------------------------------------------------------------------ +# ----------------------------------------------------------------------------- # Cache configuration -# Doc: https://docs.aahframework.org/static-files.html#cache-control -# ------------------------------------------------------------------ +# Doc: https://docs.aahframework.org/app-config.html#section-cache +# ----------------------------------------------------------------------------- cache { + + # ----------------------------------------------------------------------------- + # HTTP Cache-Control Configuration for Static files + # Doc: https://docs.aahframework.org/static-files.html#http-cache-control + # ----------------------------------------------------------------------------- static { # Default `Cache-Control` for all static files, # if specific mime type is not defined. @@ -450,43 +155,39 @@ view { engine = "{{ .App.ViewEngine }}" # View file extension. - # Default value is chosen based on `view.engine`, during a new app creation - # via `aah new` + # + # Default value is chosen based on `view.engine` while creating a new app + # via command `aah new`. ext = "{{ .App.ViewFileExt }}" - # View file name and path resolve case sensitive or not. - # For e.g.: "/views/pages/app/login.tmpl" == "/views/pages/App/Login.tmpl" - # Default value is `false`. - #case_sensitive = false - # To use custom Go template delimiters for view files. # Default value is `{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}`. #delimiters = "{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}" - - # aah chooses the default app layout as `master.html`. Also aah supports - # non-layout too. - # Option to disable the default layout for HTML. - # Default value is `true`. Available since v0.6 - #default_layout = false }{{ end }} -# -------------------------------------------------------------- -# Application Security +# ----------------------------------------------------------------------------- +# Security configuration # Doc: https://docs.aahframework.org/security-config.html -# -------------------------------------------------------------- +# ----------------------------------------------------------------------------- include "./security.conf"{{ end }} -# -------------------------------------------------------------- -# Environment Profiles e.g.: dev, qa, prod +# ----------------------------------------------------------------------------- +# Environment Profiles +# +# For e.g.: dev, qa, prod, etc. +# # Doc: https://docs.aahframework.org/app-config.html#section-env -# -------------------------------------------------------------- +# ----------------------------------------------------------------------------- env { - # Indicates active profile name for application configuration. + # Active profile name for the application configuration. + # + # For e.g.: To activate environment profile via application binary + # /path/to/binary/aahwebsite -profile prod + # # Default value is `dev`. #active = "dev" - # ---------------------------------- # Environment profile configurations - # ---------------------------------- + # Load all the configuration files from `appbasedir/config/env/*.conf`. include "./env/*.conf" } diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index 8faef2d..c7eaf02 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -1,13 +1,13 @@ -# --------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # {{ .App.Name }} - Application Routes Configuration # # Complete routes configuration reference: # Doc: https://docs.aahframework.org/routes-config.html -# --------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#------------------------------------------------------------------------ +#------------------------------------------------------------------------------ # Domain and sub-domain configuration goes into section `domains { ... }` -#------------------------------------------------------------------------ +#------------------------------------------------------------------------------ domains { # Pick your choice of an `unique keyname` to define your domain section @@ -98,23 +98,24 @@ domains { {{ end }}{{ if .App.IsWebApp }} - #---------------------------------------------------------------------------- + #------------------------------------------------------------------------------ # Static Routes Configuration # To serve static files, it can be directory or individual file. # This section optional one, for e.g: RESTful APIs doesn't need this section. # Static files are delivered via `http.ServeContent`. # # Supported features: - # * Serve directory - # * Serve individual file - # * Directory listing + # - Serve directory + # - Serve individual file + # - Directory listing # - # Pick your choice of `unique name` for each `directory` or `individual` file + # Choose an `unique name` for each `directory` or `individual` file # static route definition. It is called `route name`. + # # Doc: https://docs.aahframework.org/routes-config.html#section-static - #---------------------------------------------------------------------------- + #------------------------------------------------------------------------------ static { - # Static route name, pick a unique one + # Static route name. public_assets { # URL 'path' for serving directory # Below definition means '/static/**' @@ -127,7 +128,7 @@ domains { #list = false } - # serving single file + # Serving single file favicon { path = "/favicon.ico" @@ -147,20 +148,22 @@ domains { } } - {{ end }}#----------------------------------------------------------------------------- + {{ end }} + + # ----------------------------------------------------------------------------- # Application routes definitions # # Doc: https://docs.aahframework.org/routes-config.html#section-routes # Doc: https://docs.aahframework.org/routes-config.html#namespace-group-routes - #----------------------------------------------------------------------------- + # ----------------------------------------------------------------------------- routes { - {{ if not .App.IsWebSocketApp -}}#------------------------------------------------------ + {{ if not .App.IsWebSocketApp -}}# ----------------------------------------------------------------------------- # Pick an unique name, it's called `route name`, # used for reverse URL. # # Pro Tip: https://docs.aahframework.org/routes-config.html#pro-tips-for-nested-namespace-routes - #------------------------------------------------------ + # ----------------------------------------------------------------------------- index { # path is used to match incoming requests # It can contain `:name` - Named parameter and diff --git a/aah/build.go b/aah/build.go index c0be65c..0665841 100644 --- a/aah/build.go +++ b/aah/build.go @@ -23,7 +23,7 @@ var buildCmd = cli.Command{ Aliases: []string{"b"}, Usage: "Builds aah application for deployment (single or non-single)", Description: `Builds aah application for deployment. It supports single and non-single - binary. It is a trade-off learn more https://docs.aahframework.org/build-packaging.html + binary. It is a trade-off learn more https://docs.aahframework.org/vfs.html Artifact naming convention: ---.zip For e.g.: aahwebsite-381eaa8-darwin-amd64.zip @@ -132,9 +132,11 @@ func processVFSConfig(projectCfg *config.Config, mode bool) { excludes, _ := projectCfg.StringList("build.excludes") noGzipList, _ := projectCfg.StringList("vfs.no_gzip") - // Default mount point - if err := processMount(mode, appBaseDir, "/app", appBaseDir, ess.Excludes(excludes), noGzipList); err != nil { - logFatal(err) + if mode { + // Default mount point + if err := processMount(mode, appBaseDir, "/app", appBaseDir, ess.Excludes(excludes), noGzipList); err != nil { + logFatal(err) + } } // Custom mount points diff --git a/aah/compile.go b/aah/compile.go index 77add4d..8946225 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -278,6 +278,7 @@ func appSecurity(appCfg *config.Config, appImportPaths map[string]string) map[st isAuthSchemeCfg := false authSchemeInfo := struct { Authenticator string + Principal string Authorizer string }{} @@ -289,6 +290,14 @@ func appSecurity(appCfg *config.Config, appImportPaths map[string]string) map[st isAuthSchemeCfg = true } + // Principal Provider + principal := appCfg.StringDefault(keyPrefixAuthSchemeCfg+".principal", "") + if !ess.IsStrEmpty(principal) { + authSchemeInfo.Principal = prepareAuthAlias( + keyAuthScheme+"sec", principal, importPathPrefix, appImportPaths) + isAuthSchemeCfg = true + } + // Authorizer authorizer := appCfg.StringDefault(keyPrefixAuthSchemeCfg+".authorizer", "") if !ess.IsStrEmpty(authorizer) { @@ -352,7 +361,9 @@ import ( "aahframework.org/aruntime.v0" "aahframework.org/config.v0" "aahframework.org/essentials.v0" - "aahframework.org/log.v0"{{ range $k, $v := $.AppImportPaths }} + "aahframework.org/log.v0"{{ if .AppSecurity }} + "aahframework.org/security.v0/authc" + "aahframework.org/security.v0/authz"{{ end }}{{ range $k, $v := $.AppImportPaths }} {{ $v }} "{{ $k }}"{{ end }} ) @@ -493,21 +504,39 @@ func main() { {{ end -}} {{ if .AppSecurity }} - // Initialize application security auth schemes - Authenticator & Authorizer - secMgr := aah.AppSecurityManager() - {{- range $k, $v := $.AppSecurity }} - {{ if $v.Authenticator -}} - aah.AppLog().Debugf("Calling authenticator Init for auth scheme '%s'", "{{ $k }}") - if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { - aah.AppLog().Fatal(err) + type setprincipal interface { + SetPrincipalProvider(principal authc.PrincipalProvider) error } - {{ end -}} - {{ if $v.Authorizer -}} - aah.AppLog().Debugf("Calling authorizer Init for auth scheme '%s'", "{{ $k }}") - if err := secMgr.GetAuthScheme("{{ $k }}").SetAuthorizer(&{{ $v.Authorizer }}{}); err != nil { - aah.AppLog().Fatal(err) + type setauthenticator interface { + SetAuthenticator(authenticator authc.Authenticator) error } - {{ end -}} + type setauthorizer interface { + SetAuthorizer(authorizer authz.Authorizer) error + } + + // Initialize application security auth schemes - Authenticator, + // PrincipalProvider & Authorizer + secMgr := aah.AppSecurityManager() + {{- range $k, $v := $.AppSecurity }} + authScheme := secMgr.AuthScheme("{{ $k }}") + {{ if $v.Authenticator }}if sauthc, ok := authScheme.(setauthenticator); ok { + aah.AppLog().Debugf("Initializing authenticator for auth scheme '%s'", "{{ $k }}") + if err := sauthc.SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { + aah.AppLog().Fatal(err) + } + }{{ end }} + {{ if $v.Principal -}}if sprincipal, ok := authScheme.(setprincipal); ok { + aah.AppLog().Debugf("Initializing principalprovider for auth scheme '%s'", "{{ $k }}") + if err := sprincipal.SetPrincipalProvider(&{{ $v.Principal }}{}); err != nil { + aah.AppLog().Fatal(err) + } + }{{ end }} + {{ if $v.Authorizer }}if sauthz, ok := authScheme.(setauthorizer); ok { + aah.AppLog().Debugf("Initializing authorizer for auth scheme '%s'", "{{ $k }}") + if err := sauthz.SetAuthorizer(&{{ $v.Authorizer }}{}); err != nil { + aah.AppLog().Fatal(err) + } + }{{ end }} {{ end -}} {{ end }} diff --git a/aah/embed.go b/aah/embed.go index 7a1a0ab..424b47a 100644 --- a/aah/embed.go +++ b/aah/embed.go @@ -241,7 +241,7 @@ func init() { {{ if .Mode }}aah.AppVFS().SetEmbeddedMode(){{ end }} if err := aah.AppVFS().AddMount("{{ .MountPath }}", "{{ .PhysicalPath }}"); err != nil { - log.Fatal(err) + log.Fatal("vfs: ", err) } {{ end }} @@ -250,7 +250,7 @@ func init() { // Find Mount point m, err := aah.AppVFS().FindMount("{{ .MountPath }}") if err != nil { - log.Fatal(err) + log.Fatal("vfs: ", err) } // Adding directories into VFS diff --git a/aah/migrate.go b/aah/migrate.go index b28de7c..c18f4cb 100644 --- a/aah/migrate.go +++ b/aah/migrate.go @@ -39,13 +39,13 @@ var migrateCmd = cli.Command{ cli.Command{ Name: "code", Aliases: []string{"c"}, - Usage: "Migrates Go source code by making it compatible with current version of aah", + Usage: "Migrates application codebase by making it compatible with current version of aah", Description: `Command code is to fix/upgrade aah's breaking changes and deprecated elements - in Go source file to the current version of aah. + in application codebase to the current version of aah. The goal of 'Code' command is to keep aah users always up-to-date with latest version of aah. - Note: It does not take file backup, assumes aah application uses version control. + Note: Migrate does not take file backup, assumes application use version control. Example of script command: aah m c -i github.com/user/appname @@ -62,8 +62,6 @@ var migrateCmd = cli.Command{ }, } -// TODO use golang.org/x/tools/imports - func migrateCodeAction(c *cli.Context) error { importPath := appImportPath(c) if err := aah.Init(importPath); err != nil { @@ -72,24 +70,42 @@ func migrateCodeAction(c *cli.Context) error { projectCfg := aahProjectCfg(aah.AppBaseDir()) cliLog = initCLILogger(projectCfg) + + cliLog.Warn("Migrate command does not take file backup. It assumes application use version control.") + if !collectYesOrNo(reader, "Would you like to continue ([Y]es or [N]o)? default is 'N'") { + cliLog.Info("Okay, I respect your choice. Bye.") + return nil + } + grammarFile := filepath.Join(aahToolsPath().Dir, aahGrammarIdentifier) grammarCfg, err := config.LoadFile(grammarFile) if err != nil { logFatal(err) } - cliLog.Info("Note:") + cliLog.Info("\nNote:") cliLog.Info("-----") - cliLog.Info("Command operates based on grammer file. If you identify a new grammar entry, \n" + - "create an issue here https://aahframework.org/issues, to include in the grammar file.\n") + cliLog.Info("Command works based on grammer file. If you identify a new grammar entry, \n" + + "create an issue at https://aahframework.org/issues to let me know.\n") cliLog.Infof("Loaded grammar file: %s", grammarFile) cliLog.Infof("Loaded aah project file: %s", filepath.Join(aah.AppBaseDir(), aahProjectIdentifier)) cliLog.Infof("Migrate starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) // Go Source files + cliLog.Infof("Go source code migrate starts ...") if migrateGoSrcFiles(projectCfg, grammarCfg) == 0 { - cliLog.Info("It seems application codebase 'app/**' is up-to-date") + cliLog.Info(" It seems application Go source code are up-to-date") + } + cliLog.Infof("Go source code migrate successful") + + if ess.IsFileExists(filepath.Join(aah.AppBaseDir(), "views")) { + // View files + cliLog.Infof("View file migrate starts ...") + if migrateViewFiles(projectCfg, grammarCfg) == 0 { + cliLog.Info(" It seems application view files are up-to-date") + } + cliLog.Infof("View file migrate successful") } cliLog.Infof("Migrate successful for '%s' [%s]\n", aah.AppName(), aah.AppImportPath()) @@ -97,51 +113,90 @@ func migrateCodeAction(c *cli.Context) error { } func migrateGoSrcFiles(projectCfg, grammarCfg *config.Config) int { + count := 0 grammar, found := grammarCfg.StringList("file.go.upgrade_replacer") if !found { - logFatalf("Config 'file.go.upgrades_replacer' not found in grammar file") + cliLog.Info("Config 'file.go.upgrades_replacer' not found in the grammar file") + return count } fixer := strings.NewReplacer(grammar...) excludes, _ := projectCfg.StringList("build.ast_excludes") files, _ := ess.FilesPathExcludes(filepath.Join(aah.AppBaseDir(), "app"), true, ess.Excludes(excludes)) - count := 0 for _, f := range files { - df := strings.TrimPrefix(filepath.ToSlash(stripGoSrcPath(f)), aah.AppImportPath()+"/") - fileBytes, err := ioutil.ReadFile(f) - if err != nil { - logError(err) - cliLog.Infof(" |-- skipped: %s", df) + if filepath.Ext(f) != ".go" { continue } - - modFileBytes := []byte(fixer.Replace(string(fileBytes))) - if bytes.Equal(fileBytes, modFileBytes) { - // not modified + if !migrateFile(f, fixer) { continue } + count++ + } - // file modified - fmtFileBytes, err := format.Source(modFileBytes) - if err != nil { - logErrorf("While formating: %s", err) - cliLog.Infof(" |-- skipped: %s", df) + return count +} + +func migrateViewFiles(projectCfg, grammarCfg *config.Config) int { + count := 0 + grammar, found := grammarCfg.StringList("file.view.upgrade_replacer") + if !found { + cliLog.Info("Config 'file.view.upgrades_replacer' not found in the grammar file") + return count + } + + fixer := strings.NewReplacer(grammar...) + files, _ := ess.FilesPath(filepath.Join(aah.AppBaseDir(), "views"), true) + fileExt := aah.AppConfig().StringDefault("view.ext", ".html") + for _, f := range files { + if filepath.Ext(f) != fileExt { continue } - - if err = os.Truncate(f, 0); err != nil { - logErrorf("While truncate: %s", err) - cliLog.Infof(" |-- skipped: %s", df) + if !migrateFile(f, fixer) { continue } + count++ + } - if err = ioutil.WriteFile(f, []byte(fmtFileBytes), permRWRWRW); err != nil { - logError(err) + return count +} + +func migrateFile(f string, fixer *strings.Replacer) bool { + df := strings.TrimPrefix(filepath.ToSlash(stripGoSrcPath(f)), aah.AppImportPath()+"/") + fileBytes, err := ioutil.ReadFile(f) + if err != nil { + logError(err) + cliLog.Infof(" |-- skipped: %s", df) + return false + } + + modFileBytes := []byte(fixer.Replace(string(fileBytes))) + if bytes.Equal(fileBytes, modFileBytes) { + // not modified + return false + } + + if filepath.Ext(f) == ".go" { + // format go src file + var err error + if modFileBytes, err = format.Source(modFileBytes); err != nil { + logErrorf("While formating: %s", err) + cliLog.Infof(" |-- skipped: %s", df) + return false } + } + + if err = os.Truncate(f, 0); err != nil { + logErrorf("While truncate: %s", err) + cliLog.Infof(" |-- skipped: %s", df) + return false + } + if err = ioutil.WriteFile(f, modFileBytes, permRWRWRW); err != nil { + logError(err) + cliLog.Infof(" |-- [ERROR] processed: %s", df) + } else { cliLog.Infof(" |-- processed: %s", df) - count++ } - return count + return true } diff --git a/aah/migrate.grammar b/aah/migrate.grammar index d0a523b..5e90357 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -15,6 +15,7 @@ file { "aah.AddServerTLSConfig(", "aah.SetTLSConfig(", "aah.EventOnAfterReply", "aah.EventOnPostReply", "aah.OnShutdown(", "aah.OnPostShutdown(", + "// Event: OnShutdown", "// Event: OnPostShutdown", "aah.OnRequest(", "aah.AppHTTPEngine().OnRequest(", "aah.OnPreReply(", "aah.AppHTTPEngine().OnPreReply(", "aah.OnPostReply(", "aah.AppHTTPEngine().OnPostReply(", @@ -24,6 +25,7 @@ file { "aah.Middlewares(", "aah.AppHTTPEngine().Middlewares(", ".SubscribeEventf(", ".SubscribeEventFunc(", ".UnsubscribeEventf(", ".UnsubscribeEventFunc(", + "aah.KeyViewArgRequestParams", "aah.KeyViewArgRequest", "ahttp.GetResponseWriter(", "ahttp.AcquireResponseWriter(", "ahttp.PutResponseWriter(", "ahttp.ReleaseResponseWriter(", "ahttp.GetGzipResponseWriter(", "ahttp.WrapGzipWriter(", @@ -36,7 +38,19 @@ file { "Req.Locale", "Req.Locale()", ".Readfrom(", ".FromReader(", ".RedirectSts(", ".RedirectWithStatus(", - ".FindDomain(", ".Lookup(" + ".FindDomain(", ".Lookup(", + ".GetAuthScheme(", ".AuthScheme(" + ] + } + + # Grammar for View files + # Each line have "from" and "to" replacement + # + # Format: "oldsignature", "newsignature" + view { + upgrade_replacer = [ + "{{ anitcsrftoken . }}", "{{ anticsrftoken . }}", + "name=\"anit_csrf_token\"", "name=\"anti_csrf_token\"" ] } diff --git a/aah/new.go b/aah/new.go index 1880ea5..5760754 100644 --- a/aah/new.go +++ b/aah/new.go @@ -157,7 +157,7 @@ func collectInputsForWebApp(app *appTmplData) { // In the web application user may like to have API also WebSocket within it. collectAppSubTypesChoice(reader, app) - app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o), default is 'N'") + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o)? default is 'N'") } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ @@ -173,20 +173,20 @@ func collectInputsForAPIApp(app *appTmplData) { passwordHashAlgorithm(reader, app) - app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o), default is 'N'") + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o)? default is 'N'") } func collectAppSubTypesChoice(reader *bufio.Reader, app *appTmplData) { app.SubTypes = make([]string, 0) // API choice - choice := collectYesOrNo(reader, "Would you like to add API [/api/v1/*] within your Web App ([Y]es or [N]o), default is 'N'") + choice := collectYesOrNo(reader, "Would you like to add API [/api/v1/*] within your Web App ([Y]es or [N]o)? default is 'N'") if choice { app.SubTypes = append(app.SubTypes, typeAPI) } // WebSocket choice - choice = collectYesOrNo(reader, "Would you like to add WebSocket [/ws/*] within your Web App ([Y]es or [N]o), default is 'N'") + choice = collectYesOrNo(reader, "Would you like to add WebSocket [/ws/*] within your Web App ([Y]es or [N]o)? default is 'N'") if choice { app.SubTypes = append(app.SubTypes, typeWebSocket) } From 889c8eb6c217f799038ac6cb89876777de4d143e Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 8 Jun 2018 15:47:40 -0700 Subject: [PATCH 36/48] aah main file updates --- aah/app_tmpl.go | 5 +++++ aah/compile.go | 10 +++++----- aah/util.go | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/aah/app_tmpl.go b/aah/app_tmpl.go index a7fa4ce..f40e928 100644 --- a/aah/app_tmpl.go +++ b/aah/app_tmpl.go @@ -94,8 +94,13 @@ func (a *appTmplData) checkSubType(t string) bool { // Template funcs //______________________________________________________________________________ +var vreplace = strings.NewReplacer("_auth", "", "auth_", "") + var appTemplateFuncs = template.FuncMap{ "securerandomstring": func(length int) string { return ess.SecureRandomString(length) }, + "variablename": func(v string) string { + return toLowerCamelCase(vreplace.Replace(v)) + }, } diff --git a/aah/compile.go b/aah/compile.go index 8946225..ba44cf2 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -517,21 +517,21 @@ func main() { // Initialize application security auth schemes - Authenticator, // PrincipalProvider & Authorizer secMgr := aah.AppSecurityManager() - {{- range $k, $v := $.AppSecurity }} - authScheme := secMgr.AuthScheme("{{ $k }}") - {{ if $v.Authenticator }}if sauthc, ok := authScheme.(setauthenticator); ok { + {{- range $k, $v := $.AppSecurity }}{{ $vPrefix := (variablename $k) }} + {{ $vPrefix }}AuthScheme := secMgr.AuthScheme("{{ $k }}") + {{- if $v.Authenticator }}if sauthc, ok := {{ $vPrefix }}AuthScheme.(setauthenticator); ok { aah.AppLog().Debugf("Initializing authenticator for auth scheme '%s'", "{{ $k }}") if err := sauthc.SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { aah.AppLog().Fatal(err) } }{{ end }} - {{ if $v.Principal -}}if sprincipal, ok := authScheme.(setprincipal); ok { + {{ if $v.Principal -}}if sprincipal, ok := {{ $vPrefix }}AuthScheme.(setprincipal); ok { aah.AppLog().Debugf("Initializing principalprovider for auth scheme '%s'", "{{ $k }}") if err := sprincipal.SetPrincipalProvider(&{{ $v.Principal }}{}); err != nil { aah.AppLog().Fatal(err) } }{{ end }} - {{ if $v.Authorizer }}if sauthz, ok := authScheme.(setauthorizer); ok { + {{ if $v.Authorizer }}if sauthz, ok := {{ $vPrefix }}AuthScheme.(setauthorizer); ok { aah.AppLog().Debugf("Initializing authorizer for auth scheme '%s'", "{{ $k }}") if err := sauthz.SetAuthorizer(&{{ $v.Authorizer }}{}); err != nil { aah.AppLog().Fatal(err) diff --git a/aah/util.go b/aah/util.go index caa6ce6..5d34525 100644 --- a/aah/util.go +++ b/aah/util.go @@ -517,3 +517,17 @@ func cleanupAutoGenVFSFiles(appBaseDir string) { ess.DeleteFiles(vfsFiles...) } } + +func toLowerCamelCase(v string) string { + var st []byte + for idx := 0; idx < len(v); idx++ { + c := v[idx] + if c == '_' || c == ' ' { + idx++ + st = append(st, []byte(strings.ToUpper(string(v[idx])))...) + } else { + st = append(st, c) + } + } + return string(st) +} From b40a493e161966d1d0a8bbc4765602fe6a8e8658 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 15 Jun 2018 10:21:01 -0700 Subject: [PATCH 37/48] app template update and improvement --- aah/app-template/config/aah.conf.atmpl | 56 +-- aah/app-template/config/security.conf.atmpl | 476 +++----------------- aah/app_tmpl.go | 13 + aah/compile.go | 2 +- aah/run.go | 8 +- 5 files changed, 81 insertions(+), 474 deletions(-) diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index 63aa871..dc85e5d 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -1,8 +1,7 @@ # ----------------------------------------------------------------------------- -# {{ .App.Name }} - aah Application +# {{ .App.Name }} - aah Application Configuration # -# NOTE: Since v0.11.0, app config file has minimal configuration. -# To explore all the supported configurations, refer documentation. +# Refer documentation to explore and customize the configurations. # # Doc: https://docs.aahframework.org/app-config.html # ----------------------------------------------------------------------------- @@ -44,18 +43,9 @@ server { # Doc: https://docs.aahframework.org/websocket.html#configuration # -------------------------------------------------------------------------- websocket { - # Default value is `false`. enable = true - - # Origin configuration origin { - # When origin check is enabled, aah does check against HTTP Header - # `Origin` value. - # Default value is `false`. check = false - - # Origin whitelist - # No default values. #whitelist = [ # "https://websocket.org", #] @@ -63,48 +53,37 @@ server { }{{ end }} } -# ------------------------------------------------------------------ +# ----------------------------------------------------------------------------- # Request configuration # Doc: https://docs.aahframework.org/app-config.html#section-request -# ------------------------------------------------------------------ +# ----------------------------------------------------------------------------- request { # ----------------------------------------------------------------------------- # aah framework encourages to have a unique `Request Id` for each incoming # request. It helps in traceability. If a request already has `X-Request-Id` # HTTP header, then aah does not generate one. # - # GUID is generated using MangoDB ObjectId algorithm. + # GUID is generated using MongoDB ObjectId algorithm. # # Doc: https://docs.aahframework.org/app-config.html#section-request-id # ----------------------------------------------------------------------------- id { - # To enable/disable request ID generation. - # Default value is `true`. enable = true - - # HTTP header name for Request ID. If a request already has HTTP header, then - # aah does not generate one. - # - # Default value is `X-Request-Id`, customize it per use case. - #header = "X-Request-Id" } }{{ if not .App.IsWebSocketApp }} -# ----------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Render configuration # Doc: https://docs.aahframework.org/app-config.html#section-render -# ----------------------------------------------------------------- +# ----------------------------------------------------------------------------- render { - # aah infers the `Content-Type` value automatically - # if `Reply()` builder does not have value. + # aah infers the `Content-Type` automatically, if `Reply()` builder does not have value. # # It infers in the order of: # - URL file extension - supports `.html`, `.htm`, `.json`, `.js`, `.xml` # and `.txt` - # # - Request Accept Header - Most Qualified one as per RFC 7321 # * Supports vendor type as per RFC 4288 - # # - Fallback to `render.default` value - supports `html`, `json`, `xml` and `text` # # Default value is `empty` string. @@ -122,8 +101,7 @@ cache { # Doc: https://docs.aahframework.org/static-files.html#http-cache-control # ----------------------------------------------------------------------------- static { - # Default `Cache-Control` for all static files, - # if specific mime type is not defined. + # Default `Cache-Control` for all static files, if specific is not defined. default_cache_control = "public, max-age=31536000" # Define by mime types, if mime is not present then default is applied. @@ -145,24 +123,14 @@ cache { } } -# --------------------------------------------------------------- +# ----------------------------------------------------------------------------- # View configuration # Doc: https://docs.aahframework.org/app-config.html#section-view -# --------------------------------------------------------------- +# ----------------------------------------------------------------------------- view { - # Application view engine. - # Default value is `go`. engine = "{{ .App.ViewEngine }}" - - # View file extension. - # - # Default value is chosen based on `view.engine` while creating a new app - # via command `aah new`. ext = "{{ .App.ViewFileExt }}" - - # To use custom Go template delimiters for view files. - # Default value is `{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}`. - #delimiters = "{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}" + delimiters = "{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}" }{{ end }} # ----------------------------------------------------------------------------- diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index d198ba3..2144df4 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -1,503 +1,151 @@ -# --------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # {{ .App.Name }} - Application Security Configuration # -# Complete routes configuration reference: +# Refer documentation to explore and customize the configurations. +# # Doc: https://docs.aahframework.org/security-config.html -# --------------------------------------------------------------------------- - +# ----------------------------------------------------------------------------- security { - # ------------------------------------------------------- - # Authentication & Authorization configuration - # - # Doc: https://docs.aahframework.org/security-design.html - # ------------------------------------------------------- - auth_schemes { - {{ if eq .App.AuthScheme "form" -}} - # HTTP Form Auth Scheme - # It is custom defined name, this is used in the routes `auth` attribute. + + # ----------------------------------------------------------------------------- + # Auth Schemes configuration + # Doc: https://docs.aahframework.org/authentication.html + # ----------------------------------------------------------------------------- + auth_schemes { {{ if (isauth . "form") }} + + # ----------------------------------------------------------------------------- + # Form auth scheme + # Choose unique key name, it gets used as route auth. + # + # Doc: https://docs.aahframework.org/auth-schemes/form.html + # ----------------------------------------------------------------------------- form_auth { - # Auth scheme name. - # Currently supported values are `form`, `basic` and `generic`. - # - # It is required value, no default. scheme = "form" - - # aah calls `Authenticator` to get the Subject's authentication - # information. Then aah validates the credential using password - # encoder. - # - # It is required value, no default. authenticator = "security/AuthenticationProvider" - - # aah calls `Authorizer` only after successful authentication; to get - # Subject's authorization information such as Roles, Permissions. - # - # It is required value, no default. authorizer = "security/AuthorizationProvider" - # Password encoder is used to encode the given credential and then compares - # it with application provided credential. - # + # Password encoder # Doc: https://docs.aahframework.org/password-encoders.html - # - # Default value is `bcrypt`. password_encoder = "{{ .App.PasswordEncoderAlgo }}" + }{{ end }} {{ if (isauth . "basic") }} - # Field names are used to extract `AuthenticationToken` from the HTTP request. - field { - # Default value is `username` - #identity = "username" - - # Default value is `password` - #credential = "password" - } - - # URLs is used when appropriate. - url { - # Login page URL, implement app login page and configure here. - # - # Default value is `/login.html`. - #login = "/login.html" - - # Login submit URL, used to submit login credential for authentication. - # Basically login form submit values to this URL as POST request. - # - # This URL can be accessed via `{{ .App.TmplDelimLeft }} rurl . aah_login_submit {{ .App.TmplDelimRight }}` on view file. - # - # Default value is `/login`. - #login_submit = "/login" - - # Login failure, any failure during authentication process. aah - # sends user to this URL and logs detailed information in the log. - #login_failure = "/login.html?error=true" - - # Default page URL after the successful authentication. By default aah - # redirects the user to requested page after authentication. Otherwise it - # sends to this URL. - # - # Default value is `/`. - #default_target = "/" - - # Always redirect to default URL, regardless of the requested page/URL. - # - # Default value is `false`. - #always_to_default = false - } - }{{ end -}} - - {{ if eq .App.AuthScheme "basic" -}} - # HTTP Basic Auth Scheme - # It is custom defined name, this is used in the routes `auth` attribute. + # ----------------------------------------------------------------------------- + # Basic auth scheme + # Choose unique key name, it gets used as route auth. + # + # Doc: https://docs.aahframework.org/auth-schemes/basic.html + # ----------------------------------------------------------------------------- basic_auth { - # Auth scheme name. - # Currently supported values are `form`, `basic` and `generic`. - # It is required value, no default. scheme = "basic" - - # Realm name is used for `Www-Authenticate` HTTP header. - # - # Note: Modern browsers are not respecting this values now a days. - # However aah framework does its due diligence. - realm_name = "Protected" - - {{ if eq .App.BasicAuthMode "file-realm" }}# Basic auth realm file path. Use absolute path or - # environment variable to provide path. - # - # It is required value, no default. + realm_name = "Protected" {{ if eq .App.BasicAuthMode "file-realm" }} file_realm = "{{ .App.BasicAuthFileRealmPath }}"{{ else }} - # aah calls `Authenticator` to get the Subject's authentication - # information. Then framework validates the credential using password - # encoder. - # - # It is required value when `file_realm` attribute is not configured, - #no default. authenticator = "security/AuthenticationProvider" - - # aah calls `Authorizer` only after successful authentication to get - # Subject's authorization information such as Roles and Permissions. - # - # It is required value when `file_realm` attribute is not configured, - # no default. authorizer = "security/AuthorizationProvider"{{ end }} - # Password encoder is used to encode the given credential and then compares - # it with application provide credential. - # + # Password encoder # Doc: https://docs.aahframework.org/password-encoders.html - # - # Default value is `bcrypt`. password_encoder = "{{ .App.PasswordEncoderAlgo }}" - } + }{{ end }} {{ if (isauth . "generic") }} - {{ end }}{{ if eq .App.AuthScheme "generic" }}# Generic Auth Scheme - # It is custom defined name, this is used in the routes `auth` attribute. + # ----------------------------------------------------------------------------- + # Generic auth scheme + # Choose unique key name, it gets used as route auth. + # + # Doc: https://docs.aahframework.org/auth-schemes/generic.html + # ----------------------------------------------------------------------------- generic_auth { - # Auth scheme name. - # Currently supported values are `form`, `basic` and `generic`. - # It is required value, no default. scheme = "generic" - - # aah calls `Authenticator` to get the Subject's authentication - # information. The credential validation is not done by framework. - # It is interface implementation responsibility. - # - # It is required value, no default. authenticator = "security/AuthenticationProvider" - - # aah calls `Authorizer` only after successful authentication to get - # Subject's authorization information such as Roles and Permissions. - # - # It is required value, no default. authorizer = "security/AuthorizationProvider" - - # Header names are used to extract `AuthenticationToken` from the HTTP request. - header { - # Default value is 'Authorization' - #identity = "Authorization" - - # Optional credential header - # Typically it's not used, however in the industry people do use it. - # - # Default value is empty string. - #credential = "X-AuthPass" - } }{{ end }} } - # ------------------------------------------------------------ + # ----------------------------------------------------------------------------- # Password Encoders Configuration - # aah supports `bcrypt`, `scrypt`, `pbkdf2` password algorithm + # aah supports `bcrypt`, `scrypt`, `pbkdf2` password algorithm. # # Doc: https://docs.aahframework.org/password-encoders.html - # ------------------------------------------------------------ + # ----------------------------------------------------------------------------- password_encoder { {{ if eq .App.PasswordEncoderAlgo "bcrypt" }} - # bcrypt algorithm - # - # Learn more: - # https://crackstation.net/hashing-security.htm - # https://security.stackexchange.com/a/6415 - # https://en.wikipedia.org/wiki/Bcrypt + bcrypt { - # Default value is `true` enable = true - - # https://godoc.org/golang.org/x/crypto/bcrypt#pkg-constants - # Default value is `12`. cost = 12 - }{{ end }} - {{ if eq .App.PasswordEncoderAlgo "scrypt" }} - # scrypt algorithm - # - # Learn more: - # https://crackstation.net/hashing-security.htm - # https://pthree.org/2016/06/28/lets-talk-password-hashing/ - # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet - # Default values are chosen carefully to provide secure password. + }{{ end }} {{ if eq .App.PasswordEncoderAlgo "scrypt" }} + scrypt { - # Default value is `false` enable = true + }{{ end }} {{ if eq .App.PasswordEncoderAlgo "pbkdf2" }} - # CPU/Memory Cost - # Default value is `2^15` - #cpu_memory_cost = 32768 - - # Default value is `8` - #block_size = 8 - - # Default value is `1` - #parallelization = 1 - - # Default value is `32` - #derived_key_length = 32 - - # Default value is `24` - #salt_length = 24 - }{{ end }} - {{ if eq .App.PasswordEncoderAlgo "pbkdf2" }} - # pbkdf2 algorithm - # - # Learn more: - # https://crackstation.net/hashing-security.htm - # https://cryptosense.com/parameter-choice-for-pbkdf2/ - # https://pthree.org/2016/06/28/lets-talk-password-hashing/ - # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet - # Default values are chosen carefully to provide secure password. pbkdf2 { - # Default value is `false` enable = true - - # Default value is `10000` - #iteration = 10000 - - # Default value is `32` - #derived_key_length = 32 - - # Default value is `24` - #salt_length = 24 - - # Supported SHA's are `sha-1`, `sha-224`, `sha-256`, `sha-384`, `sha-512`. - # Default value is `sha-512` - #hash_algorithm = "sha-512" }{{ end }} - } + } {{ if .App.IsWebApp }}{{ if .App.IsSessionConfigRequired }} - {{ if .App.IsWebApp }}{{ if or (eq .App.AuthScheme "form") (eq .App.AuthScheme "basic") -}} - # ----------------------------------------------------------------------- + # ----------------------------------------------------------------------------- # Session configuration # HTTP state management across multiple requests. # # Doc: https://docs.aahframework.org/security-config.html#section-session - # ----------------------------------------------------------------------- + # ----------------------------------------------------------------------------- session { - # Session mode to choose whether HTTP session should be persisted or - # destroyed at the end of the request. Supported values are `stateless` - # and `stateful`. - # Default value is `stateless` for API and `stateful` for Web app. mode = "{{ if eq .App.AuthScheme "" }}stateless{{ else }}stateful{{ end }}" - - # Session store is to choose where session value should be persisted. store { - # Currently aah framework supports `cookie` and `file` as store type. - # Also framework provide extensible `session.Storer` interface to - # add custom session store. - # Default value is `cookie`. - type = "{{ .App.SessionStore }}" - {{ if eq .App.SessionStore "file" -}} - # Filepath is used for file store to store session file in the file system. - # This is only applicable for `type = "file"`, make sure application has - # Read/Write access to the directory. Provide absolute path. + type = "{{ .App.SessionStore }}"{{ if eq .App.SessionStore "file" }} filepath = "{{ .App.SessionFileStorePath }}"{{ end }} } - - # Session ID length - # Default value is `32`. - #id_length = 32 - - # Time-to-live for session data. Valid time units are "m = minutes", - # "h = hours" and 0. - # Default value is `0`, cookie is deleted when the browser is closed. - #ttl = "0" - - # Session cookie name prefix. - # Default value is `aah_` For e.g.: `aah_myapp_session` prefix = "aah_{{ .App.Name }}" - - # Default value is `empty` string. - #domain = "" - - # Default value is `/`. - #path = "/" - - # HTTP session cookie HTTPOnly value. This option prevents XSS - # (Cross Site Scripting) attacks. - # Default value is `true`. - #http_only = true - - # HTTP session cookie secure value. - # However if aah server is not configured with SSL then - # framework sets this value as false - # Default value is `true`. - #secure = true - - # HTTP session cookie value signing using `HMAC`. For server farm this - # should be same in all instance. For HMAC sign & verify it recommend to use - # key size is `32` or `64` bytes. - # Default value is `64` bytes (`aah new` generates strong one). sign_key = "{{ securerandomstring 64 }}" - - # HTTP session cookie value encryption and decryption using `AES`. For server - # farm this should be same in all instance. AES algorithm is used, valid - # lengths are `16`, `24`, or `32` bytes to select `AES-128`, `AES-192`, or `AES-256`. - # Default value is `32` bytes (`aah new` generates strong one). enc_key = "{{ securerandomstring 32 }}" + }{{ end }}{{ end }} {{ if .App.IsWebApp }} - # Cleanup Interval is used to clean the expired session objects from store. - # This is only applicable for non-cookie store type. - # Cleanup performed in dedicated goroutine. Valid time units are - # `m -> minutes`, `h -> hours`. - # Default value is `30m`. - #cleanup_interval = "30m" - }{{ end }}{{ end }} - - {{ if .App.IsWebApp }} - # ------------------------------------------------------------ - # Anti-CSRF Protection + # ----------------------------------------------------------------------------- + # Anti-CSRF (Cross Site Request Forgery protection) # # Doc: https://docs.aahframework.org/anti-csrf-protection.html - # ------------------------------------------------------------ + # ----------------------------------------------------------------------------- anti_csrf { - # Enabling Anti-CSRF Protection. - # Default value is `true`. - #enable = true - - # Anti-CSRF secret length - # Default value is `32`. - #secret_length = 32 - - # HTTP Header name for cipher token - # Default value is `X-Anti-CSRF-Token`. - #header_name = "X-Anti-CSRF-Token" - - # Form field name for cipher token - # Default value is `anti_csrf_token`. - #form_field_name = "anti_csrf_token" - - #Anti-CSRF secure cookie prefix - # Default value is `aah`. Cookie name would be `aah_anti_csrf`. - #prefix = "aah" - - # Default value is `empty` string. - #domain = "" - - # Default value is `/`. - #path = "/" - - # Time-to-live for Anti-CSRF secret. Valid time units are "m = minutes", - # "h = hours" and 0. - # Default value is `24h`. - #ttl = "24h" - - # Anti-CSRF cookie value signing using `HMAC`. For server farm this - # should be same in all instance. For HMAC sign & verify it recommend to use - # key size is `32` or `64` bytes. - # Default value is `64` bytes (`aah new` generates strong one). + enable = true sign_key = "{{ securerandomstring 64 }}" - - # Anti-CSRF cookie value encryption and decryption using `AES`. For server - # farm this should be same in all instance. AES algorithm is used, valid - # lengths are `16`, `24`, or `32` bytes to select `AES-128`, `AES-192`, or `AES-256`. - # Default value is `32` bytes (`aah new` generates strong one). enc_key = "{{ securerandomstring 32 }}" }{{ end }} # --------------------------------------------------------------------------- # HTTP Secure Header(s) # Application security headers with many safe defaults. + # # Doc: https://docs.aahframework.org/security-config.html#section-http-header # # Tip: Quick way to verify secure headers - https://securityheaders.io # --------------------------------------------------------------------------- http_header { - # Enabling HTTP secure headers. - # Default value is `true`. enable = true # X-XSS-Protection - # Designed to enable the cross-site scripting (XSS) filter built into modern - # web browsers. This is usually enabled by default, but using this header - # will enforce it. - # - # Learn more: - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xxxsp - # https://www.keycdn.com/blog/x-xss-protection/ - # - # Encouraged to make use of header `Content-Security-Policy` with enhanced - # policy to reduce XSS risk along with header `X-XSS-Protection`. - # Default values is `1; mode=block`. {{ if .App.IsWebApp -}}#xxssp = "1; mode=block"{{ else }}xxssp = ""{{ end }} # X-Content-Type-Options - # Prevent Content Sniffing or MIME sniffing. - # - # Learn more: - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xcto - # https://en.wikipedia.org/wiki/Content_sniffing - # Default value is `nosniff`. #xcto = "nosniff" # X-Frame-Options - # Prevents Clickjacking. - # - # Learn more: - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xfo - # https://www.keycdn.com/blog/x-frame-options/ - # Default value is `SAMEORIGIN`. {{ if .App.IsWebApp -}}#xfo = "SAMEORIGIN"{{ else }}xfo = "DENY"{{ end }} # Referrer-Policy - # This header governs which referrer information, sent in the Referer header, should - # be included with requests made. - # Referrer Policy has been a W3C Candidate Recommendation since 26 January 2017. - # - # Learn more: - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - # https://scotthelme.co.uk/a-new-security-header-referrer-policy/ - # https://www.w3.org/TR/referrer-policy/ - # Default value is `no-referrer-when-downgrade`. {{ if .App.IsWebApp -}}#rp = "no-referrer-when-downgrade"{{ else }}rp = ""{{ end }} # Strict-Transport-Security (STS, aka HSTS) - # STS header that lets a web site tell browsers that it should only be communicated - # with using HTTPS, instead of using HTTP. - # - # Learn more: - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#hsts - # https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security - # - # Note: Framework checks that application uses SSL on startup then applies - # this header. Otherwise it does not apply. sts { - # The time, in seconds, that the browser should remember that this site - # is only to be accessed using HTTPS. Valid time units are - # "s -> seconds", "m -> minutes", "h - hours". - # Default value is `30 days` in hours. #max_age = "720h" - - # If enabled the STS rule applies to all of the site's subdomains as well. - # Default value is `false`. - #include_subdomains = true - - # Before enabling preload option, please read about pros and cons from above links. - # Default value is `false`. - #preload = false } # Content-Security-Policy (CSP) - # Provides a rich set of policy directives that enable fairly granular control - # over the resources that a page is allowed. Prevents XSS risks. - # - # Learn more: - # https://content-security-policy.com/ - # https://developers.google.com/web/fundamentals/security/csp/ - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#csp - # - # Read above references and define app policy. - # - # Note: It is highly recommended to verify policy directives in report - # only mode before enabling this header. Since its highly controls how - # page is rendered. - # - # No default values, you have to provide it. csp { - # Set of directives to govern the resources load on a page. #directives = "" - - # By default, violation reports aren't sent. To enable violation reporting, - # you need to specify the report-uri policy directive. report_uri = "" - - # By default aah puts `Content-Security-Policy` in report only mode, - # so that could be verified before making it live mode - # (set `csp_report_only` to false). - # - # Don't forget to set the `report-uri` for validation. report_only = true } # Public-Key-Pins PKP (aka HPKP) - # This header prevents the Man-in-the-Middle Attack (MITM) with forged certificates. - # - # Learn more: - # https://scotthelme.co.uk/hpkp-http-public-key-pinning/ - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning - # Read above references and define app keys. - # - # Note: - # - HPKP has the potential to lock out users for a long time if used incorrectly! - # The use of backup certificates and/or pinning the CA certificate is recommended. - # - It is highly recommended to verify app PKP in report only mode before enabling this header. - # - # No default values, aah user have to provide it. pkp { # The Base64 encoded Subject Public Key Information (SPKI) fingerprint. # These values gets added as `pin-sha256=; ...`. @@ -506,34 +154,12 @@ security { #"MHJYVThihUrJcxW6wcqyOISTXIsInsdj3xK8QrZbHec=" #] - # The time that the browser should remember that this site is only to be - # accessed using one of the defined keys. - # Valid time units are "s -> seconds", "m -> minutes", "h - hours". max_age = "720h" - - # If enabled the PKP keys applies to all of the site's subdomains as well. - # Default value is `false`. - include_subdomains = false - - # By default, Pin validation failure reports aren't sent. To enable Pin validation - # failure reporting, you need to specify the report-uri. report_uri = "" - - # By default aah puts `Public-Key-Pins` in report only mode, so that it - # could be verified before making it live (set `pkp_report_only` to false). - # - # Don't forget to set the `report-uri` for validation. report_only = true } # X-Permitted-Cross-Domain-Policies - # Restrict Adobe Flash Player's or PDF documents access via crossdomain.xml, - # and this header. - # - # Learn more: - # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xpcdp - # https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html - # Default value is `master-only`. {{ if .App.IsWebApp -}}#xpcdp = "master-only"{{ else }}xpcdp = ""{{ end }} } } diff --git a/aah/app_tmpl.go b/aah/app_tmpl.go index f40e928..0e4e940 100644 --- a/aah/app_tmpl.go +++ b/aah/app_tmpl.go @@ -20,6 +20,7 @@ const ( aahTmplExt = ".atmpl" authForm = "form" authBasic = "basic" + authOAuth2 = "oauth2" authGeneric = "generic" authNone = "none" basicFileRealm = "file-realm" @@ -81,6 +82,14 @@ func (a *appTmplData) IsSubTypeWebSocket() bool { return a.checkSubType(typeWebSocket) } +func (a *appTmplData) IsSessionConfigRequired() bool { + return a.AuthScheme == authForm || a.AuthScheme == authOAuth2 || a.AuthScheme == authBasic +} + +func (a *appTmplData) IsAuth(name string) bool { + return strings.Contains(a.AuthScheme, name) +} + func (a *appTmplData) checkSubType(t string) bool { for _, v := range a.SubTypes { if v == t { @@ -103,4 +112,8 @@ var appTemplateFuncs = template.FuncMap{ "variablename": func(v string) string { return toLowerCamelCase(vreplace.Replace(v)) }, + "isauth": func(args map[string]interface{}, name string) bool { + app := args["App"].(*appTmplData) + return app.IsAuth(name) + }, } diff --git a/aah/compile.go b/aah/compile.go index ba44cf2..ec62a75 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -519,7 +519,7 @@ func main() { secMgr := aah.AppSecurityManager() {{- range $k, $v := $.AppSecurity }}{{ $vPrefix := (variablename $k) }} {{ $vPrefix }}AuthScheme := secMgr.AuthScheme("{{ $k }}") - {{- if $v.Authenticator }}if sauthc, ok := {{ $vPrefix }}AuthScheme.(setauthenticator); ok { + {{ if $v.Authenticator -}}if sauthc, ok := {{ $vPrefix }}AuthScheme.(setauthenticator); ok { aah.AppLog().Debugf("Initializing authenticator for auth scheme '%s'", "{{ $k }}") if err := sauthc.SetAuthenticator(&{{ $v.Authenticator }}{}); err != nil { aah.AppLog().Fatal(err) diff --git a/aah/run.go b/aah/run.go index 95d9336..ec4d60d 100644 --- a/aah/run.go +++ b/aah/run.go @@ -348,11 +348,11 @@ func startWatcher(projectCfg *config.Config, baseDir string, w *watcher.Watcher, for { select { case e := <-w.Event: - if e.Op == watcher.Create { - _ = w.Add(e.Path) - } if !e.IsDir() { watch <- true + if e.Op == watcher.Create { + _ = w.Add(e.Path) + } } case err := <-w.Error: if err == watcher.ErrWatchedFileDeleted { @@ -397,7 +397,7 @@ func loadWatchFiles(projectCfg *config.Config, baseDir string, w *watcher.Watche } // standard dir ignore list for aah project - dirExcludes = append(dirExcludes, "build", "static", "vendor", "tests", "logs") + dirExcludes = append(dirExcludes, "build", "static", "vendor", "views", "tests", "logs") dirs, _ := ess.DirsPathExcludes(baseDir, true, dirExcludes) for _, d := range dirs { From 4f998f09fd29db8d0e1a32441ef2d0bcb4dadd74 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 20 Jun 2018 20:40:00 -0700 Subject: [PATCH 38/48] app template and grammer update --- aah/app-template/aah.project.atmpl | 1 + aah/app-template/app/controllers/app.go.atmpl | 2 +- .../app/controllers/v1/value.go.atmpl | 5 +- aah/app-template/app/init.go.atmpl | 4 +- .../app/security/authentication_provider.go | 6 +- .../app/security/authorization_provider.go | 4 +- .../app/websockets/sample.go.atmpl | 20 +- aah/app-template/config/aah.conf.atmpl | 5 +- aah/app-template/config/routes.conf.atmpl | 200 +++--------------- aah/app-template/config/security.conf.atmpl | 1 - aah/app-template/i18n/messages.en.atmpl | 7 +- aah/migrate.grammar | 5 +- 12 files changed, 55 insertions(+), 205 deletions(-) diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl index 0811e7c..47a5fb7 100644 --- a/aah/app-template/aah.project.atmpl +++ b/aah/app-template/aah.project.atmpl @@ -1,6 +1,7 @@ # --------------------------------------------------------------------------- # {{ .App.Name }} - aah framework project # +# Doc: https://docs.aahframework.org/aah-project-file.html # Note: Add it to version control # --------------------------------------------------------------------------- diff --git a/aah/app-template/app/controllers/app.go.atmpl b/aah/app-template/app/controllers/app.go.atmpl index ddb1f76..8adcab6 100644 --- a/aah/app-template/app/controllers/app.go.atmpl +++ b/aah/app-template/app/controllers/app.go.atmpl @@ -11,7 +11,7 @@ type AppController struct { *aah.Context } -// Index method is application {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} +// Index method is application's {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} func (a *AppController) Index() { {{- if .App.IsWebApp }} data := aah.Data{ diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index a532272..f0b0ff9 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -2,7 +2,7 @@ package v1 import ( "fmt" - + "aahframework.org/aah.v0" "aahframework.org/ahttp.v0" @@ -46,8 +46,9 @@ func (v *ValueController) Create(val *models.Value) { // Add it to values map values[val.Key] = val + newResourceURL := fmt.Sprintf("%s:%s", v.Req.Scheme, v.RouteURL("value_get", val.Key) v.Reply().Created(). - Header(ahttp.HeaderLocation, fmt.Sprintf("%s:%s", v.Req.Scheme, v.ReverseURL("value_get", val.Key))). + Header(ahttp.HeaderLocation, newResourceURL)). JSON(aah.Data{ "key": val.Key, }) diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl index b3512fa..0db256b 100644 --- a/aah/app-template/app/init.go.atmpl +++ b/aah/app-template/app/init.go.atmpl @@ -1,5 +1,5 @@ // aah application initialization - configuration, server extensions, middleware's, etc. -// Customize it per your application needs. +// Customize it per application use case. package main @@ -8,7 +8,7 @@ import ({{ if .App.IsSecurityEnabled }} "aahframework.org/aah.v0"{{ if .App.IsWebApp }} - // Registering HTML minifier for web application + // Registering HTML minifier _ "github.com/aah-cb/minify"{{ end }}{{ if eq .App.ViewEngine "pug" }} // Registering Pug View Engine (formerly known as Jade) diff --git a/aah/app-template/app/security/authentication_provider.go b/aah/app-template/app/security/authentication_provider.go index 0740e05..9ec6d37 100644 --- a/aah/app-template/app/security/authentication_provider.go +++ b/aah/app-template/app/security/authentication_provider.go @@ -14,10 +14,8 @@ type AuthenticationProvider struct { // Init method initializes the AuthenticationProvider, this method gets called // during server start up. -func (a *AuthenticationProvider) Init(cfg *config.Config) error { - +func (a *AuthenticationProvider) Init(appCfg *config.Config) error { // NOTE: Init is called on application startup - return nil } @@ -60,7 +58,7 @@ func (a *AuthenticationProvider) GetAuthenticationInfo(authcToken *authc.Authent func PostAuthEvent(e *aah.Event) { ctx := e.Data.(*aah.Context) - ctx.Log().Info("Called method security.PostAuthEvent") + ctx.Log().Info("Method security.PostAuthEvent called") // Do post successful authentication actions... } diff --git a/aah/app-template/app/security/authorization_provider.go b/aah/app-template/app/security/authorization_provider.go index 6f45ac6..c87cbee 100644 --- a/aah/app-template/app/security/authorization_provider.go +++ b/aah/app-template/app/security/authorization_provider.go @@ -14,10 +14,8 @@ type AuthorizationProvider struct { // Init method initializes the AuthorizationProvider, this method gets called // during server start up. -func (a *AuthorizationProvider) Init(cfg *config.Config) error { - +func (a *AuthorizationProvider) Init(appCfg *config.Config) error { // NOTE: Init is called on application startup - return nil } diff --git a/aah/app-template/app/websockets/sample.go.atmpl b/aah/app-template/app/websockets/sample.go.atmpl index 31f87e8..03195df 100644 --- a/aah/app-template/app/websockets/sample.go.atmpl +++ b/aah/app-template/app/websockets/sample.go.atmpl @@ -9,14 +9,12 @@ import ( "{{ .App.ImportPath }}/app/models" ) -// SampleWebSocket struct is sample WebSocket implementation. +// SampleWebSocket is aah's sample WebSocket implementation. type SampleWebSocket struct { *ws.Context } -// Handle method is sample WebSocket action implementation. -// -// It handles Text and JSON encoding based on Path param value. +// Handle method handles Text and JSON data based on Path param value. func (s *SampleWebSocket) Handle(mode string) { switch strings.ToLower(mode) { case "text": @@ -26,7 +24,7 @@ func (s *SampleWebSocket) Handle(mode string) { } } -// handleTextMode method is used to communicate in Text +// handleTextMode method is used to communicate in Text data func (s *SampleWebSocket) handleTextMode() { s.Log().Info("Handling mode: text") @@ -39,17 +37,17 @@ func (s *SampleWebSocket) handleTextMode() { } s.Log().Error(err) - continue // we are moving on to next WS frame/msg + continue // we are moving on to next WS frame } - // if no error, echo back Text to WebSocket client + // if no error then echo back Text data to WebSocket client if err = s.ReplyText(str); err != nil { s.Log().Error(err) } } } -// handleJSONMode method is used to communicate in JSON +// handleJSONMode method is used to communicate in JSON data func (s *SampleWebSocket) handleJSONMode() { s.Log().Infof("Handling mode: json") @@ -63,13 +61,13 @@ func (s *SampleWebSocket) handleJSONMode() { s.Log().Error(err) - // Its a JSON read error + // JSON read error happened _ = s.ReplyJSON(aah.Data{"message": "invalid JSON"}) - continue // we are moving on to next WS frame/msg + continue // we are moving on to next WS frame } - // if no error, echo back JSON to WebSocket client + // if no error then echo back JSON data to WebSocket client if err := s.ReplyJSON(greet); err != nil { s.Log().Error(err) } diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl index dc85e5d..a691ec7 100644 --- a/aah/app-template/config/aah.conf.atmpl +++ b/aah/app-template/config/aah.conf.atmpl @@ -2,7 +2,6 @@ # {{ .App.Name }} - aah Application Configuration # # Refer documentation to explore and customize the configurations. -# # Doc: https://docs.aahframework.org/app-config.html # ----------------------------------------------------------------------------- @@ -16,10 +15,10 @@ desc = "aah {{ .App.Type }} application" # Application type, typically these `web`, `api`, `websocket` type = "{{ .App.Type }}" -# ----------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Server configuration # Doc: https://docs.aahframework.org/app-config.html#section-server -# ----------------------------------------------------------------- +# ----------------------------------------------------------------------------- server { # Address is used to bind against host address, IP address, UNIX socket. # diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index c7eaf02..fec0602 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -1,105 +1,36 @@ # ----------------------------------------------------------------------------- # {{ .App.Name }} - Application Routes Configuration # -# Complete routes configuration reference: -# Doc: https://docs.aahframework.org/routes-config.html +# Refer documentation to explore and configure routes. +# Doc: https://docs.aahframework.org/routing.html # ----------------------------------------------------------------------------- -#------------------------------------------------------------------------------ -# Domain and sub-domain configuration goes into section `domains { ... }` -#------------------------------------------------------------------------------ domains { - - # Pick your choice of an `unique keyname` to define your domain section - # in the routes configuration. - # For e.g.: Domain name/ip address with port no + #------------------------------------------------------------------------------ + # Domain/subdomain Key Name + # Choose an `unique keyname` to define domain section and its configuration. + # Tip: domain name address, port no values to create a domain key, etc. + # + # Doc: https://docs.aahframework.org/routes-config.html#domain-configuration + #------------------------------------------------------------------------------ {{ .App.DomainNameKey }} { name = "{{ .App.Name }} routes" - - # aah supports multi-domain routes configuration out-of-the-box. - # `host` used to determine domain routes for the incoming request. - # For e.g: example.org host = "localhost"{{ if not .App.IsWebSocketApp }} - - # Redirect trailing slash is to enable automatic redirection if the current - # route can't be matched but a `route` for the path with (without) - # the trailing slash exists. - # Default value is `true`. - redirect_trailing_slash = true - - # aah supports out-of-the-box `405 MethodNotAllowed` status with `Allow` - # header as per `RFC7231`. Perfect for RESTful APIs. - # Default value is `true`. - {{ if .App.IsWebApp }}#{{ end }}method_not_allowed = true - - # aah framework supports out-of-the-box `OPTIONS` request replies. - # User defined `OPTIONS` routes take priority over the automatic replies. - # Perfect for RESTful APIs. - # Default value is `true`. - {{ if .App.IsWebApp }}#{{ end }}auto_options = true - - # Default auth is used when route does not have attribute `auth` defined. - # If you don't define attribute `auth` then framework treats that route as - # `anonymous` auth scheme. - # Default value is empty string. - #default_auth = "" - - # Anti-CSRF protection. - # Typically if you have `` usage in your application, - # don't disable the check. `aah new` set to false for API app. - # Default value is `true`. anti_csrf_check = {{ if .App.IsAPIApp }}false{{ else }}true{{ end }} - - {{ end }}{{ if .App.CORSEnable }} + {{ end }} {{ if .App.CORSEnable }} #---------------------------------------------------------------------------- # CORS (Cross-Origin Resource Sharing) # Doc: https://docs.aahframework.org/cors.html #---------------------------------------------------------------------------- cors { - # Default value is `false`. enable = true - - # Specify `Access-Control-Allow-Origin` header values. - # # Default value is `*`. - #allow_origins = [ - # "https://www.example.com", - # "http://sample.com" - #] - - # Specify `Access-Control-Allow-Headers` header values. - # Default values are `Accept`, `AcceptLanguage`, `Authorization`, `Origin`. - #allow_headers = [ - # "Accept", - # "Authorization", - # "Content-Type", - # "Origin" - #] - - # Specify `Access-Control-Allow-Methods` header values. - # Default values are `GET`, `POST`, `HEAD`. - #allow_methods = ["GET", "POST", "HEAD"] - - # Specify `Access-Control-Expose-Headers` header values. - # Default value - `cors.allow_headers` values are used if not provided. - #expose_headers = [ - # "X-Custom-Header1", - # "X-Custom-Header2" - #] - - # Specify `Access-Control-Max-Age` header value. - # Default value is `24h`. - #max_age = "48h" - - # Specify `Access-Control-Allow-Credentials` header value. - # Default value is `false`. - #allow_credentials = true + allow_origins = ["*"] } - - {{ end }}{{ if .App.IsWebApp }} + {{ end }} {{ if .App.IsWebApp }} #------------------------------------------------------------------------------ - # Static Routes Configuration + # Static files Routes Configuration # To serve static files, it can be directory or individual file. # This section optional one, for e.g: RESTful APIs doesn't need this section. # Static files are delivered via `http.ServeContent`. @@ -115,148 +46,73 @@ domains { # Doc: https://docs.aahframework.org/routes-config.html#section-static #------------------------------------------------------------------------------ static { - # Static route name. public_assets { - # URL 'path' for serving directory - # Below definition means '/static/**' path = "/static" - - # Relative to application base directory or an absolute path dir = "static" - - # list directory, default is 'false' - #list = false } - # Serving single file favicon { path = "/favicon.ico" - - # Direct file mapping, It can be relative to application base directory - # or an absolute path. For relative path, it uses below `base_dir` config value. file = "img/favicon.ico" - - # Default value for relative path file mapping is `public_assets.dir` - #base_dir = "assets" } - # Robots Configuration file. - # Know more: https://en.wikipedia.org/wiki/Robots_exclusion_standard robots_txt { path = "/robots.txt" file = "robots.txt" } } - {{ end }} # ----------------------------------------------------------------------------- - # Application routes definitions + # Routes Configuration # - # Doc: https://docs.aahframework.org/routes-config.html#section-routes - # Doc: https://docs.aahframework.org/routes-config.html#namespace-group-routes + # Doc: https://docs.aahframework.org/routes-config.html#routes-configuration # ----------------------------------------------------------------------------- routes { - - {{ if not .App.IsWebSocketApp -}}# ----------------------------------------------------------------------------- - # Pick an unique name, it's called `route name`, - # used for reverse URL. - # - # Pro Tip: https://docs.aahframework.org/routes-config.html#pro-tips-for-nested-namespace-routes - # ----------------------------------------------------------------------------- - index { - # path is used to match incoming requests - # It can contain `:name` - Named parameter and - # `*name` - Catch-all parameter + {{ if not .App.IsWebSocketApp }}index { path = "/" - - # HTTP method mapping, It can be multiple `HTTP` methods with comma separated - # Default value is `GET`, it can be lowercase or uppercase - #method = "GET" - - # The controller to be called for mapped URL path. - # * `controller` attribute supports with or without package prefix. For e.g.: `v1/User` or `User` - # * `controller` attribute supports both naming conventions. For e.g.: `User` or `UserController` controller = "AppController" - - # The action/method name in the controller to be called for mapped URL path. - # Default values are mapped based on `HTTP` method. Refer doc for more info. - # Default action value for GET is 'Index'. - #action = "Index" - - # Auth config attribute is used to assign auth scheme for the route. - # If you do not this attribute then framework acquire value as follows. - # - # - Inherits the parent route `auth` attribute value if present. - # - Inherits the `default_auth` attribute config value if defined. - # - Otherwise it becomes not defined. - # - # When routes auth attribute is not defined; two possible actions are taken: - # - If one or more auth schemes are defined in security.auth_schemes { ... } - # and routes auth attribute is not defined then framework treats that route as 403 Forbidden. - # - Else framework treats that route as anonymous. - # - # When you want to define particular route as anonymous then define - # `auth` attribute as `anonymous`. - # Default value is empty string. auth = "anonymous" + }{{ end }} {{ if or .App.IsAPIApp .App.IsSubTypeAPI }} - # Max request body size for this route. If its happen to be `MultipartForm` - # then this value ignored since `request.multipart_size` config from `aah.conf` - # is applied. - # - # If this value is not provided then global `request.max_body_size` config - # from `aah.conf` is applied. So use it for specific cases. - # No default value, global value is applied. - #max_body_size = "5mb"{{ if .App.IsWebApp }} - - # Optionally you can disable Anti-CSRF check for particular route. - # There are cases you might need this option. In-general don't disable the check. - # Default value is `true`. - #anti_csrf_check = false{{ end }} - } - - {{ end -}}{{ if or .App.IsAPIApp .App.IsSubTypeAPI }} - - # Below routes definition demonstrates part of aah routes configuration - # capabilities + # Definition demonstrates part of aah routes configuration capabilities api_v1 { path = "{{ if .App.IsWebApp }}/api{{ end }}/v1" - auth = "anonymous" - {{ if .App.IsWebApp }}anti_csrf_check = false{{ end }} + auth = "anonymous"{{ if .App.IsWebApp }} + anti_csrf_check = false{{ end }} routes { all_values { path = "/values" - controller = "{{ if .App.IsWebApp }}/api{{ end }}v1/ValueController" + controller = "{{ if .App.IsWebApp }}api/{{ end }}v1/ValueController" action = "List" routes { - value_get { # /v1/values/:key + value_get { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key path = "/:key" } - value_create { # /v1/values + value_create { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values method = "POST" } - value_update { # /v1/values/:key + value_update { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key path = "/:key" method = "PUT" } - value_delete { # /v1/values/:key + value_delete { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key path = "/:key" method = "DELETE" } } } # end - all_values } - } # end - api_v1 + } # end - api_v1 {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }}# WebSocket routes definitions + # WebSocket routes definitions # Sample implementation handles Text and JSON mode ws_sample { path = "/ws/:mode" - websocket = "SampleWebSocket" method = "WS" + websocket = "SampleWebSocket" action = "Handle" }{{ end }} diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index 2144df4..27091b6 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -2,7 +2,6 @@ # {{ .App.Name }} - Application Security Configuration # # Refer documentation to explore and customize the configurations. -# # Doc: https://docs.aahframework.org/security-config.html # ----------------------------------------------------------------------------- security { diff --git a/aah/app-template/i18n/messages.en.atmpl b/aah/app-template/i18n/messages.en.atmpl index 6357dd8..ed1b59e 100644 --- a/aah/app-template/i18n/messages.en.atmpl +++ b/aah/app-template/i18n/messages.en.atmpl @@ -1,9 +1,8 @@ -############################################# +# --------------------------------------------------------------------------- # i18n messages for {{ .App.Name }} application # -# Complete configuration reference: -# https://docs.aahframework.org/i18n.html -############################################# +# Doc: https://docs.aahframework.org/i18n.html +# --------------------------------------------------------------------------- # This structure is example purpose # So choose your suitable structure for your application diff --git a/aah/migrate.grammar b/aah/migrate.grammar index 5e90357..0d47b66 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -39,6 +39,8 @@ file { ".Readfrom(", ".FromReader(", ".RedirectSts(", ".RedirectWithStatus(", ".FindDomain(", ".Lookup(", + ".ReverseURL(", ".RouteURL(", + ".ReverseURLm(", ".RouteURLNamedArgs(", ".GetAuthScheme(", ".AuthScheme(" ] } @@ -53,6 +55,5 @@ file { "name=\"anit_csrf_token\"", "name=\"anti_csrf_token\"" ] } - - # upcoming elements + } From 1c768d56722c1b41be5c69823eb31d8ab3d99ffa Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 21 Jun 2018 22:01:46 -0700 Subject: [PATCH 39/48] app template update and temporarily removed pug view engine support due to upstream library issue. however will be added again --- .../app/controllers/v1/value.go.atmpl | 4 +- aah/app-template/config/env/dev.conf.atmpl | 40 ++++--------- aah/app-template/config/env/prod.conf.atmpl | 57 +++++-------------- aah/app-template/config/routes.conf.atmpl | 23 ++++---- aah/new.go | 2 +- 5 files changed, 41 insertions(+), 85 deletions(-) diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index f0b0ff9..9418cb5 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -46,9 +46,9 @@ func (v *ValueController) Create(val *models.Value) { // Add it to values map values[val.Key] = val - newResourceURL := fmt.Sprintf("%s:%s", v.Req.Scheme, v.RouteURL("value_get", val.Key) + newResourceURL := fmt.Sprintf("%s:%s", v.Req.Scheme, v.RouteURL("value_get", val.Key)) v.Reply().Created(). - Header(ahttp.HeaderLocation, newResourceURL)). + Header(ahttp.HeaderLocation, newResourceURL). JSON(aah.Data{ "key": val.Key, }) diff --git a/aah/app-template/config/env/dev.conf.atmpl b/aah/app-template/config/env/dev.conf.atmpl index 326b737..1de01fa 100644 --- a/aah/app-template/config/env/dev.conf.atmpl +++ b/aah/app-template/config/env/dev.conf.atmpl @@ -1,37 +1,21 @@ -# --------------------------------- -# Development Configuration Section -# --------------------------------- +# ----------------------------------------------------------------------------- +# Development Configuration +# ----------------------------------------------------------------------------- dev { - # -------------------------------------------------- + # ----------------------------------------------------------------------------- # Log Configuration + # # Doc: https://docs.aahframework.org/logging.html - # -------------------------------------------------- + # Doc: https://docs.aahframework.org/log-config.html + # ----------------------------------------------------------------------------- log { - # Receiver is where is log values gets logged. aah - # supports `console` and `file` receivers. Hooks for extension. - # Default value is `console`. - #receiver = "file" - - # Level indicates the logging levels like `ERROR`, `WARN`, `INFO`, `DEBUG`, - # `TRACE`, FATAL and PANIC. Config value can be in lowercase or uppercase. - # Default value is `debug`. - #level = "debug" - - # Format to define log entry output format. Supported formats are `text` and `json`. - # Default value is `text`. - #format = "json" - - # Pattern config defines the message flags and formatting while logging - # into receivers. Customize it as per your need, learn more about flags - # and format - https://docs.aahframework.org/log-config.html#pattern - # Default value is `%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields` - #pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" - - # Log colored output, applicable only to `console` receiver type. - # Default value is `true`. - #color = false + receiver = "console" + level = "debug" + format = "text" + pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" + color = true } } diff --git a/aah/app-template/config/env/prod.conf.atmpl b/aah/app-template/config/env/prod.conf.atmpl index 0a41c1d..990079f 100644 --- a/aah/app-template/config/env/prod.conf.atmpl +++ b/aah/app-template/config/env/prod.conf.atmpl @@ -1,13 +1,13 @@ -# --------------------------------- -# Production Configuration Section -# --------------------------------- +# ----------------------------------------------------------------------------- +# Production Configuration +# ----------------------------------------------------------------------------- prod { - # ------------------------------------------------------------------ + # ----------------------------------------------------------------------------- # Runtime configuration # Doc: https://docs.aahframework.org/app-config.html#section-runtime - # ------------------------------------------------------------------ + # ----------------------------------------------------------------------------- runtime { debug { # Whether to strip source `src` base path from file path. @@ -16,50 +16,21 @@ prod { } } - # -------------------------------------------------- + # ----------------------------------------------------------------------------- # Log Configuration + # # Doc: https://docs.aahframework.org/logging.html - # -------------------------------------------------- + # Doc: https://docs.aahframework.org/log-config.html + # ----------------------------------------------------------------------------- log { - # Receiver is where is log values gets logged. aah - # supports `console` and `file` receivers. Hooks for extension. - # Default value is `console`. receiver = "file" - - # Level indicates the logging levels like `ERROR`, `WARN`, `INFO`, `DEBUG`, - # `TRACE`, FATAL and PANIC. Config value can be in lowercase or uppercase. - # Default value is `debug`. - level = "warn" - - # Format to define log entry output format. Supported formats are `text` and `json`. - # Default value is `text`. - #format = "json" - - # Pattern config defines the message flags and formatting while logging - # into receivers. Customize it as per your need, learn more about flags - # and format - https://docs.aahframework.org/log-config.html#pattern - # Default value is `%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields` - #pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" - - # File config attribute is applicable only to `file` receiver type. - # Default value is `aah-log-file.log`. file = "{{ .App.Name }}.log" - - # Rotate config section is applicable only to `file` receiver type. - # Default rotation is 'daily'. + level = "warn" + format = "text" + pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" + color = false rotate { - # Policy is used to determine rotate policy. aah supports `daily`, - # `lines` and `size` policies. - # Default value is `daily`. - #policy = "daily" - - # This is applicable only to if `mode` is `size`. - # Default value is 100MB. - #size = 500 - - # This is applicable only to if `mode` is `lines`. - # Default value is unlimited. - #lines = 100000 + policy = "daily" } } diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index fec0602..6a41e48 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -88,20 +88,21 @@ domains { action = "List" routes { - value_get { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key - path = "/:key" - } - value_create { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values + value_create { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values method = "POST" } - value_update { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key - path = "/:key" - method = "PUT" - } - value_delete { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key + value_get { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key path = "/:key" - method = "DELETE" - } + + routes { + value_update { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key + method = "PUT" + } + value_delete { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key + method = "DELETE" + } + } + } # end - value_get } } # end - all_values } diff --git a/aah/new.go b/aah/new.go index 5760754..70244c1 100644 --- a/aah/new.go +++ b/aah/new.go @@ -193,7 +193,7 @@ func collectAppSubTypesChoice(reader *bufio.Reader, app *appTmplData) { } func viewEngine(reader *bufio.Reader, app *appTmplData) { - builtInViewEngines := []string{"go", "pug"} + builtInViewEngines := []string{"go"} var engine string for { engine = strings.ToLower(readInput(reader, fmt.Sprintf("\nChoose your application View Engine (%s), default is 'go': ", From 254a61306ac23614ba95393024a6f53b4270b35c Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sun, 24 Jun 2018 22:52:40 -0700 Subject: [PATCH 40/48] template update, grammar, compile import paths --- .gitignore | 1 + aah/app-template/config/env/prod.conf.atmpl | 2 -- aah/app-template/config/routes.conf.atmpl | 7 +++---- aah/app-template/config/security.conf.atmpl | 7 ++++--- aah/compile.go | 17 ++++++++++------- aah/migrate.grammar | 3 +++ aah/version.go | 4 ++-- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 89dd93e..20996fc 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ _testmain.go *.exe *.test *.prof +*.bak diff --git a/aah/app-template/config/env/prod.conf.atmpl b/aah/app-template/config/env/prod.conf.atmpl index 990079f..c006293 100644 --- a/aah/app-template/config/env/prod.conf.atmpl +++ b/aah/app-template/config/env/prod.conf.atmpl @@ -10,8 +10,6 @@ prod { # ----------------------------------------------------------------------------- runtime { debug { - # Whether to strip source `src` base path from file path. - # Default value is `false`. strip_src_base = true } } diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl index 6a41e48..aab0caa 100644 --- a/aah/app-template/config/routes.conf.atmpl +++ b/aah/app-template/config/routes.conf.atmpl @@ -8,8 +8,9 @@ domains { #------------------------------------------------------------------------------ # Domain/subdomain Key Name - # Choose an `unique keyname` to define domain section and its configuration. - # Tip: domain name address, port no values to create a domain key, etc. + # Choose a `unique keyname` to define domain section and its configuration. + # Tip: domain name address, port no values could be used to create a + # meaningful domain key, etc. # # Doc: https://docs.aahframework.org/routes-config.html#domain-configuration #------------------------------------------------------------------------------ @@ -50,12 +51,10 @@ domains { path = "/static" dir = "static" } - favicon { path = "/favicon.ico" file = "img/favicon.ico" } - robots_txt { path = "/robots.txt" file = "robots.txt" diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl index 27091b6..7a3122e 100644 --- a/aah/app-template/config/security.conf.atmpl +++ b/aah/app-template/config/security.conf.atmpl @@ -4,6 +4,7 @@ # Refer documentation to explore and customize the configurations. # Doc: https://docs.aahframework.org/security-config.html # ----------------------------------------------------------------------------- + security { # ----------------------------------------------------------------------------- @@ -14,7 +15,7 @@ security { # ----------------------------------------------------------------------------- # Form auth scheme - # Choose unique key name, it gets used as route auth. + # Choose a unique key name. It gets used as route auth. # # Doc: https://docs.aahframework.org/auth-schemes/form.html # ----------------------------------------------------------------------------- @@ -30,7 +31,7 @@ security { # ----------------------------------------------------------------------------- # Basic auth scheme - # Choose unique key name, it gets used as route auth. + # Choose a unique key name. It gets used as route auth. # # Doc: https://docs.aahframework.org/auth-schemes/basic.html # ----------------------------------------------------------------------------- @@ -48,7 +49,7 @@ security { # ----------------------------------------------------------------------------- # Generic auth scheme - # Choose unique key name, it gets used as route auth. + # Choose a unique key name. It gets used as route auth. # # Doc: https://docs.aahframework.org/auth-schemes/generic.html # ----------------------------------------------------------------------------- diff --git a/aah/compile.go b/aah/compile.go index ec62a75..c171cba 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -81,9 +81,17 @@ func compileApp(args *compileArgs) (string, error) { } } + appImportPaths := map[string]string{ + "aahframework.org/aah.v0": "aah", + "aahframework.org/aruntime.v0": "aruntime", + "aahframework.org/config.v0": "config", + "aahframework.org/essentials.v0": "ess", + "aahframework.org/log.v0": "log", + } + // get all the types info referred aah framework context embedded appControllers := acntlr.FindTypeByEmbeddedType(fmt.Sprintf("%s.Context", libImportPath("aah"))) - appImportPaths := acntlr.CreateImportPaths(appControllers, map[string]string{}) + appImportPaths = acntlr.CreateImportPaths(appControllers, appImportPaths) appSecurity := appSecurity(aah.AppConfig(), appImportPaths) // Go AST processing for WebSockets @@ -356,12 +364,7 @@ import ( "reflect" "regexp" "syscall" - - "aahframework.org/aah.v0" - "aahframework.org/aruntime.v0" - "aahframework.org/config.v0" - "aahframework.org/essentials.v0" - "aahframework.org/log.v0"{{ if .AppSecurity }} + {{ if .AppSecurity }} "aahframework.org/security.v0/authc" "aahframework.org/security.v0/authz"{{ end }}{{ range $k, $v := $.AppImportPaths }} {{ $v }} "{{ $k }}"{{ end }} diff --git a/aah/migrate.grammar b/aah/migrate.grammar index 0d47b66..077fd53 100644 --- a/aah/migrate.grammar +++ b/aah/migrate.grammar @@ -31,6 +31,9 @@ file { "ahttp.GetGzipResponseWriter(", "ahttp.WrapGzipWriter(", "ahttp.PutGzipResponseWiriter(", "ahttp.ReleaseResponseWriter(", "Req.Raw", "Req.Unwrap()", + "Req.Raw.URL", "Req.URL()", + "Req.Unwrap().URL", "Req.URL()", + ".Unwrap().FormValue(", ".Req.FormValue(", "Req.AcceptContentType", "Req.AcceptContentType()", "Req.AcceptEncoding", "Req.AcceptEncoding()", "Req.ClientIP", "Req.ClientIP()", diff --git a/aah/version.go b/aah/version.go index 2f4c4be..ce52cb7 100644 --- a/aah/version.go +++ b/aah/version.go @@ -25,8 +25,8 @@ var ( // VersionPrinter method prints the versions info. func VersionPrinter(c *cli.Context) { cliLog = initCLILogger(nil) - fmt.Printf("%-4s v%s\n", "cli", Version) - fmt.Printf("%-4s v%s\n", "aah", aahVer) + fmt.Printf("%-3s v%s\n", "cli", Version) + fmt.Printf("%-3s v%s\n", "aah", aahVer) if goVer := goVersion(); len(goVer) > 0 { fmt.Printf("%-4s v%s\n", "go", goVer) } From 4d1473104ea00e681f8a476d1b0e987060cd0a81 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 26 Jun 2018 10:16:50 -0700 Subject: [PATCH 41/48] excluded examples directory from aah libs list --- aah/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aah/util.go b/aah/util.go index 5d34525..c146228 100644 --- a/aah/util.go +++ b/aah/util.go @@ -421,7 +421,7 @@ func inferVersionSeries() string { } func aahLibraryDirs() []string { - dirs, err := ess.DirsPath(filepath.Join(gosrcDir, importPrefix), false) + dirs, err := ess.DirsPathExcludes(filepath.Join(gosrcDir, importPrefix), false, ess.Excludes{"examples"}) if err != nil { return []string{} } From de7407eb2bc4266c31e22a4526353fe440e35631 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Sat, 30 Jun 2018 00:46:32 -0700 Subject: [PATCH 42/48] view template and go template update --- aah/app-template/app/controllers/app.go.atmpl | 6 ++-- .../app/controllers/v1/value.go.atmpl | 36 +++++++++---------- .../app/websockets/sample.go.atmpl | 32 ++++++++--------- aah/app-template/views/go/layouts/master.html | 8 +++-- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/aah/app-template/app/controllers/app.go.atmpl b/aah/app-template/app/controllers/app.go.atmpl index 8adcab6..b6a7fc2 100644 --- a/aah/app-template/app/controllers/app.go.atmpl +++ b/aah/app-template/app/controllers/app.go.atmpl @@ -12,7 +12,7 @@ type AppController struct { } // Index method is application's {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} -func (a *AppController) Index() { +func (c *AppController) Index() { {{- if .App.IsWebApp }} data := aah.Data{ "Greet": models.Greet{ @@ -20,9 +20,9 @@ func (a *AppController) Index() { }, } - a.Reply().Ok().HTML(data) + c.Reply().Ok().HTML(data) {{- else }} - a.Reply().Ok().JSON(models.Greet{ + c.Reply().Ok().JSON(models.Greet{ Message: "Welcome to aah framework - API application", }){{ end }} } diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl index 9418cb5..b8e5dd0 100644 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ b/aah/app-template/app/controllers/v1/value.go.atmpl @@ -17,28 +17,28 @@ type ValueController struct { } // List method returns all the values. -func (v *ValueController) List() { - v.Reply().Ok().JSON(values) +func (c *ValueController) List() { + c.Reply().Ok().JSON(values) } // Index method returns value for given key. // If key not found then returns 404 NotFound error. -func (v *ValueController) Index(key string) { +func (c *ValueController) Index(key string) { if val, found := values[key]; found { - v.Reply().Ok().JSON(val) + c.Reply().Ok().JSON(val) return } - v.Reply().NotFound().JSON(aah.Data{ + c.Reply().NotFound().JSON(aah.Data{ "message": "Value not exists", }) } // Create method creates new entry in the values map with given payload. // If key already exists then returns 409 Conflict error. -func (v *ValueController) Create(val *models.Value) { +func (c *ValueController) Create(val *models.Value) { if _, found := values[val.Key]; found { - v.Reply().Conflict().JSON(aah.Data{ + c.Reply().Conflict().JSON(aah.Data{ "message": "Key already exists", }) return @@ -46,8 +46,8 @@ func (v *ValueController) Create(val *models.Value) { // Add it to values map values[val.Key] = val - newResourceURL := fmt.Sprintf("%s:%s", v.Req.Scheme, v.RouteURL("value_get", val.Key)) - v.Reply().Created(). + newResourceURL := fmt.Sprintf("%s:%s", c.Req.Scheme, c.RouteURL("value_get", val.Key)) + c.Reply().Created(). Header(ahttp.HeaderLocation, newResourceURL). JSON(aah.Data{ "key": val.Key, @@ -56,33 +56,33 @@ func (v *ValueController) Create(val *models.Value) { // Update method updates value entry on map for given key and Payload. // If key not exists then returns 400 BadRequest error. -func (v *ValueController) Update(key string, val *models.Value) { - if c, found := values[key]; found { - c.Value = val.Value - values[key] = c - v.Reply().Ok().JSON(aah.Data{ +func (c *ValueController) Update(key string, val *models.Value) { + if r, found := values[key]; found { + r.Value = val.Value + values[key] = r + c.Reply().Ok().JSON(aah.Data{ "message": "Value updated successfully", }) return } - v.Reply().BadRequest().JSON(aah.Data{ + c.Reply().BadRequest().JSON(aah.Data{ "message": "Invalid input", }) } // Delete method deletes value for given key. // If key not exists then returns 400 BadRequest error. -func (v *ValueController) Delete(key string) { +func (c *ValueController) Delete(key string) { if _, found := values[key]; found { delete(values, key) - v.Reply().Ok().JSON(aah.Data{ + c.Reply().Ok().JSON(aah.Data{ "message": "Value deleted successfully", }) return } - v.Reply().BadRequest().JSON(aah.Data{ + c.Reply().BadRequest().JSON(aah.Data{ "message": "Invalid input", }) } diff --git a/aah/app-template/app/websockets/sample.go.atmpl b/aah/app-template/app/websockets/sample.go.atmpl index 03195df..0c80c2d 100644 --- a/aah/app-template/app/websockets/sample.go.atmpl +++ b/aah/app-template/app/websockets/sample.go.atmpl @@ -15,61 +15,61 @@ type SampleWebSocket struct { } // Handle method handles Text and JSON data based on Path param value. -func (s *SampleWebSocket) Handle(mode string) { +func (w *SampleWebSocket) Handle(mode string) { switch strings.ToLower(mode) { case "text": - s.handleTextMode() + w.handleTextMode() case "json": - s.handleJSONMode() + w.handleJSONMode() } } // handleTextMode method is used to communicate in Text data -func (s *SampleWebSocket) handleTextMode() { - s.Log().Info("Handling mode: text") +func (w *SampleWebSocket) handleTextMode() { + w.Log().Info("Handling mode: text") for { - str, err := s.ReadText() + str, err := w.ReadText() if err != nil { if ws.IsDisconnected(err) { // WebSocket client is gone, exit here return } - s.Log().Error(err) + w.Log().Error(err) continue // we are moving on to next WS frame } // if no error then echo back Text data to WebSocket client - if err = s.ReplyText(str); err != nil { - s.Log().Error(err) + if err = w.ReplyText(str); err != nil { + w.Log().Error(err) } } } // handleJSONMode method is used to communicate in JSON data -func (s *SampleWebSocket) handleJSONMode() { - s.Log().Infof("Handling mode: json") +func (w *SampleWebSocket) handleJSONMode() { + w.Log().Infof("Handling mode: json") for { var greet models.Greet - if err := s.ReadJSON(&greet); err != nil { + if err := w.ReadJSON(&greet); err != nil { if ws.IsDisconnected(err) { // WebSocket client is gone, exit here return } - s.Log().Error(err) + w.Log().Error(err) // JSON read error happened - _ = s.ReplyJSON(aah.Data{"message": "invalid JSON"}) + _ = w.ReplyJSON(aah.Data{"message": "invalid JSON"}) continue // we are moving on to next WS frame } // if no error then echo back JSON data to WebSocket client - if err := s.ReplyJSON(greet); err != nil { - s.Log().Error(err) + if err := w.ReplyJSON(greet); err != nil { + w.Log().Error(err) } } } diff --git a/aah/app-template/views/go/layouts/master.html b/aah/app-template/views/go/layouts/master.html index 9935987..fd2bba3 100644 --- a/aah/app-template/views/go/layouts/master.html +++ b/aah/app-template/views/go/layouts/master.html @@ -4,11 +4,15 @@ - {{ template "title" . }} + {{ block "title" . }} + Default page title from master layout + {{ end }} {{ import "head_tags.html" . }} - {{ template "body" . -}} + {{ block "body" . -}} +

Default body content from master layout

+ {{ end }} {{ import "footer_scripts.html" . }} From ae0ffdc640587796fa905a6f06a6c81b65efc014 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Mon, 2 Jul 2018 00:53:20 -0700 Subject: [PATCH 43/48] just print version of cli, aah, go for now --- aah/version.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/aah/version.go b/aah/version.go index ce52cb7..f38c3ff 100644 --- a/aah/version.go +++ b/aah/version.go @@ -31,14 +31,16 @@ func VersionPrinter(c *cli.Context) { fmt.Printf("%-4s v%s\n", "go", goVer) } - fmt.Printf("\nLibraries:\n") - for _, bd := range aahLibraryDirs() { - bn := filepath.Base(bd) - if strings.HasPrefix(bn, "aah") || strings.HasPrefix(bn, "tools") { - continue - } - if verNo, err := readVersionNo(bd); err == nil { - fmt.Printf(" %s v%s\n", bn[:len(bn)-3], verNo) + if c.Bool("all") { // currently not-executed intentionally + fmt.Printf("\nLibraries:\n") + for _, bd := range aahLibraryDirs() { + bn := filepath.Base(bd) + if strings.HasPrefix(bn, "aah") || strings.HasPrefix(bn, "tools") { + continue + } + if verNo, err := readVersionNo(bd); err == nil { + fmt.Printf(" %s v%s\n", bn[:len(bn)-3], verNo) + } } } fmt.Println() From 0381ca564caaf3d6a9429fb303f714e55d6d49e6 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Tue, 3 Jul 2018 00:40:24 -0700 Subject: [PATCH 44/48] CLI build and homebrew tap update script --- aah/publish_cli.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 aah/publish_cli.sh diff --git a/aah/publish_cli.sh b/aah/publish_cli.sh new file mode 100755 index 0000000..b32b470 --- /dev/null +++ b/aah/publish_cli.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Creator: Jeevanandam M. (https://github.com/jeevatkm) jeeva@myjeeva.com +# License: MIT + +# currently this script focused on mac os + +BASEDIR=$(dirname "$0") +cd $BASEDIR + +# Inputs +version=$1 + +# Build for macOS +build_dir="/tmp/aah_cli_mac_$version" +mkdir -p $build_dir +env GOOS=darwin GOARCH=amd64 go build -o $build_dir/aah -ldflags="-s -w" +cd $build_dir && zip aah_darwin_amd64.zip aah + +# sha256 and upload to aah server +sha256=$(/usr/bin/shasum -a 256 $build_dir/aah_darwin_amd64.zip | cut -d " " -f 1) +ssh root@aahframework.org "mkdir -p /srv/www/aahframework.org/public/releases/cli/$version" +scp $build_dir/aah_darwin_amd64.zip root@aahframework.org:/srv/www/aahframework.org/public/releases/cli/$version + +# update homebrew tap +if [ ! -d "$GOPATH/src/github.com/go-aah/homebrew-tap" ]; then + git clone git@github.com:go-aah/homebrew-tap.git $GOPATH/src/github.com/go-aah/homebrew-tap +fi +cd $GOPATH/src/github.com/go-aah/homebrew-tap +sed -i '' -e 's/cli\/.*\/aah_darwin_amd64.zip/cli\/'"$version"'\/aah_darwin_amd64.zip/g' ./Formula/aah.rb +sed -i '' -e 's/sha256 ".*"/sha256 "'"$sha256"'"/g' ./Formula/aah.rb +sed -i '' -e 's/version ".*"/version "'"$version"'"/g' ./Formula/aah.rb +git add -u && git commit -m "brew tap update with $version release" && git push + +# Cleanup +rm -rf $build_dir + +# .. next upcoming OS support \ No newline at end of file From 949db9de74296eb0f990c07a89e0c1d388eca159 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 5 Jul 2018 01:39:00 -0700 Subject: [PATCH 45/48] added vgo trail support in aah cli via AAHVGO=true, app template load from AAHPATH default is /Users/jeeva/.aah, improved pkg existence check --- aah/aah.go | 29 ++++++++++++++++++++--- aah/compile.go | 58 +++++++++++++++++++++++++++------------------- aah/new.go | 7 +++--- aah/publish_cli.sh | 2 +- aah/update.go | 8 +++++-- aah/util.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ aah/version.go | 2 +- 7 files changed, 129 insertions(+), 34 deletions(-) diff --git a/aah/aah.go b/aah/aah.go index 2547379..cdd8d53 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -40,8 +40,13 @@ var ( // cli logger cliLog *log.Logger + + // CliPackaged is identify cli from go get or binary dist + CliPackaged string ) +var errStopHere = errors.New("stop here") + func checkPrerequisites() error { // check go is installed or not if !ess.LookExecutable("go") { @@ -56,7 +61,8 @@ func checkPrerequisites() error { } // Go executable - if gocmd, err = exec.LookPath("go"); err != nil { + gocmdName := goCmdName() + if gocmd, err = exec.LookPath(gocmdName); err != nil { return err } @@ -69,7 +75,21 @@ func checkPrerequisites() error { // aah if aahVer, err = aahVersion(); err == errVersionNotExists { - return errors.New("aah framework is not installed, its easy to install. Run 'go get aahframework.org/tools.v0/aah'") + if collectYesOrNo(reader, "aah framework is not installed in GOPATH, would you like to install [Y]es or [N]o") { + args := []string{"get"} + if gocmdName == "go" { + args = append(args, "-u") + } + args = append(args, "aahframework.org/aah.v0") + if _, err := execCmd(gocmd, args, false); err != nil { + return err + } + aahVer, _ = aahVersion() + fmt.Printf("\naah framework successfully installed in GOPATH\n\n") + return nil + } + fmt.Printf("\nOkay, you could do it manually, run '%s get aahframework.org/aah.v0'\n", gocmdName) + return errStopHere } return nil @@ -86,7 +106,10 @@ func main() { } }() - if err := checkPrerequisites(); err != nil { + err := checkPrerequisites() + if err == errStopHere { + return + } else if err != nil { logFatal(err) } diff --git a/aah/compile.go b/aah/compile.go index c171cba..80a32f7 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -5,13 +5,16 @@ package main import ( + "bufio" "bytes" "errors" "fmt" "go/format" "io/ioutil" + "os/exec" "path" "path/filepath" + "regexp" "strings" "aahframework.org/aah.v0" @@ -221,6 +224,8 @@ func generateSource(dir, filename, templateSource string, templateArgs map[strin return nil } +var notExistRegex = regexp.MustCompile(`cannot find package "(.*)" in any of`) + // checkAndGetAppDeps method project dependencies is present otherwise // it tries to get it if any issues it will return error. It internally uses // go list command. @@ -229,39 +234,44 @@ func generateSource(dir, filename, templateSource string, templateArgs map[strin func checkAndGetAppDeps(appImportPath string, cfg *config.Config) error { importPath := path.Join(appImportPath, "app", "...") args := []string{"list", "-f", "{{.Imports}}", importPath} - output, err := execCmd(gocmd, args, false) if err != nil { return err } - lines := strings.Split(strings.TrimSpace(output), "\r\n") - for _, line := range lines { - line = strings.Replace(strings.Replace(line, "]", "", -1), "[", "", -1) - line = strings.Replace(strings.Replace(line, "\r", " ", -1), "\n", " ", -1) - if ess.IsStrEmpty(line) { - // all dependencies is available - return nil - } - - notExistsPkgs := []string{} - for _, pkg := range strings.Fields(line) { - if ess.IsStrEmpty(pkg) || ess.IsImportPathExists(pkg) { - continue + pkgList := make(map[string]string) + replacer := strings.NewReplacer("[", "", "]", "") + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + if ln := replacer.Replace(strings.TrimSpace(scanner.Text())); ln != "" { + for _, p := range strings.Fields(ln) { + if p := strings.TrimSpace(p); p != "" { + pkgList[p] = p + } } - notExistsPkgs = append(notExistsPkgs, pkg) } + } - if cfg.BoolDefault("build.dep_get", true) && len(notExistsPkgs) > 0 { - cliLog.Info("Getting application dependencies ...") - if err := goGet(notExistsPkgs...); err != nil { - return err - } - } else if len(notExistsPkgs) > 0 { - return fmt.Errorf("Below application dependencies does not exist, "+ - "enable 'build.dep_get=true' in 'aah.project' for auto fetch\n---> %s", - strings.Join(notExistsPkgs, "\n---> ")) + args = []string{"list"} + for _, p := range pkgList { + args = append(args, p) + } + b, _ := exec.Command(gocmd, args...).CombinedOutput() + notExistsPkgs := []string{} + matches := notExistRegex.FindAllStringSubmatch(string(b), -1) + for _, m := range matches { + notExistsPkgs = append(notExistsPkgs, m[1]) + } + + if cfg.BoolDefault("build.dep_get", true) && len(notExistsPkgs) > 0 { + cliLog.Info("Getting application dependencies ...", notExistsPkgs) + if err := goGet(notExistsPkgs...); err != nil { + return err } + } else if len(notExistsPkgs) > 0 { + return fmt.Errorf("Below application dependencies does not exist, "+ + "enable 'build.dep_get=true' in 'aah.project' for auto fetch\n---> %s", + strings.Join(notExistsPkgs, "\n---> ")) } return nil diff --git a/aah/new.go b/aah/new.go index 70244c1..6ad2b03 100644 --- a/aah/new.go +++ b/aah/new.go @@ -323,10 +323,11 @@ type file struct { func createAahApp(appDir string, data map[string]interface{}) error { app := data["App"].(*appTmplData) - aahToolsPath := aahToolsPath() - appTemplatePath := filepath.Join(aahToolsPath.Dir, "app-template") - appTmplBaseDir := appTemplatePath appBaseDir := app.BaseDir + appTmplBaseDir := inferAppTmplBaseDir() + if ess.IsStrEmpty(appTmplBaseDir) { + logFatal("Unable to find aah app template at $HOME/.aah/app-templates") + } // app directory creation if err := ess.MkDirAll(appDir, permRWXRXRX); err != nil { diff --git a/aah/publish_cli.sh b/aah/publish_cli.sh index b32b470..fa9dbda 100755 --- a/aah/publish_cli.sh +++ b/aah/publish_cli.sh @@ -14,7 +14,7 @@ version=$1 # Build for macOS build_dir="/tmp/aah_cli_mac_$version" mkdir -p $build_dir -env GOOS=darwin GOARCH=amd64 go build -o $build_dir/aah -ldflags="-s -w" +env GOOS=darwin GOARCH=amd64 go build -o $build_dir/aah -ldflags="-s -w -X main.CliPackaged=true" cd $build_dir && zip aah_darwin_amd64.zip aah # sha256 and upload to aah server diff --git a/aah/update.go b/aah/update.go index a22a6d7..c3f1d95 100644 --- a/aah/update.go +++ b/aah/update.go @@ -39,8 +39,12 @@ func updateAction(c *cli.Context) error { } fmt.Printf("Update aah version to the latest release ...\n\n") - - args := []string{"get", "-u", path.Join(importPrefix, "tools.v0", "aah")} + gocmdName := goCmdName() + args := []string{"get"} + if gocmdName == "go" { + args = append(args, "-u") + } + args = append(args, path.Join(importPrefix, "tools.v0", "aah")) if _, err := execCmd(gocmd, args, false); err != nil { logFatalf("Unable to update aah to the latest release version: %s", err) } diff --git a/aah/util.go b/aah/util.go index c146228..971c2eb 100644 --- a/aah/util.go +++ b/aah/util.go @@ -135,6 +135,7 @@ func getBuildDate() string { func execCmd(cmdName string, args []string, stdout bool) (string, error) { cmd := exec.Command(cmdName, args...) // #nosec + cliLog = initCLILogger(nil) cliLog.Trace("Executing ", strings.Join(cmd.Args, " ")) if stdout { @@ -228,6 +229,9 @@ func findAvailablePort() string { } func initCLILogger(cfg *config.Config) *log.Logger { + if cliLog != nil { + return cliLog + } if cfg == nil { cfg, _ = config.ParseString("") } @@ -320,6 +324,9 @@ func waitForConnReady(port string) { } func installCLI() { + if CliPackaged != "" { + return + } verser := inferVersionSeries() args := []string{"install", fmt.Sprintf("%s/tools.%s/aah", importPrefix, verser)} if _, err := execCmd(gocmd, args, false); err != nil { @@ -531,3 +538,53 @@ func toLowerCamelCase(v string) string { } return string(st) } + +func inferAppTmplBaseDir() string { + aahBasePath := aahPath() + baseDir := filepath.Join(aahBasePath, "app-templates", "generic") + if !ess.IsFileExists(baseDir) { + tmplRepo := "https://github.com/go-aah/app-templates.git" + cliLog.Debugf("Downloading aah quick start app templates from %s", tmplRepo) + gitArgs := []string{"clone", tmplRepo, filepath.Dir(baseDir)} + if _, err := execCmd(gitcmd, gitArgs, false); err != nil { + logErrorf("Unable to download aah app-template from %s", tmplRepo) + return "" + } + } + return baseDir +} + +func aahPath() string { + s := os.Getenv("AAHPATH") + if s == "" { + return filepath.Join(userHomeDir(), ".aah") + } + return s +} + +func userHomeDir() string { + if isWindowsOS() { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if ess.IsStrEmpty(home) { + home = os.Getenv("USERPROFILE") + } + return filepath.Clean(home) + } + + env := "HOME" + if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + return filepath.Clean(home) + } + + return "" +} + +func goCmdName() string { + if name := os.Getenv("AAHVGO"); name != "" { + return "vgo" + } + return "go" +} diff --git a/aah/version.go b/aah/version.go index f38c3ff..714b60c 100644 --- a/aah/version.go +++ b/aah/version.go @@ -28,7 +28,7 @@ func VersionPrinter(c *cli.Context) { fmt.Printf("%-3s v%s\n", "cli", Version) fmt.Printf("%-3s v%s\n", "aah", aahVer) if goVer := goVersion(); len(goVer) > 0 { - fmt.Printf("%-4s v%s\n", "go", goVer) + fmt.Printf("%-3s v%s\n", "go", goVer) } if c.Bool("all") { // currently not-executed intentionally From f032c2fdeb41167c3bbc27fd5574cecb9ebb3b08 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Thu, 5 Jul 2018 01:40:26 -0700 Subject: [PATCH 46/48] app-template is moved to dedicated go-aah/app-templates repo --- aah/app-template/.gitignore | 33 --- aah/app-template/aah.project.atmpl | 120 ----------- aah/app-template/app/controllers/app.go.atmpl | 28 --- .../app/controllers/v1/value.go.atmpl | 88 -------- aah/app-template/app/init.go.atmpl | 193 ------------------ aah/app-template/app/models/greet.go | 6 - aah/app-template/app/models/value.go | 7 - .../app/security/authentication_provider.go | 64 ------ .../app/security/authorization_provider.go | 41 ---- .../app/websockets/sample.go.atmpl | 75 ------- aah/app-template/config/aah.conf.atmpl | 160 --------------- aah/app-template/config/env/dev.conf.atmpl | 21 -- aah/app-template/config/env/prod.conf.atmpl | 35 ---- aah/app-template/config/routes.conf.atmpl | 123 ----------- aah/app-template/config/security.conf.atmpl | 165 --------------- aah/app-template/i18n/messages.en.atmpl | 17 -- aah/app-template/static/css/aah.css | 47 ----- .../static/img/aah-framework-logo.png | Bin 6990 -> 0 bytes aah/app-template/static/img/favicon.ico | Bin 15086 -> 0 bytes aah/app-template/static/js/aah.js | 0 aah/app-template/static/robots.txt | 3 - .../views/go/common/error_footer.html | 1 - .../views/go/common/error_header.html | 34 --- .../views/go/common/footer_scripts.html | 1 - .../views/go/common/head_tags.html | 2 - aah/app-template/views/go/errors/404.html | 14 -- aah/app-template/views/go/errors/500.html | 14 -- aah/app-template/views/go/layouts/master.html | 18 -- .../views/go/pages/app/index.html | 12 -- .../views/pug/common/error_footer.pug | 0 .../views/pug/common/error_header.pug | 33 --- .../views/pug/common/footer_scripts.pug | 2 - .../views/pug/common/head_tags.pug | 5 - aah/app-template/views/pug/errors/404.pug | 12 -- aah/app-template/views/pug/errors/500.pug | 12 -- aah/app-template/views/pug/layouts/master.pug | 12 -- .../views/pug/pages/app/index.pug | 10 - 37 files changed, 1408 deletions(-) delete mode 100644 aah/app-template/.gitignore delete mode 100644 aah/app-template/aah.project.atmpl delete mode 100644 aah/app-template/app/controllers/app.go.atmpl delete mode 100644 aah/app-template/app/controllers/v1/value.go.atmpl delete mode 100644 aah/app-template/app/init.go.atmpl delete mode 100644 aah/app-template/app/models/greet.go delete mode 100644 aah/app-template/app/models/value.go delete mode 100644 aah/app-template/app/security/authentication_provider.go delete mode 100644 aah/app-template/app/security/authorization_provider.go delete mode 100644 aah/app-template/app/websockets/sample.go.atmpl delete mode 100644 aah/app-template/config/aah.conf.atmpl delete mode 100644 aah/app-template/config/env/dev.conf.atmpl delete mode 100644 aah/app-template/config/env/prod.conf.atmpl delete mode 100644 aah/app-template/config/routes.conf.atmpl delete mode 100644 aah/app-template/config/security.conf.atmpl delete mode 100644 aah/app-template/i18n/messages.en.atmpl delete mode 100644 aah/app-template/static/css/aah.css delete mode 100644 aah/app-template/static/img/aah-framework-logo.png delete mode 100644 aah/app-template/static/img/favicon.ico delete mode 100644 aah/app-template/static/js/aah.js delete mode 100644 aah/app-template/static/robots.txt delete mode 100644 aah/app-template/views/go/common/error_footer.html delete mode 100644 aah/app-template/views/go/common/error_header.html delete mode 100644 aah/app-template/views/go/common/footer_scripts.html delete mode 100644 aah/app-template/views/go/common/head_tags.html delete mode 100644 aah/app-template/views/go/errors/404.html delete mode 100644 aah/app-template/views/go/errors/500.html delete mode 100644 aah/app-template/views/go/layouts/master.html delete mode 100644 aah/app-template/views/go/pages/app/index.html delete mode 100644 aah/app-template/views/pug/common/error_footer.pug delete mode 100644 aah/app-template/views/pug/common/error_header.pug delete mode 100644 aah/app-template/views/pug/common/footer_scripts.pug delete mode 100644 aah/app-template/views/pug/common/head_tags.pug delete mode 100644 aah/app-template/views/pug/errors/404.pug delete mode 100644 aah/app-template/views/pug/errors/500.pug delete mode 100644 aah/app-template/views/pug/layouts/master.pug delete mode 100644 aah/app-template/views/pug/pages/app/index.pug diff --git a/aah/app-template/.gitignore b/aah/app-template/.gitignore deleted file mode 100644 index f9e9b01..0000000 --- a/aah/app-template/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# .gitignore config - -# aah application -aah.go -aah_*_vfs.go -*.pid -build/ -vendor/*/ - -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/aah/app-template/aah.project.atmpl b/aah/app-template/aah.project.atmpl deleted file mode 100644 index 47a5fb7..0000000 --- a/aah/app-template/aah.project.atmpl +++ /dev/null @@ -1,120 +0,0 @@ -# --------------------------------------------------------------------------- -# {{ .App.Name }} - aah framework project -# -# Doc: https://docs.aahframework.org/aah-project-file.html -# Note: Add it to version control -# --------------------------------------------------------------------------- - -# --------------------------------------------------------------------------- -# Build section is used during aah application run and build command. -# -# Doc: https://docs.aahframework.org/aah-project-file.html#section-build -# --------------------------------------------------------------------------- -build { - # Application binary name - # Default value is `name` attribute value from `aah.conf` - #binary_name = "{{ .App.Name }}" - - # Used as fallback if - # - `git commit sha` or - # - `AAH_APP_VERSION` environment value is not available. - version = "0.0.1" - - # If application misses any dependencies in `build import path` - # during compile and build, aah CLI will try to get the missing - # dependencies using 'go get '. - # - # Default value is `true` since v0.11.0 - #dep_get = true - - # `-i` may not be required for go1.10 and above, however no impact. - flags = ["-i"] - - ldflags = "" - - tags = "" - - # AST excludes is used for `aah.Context` inspection and for generating aah - # application main Go file. - # - # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match - ast_excludes = ["*_test.go", ".*", "*.bak", "*.tmp", "vendor"] - - # Packing excludes is used to exclude file/directory during aah application - # build archive. - # - # Note: It is also applicable to aah single binary build. This list is excluded - # from embedding. - # - # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match - excludes = ["*.go", ".*", "*.bak", "*.tmp", "*.pid", - "vendor", "app", "build", "tests", "logs"] -} - -# --------------------------------------------------------------------------- -# Logger configuration for aah CLI tool -# --------------------------------------------------------------------------- -log { - # Log level - # Default value is `info`. - #level = "info" - - # Log colored output - # Default value is `true`. - #color = false -} - -# --------------------------------------------------------------------------- -# Hot-Reload helps developer for rapid development. -# Read more about implementation here - https://aahframework.org/issues/4 -# -# NOTE: Do not use hot-reload for production purposes, it is not recommended. -# Use app binary reload capability - `kill -HUP ` instead. -# --------------------------------------------------------------------------- -hot_reload { - # Default value is `true`. - enable = true - - # Watch configuration - files/directories exclusion list. - # - # For valid pattern syntax, refer to https://golang.org/pkg/path/filepath/#Match - watch { - # Note: static directory is not required to be monitored since aah server - # delivers up-to-date file on environment profile `dev`. - dir_excludes = [".*"] - - file_excludes = [".*", "_test.go", "LICENSE", "README.md"] - } -} - -#------------------------------------------------------------------------ -# Virtual FileSystem (VFS) configuration -# -# Doc: https://docs.aahframework.org/vfs.html -#------------------------------------------------------------------------ -vfs { - # Adding custom mount points. - # - # Configured mount point directories and sub-directories - # are embedded into the aah app binary during single binary build. - # - # Note: Embed packaging uses the exclusion list from config `build.excludes`. - mount { - # Choose a unique key name - #my_mount { - # # Mount point (path separator is '/'). - # mount_path = "/my_mount" - # - # # Physical FileSystem path. - # # Note: Only absolute path is accepted. - # physical_path = "/Users/jeeva/path/to/physical" - #} - } - - # This config speeds up the single binary build process. - # No need to spend time in gzipping for compressed content type. - # - # Even without this config, aah decides best on what to gzip. - no_gzip = [".png", ".jpeg", ".jpg", ".gif", ".bmp", ".tiff", ".tif" - ".woff", ".woff2"] -} diff --git a/aah/app-template/app/controllers/app.go.atmpl b/aah/app-template/app/controllers/app.go.atmpl deleted file mode 100644 index b6a7fc2..0000000 --- a/aah/app-template/app/controllers/app.go.atmpl +++ /dev/null @@ -1,28 +0,0 @@ -package controllers - -import ( - "aahframework.org/aah.v0" - - "{{ .App.ImportPath }}/app/models" -) - -// AppController struct application controller -type AppController struct { - *aah.Context -} - -// Index method is application's {{ if .App.IsWebApp -}}home page.{{ else }}root API endpoint.{{- end }} -func (c *AppController) Index() { - {{- if .App.IsWebApp }} - data := aah.Data{ - "Greet": models.Greet{ - Message: "Welcome to aah framework - Web Application", - }, - } - - c.Reply().Ok().HTML(data) -{{- else }} - c.Reply().Ok().JSON(models.Greet{ - Message: "Welcome to aah framework - API application", - }){{ end }} -} diff --git a/aah/app-template/app/controllers/v1/value.go.atmpl b/aah/app-template/app/controllers/v1/value.go.atmpl deleted file mode 100644 index b8e5dd0..0000000 --- a/aah/app-template/app/controllers/v1/value.go.atmpl +++ /dev/null @@ -1,88 +0,0 @@ -package v1 - -import ( - "fmt" - - "aahframework.org/aah.v0" - "aahframework.org/ahttp.v0" - - "{{ .App.ImportPath }}/app/models" -) - -var values = make(map[string]*models.Value) - -// ValueController is kickstart sample for API implementation. -type ValueController struct { - *aah.Context -} - -// List method returns all the values. -func (c *ValueController) List() { - c.Reply().Ok().JSON(values) -} - -// Index method returns value for given key. -// If key not found then returns 404 NotFound error. -func (c *ValueController) Index(key string) { - if val, found := values[key]; found { - c.Reply().Ok().JSON(val) - return - } - - c.Reply().NotFound().JSON(aah.Data{ - "message": "Value not exists", - }) -} - -// Create method creates new entry in the values map with given payload. -// If key already exists then returns 409 Conflict error. -func (c *ValueController) Create(val *models.Value) { - if _, found := values[val.Key]; found { - c.Reply().Conflict().JSON(aah.Data{ - "message": "Key already exists", - }) - return - } - - // Add it to values map - values[val.Key] = val - newResourceURL := fmt.Sprintf("%s:%s", c.Req.Scheme, c.RouteURL("value_get", val.Key)) - c.Reply().Created(). - Header(ahttp.HeaderLocation, newResourceURL). - JSON(aah.Data{ - "key": val.Key, - }) -} - -// Update method updates value entry on map for given key and Payload. -// If key not exists then returns 400 BadRequest error. -func (c *ValueController) Update(key string, val *models.Value) { - if r, found := values[key]; found { - r.Value = val.Value - values[key] = r - c.Reply().Ok().JSON(aah.Data{ - "message": "Value updated successfully", - }) - return - } - - c.Reply().BadRequest().JSON(aah.Data{ - "message": "Invalid input", - }) -} - -// Delete method deletes value for given key. -// If key not exists then returns 400 BadRequest error. -func (c *ValueController) Delete(key string) { - if _, found := values[key]; found { - delete(values, key) - c.Reply().Ok().JSON(aah.Data{ - "message": "Value deleted successfully", - }) - return - } - - c.Reply().BadRequest().JSON(aah.Data{ - "message": "Invalid input", - }) -} diff --git a/aah/app-template/app/init.go.atmpl b/aah/app-template/app/init.go.atmpl deleted file mode 100644 index 0db256b..0000000 --- a/aah/app-template/app/init.go.atmpl +++ /dev/null @@ -1,193 +0,0 @@ -// aah application initialization - configuration, server extensions, middleware's, etc. -// Customize it per application use case. - -package main - -import ({{ if .App.IsSecurityEnabled }} - "{{ .App.ImportPath }}/app/security"{{ end }} - - "aahframework.org/aah.v0"{{ if .App.IsWebApp }} - - // Registering HTML minifier - _ "github.com/aah-cb/minify"{{ end }}{{ if eq .App.ViewEngine "pug" }} - - // Registering Pug View Engine (formerly known as Jade) - _ "github.com/aah-cb/ve-pug"{{ end }} -) - -func init() { - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Server Extensions - // Doc: https://docs.aahframework.org/server-extension.html - // - // Best Practice: Define a function with meaningful name in a package and - // register it here. Extensions function name gets logged in the log, - // its very helpful to have meaningful log information. - // - // Such as: - // - Dedicated package for config loading - // - Dedicated package for datasource connections - // - etc - //__________________________________________________________________________ - - // Event: OnInit - // Doc: https://docs.aahframework.org/server-extension.html#event-oninit - // - // aah.OnInit(config.LoadRemote) - - // Event: OnStart - // Doc: https://docs.aahframework.org/server-extension.html#event-onstart - // - // aah.OnStart(db.Connect) - // aah.OnStart(cache.Load){{ if or .App.IsWebApp .App.IsAPIApp }} - aah.OnStart(SubscribeHTTPEvents){{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - aah.OnStart(SubscribeWebSocketEvents){{ end }} - - // Event: OnPreShutdown - // Doc: https://docs.aahframework.org/server-extension.html#event-onpreshutdown - // - // aah.OnPreShutdown(notify.AnnounceImGonnaShutdown) - - // Event: OnPostShutdown - // Doc: https://docs.aahframework.org/server-extension.html#event-onpostshutdown - // - // aah.OnPostShutdown(cache.Flush) - // aah.OnPostShutdown(db.Disconnect) - - {{ if not .App.IsWebSocketApp }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Middleware's - // Doc: https://docs.aahframework.org/middleware.html - // - // Executed in the order they are defined. It is recommended; NOT to change - // the order of pre-defined aah framework middleware's. - //__________________________________________________________________________ - aah.AppHTTPEngine().Middlewares( - aah.RouteMiddleware, - aah.CORSMiddleware, - aah.BindMiddleware,{{ if .App.IsWebApp }} - aah.AntiCSRFMiddleware,{{ end }} - aah.AuthcAuthzMiddleware, - - // - // NOTE: Register your Custom middleware's right here - // - - aah.ActionMiddleware, - ) - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Application Error Handler - // Doc: https://docs.aahframework.org/error-handling.html - //__________________________________________________________________________ - // aah.SetErrorHandler(AppErrorHandler){{ end }}{{ if .App.IsWebApp }} - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Custom Template Functions - // Doc: https://docs.aahframework.org/template-funcs.html - //__________________________________________________________________________ - // aah.AddTemplateFunc(template.FuncMap{ - // // ... - // }) - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Custom Session Store - // Doc: https://docs.aahframework.org/session.html - //__________________________________________________________________________ - // aah.AddSessionStore("redis", &RedisSessionStore{}) - - {{ end }}//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Custom Value Parser - // Doc: https://docs.aahframework.org/request-parameters-auto-bind.html - //__________________________________________________________________________ - // if err := aah.AddValueParser(reflect.TypeOf(CustomType{}), customParser); err != nil { - // log.Error(err) - // } - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // Add Custom Validation Functions - // Doc: https://godoc.org/gopkg.in/go-playground/validator.v9 - //__________________________________________________________________________ - // Obtain aah validator instance, then add yours - // validator := aah.Validator() - // - // // Add your validation funcs - -}{{ if or .App.IsWebApp .App.IsAPIApp }} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// HTTP Events -// -// Subscribing HTTP events on app start. -//__________________________________________________________________________ - -func SubscribeHTTPEvents(_ *aah.Event) { - {{ if not .App.IsSecurityEnabled }}//{{ end }} he := aah.AppHTTPEngine() - - // Event: OnRequest - // Doc: https://docs.aahframework.org/server-extension.html#event-onrequest - // - // he.OnRequest(myserverext.OnRequest) - - // Event: OnPreReply - // Doc: https://docs.aahframework.org/server-extension.html#event-onprereply - // - // he.OnPreReply(myserverext.OnPreReply) - - // Event: OnHeaderReply - // Doc: https://docs.aahframework.org/server-extension.html#event-onheaderreply - // - // he.OnHeaderReply(myserverext.OnHeaderReply) - - // Event: OnPostReply - // Doc: https://docs.aahframework.org/server-extension.html#event-onpostreply - // - // he.OnPostReply(myserverext.OnPostReply) - - // Event: OnPreAuth - // Doc: https://docs.aahframework.org/server-extension.html#event-onpreauth - // - // he.OnPreAuth(myserverext.OnPreAuth){{ if .App.IsSecurityEnabled }} - - // Event: OnPostAuth - // Doc: https://docs.aahframework.org/server-extension.html#event-onpostauth - // - he.OnPostAuth(security.PostAuthEvent){{ else }} - - // Event: OnPostAuth - // Doc: https://docs.aahframework.org/server-extension.html#event-onpostauth - // - // he.OnPostAuth(myserverext.PostAuthEvent){{ end }} -} -{{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - -//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ -// WebSocket Events -// Doc: https://docs.aahframework.org/websocket.html#events -// -// Subscribing WebSocket events on app start. -//__________________________________________________________________________ - -func SubscribeWebSocketEvents(_ *aah.Event) { - // wse := aah.AppWSEngine() - - // Custom ID Generator - // - // wse.SetIDGenerator(websockets.MyCustomIDGenerator) - - // Event: OnPreConnect - // - // wse.OnPreConnect(mywebsockets.HandleEvents) - - // Event: OnPostConnect - // - // wse.OnPostConnect(mywebsockets.HandleEvents) - - // Event: OnPostDisconnect - // - // wse.OnPostDisconnect(mywebsockets.HandleEvents) - - // Event: OnError - // - // wse.OnError(mywebsockets.HandleEvents) -}{{ end }} diff --git a/aah/app-template/app/models/greet.go b/aah/app-template/app/models/greet.go deleted file mode 100644 index 0479b35..0000000 --- a/aah/app-template/app/models/greet.go +++ /dev/null @@ -1,6 +0,0 @@ -package models - -// Greet holds the greeting message. -type Greet struct { - Message string `json:"message"` -} diff --git a/aah/app-template/app/models/value.go b/aah/app-template/app/models/value.go deleted file mode 100644 index aa817d9..0000000 --- a/aah/app-template/app/models/value.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -// Value model is used by ValueController -type Value struct { - Key string `json:"key" validate:"required"` - Value interface{} `json:"value" validate:"required"` -} diff --git a/aah/app-template/app/security/authentication_provider.go b/aah/app-template/app/security/authentication_provider.go deleted file mode 100644 index 9ec6d37..0000000 --- a/aah/app-template/app/security/authentication_provider.go +++ /dev/null @@ -1,64 +0,0 @@ -package security - -import ( - "aahframework.org/aah.v0" - "aahframework.org/config.v0" - "aahframework.org/security.v0/authc" -) - -var _ authc.Authenticator = (*AuthenticationProvider)(nil) - -// AuthenticationProvider struct implements `authc.Authenticator` interface. -type AuthenticationProvider struct { -} - -// Init method initializes the AuthenticationProvider, this method gets called -// during server start up. -func (a *AuthenticationProvider) Init(appCfg *config.Config) error { - // NOTE: Init is called on application startup - return nil -} - -// GetAuthenticationInfo method is `authc.Authenticator` interface -func (a *AuthenticationProvider) GetAuthenticationInfo(authcToken *authc.AuthenticationToken) (*authc.AuthenticationInfo, error) { - - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // This code snippet provided as a reference - // - // Call your appropriate datasource here (such as DB, API, LDAP, etc) - // to get the subject (aka user) authentication information. - // - // Form Auth Values from authcToken - // authcToken.Identity => username - // authcToken.Credential => passowrd - //_____________________________________________________________________ - - // user := models.FindUserByEmail(authcToken.Identity) - // if user == nil { - // // No subject exists, return nil and error - // return nil, authc.ErrSubjectNotExists - // } - - // User found, now create authentication info and return to the framework - authcInfo := authc.NewAuthenticationInfo() - // authcInfo.Principals = append(authcInfo.Principals, - // &authc.Principal{ - // Value: user.Email, - // IsPrimary: true, - // Realm: "inmemory", - // }) - // authcInfo.Credential = []byte(user.Password) - // authcInfo.IsLocked = user.IsLocked - // authcInfo.IsExpired = user.IsExpried - - return authcInfo, nil -} - -// PostAuthEvent method used for activities after authentication successful. -func PostAuthEvent(e *aah.Event) { - ctx := e.Data.(*aah.Context) - - ctx.Log().Info("Method security.PostAuthEvent called") - - // Do post successful authentication actions... -} diff --git a/aah/app-template/app/security/authorization_provider.go b/aah/app-template/app/security/authorization_provider.go deleted file mode 100644 index c87cbee..0000000 --- a/aah/app-template/app/security/authorization_provider.go +++ /dev/null @@ -1,41 +0,0 @@ -package security - -import ( - "aahframework.org/config.v0" - "aahframework.org/security.v0/authc" - "aahframework.org/security.v0/authz" -) - -var _ authz.Authorizer = (*AuthorizationProvider)(nil) - -// AuthorizationProvider struct implements `authz.Authorizer` interface. -type AuthorizationProvider struct { -} - -// Init method initializes the AuthorizationProvider, this method gets called -// during server start up. -func (a *AuthorizationProvider) Init(appCfg *config.Config) error { - // NOTE: Init is called on application startup - return nil -} - -// GetAuthorizationInfo method is `authz.Authorizer` interface. -// -// GetAuthorizationInfo method gets called after authentication is successful -// to get Subject's (aka User) access control information such as roles and permissions. -func (a *AuthorizationProvider) GetAuthorizationInfo(authcInfo *authc.AuthenticationInfo) *authz.AuthorizationInfo { - //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ - // This code snippet provided as a reference - // - // Call your appropriate datasource here (such as DB, API, etc) - // to get the subject (aka user) authorization details (roles, permissions) - //__________________________________________________________________________ - - // authorities := models.FindUserByEmail(authcInfo.PrimaryPrincipal().Value) - - authzInfo := authz.NewAuthorizationInfo() - // authzInfo.AddRole(authorities.Roles...) - // authzInfo.AddPermissionString(authorities.Permissions...) - - return authzInfo -} diff --git a/aah/app-template/app/websockets/sample.go.atmpl b/aah/app-template/app/websockets/sample.go.atmpl deleted file mode 100644 index 0c80c2d..0000000 --- a/aah/app-template/app/websockets/sample.go.atmpl +++ /dev/null @@ -1,75 +0,0 @@ -package websockets - -import ( - "strings" - - "aahframework.org/aah.v0" - "aahframework.org/ws.v0" - - "{{ .App.ImportPath }}/app/models" -) - -// SampleWebSocket is aah's sample WebSocket implementation. -type SampleWebSocket struct { - *ws.Context -} - -// Handle method handles Text and JSON data based on Path param value. -func (w *SampleWebSocket) Handle(mode string) { - switch strings.ToLower(mode) { - case "text": - w.handleTextMode() - case "json": - w.handleJSONMode() - } -} - -// handleTextMode method is used to communicate in Text data -func (w *SampleWebSocket) handleTextMode() { - w.Log().Info("Handling mode: text") - - for { - str, err := w.ReadText() - if err != nil { - if ws.IsDisconnected(err) { - // WebSocket client is gone, exit here - return - } - - w.Log().Error(err) - continue // we are moving on to next WS frame - } - - // if no error then echo back Text data to WebSocket client - if err = w.ReplyText(str); err != nil { - w.Log().Error(err) - } - } -} - -// handleJSONMode method is used to communicate in JSON data -func (w *SampleWebSocket) handleJSONMode() { - w.Log().Infof("Handling mode: json") - - for { - var greet models.Greet - if err := w.ReadJSON(&greet); err != nil { - if ws.IsDisconnected(err) { - // WebSocket client is gone, exit here - return - } - - w.Log().Error(err) - - // JSON read error happened - _ = w.ReplyJSON(aah.Data{"message": "invalid JSON"}) - - continue // we are moving on to next WS frame - } - - // if no error then echo back JSON data to WebSocket client - if err := w.ReplyJSON(greet); err != nil { - w.Log().Error(err) - } - } -} diff --git a/aah/app-template/config/aah.conf.atmpl b/aah/app-template/config/aah.conf.atmpl deleted file mode 100644 index a691ec7..0000000 --- a/aah/app-template/config/aah.conf.atmpl +++ /dev/null @@ -1,160 +0,0 @@ -# ----------------------------------------------------------------------------- -# {{ .App.Name }} - aah Application Configuration -# -# Refer documentation to explore and customize the configurations. -# Doc: https://docs.aahframework.org/app-config.html -# ----------------------------------------------------------------------------- - -# Application name (non-whitespace) -# Default value is `basename` of import path. -name = "{{ .App.Name }}" - -# Friendly description of application -desc = "aah {{ .App.Type }} application" - -# Application type, typically these `web`, `api`, `websocket` -type = "{{ .App.Type }}" - -# ----------------------------------------------------------------------------- -# Server configuration -# Doc: https://docs.aahframework.org/app-config.html#section-server -# ----------------------------------------------------------------------------- -server { - # Address is used to bind against host address, IP address, UNIX socket. - # - # For unix socket: unix:/tmp/aahframework.sock - # Default value is `empty` string. - #address = "" - - # Port is used to bind server listener on particular port. - # - # For standard port `80` and `443`, put empty string or a value - # Default value is 8080. - #port = "" - - # Header value is written as HTTP header `Server: aah-go-server`. - # - # To exclude header `Server` from writing, simply comment it out. - header = "aah-go-server"{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - - # -------------------------------------------------------------------------- - # WebSocket configurations - # Doc: https://docs.aahframework.org/websocket.html#configuration - # -------------------------------------------------------------------------- - websocket { - enable = true - origin { - check = false - #whitelist = [ - # "https://websocket.org", - #] - } - }{{ end }} -} - -# ----------------------------------------------------------------------------- -# Request configuration -# Doc: https://docs.aahframework.org/app-config.html#section-request -# ----------------------------------------------------------------------------- -request { - # ----------------------------------------------------------------------------- - # aah framework encourages to have a unique `Request Id` for each incoming - # request. It helps in traceability. If a request already has `X-Request-Id` - # HTTP header, then aah does not generate one. - # - # GUID is generated using MongoDB ObjectId algorithm. - # - # Doc: https://docs.aahframework.org/app-config.html#section-request-id - # ----------------------------------------------------------------------------- - id { - enable = true - } -}{{ if not .App.IsWebSocketApp }} - -# ----------------------------------------------------------------------------- -# Render configuration -# Doc: https://docs.aahframework.org/app-config.html#section-render -# ----------------------------------------------------------------------------- -render { - # aah infers the `Content-Type` automatically, if `Reply()` builder does not have value. - # - # It infers in the order of: - # - URL file extension - supports `.html`, `.htm`, `.json`, `.js`, `.xml` - # and `.txt` - # - Request Accept Header - Most Qualified one as per RFC 7321 - # * Supports vendor type as per RFC 4288 - # - Fallback to `render.default` value - supports `html`, `json`, `xml` and `text` - # - # Default value is `empty` string. - default = "{{ if .App.IsWebApp }}html{{ else }}json{{ end }}" -}{{ if .App.IsWebApp }} - -# ----------------------------------------------------------------------------- -# Cache configuration -# Doc: https://docs.aahframework.org/app-config.html#section-cache -# ----------------------------------------------------------------------------- -cache { - - # ----------------------------------------------------------------------------- - # HTTP Cache-Control Configuration for Static files - # Doc: https://docs.aahframework.org/static-files.html#http-cache-control - # ----------------------------------------------------------------------------- - static { - # Default `Cache-Control` for all static files, if specific is not defined. - default_cache_control = "public, max-age=31536000" - - # Define by mime types, if mime is not present then default is applied. - # Config is very flexible to define by mime type. - # - # Create a unique name and provide `mime` with comma separated value - # and `cache_control`. - # mime_types { - # css_js { - # mime = "text/css, application/javascript" - # cache_control = "public, max-age=604800, proxy-revalidate" - # } - # - # images { - # mime = "image/jpeg, image/png, image/gif, image/svg+xml, image/x-icon" - # cache_control = "public, max-age=2628000, proxy-revalidate" - # } - # } - } -} - -# ----------------------------------------------------------------------------- -# View configuration -# Doc: https://docs.aahframework.org/app-config.html#section-view -# ----------------------------------------------------------------------------- -view { - engine = "{{ .App.ViewEngine }}" - ext = "{{ .App.ViewFileExt }}" - delimiters = "{{ .App.TmplDelimLeft }}.{{ .App.TmplDelimRight }}" -}{{ end }} - -# ----------------------------------------------------------------------------- -# Security configuration -# Doc: https://docs.aahframework.org/security-config.html -# ----------------------------------------------------------------------------- -include "./security.conf"{{ end }} - -# ----------------------------------------------------------------------------- -# Environment Profiles -# -# For e.g.: dev, qa, prod, etc. -# -# Doc: https://docs.aahframework.org/app-config.html#section-env -# ----------------------------------------------------------------------------- -env { - # Active profile name for the application configuration. - # - # For e.g.: To activate environment profile via application binary - # /path/to/binary/aahwebsite -profile prod - # - # Default value is `dev`. - #active = "dev" - - # Environment profile configurations - # Load all the configuration files from `appbasedir/config/env/*.conf`. - include "./env/*.conf" -} diff --git a/aah/app-template/config/env/dev.conf.atmpl b/aah/app-template/config/env/dev.conf.atmpl deleted file mode 100644 index 1de01fa..0000000 --- a/aah/app-template/config/env/dev.conf.atmpl +++ /dev/null @@ -1,21 +0,0 @@ -# ----------------------------------------------------------------------------- -# Development Configuration -# ----------------------------------------------------------------------------- - -dev { - - # ----------------------------------------------------------------------------- - # Log Configuration - # - # Doc: https://docs.aahframework.org/logging.html - # Doc: https://docs.aahframework.org/log-config.html - # ----------------------------------------------------------------------------- - log { - receiver = "console" - level = "debug" - format = "text" - pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" - color = true - } - -} diff --git a/aah/app-template/config/env/prod.conf.atmpl b/aah/app-template/config/env/prod.conf.atmpl deleted file mode 100644 index c006293..0000000 --- a/aah/app-template/config/env/prod.conf.atmpl +++ /dev/null @@ -1,35 +0,0 @@ -# ----------------------------------------------------------------------------- -# Production Configuration -# ----------------------------------------------------------------------------- - -prod { - - # ----------------------------------------------------------------------------- - # Runtime configuration - # Doc: https://docs.aahframework.org/app-config.html#section-runtime - # ----------------------------------------------------------------------------- - runtime { - debug { - strip_src_base = true - } - } - - # ----------------------------------------------------------------------------- - # Log Configuration - # - # Doc: https://docs.aahframework.org/logging.html - # Doc: https://docs.aahframework.org/log-config.html - # ----------------------------------------------------------------------------- - log { - receiver = "file" - file = "{{ .App.Name }}.log" - level = "warn" - format = "text" - pattern = "%time:2006-01-02 15:04:05.000 %level:-5 %appname %insname %reqid %principal %message %fields" - color = false - rotate { - policy = "daily" - } - } - -} diff --git a/aah/app-template/config/routes.conf.atmpl b/aah/app-template/config/routes.conf.atmpl deleted file mode 100644 index aab0caa..0000000 --- a/aah/app-template/config/routes.conf.atmpl +++ /dev/null @@ -1,123 +0,0 @@ -# ----------------------------------------------------------------------------- -# {{ .App.Name }} - Application Routes Configuration -# -# Refer documentation to explore and configure routes. -# Doc: https://docs.aahframework.org/routing.html -# ----------------------------------------------------------------------------- - -domains { - #------------------------------------------------------------------------------ - # Domain/subdomain Key Name - # Choose a `unique keyname` to define domain section and its configuration. - # Tip: domain name address, port no values could be used to create a - # meaningful domain key, etc. - # - # Doc: https://docs.aahframework.org/routes-config.html#domain-configuration - #------------------------------------------------------------------------------ - {{ .App.DomainNameKey }} { - name = "{{ .App.Name }} routes" - host = "localhost"{{ if not .App.IsWebSocketApp }} - anti_csrf_check = {{ if .App.IsAPIApp }}false{{ else }}true{{ end }} - {{ end }} {{ if .App.CORSEnable }} - - #---------------------------------------------------------------------------- - # CORS (Cross-Origin Resource Sharing) - # Doc: https://docs.aahframework.org/cors.html - #---------------------------------------------------------------------------- - cors { - enable = true - allow_origins = ["*"] - } - {{ end }} {{ if .App.IsWebApp }} - - #------------------------------------------------------------------------------ - # Static files Routes Configuration - # To serve static files, it can be directory or individual file. - # This section optional one, for e.g: RESTful APIs doesn't need this section. - # Static files are delivered via `http.ServeContent`. - # - # Supported features: - # - Serve directory - # - Serve individual file - # - Directory listing - # - # Choose an `unique name` for each `directory` or `individual` file - # static route definition. It is called `route name`. - # - # Doc: https://docs.aahframework.org/routes-config.html#section-static - #------------------------------------------------------------------------------ - static { - public_assets { - path = "/static" - dir = "static" - } - favicon { - path = "/favicon.ico" - file = "img/favicon.ico" - } - robots_txt { - path = "/robots.txt" - file = "robots.txt" - } - } - {{ end }} - - # ----------------------------------------------------------------------------- - # Routes Configuration - # - # Doc: https://docs.aahframework.org/routes-config.html#routes-configuration - # ----------------------------------------------------------------------------- - routes { - {{ if not .App.IsWebSocketApp }}index { - path = "/" - controller = "AppController" - auth = "anonymous" - }{{ end }} {{ if or .App.IsAPIApp .App.IsSubTypeAPI }} - - # Definition demonstrates part of aah routes configuration capabilities - api_v1 { - path = "{{ if .App.IsWebApp }}/api{{ end }}/v1" - auth = "anonymous"{{ if .App.IsWebApp }} - anti_csrf_check = false{{ end }} - - routes { - all_values { - path = "/values" - controller = "{{ if .App.IsWebApp }}api/{{ end }}v1/ValueController" - action = "List" - - routes { - value_create { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values - method = "POST" - } - value_get { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key - path = "/:key" - - routes { - value_update { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key - method = "PUT" - } - value_delete { # {{ if .App.IsWebApp }}/api{{ end }}/v1/values/:key - method = "DELETE" - } - } - } # end - value_get - } - } # end - all_values - } - } # end - api_v1 {{ end }}{{ if or .App.IsWebSocketApp .App.IsSubTypeWebSocket }} - - # WebSocket routes definitions - # Sample implementation handles Text and JSON mode - ws_sample { - path = "/ws/:mode" - method = "WS" - websocket = "SampleWebSocket" - action = "Handle" - }{{ end }} - - } # end - routes - - } # end - localhost - -} # end - domains diff --git a/aah/app-template/config/security.conf.atmpl b/aah/app-template/config/security.conf.atmpl deleted file mode 100644 index 7a3122e..0000000 --- a/aah/app-template/config/security.conf.atmpl +++ /dev/null @@ -1,165 +0,0 @@ -# ----------------------------------------------------------------------------- -# {{ .App.Name }} - Application Security Configuration -# -# Refer documentation to explore and customize the configurations. -# Doc: https://docs.aahframework.org/security-config.html -# ----------------------------------------------------------------------------- - -security { - - # ----------------------------------------------------------------------------- - # Auth Schemes configuration - # Doc: https://docs.aahframework.org/authentication.html - # ----------------------------------------------------------------------------- - auth_schemes { {{ if (isauth . "form") }} - - # ----------------------------------------------------------------------------- - # Form auth scheme - # Choose a unique key name. It gets used as route auth. - # - # Doc: https://docs.aahframework.org/auth-schemes/form.html - # ----------------------------------------------------------------------------- - form_auth { - scheme = "form" - authenticator = "security/AuthenticationProvider" - authorizer = "security/AuthorizationProvider" - - # Password encoder - # Doc: https://docs.aahframework.org/password-encoders.html - password_encoder = "{{ .App.PasswordEncoderAlgo }}" - }{{ end }} {{ if (isauth . "basic") }} - - # ----------------------------------------------------------------------------- - # Basic auth scheme - # Choose a unique key name. It gets used as route auth. - # - # Doc: https://docs.aahframework.org/auth-schemes/basic.html - # ----------------------------------------------------------------------------- - basic_auth { - scheme = "basic" - realm_name = "Protected" {{ if eq .App.BasicAuthMode "file-realm" }} - file_realm = "{{ .App.BasicAuthFileRealmPath }}"{{ else }} - authenticator = "security/AuthenticationProvider" - authorizer = "security/AuthorizationProvider"{{ end }} - - # Password encoder - # Doc: https://docs.aahframework.org/password-encoders.html - password_encoder = "{{ .App.PasswordEncoderAlgo }}" - }{{ end }} {{ if (isauth . "generic") }} - - # ----------------------------------------------------------------------------- - # Generic auth scheme - # Choose a unique key name. It gets used as route auth. - # - # Doc: https://docs.aahframework.org/auth-schemes/generic.html - # ----------------------------------------------------------------------------- - generic_auth { - scheme = "generic" - authenticator = "security/AuthenticationProvider" - authorizer = "security/AuthorizationProvider" - }{{ end }} - } - - # ----------------------------------------------------------------------------- - # Password Encoders Configuration - # aah supports `bcrypt`, `scrypt`, `pbkdf2` password algorithm. - # - # Doc: https://docs.aahframework.org/password-encoders.html - # ----------------------------------------------------------------------------- - password_encoder { {{ if eq .App.PasswordEncoderAlgo "bcrypt" }} - - bcrypt { - enable = true - cost = 12 - }{{ end }} {{ if eq .App.PasswordEncoderAlgo "scrypt" }} - - scrypt { - enable = true - }{{ end }} {{ if eq .App.PasswordEncoderAlgo "pbkdf2" }} - - pbkdf2 { - enable = true - }{{ end }} - } {{ if .App.IsWebApp }}{{ if .App.IsSessionConfigRequired }} - - # ----------------------------------------------------------------------------- - # Session configuration - # HTTP state management across multiple requests. - # - # Doc: https://docs.aahframework.org/security-config.html#section-session - # ----------------------------------------------------------------------------- - session { - mode = "{{ if eq .App.AuthScheme "" }}stateless{{ else }}stateful{{ end }}" - store { - type = "{{ .App.SessionStore }}"{{ if eq .App.SessionStore "file" }} - filepath = "{{ .App.SessionFileStorePath }}"{{ end }} - } - prefix = "aah_{{ .App.Name }}" - sign_key = "{{ securerandomstring 64 }}" - enc_key = "{{ securerandomstring 32 }}" - }{{ end }}{{ end }} {{ if .App.IsWebApp }} - - # ----------------------------------------------------------------------------- - # Anti-CSRF (Cross Site Request Forgery protection) - # - # Doc: https://docs.aahframework.org/anti-csrf-protection.html - # ----------------------------------------------------------------------------- - anti_csrf { - enable = true - sign_key = "{{ securerandomstring 64 }}" - enc_key = "{{ securerandomstring 32 }}" - }{{ end }} - - # --------------------------------------------------------------------------- - # HTTP Secure Header(s) - # Application security headers with many safe defaults. - # - # Doc: https://docs.aahframework.org/security-config.html#section-http-header - # - # Tip: Quick way to verify secure headers - https://securityheaders.io - # --------------------------------------------------------------------------- - http_header { - enable = true - - # X-XSS-Protection - {{ if .App.IsWebApp -}}#xxssp = "1; mode=block"{{ else }}xxssp = ""{{ end }} - - # X-Content-Type-Options - #xcto = "nosniff" - - # X-Frame-Options - {{ if .App.IsWebApp -}}#xfo = "SAMEORIGIN"{{ else }}xfo = "DENY"{{ end }} - - # Referrer-Policy - {{ if .App.IsWebApp -}}#rp = "no-referrer-when-downgrade"{{ else }}rp = ""{{ end }} - - # Strict-Transport-Security (STS, aka HSTS) - sts { - #max_age = "720h" - } - - # Content-Security-Policy (CSP) - csp { - #directives = "" - report_uri = "" - report_only = true - } - - # Public-Key-Pins PKP (aka HPKP) - pkp { - # The Base64 encoded Subject Public Key Information (SPKI) fingerprint. - # These values gets added as `pin-sha256=; ...`. - #keys = [ - #"X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg=", - #"MHJYVThihUrJcxW6wcqyOISTXIsInsdj3xK8QrZbHec=" - #] - - max_age = "720h" - report_uri = "" - report_only = true - } - - # X-Permitted-Cross-Domain-Policies - {{ if .App.IsWebApp -}}#xpcdp = "master-only"{{ else }}xpcdp = ""{{ end }} - } -} diff --git a/aah/app-template/i18n/messages.en.atmpl b/aah/app-template/i18n/messages.en.atmpl deleted file mode 100644 index ed1b59e..0000000 --- a/aah/app-template/i18n/messages.en.atmpl +++ /dev/null @@ -1,17 +0,0 @@ -# --------------------------------------------------------------------------- -# i18n messages for {{ .App.Name }} application -# -# Doc: https://docs.aahframework.org/i18n.html -# --------------------------------------------------------------------------- - -# This structure is example purpose -# So choose your suitable structure for your application -label { - pages { - app { - index { - title = "aah framework application - Home" - } - } - } -} diff --git a/aah/app-template/static/css/aah.css b/aah/app-template/static/css/aah.css deleted file mode 100644 index fd463e9..0000000 --- a/aah/app-template/static/css/aah.css +++ /dev/null @@ -1,47 +0,0 @@ -/* -Minimal aah framework application template CSS. -Based on your need choose your CSS framework. -*/ - -html { - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -body { - margin: 0; -} - -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} - -.row { - margin-right: -15px; - margin-left: -15px; -} - -.text-center { - text-align: center; -} - -.welcome-msg { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; -} diff --git a/aah/app-template/static/img/aah-framework-logo.png b/aah/app-template/static/img/aah-framework-logo.png deleted file mode 100644 index 4287e4adbeb33dc33b1ad4f03432cbc71c654a75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6990 zcmV-U8?oexP)s0)sdM0wOSA5W+w>%#96(7$IXU zFkpi~mJBkd(S4y4A)y1ENGpNvMF$c>_p!U3_xoyIb-Vl9?V0^{W_Ffus-9+6GxL4j z@BRB-{a(+X)WYNV^Twpnp9=k%&}Vqv%x8G-AfZk|Ul%$<=u)A+Lf56x#X_eGognnD zLVqK4Kr`1qfa}}$L&_iJ5_&q*|F$5w%Z2(24HKF#v|ebZP@|9$YU0@^?>#=dU1*)q zY@t_$9u&Gr=xCw8OalaCV~jZv+FJ1GLT7b$F9>Fm&_`B?P#{#E8w=EsO8UNjC%leQ zz0hc(I}4iS^Zt0DTq|S~`~fLUJVdCs&?`b4Qv{Ci_B+&|0&NW`SiEaCrQCP5(DOp) zpo!cu=USl^g7;SYfkHhCYIs|qs^fw-87aJ`3$)O*Y!VtM^c|r;&w=% z5#ONZZ9UHL7R`;uMz6?hnv`Ujdq6?7|yQ<&!TbtyDWm^{7PG97pB=;Zmt^Mx+p*}gvE ze0@JN2-NXjMz+xN(c0YZQ|3H9_xOt!KF>$?7-70Nbj0Z+%+Y6!()ZgkG8h!m0-06+ z{khXu8VFeJSKK+n{NUbq%vJZyG*{g{(_H)GS>~)hQ_L}ETM)FNKX6I9KagGZAEz5T zx!o_0j_End{MY|XFrRGSXLvLSRkQ;@x+ zYg(@QO9#MrE*o#Qe6rW@XlQ6M`}Q{)9?!q_K?DGqOu@h`rXWrA_sLa%YXLC9Y~7Xs zz%;4`XnO!aO`ay`?t!3W;j8-ig3x;P`g7s89so_{1%QG;HT29#mayp6-!EVNH3ERp zA-uwqWy!0*6ES~Y)KMb<2pvLl1Apylfs#D`r*-w`!f#suJojq(0N`oto6@yFeDxnD zv@>7*j7XV#m<*QZmzsaIr^+)%MO5N zUQPvo9n*Q)$KsiRp&5EU%LdkFR}ir1|QZ2_?H=|8~x3CduS^ zt^^qD*qr+w+jBJI(Xla3gvRIi4D-&)8ygq)7sax?$vJOF!nm(XqMjuUxmo{OmW20VgXq z!u&V@*b&qy;ND*@aNmQP8H-7t-z20??V9)sI*!shj5@m}zg6mAR~F9*|F(Yb znhjR)tSFGKD=Q%WA4c#Qcj6OgZZM~Of1)`;t_h*ZG$W0KFb7oa^&wDDcDQS0jMYu{fW7>cwSf0<=;%j<^}< zyx@HT&VX@OeFT_C~`48}Cj;1+0I&59iN-0hZBC=H3AdSZ%&Jh?ig$R{kT; zzQ<)<0ouQXtUyAQoBlm(zsF34`J=d>s^L}vz+T(sL-@>i?ccVjPrOiPe)7aZbKm0&%r6ElO`O*i zYq$ULcv6@@Y~osqJw4_}0gwtbeZ73uHuL*YtIU8Qb>>$uEHlI99vftwY2#qg@nGx$ zkK!OWNzH(3|6lY0kj~)YpNG>bP95VJ#<+BxJd?zt zcoPlhfHGwMW4~P*Fh33e`>bOEaMwRDTQut^#9;m2`5FJF$mRcUngb+hO>eQxs)M# zO|^9rl5NG_Ml}GRA*u4gn(cfou&q2lEuj}e(HsTi@W-dZWUc@NKVsXCeZe&~r0aF_ zTg+7f8UsKed>mB9&6t#5E-}tSPkMte5MjFja&rnE5Tky;D2)UZ0EfjYez-7$S+sns zWrp+2N+x;HiGh&aDk5{m=kR0?lL_{F#@cLef`nn-1Vhb))Ayu!$L{xBNA8YC=b1yh zzoAUlbTyRfNGdoEKY5rWil!2i>-ZF)DnLR_fLyc>px0O>A?APlcM_@sf`DK8n$Iu} zO3UqatcI)KxC@Pj#x2$atQI=ZjRJaj@qgf0p^onO`8-!$L+ZJcMMJeSkz&I)b)oQ@=nbiaiqv|)v1TRXTAm1l_+Fde^?h0|${zJcBjPOGy$8!lVAMmjPNff0ClBk6o zfhLNI2Ea`Z&EeY0vlhp~XAT#0_1gF~Dg*ag(rZ8-g)tO;2vuhn0JnJdKVUK;$DBo* z?J9L{e!BpJ!7<2I#_imLgpGd>U1=H>07kwO_$4@+npkp$KlZH;0WaSxR`?MEW#XCwDXQ?)oiWchp(LYkLE5Gqz}bhbSOq4!sB5AL%| zQnzRPU=pFBjxU-C96o8C0<-9Gp#i@ks(zzJ2F~?f8@EQUSF9s=vkQPpo(b?*7S z_1q|G3}-1mkvJ*XW-Z+8JB92i_}(s`N4S_DQO^wllSc@Xi_ay*Lrnaehb!V0>;(+^!wR{cCcBEwb3mhsWC|d(_aFDxhXpwl9SJt?dAu|BT`;&B0hD8Cu-;WQe3w(g>JNKEBukdr^+38qM05uGNz3c!3fb&`c z02k!Kqo>vfs9&pgwk=lKxweRdT|>QB3jok4fL+7X4C1x-x!JMo<_U-L^s8L|q~-ze z)ldVZhi2?ZrR4$0Pb-tN#@bv16-#uX0&Nihng{6FcM3anK_1|x(W^Ap&OA2d1Cfa3 zL&wknApkaN>ukG#un zBZ4e>H;}QBO=M4D+ji&rkTEr{0it1`lGeq$n<4J=VyP4O)+OT^1WdCTF(%i1PDM2DCBS}cX-4B5HK46qK$k(X)T}ZF)Y?56x;sg zjgSA+O)2h0zXH2&DZ=Mb^NFBnDp<)4yD0SJp`N!}#|NV+Y-H1fw5is%!E{thFjU7#rhae7t772>*O(V;eqkor2N_RLzEg zVJbMj8Kg{BQ@|jo14u0lg;ed*$wGxItzxuUQTRx8FsWNIi&i*3Q%i~h0#oql^Gj@I z&;x0m$B*^cuAcfN(P?+zyU+1W|&8oo6+r z_XYT>yoK8PkhvbyV*m)#wX9^ys#Oy}M#ERVc4TZi4-edWjgA0-5iJyi#A9}1YhI6> zyiNz>)CyQeF=1u8e#UDX(=BF=-6`*)zOMuTgbo~D#X-ZCO01LfTg-6n*dgt<3;nyz)lp zl4&QEdvGrv+*=D(>BaDhU!o;p@uJXpMK9U_Ml(@XD3eWfmcqX+kzk_1l!yR85q7a1 z96wko-W^PKk?Y?1=sZ@AcYnVdIQ*Lk0Kb41Uu+<%CfQd8KmudofM9RWT>cj=@Oo&iS>f`1mt7PTc?R2yRT{ZOJ)AvrJ zCDlKqSh3rhuKh-@akARqr_cjc$>);DqVEH<5U75x=oB-rxjtj0<7ddQm4FV_oUC*= zlG!22Br1M}y}1@pQu4;UXk@|fNy6KE#Bm%tVikS+3H@&PO3W4xa{?iz=g3A9+s~KE z_-FuO??7`D5c>>%moghxhZn{L@tW1Asb;4TMX$wtl=Nhj)^t8|4?2j?h| zD=hr*fQaZ3F%>4=-lf%w(yO2W;|~+oM!J2fPg*@pdRe0jHw1iY=yIJydB+kzA|`%F zR5eJ8Lngev-tZvVp&L&hONOv}?>8|Kx`L`kk)2bHlF{)ad_6CT^Ad45%df@aheXAX ziK`feyN+zmr1s&l!IUIXwD_d`2w%67F;##z*od07V6zvmXQr&j#KjMatOn_cA8S6` z>DT(PPrwPJeyv~CNwbfDcjV;yH1Lv<^t*yHQuc!)+nE_i@B3H#>9#|6_9}j>F8m7< z;ZuE-Ry#>GRpkL2KPonUSag*=Y09zE7|Q9Ndf;-EKoIVbvS+YvgKDkR7V^r0Hhx%i zhU4NZs7^W6%T=dt*>}(DrjXvPygv}0nu_h$o(2`Vwfze>6TVh%Z1LmbYvTtav?F&s z5rH)Yss->7XeQ+|stGE+@_8m|;owGB_zG6GjoFrFBP)^xto{#1RR1Sq!qtUp#p&Z9 zY8Ad;j7mc(+QK~usv;VSaFl(7SI4e#`x7Del(X2@Z$*AGrY3(hN*T=x`~F=sY)^uB zp5VqUyE$Ko!<_hD)Wc>K zQEOn!v7CB6E&cwbq*}4H+OG&@*9d&q3LcHbSgyQlrddsx%6L+QKV#k|s*iDIWBV7C z{@J+N`{78p9sI$k7OCJNzrd9KJ$|#F6$%h^S1x@gGQL}=Tf>|Y#Qiy}ik7p~B2g9o z94T+(e1c;tovVodaAb}BbZl5_fDow9E2!K*cDmfxsOGEgnOUe-8jcgjYIOJW5v)gc z-(Js0;wN1I(wWyxrk0Nr%bcRn;52a}0rg!nIDKXN>DU_l@#t`mT~#o}n*Qk^LFqTO zfTWKvmUDek;RsW=nvWojx zVA=QSjp#0ZLF7o8W@Ywsv?;KUs@f+w1KMsI+L~xwYK1Evf01z)t$1FTHXeqxPpg;&a zg3Aj@z~H6i?QtGC0vBhG<5y`GVbg{a(P;QU;Peb6%^lj>o`iN`IlEG| zRsWzORDWhu(8b9OG*%1*+4}&r^-M!RoJanTW)|`T2$vl_KBJYq^bF7(Vh@k-ei*wQ z2*1IJ`45Vy{x&lOz1<99djvpw*Ra=A;q?IopNVA;t1o=zJ-%7D$m(x%5DZ`t+&rQM z+9@EQC}`Rzg?7Jh#O}8_2(+POOqx@eQ+b8&ExaenE&LDw*2Wwxv?K>X)r3DXlI<%i z2-MabDpaoyNe+UFdHl1f;?@P34FK8wf#Zd?Jw*JhQ&`#n3f&Nx(NOp`3xWfjNp*9yLF1>!EQEqr@J}FeN>%xB;n%E5z_YzoXq!5T9Tf=P zoc4S5d%fUC29;m)+TcrtD%O1+Od~Y4u;Tx}o%820Ac~@RB%?t=qtMD0TZlxhl&D0r zy4^&f_6@Y6)%XAkmC$IEKEO7c_yC$M&@EeyKc_hI#CZ{$k*q(j_!UgZxaYik?=yF1 z1zI2>@vw{kfJV@CmjQhS>NIFu!#xO3G^?#a=cvgG1z7cumkmf3^uRCJX&?q2Aq|>frejc=8E!;$|Kxn8ygN53zw^BC&Im`Zqu>n;O~WX))fYpM zD+;ZG+qba`_A~_$b}K%J5wt$0IFUR6^E1sB5o7$7jVV7wCvp$Z-|vFTp|Nwq2Q-2N zQPXA|6xmHrz*2lz^>W-uf;kW%BnjU({I zTie%?ss$-6KisE(f+3Ce4ssx7%UEu;|Bo3w6Li7jb@W%R)H0}Yj=pZ;1C(f_7Z4Y- g5x(!q@jDZK53;LH!%^9DNB{r;07*qoM6N<$f-Wj%NdN!< diff --git a/aah/app-template/static/img/favicon.ico b/aah/app-template/static/img/favicon.ico deleted file mode 100644 index 8cc419e5a6bd02e7aba5e0acfc64290c0a31c785..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmdU03vgD&6~2m{c3MZMjx&8sh3T}@PN(B&%S%wf*QjH~DhP-mf+B*74^$KoqA;K$ zf(SkU6%-Mwh=M@g`4h;KguEdMB>8y+2;`kW-r0WNxp)8E|C#)M(xLfhJ~sE>-LvPM zJ-d7M?7=WDGx`}f++d&_WNiDhVGK14W6&V$d4OS*;N1X}{QmS6hOrD4{)#$iVmuD; zz5M=U7QOjzgu0oMYz0&{>*fe0W2$OrO(bYLIwG4Kp961WDqtT%ZHh8Ft13b+ec z4UoSopbb!6fORT?RA2=#8n`l;zI##^ef=7k3d8{yfUfxM>U%wCL;@3mt9rs)pthj> zGvGdee768y@fGa%CeWna-3ee8^xhBjehbh}YJp(+3bsD&a5->oZ%6<-Hv(}$2M{bT zq1JClU!3oM>xm3#&$)FN2$heXw#!7{!$Oh4;{E6)oNKTJi88=@jnMoqd|#XL4zMNb)}q8=HcA|4(JnBPL6zYA@y4kQ4+ zUje)q0&U@!Jjc(vUuCaeppNg^rp{#?P}N7WR8@Y4I-4A=j{Rr7N`LDa6*KjAQ;y)q zmveeCaJfGj@a-O;8VJf>s8`2~dsWfak5t1cr|M{LSNyfNwy4&N4XUm2qU&8-bCasf zJ*cu)zNVrcz1ftz`~8)Jw_AMikN5on?t?+|p7Q!MRdx7~QXL(t^+JOxPm5QFH@>S5 zzBx-JzdA*wyf#%GdV8)a{&u6PEj|oc+EiOpqdF1(t%`f{ZWS?pXfS>51dqS;lmYK1 z0nNeiPrauud{Q-(IYr;pEA1ot(OYB=(AG_xhuvdN@Ne>aVWRkn0S+N?%E>#S7fC?ulERF-K9Y})0aG_nro{tM=q$0 zWiOyT)~4?9P=R`bZT!E}&2~bc`b(Vqh-#`jr&=58)xof*%{8-oecN~(rb>R;EIKGp zjZ@K+Z?%2vQkM(JKlr{HND7vJ>Wg#Mx%+Fe_h8!Vx}t}dCaUv?5@mgjfBIj+%!4S< z`4uo6IIn5iOVZc#&i=R5f&FlQ;=W{WqyM(o*MD<=;CG>E?Qwh-I)3fn+z)-stCMK^ zci<_Ym2_ON`Z@5zc$NR@GF7_kE9iNPtcgi4PXf;)Wo^t^vj}te-)=hNyyjd?nLk|> zZeFdNKW$YdJ2$J`4;QP1*<(};_TtPHud1x~Ustiy?vS>rZ_H46A1_g)tz{Hmz(?$~ zJ5|o=g(~~~`Plbvvwv?yoe2Q_dN+V zen6d!`d+m()Y+S1Yv_>mrg+C^m^bHT-X_9!IA>4A{3vycwybs0B2T2pIxTh8s@Gzyd75>S|y+ z%fs4EGqtxjo z{`Z1+`le!6v5((@H6>H%)0dHb9eUOB(KgA?;SKMIF17x{ANq&bE^Ukc&Y|U4%f}7w zY-iEuyf_v8LvQjQtNEwy>8sFgxwr3s?jf;17w>wGKnBOm`{1XQ3Lmte@CR>HdF$Si z=ZdUkzw0>X3HJ*6KFzh&yAQ5awZbRJ6Cc-YEzta<U&=;d_s9XBOKKY0Kx$e~-%g0=7hhO=U zqWv8CY=!WDCNWY)VsG#oH^}Wa#uXT2t|u7x&h_WRVAmhcUvvEgU-6b*w70g3f9_?& z0&3~p0JQE$>@V1=6>rWh$J?VUAk}CM( zeVHe$V;f7?-{kASoN;niugc4SEG4SWS%kef3g`NTu=(5Ec(?Ok7Wb1nzIVHD^ZBIt-5MR9T%MZHZ-*5cN;y?8GVPKsFl!<;&#?t4!&T<}l z=`wz3$@m#|Lu1{o`CUN^3NaY_1XBjDS^l^8glf>`wI^l`sDqQO@=;u ze`k}S&)%QfWazW^*IEXanA5)orUG$5pUEGv%V5cI75Kjk;Q2Eh;Jx|(eg1|IIWB4b zj8ZlIl`;$$+6-e9pKA=GKTC&U^kW%@JR+790RMEbTx=Mqjs}L&#B!ivpjl_x-!Pnf z#!ZPX9enO*7`hCzo~1-KkY~xyJ^6*Pu@AcP-o}T6_3-RuY2#PqfgD;cEvJ@S>j5ee zy}0z`u=J+&sP&49rmjuB^HO^dFdA^>X$`{RQ9}?u%AW-qB|MsQd9%;`?<#Y`6Nx z*;)J7{?7kC=+H)g=cjMpnSJS_jhoCmzT$Z5Ud8))#-mafJR$Kl#z*z{0Cd*l+b=wM zz`Geh!0~46Bk{%Ya(BhM2;SW=?#Gfbxsy>lRr0*Yx{66$L_bsP{o{EcKwEW_)$JMj z&V2V}b>Z}J#QF*q&y;D1;qktf^7HPVcRA!Icio$y<2zPt(d6BRYaH-5=c?bnsZ+*r zd4F&!c9$9FlGvxtBQRsAjEy<>e50CcDpl5d^8$?p=jLqc0Od76qTl`@H{;^WVc5iW<2~#v`PIo1w`AUjTO7hx&uowS z&+uf)Wi`IgHFI5p6gDSbU>3axC76R zQ=AKYX53uvLNO1RFC_UJx^I+>zcHR|_1A&&HDI?qgC^s8v?IpI885zg_LRiJk7G>F zBt}Vl>Vq*u#;@yn}@Lt|_kFg!!b6AIQ zMAqj$72`g%;iKOmZ^+7Fq0Mvb87JlVHrG@MEykaDkF4(w%hMcU1B|PmPS|Jbf2720 z&nCy%`%gjNrId+rXWA|Ai)0;Pj2~c*59btPwbWhqss)O7sB%AycKR-W@#(h43#$BJ zd@%iU-!7%i6l`84cNrXKyM5Chs7uE6DL?PAsu3sGcO=wZy(gupQt`l41_%kjqv1ZVr zzNojdxZN(EpgY>%63$6}=N&>L?$3DV#&w*zB)s?F_@^$I4!(xS+Q)ks#{QY-kUVdS z_pl{w$kRRqe@Sdfmp;mJEtk}1nf3p6V_w@He&fStjzoWJPLgq^9X3n78t|DSwn}|OBY%#vw_d0hU6Bs&pqVFV%f->_Pv`mzJuvr< zGG(rK1?$&rSwl$Ev@>&$;4}5beJ2Y!8@x+0a}UjW?4SClKB-6EWt#aRBV2NTo~*yt z9^khBkUn)C*stK)Z0$u6_+JCJ62ZEnmyNo^gTla6be-C(}A zm;Q$b4Yx6|f1}Mazvk$+Pkhz!@;|KpyXAk``w8?n?Tl-Zk2*g6FN=rkP(Rj38((xn zdFR*vWdHo3?@Q)?n;Zxa1Hs27Ie*xAxMb%q%>zs5;gXy`?L1g~{7=r`5~gwVlh6S} z35Y`sWN5*OXa(UwxPb^m7=bWMpz!&VI$@~eu-Z8-I;2V3u5bnWM5uys3Wg;H0j@Yj z7yru!W5WBW5x_biAE>s#|C6v57!Dxn;{C&W#wp(f&H(=A@M(No1Rz4w`NQ)7U=zUg zTiVKAfjyDW9{T`eh8qE)f%0-73~;rjpGQA}_q$vxnHRxa7w)mBN07!sfU&%rfHFXT zliUa9xuz|8O7=JU1I$%pE{Wc+QU3%m1Xx2q&D@U$i=TCL+oy82VV)0t8RjcxES;nB zKUr$Fr=L$u7`_r^4$n5(tL6*8c^@y8ez_0lB8QUaLH3_Af0~>VnfsZ9v!R*GG&BSL zMJ01KkA1sA`Z#wmLFg19cZ4~S;_GCmDBiU?cW;%P6#C$#88LQnIo9$@`tHmv+B@cY z;e);n_Zj9m?|a~S$qAL*PAm78zPWBs{+T0D_~mNxQ<%@dv-?rx(=l(3{uO;&%FqAP zK%as6rRlhTgdYh0hpwUjOaG5L=UJHJ$1_mDrd4uwU|pVhm~%|N@;+K(`a9!?t%MG5 z0uN={m(eBltH+yT!?O*~Fgz=nb%yEj^R^$GEr7n*1^2zQ#WTeR!#kJ#(2DWwWxu+e uy|j2^+l_$d90+;-=$QnXLC@bN#DJaP#DQ5IMsOwa^W!(xVO_Ri+y4S%cH$iX diff --git a/aah/app-template/static/js/aah.js b/aah/app-template/static/js/aah.js deleted file mode 100644 index e69de29..0000000 diff --git a/aah/app-template/static/robots.txt b/aah/app-template/static/robots.txt deleted file mode 100644 index 9d0006b..0000000 --- a/aah/app-template/static/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Prevents all robots visiting your site. -User-agent: * -Disallow: / diff --git a/aah/app-template/views/go/common/error_footer.html b/aah/app-template/views/go/common/error_footer.html deleted file mode 100644 index 8b13789..0000000 --- a/aah/app-template/views/go/common/error_footer.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/aah/app-template/views/go/common/error_header.html b/aah/app-template/views/go/common/error_header.html deleted file mode 100644 index ae05058..0000000 --- a/aah/app-template/views/go/common/error_header.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - {{ .Error.Code }} {{ .Error.Message }} - - - - diff --git a/aah/app-template/views/go/common/footer_scripts.html b/aah/app-template/views/go/common/footer_scripts.html deleted file mode 100644 index ce1fb71..0000000 --- a/aah/app-template/views/go/common/footer_scripts.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/aah/app-template/views/go/common/head_tags.html b/aah/app-template/views/go/common/head_tags.html deleted file mode 100644 index db19154..0000000 --- a/aah/app-template/views/go/common/head_tags.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/aah/app-template/views/go/errors/404.html b/aah/app-template/views/go/errors/404.html deleted file mode 100644 index 05174a7..0000000 --- a/aah/app-template/views/go/errors/404.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {{ import "error_header.html" . -}} - -
{{ with .Error }} -
-
- {{ .Code }} {{ .Message }} -
-
{{ end }} -
- {{ import "error_footer.html" . -}} - - diff --git a/aah/app-template/views/go/errors/500.html b/aah/app-template/views/go/errors/500.html deleted file mode 100644 index 6bd0250..0000000 --- a/aah/app-template/views/go/errors/500.html +++ /dev/null @@ -1,14 +0,0 @@ - - -{{ import "error_header.html" . -}} - -
{{ with .Error }} -
-
- {{ .Code }} {{ .Message }} -
-
{{ end }} -
- {{ import "error_footer.html" . -}} - - diff --git a/aah/app-template/views/go/layouts/master.html b/aah/app-template/views/go/layouts/master.html deleted file mode 100644 index fd2bba3..0000000 --- a/aah/app-template/views/go/layouts/master.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - {{ block "title" . }} - Default page title from master layout - {{ end }} - {{ import "head_tags.html" . }} - - - {{ block "body" . -}} -

Default body content from master layout

- {{ end }} - {{ import "footer_scripts.html" . }} - - diff --git a/aah/app-template/views/go/pages/app/index.html b/aah/app-template/views/go/pages/app/index.html deleted file mode 100644 index cbe24ca..0000000 --- a/aah/app-template/views/go/pages/app/index.html +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "title" -}} -{{ i18n . "label.pages.app.index.title" }} -{{- end }} - -{{ define "body" -}} -
-
- aah framework logo -

{{ .Greet.Message }}

-
-
-{{- end }} diff --git a/aah/app-template/views/pug/common/error_footer.pug b/aah/app-template/views/pug/common/error_footer.pug deleted file mode 100644 index e69de29..0000000 diff --git a/aah/app-template/views/pug/common/error_header.pug b/aah/app-template/views/pug/common/error_header.pug deleted file mode 100644 index bd1cc65..0000000 --- a/aah/app-template/views/pug/common/error_header.pug +++ /dev/null @@ -1,33 +0,0 @@ -//- error_header.pug -head - meta(charset='utf-8') - meta(http-equiv='X-UA-Compatible', content='IE=edge') - meta(name='viewport', content='width=device-width, initial-scale=1') - title {{ .Error.Code }} {{ .Error.Message }} - link(rel='icon', type='image/x-icon', href='/favicon.ico') - link(href='//fonts.googleapis.com/css?family=Open+Sans:300,400,700', rel='stylesheet', type='text/css') - style. - html {-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} - html, body { - margin: 0; - background-color: #fff; - color: #636b6f; - font-family: 'Open Sans', sans-serif; - font-weight: 100; - height: 80vh; - } - .container { - align-items: center; - display: flex; - justify-content: center; - position: relative; - height: 80vh; - } - .content { - text-align: center; - } - .title { - font-size: 36px; - font-weight: bold; - padding: 20px; - } diff --git a/aah/app-template/views/pug/common/footer_scripts.pug b/aah/app-template/views/pug/common/footer_scripts.pug deleted file mode 100644 index 3246165..0000000 --- a/aah/app-template/views/pug/common/footer_scripts.pug +++ /dev/null @@ -1,2 +0,0 @@ -//- footer.pug -script(src='/static/js/aah.js') diff --git a/aah/app-template/views/pug/common/head_tags.pug b/aah/app-template/views/pug/common/head_tags.pug deleted file mode 100644 index 7d8641e..0000000 --- a/aah/app-template/views/pug/common/head_tags.pug +++ /dev/null @@ -1,5 +0,0 @@ -//- head_tags.pug, block keyword added for processing purpose. -//- jade library doesn't process this file. -block head - link(rel='icon', type='image/x-icon', href='/favicon.ico') - link(href='/static/css/aah.css', rel='stylesheet') diff --git a/aah/app-template/views/pug/errors/404.pug b/aah/app-template/views/pug/errors/404.pug deleted file mode 100644 index cc35901..0000000 --- a/aah/app-template/views/pug/errors/404.pug +++ /dev/null @@ -1,12 +0,0 @@ -//- 404.pug -doctype html -html - include "error_header.pug" . -body - .container - | {{ with .Error }} - .content - .title - | {{ .Code }} {{ .Message }} - | {{ end }} - include "error_footer.pug" . diff --git a/aah/app-template/views/pug/errors/500.pug b/aah/app-template/views/pug/errors/500.pug deleted file mode 100644 index 20e822b..0000000 --- a/aah/app-template/views/pug/errors/500.pug +++ /dev/null @@ -1,12 +0,0 @@ -//- 500.pug -doctype html -html - include "error_header.pug" . -body - .container - | {{ with .Error }} - .content - .title - | {{ .Code }} {{ .Message }} - | {{ end }} - include "error_footer.pug" . diff --git a/aah/app-template/views/pug/layouts/master.pug b/aah/app-template/views/pug/layouts/master.pug deleted file mode 100644 index 55dca52..0000000 --- a/aah/app-template/views/pug/layouts/master.pug +++ /dev/null @@ -1,12 +0,0 @@ -//- master.pug -doctype html -html - head - meta(charset='utf-8') - meta(http-equiv='X-UA-Compatible', content='IE=edge') - meta(name='viewport', content='width=device-width, initial-scale=1, viewport-fit=cover') - block title - include "head_tags.pug" . - body - block content - include "footer_scripts.pug" . diff --git a/aah/app-template/views/pug/pages/app/index.pug b/aah/app-template/views/pug/pages/app/index.pug deleted file mode 100644 index b95aa3e..0000000 --- a/aah/app-template/views/pug/pages/app/index.pug +++ /dev/null @@ -1,10 +0,0 @@ -//- pages/app/index.pug - -block title - title {{ i18n . "label.pages.app.index.title" }} - -block content - .container - .row.text-center.welcome-msg - img(src='/static/img/aah-framework-logo.png', alt='aah framework logo') - h1 {{ .Greet.Message }} From 0f98cf5721a1125e22f8b53b25c8931e70b6d2df Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 6 Jul 2018 22:31:58 -0700 Subject: [PATCH 47/48] migrate config, godoc update and shell script doc --- aah/aah.go | 2 +- aah/compile.go | 2 +- aah/{migrate.grammar => migrate.conf} | 0 aah/migrate.go | 18 +++++++++++++----- aah/new.go | 4 +++- aah/publish_cli.sh | 8 +++++++- aah/run.go | 2 ++ aah/util.go | 21 ++++++++++++++++++++- aah/version.go | 4 ++-- 9 files changed, 49 insertions(+), 12 deletions(-) rename aah/{migrate.grammar => migrate.conf} (100%) diff --git a/aah/aah.go b/aah/aah.go index cdd8d53..f7b953a 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main diff --git a/aah/compile.go b/aah/compile.go index 80a32f7..bfcf807 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/tools/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main diff --git a/aah/migrate.grammar b/aah/migrate.conf similarity index 100% rename from aah/migrate.grammar rename to aah/migrate.conf diff --git a/aah/migrate.go b/aah/migrate.go index c18f4cb..7141d11 100644 --- a/aah/migrate.go +++ b/aah/migrate.go @@ -18,7 +18,8 @@ import ( "gopkg.in/urfave/cli.v1" ) -const aahGrammarIdentifier = "migrate.grammar" +const aahGrammarIdentifier = "migrate.conf" +const aahGrammarFetchLoc = "https://cdn.aahframework.org/" + aahGrammarIdentifier var migrateCmd = cli.Command{ Name: "migrate", @@ -77,7 +78,14 @@ func migrateCodeAction(c *cli.Context) error { return nil } - grammarFile := filepath.Join(aahToolsPath().Dir, aahGrammarIdentifier) + grammarFile := filepath.Join(aahPath(), aahGrammarIdentifier) + if !ess.IsFileExists(grammarFile) { + cliLog.Info("Fetch migrate configuration from ", aahGrammarFetchLoc) + if err := fetchFile(grammarFile, aahGrammarFetchLoc); err != nil { + logFatal(err) + } + } + grammarCfg, err := config.LoadFile(grammarFile) if err != nil { logFatal(err) @@ -85,10 +93,10 @@ func migrateCodeAction(c *cli.Context) error { cliLog.Info("\nNote:") cliLog.Info("-----") - cliLog.Info("Command works based on grammer file. If you identify a new grammar entry, \n" + - "create an issue at https://aahframework.org/issues to let me know.\n") + cliLog.Info("Command works based on `migrate.conf` file. If you identify a new grammar entry, \n" + + "create an issue at https://aahframework.org/issues.\n") - cliLog.Infof("Loaded grammar file: %s", grammarFile) + cliLog.Infof("Loaded migrate configuration: %s", grammarFile) cliLog.Infof("Loaded aah project file: %s", filepath.Join(aah.AppBaseDir(), aahProjectIdentifier)) cliLog.Infof("Migrate starts for '%s' [%s]", aah.AppName(), aah.AppImportPath()) diff --git a/aah/new.go b/aah/new.go index 6ad2b03..0e0eeb0 100644 --- a/aah/new.go +++ b/aah/new.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main @@ -31,6 +31,8 @@ var ( Just provide your inputs based on your use case to generate base structure to kick start your development. + Application templates are kept at '$HOME/.aah/app-templates' for CLI binary distribution. + Go to https://docs.aahframework.org to learn more and customize your aah application. `, Action: newAction, diff --git a/aah/publish_cli.sh b/aah/publish_cli.sh index fa9dbda..03aab10 100755 --- a/aah/publish_cli.sh +++ b/aah/publish_cli.sh @@ -9,9 +9,11 @@ BASEDIR=$(dirname "$0") cd $BASEDIR # Inputs -version=$1 +version=$(cat version.go | perl -pe '($_)=/([0-9]+([.][0-9]+)+(-edge)?)/') +echo "Publish for version: $version" # Build for macOS +echo "Starting aah CLI build for macOS" build_dir="/tmp/aah_cli_mac_$version" mkdir -p $build_dir env GOOS=darwin GOARCH=amd64 go build -o $build_dir/aah -ldflags="-s -w -X main.CliPackaged=true" @@ -19,10 +21,13 @@ cd $build_dir && zip aah_darwin_amd64.zip aah # sha256 and upload to aah server sha256=$(/usr/bin/shasum -a 256 $build_dir/aah_darwin_amd64.zip | cut -d " " -f 1) +echo "sha256 $sha256" +echo "Uploading aah CLI macOS binary to aah server" ssh root@aahframework.org "mkdir -p /srv/www/aahframework.org/public/releases/cli/$version" scp $build_dir/aah_darwin_amd64.zip root@aahframework.org:/srv/www/aahframework.org/public/releases/cli/$version # update homebrew tap +echo "Updating Homebrew Tap for macOS" if [ ! -d "$GOPATH/src/github.com/go-aah/homebrew-tap" ]; then git clone git@github.com:go-aah/homebrew-tap.git $GOPATH/src/github.com/go-aah/homebrew-tap fi @@ -33,6 +38,7 @@ sed -i '' -e 's/version ".*"/version "'"$version"'"/g' ./Formula/aah.rb git add -u && git commit -m "brew tap update with $version release" && git push # Cleanup +echo "Cleanup after macOS build" rm -rf $build_dir # .. next upcoming OS support \ No newline at end of file diff --git a/aah/run.go b/aah/run.go index ec4d60d..8361c16 100644 --- a/aah/run.go +++ b/aah/run.go @@ -162,6 +162,7 @@ func runAction(c *cli.Context) error { Cmd: "RunCmd", ProjectCfg: projectCfg, AppPack: false, + AppEmbed: false, }) if err != nil { logFatal(err) @@ -219,6 +220,7 @@ func (hr *hotReload) CompileAndStart() error { ProxyPort: hr.ProxyPort, ProjectCfg: hr.ProjectConfig, AppPack: false, + AppEmbed: false, }) if err != nil { return err diff --git a/aah/util.go b/aah/util.go index 971c2eb..334bf2e 100644 --- a/aah/util.go +++ b/aah/util.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/tools/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main @@ -9,6 +9,7 @@ import ( "io" "io/ioutil" "net" + "net/http" "os" "os/exec" "path/filepath" @@ -588,3 +589,21 @@ func goCmdName() string { } return "go" } + +func fetchFile(dst, src string) error { + resp, err := http.Get(src) + if err != nil { + return err + } + defer ess.CloseQuietly(resp.Body) + + _ = ess.MkDirAll(filepath.Dir(dst), permRWXRXRX) + f, err := os.Create(dst) + if err != nil { + return err + } + defer ess.CloseQuietly(f) + + _, err = io.Copy(f, resp.Body) + return err +} diff --git a/aah/version.go b/aah/version.go index 714b60c..585e5c8 100644 --- a/aah/version.go +++ b/aah/version.go @@ -1,5 +1,5 @@ // Copyright (c) Jeevanandam M. (https://github.com/jeevatkm) -// go-aah/tools/aah source code and usage is governed by a MIT style +// aahframework.org/tools/aah source code and usage is governed by a MIT style // license that can be found in the LICENSE file. package main @@ -15,7 +15,7 @@ import ( ) // Version no. of aah framework CLI tool -const Version = "0.11.0-edge" +const Version = "0.11.0" var ( errVersionNotExists = errors.New("version not exists") From 86412197fe4362687fa62666a0d336649515de04 Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Fri, 6 Jul 2018 22:32:23 -0700 Subject: [PATCH 48/48] readme update for v0.11.0 release --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c58614f..e2a39d8 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,15 @@ -# tools - aah framework - -Home of all aah framework CLI tools. - -## aah CLI Tool -[![Build Status](https://travis-ci.org/go-aah/tools.svg?branch=master)](https://travis-ci.org/go-aah/tools) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/tools.v0)](https://goreportcard.com/report/aahframework.org/tools.v0/aah) - [![Powered by Go](https://img.shields.io/badge/powered_by-go-blue.svg)](https://golang.org) - [![Version](https://img.shields.io/badge/version-0.10-blue.svg)](https://github.com/go-aah/tools/releases/latest) - [![License](https://img.shields.io/github/license/go-aah/tools.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/twitter-@aahframework-55acee.svg)](https://twitter.com/aahframework) - -***Release [v0.10](https://github.com/go-aah/tools/releases/latest) tagged on Mar 28, 2018*** +

+ +

Home of all CLI tools by aah framework

+

+

+

Build Status Go Report Card Godoc Twitter @aahframework

+

aah - A secure, flexible, rapid Go web framework. -Requires `go1.8` and above. +### News + + * aah CLI Release Version [released](https://github.com/go-aah/tools/releases/latest) and tagged on Jul 06, 2018. -Visit official website https://aahframework.org to learn more. +Visit official website https://aahframework.org to learn more about `aah` framework.