Restructure app
This commit is contained in:
88
app/cfg/cfg.go
Normal file
88
app/cfg/cfg.go
Normal file
@ -0,0 +1,88 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type configStruct struct {
|
||||
Database struct {
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
Name string `yaml:"name"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password" json:"-"`
|
||||
} `yaml:"database"`
|
||||
Application struct {
|
||||
LogLevel string `yaml:"logLevel"`
|
||||
LogFile string `yaml:"logFile"`
|
||||
Debug bool `yaml:"debug"`
|
||||
DebugSQL bool `yaml:"debugSQL"`
|
||||
DisableSendMail bool `yaml:"disableSendMail"`
|
||||
IsProduction bool `yaml:"isProduction"`
|
||||
LoopDelay uint32 `yaml:"loopDelay"`
|
||||
} `yaml:"application"`
|
||||
SMTP struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password" json:"-"`
|
||||
StartTLS bool `yaml:"startTLS"`
|
||||
UseTLS bool `yaml:"useTLS"`
|
||||
FromEmail string `yaml:"fromEmail"`
|
||||
} `yaml:"smtp"`
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_FILE = "config.yaml"
|
||||
const ENV_PREFIX = "MAILSENDER"
|
||||
|
||||
var Config configStruct
|
||||
var log = logrus.New()
|
||||
|
||||
func processError(err error) {
|
||||
log.Error("Config file error: " + err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func readFile(cfgFile string, cfg *configStruct) {
|
||||
f, err := os.Open(cfgFile)
|
||||
if err != nil {
|
||||
processError(err)
|
||||
} else {
|
||||
decoder := yaml.NewDecoder(f)
|
||||
err = decoder.Decode(cfg)
|
||||
if err != nil {
|
||||
processError(err)
|
||||
}
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
func readEnv(cfg *configStruct) {
|
||||
err := envconfig.Process(ENV_PREFIX, cfg)
|
||||
if err != nil {
|
||||
processError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Init() {
|
||||
cfgFile := os.Getenv("MAILSENDER_CONFIG")
|
||||
if cfgFile == "" {
|
||||
cfgFile = DEFAULT_CONFIG_FILE
|
||||
}
|
||||
|
||||
readFile(cfgFile, &Config)
|
||||
readEnv(&Config)
|
||||
|
||||
maskedCfg := Config
|
||||
maskedCfg.Database.Password = "**password hidden**"
|
||||
maskedCfg.SMTP.Password = "**password hidden**"
|
||||
|
||||
fmt.Println("--- CONFIG -------------------------------")
|
||||
fmt.Println(maskedCfg)
|
||||
fmt.Println("------------------------------------------")
|
||||
}
|
||||
54
app/db/db.go
Normal file
54
app/db/db.go
Normal file
@ -0,0 +1,54 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iris-test/app/cfg"
|
||||
"iris-test/app/logging"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
gormLogger "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
const CONNECTION_MAX_IDLE_TIME = time.Minute * 1
|
||||
const DB_CONNECTION_TIMEOUT = 5
|
||||
|
||||
var DBConn *gorm.DB
|
||||
|
||||
func InitDB() *gorm.DB {
|
||||
var connectionString = strings.Join([]string{
|
||||
"postgres://",
|
||||
cfg.Config.Database.Username, ":",
|
||||
cfg.Config.Database.Password, "@",
|
||||
cfg.Config.Database.Host, ":",
|
||||
cfg.Config.Database.Port, "/",
|
||||
cfg.Config.Database.Name,
|
||||
"?sslmode=disable",
|
||||
"&TimeZone=UTC",
|
||||
"&connect_timeout=", strconv.Itoa(DB_CONNECTION_TIMEOUT),
|
||||
}, "")
|
||||
|
||||
var logLevel = gormLogger.Silent
|
||||
if cfg.Config.Application.DebugSQL {
|
||||
logLevel = gormLogger.Info
|
||||
}
|
||||
|
||||
var err error
|
||||
DBConn, err = gorm.Open(postgres.Open(connectionString), &gorm.Config{
|
||||
Logger: gormLogger.Default.LogMode(logLevel),
|
||||
})
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Error connecting to database: %s. Terminating!", err)
|
||||
logging.Error(msg)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
// set connection autodisconnect after idle time
|
||||
db, _ := DBConn.DB()
|
||||
db.SetConnMaxIdleTime(CONNECTION_MAX_IDLE_TIME)
|
||||
|
||||
return DBConn
|
||||
}
|
||||
65
app/logging/logging.go
Normal file
65
app/logging/logging.go
Normal file
@ -0,0 +1,65 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"iris-test/app/cfg"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var Log = logrus.New()
|
||||
|
||||
func Debug(message string) {
|
||||
Log.Debug(message)
|
||||
}
|
||||
|
||||
func Info(message string) {
|
||||
Log.Info(message)
|
||||
}
|
||||
|
||||
func Error(message string) {
|
||||
Log.Error(message)
|
||||
}
|
||||
|
||||
func Warn(message string) {
|
||||
Log.Warn(message)
|
||||
}
|
||||
|
||||
func Init() {
|
||||
logLevel, err := logrus.ParseLevel(cfg.Config.Application.LogLevel)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Invalid configured logLevel: %s\n", cfg.Config.Application.LogLevel))
|
||||
}
|
||||
|
||||
Log.SetLevel(logLevel)
|
||||
|
||||
Log.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
PadLevelText: true,
|
||||
DisableQuote: true,
|
||||
})
|
||||
|
||||
LogFile := cfg.Config.Application.LogFile
|
||||
file, err := os.OpenFile(
|
||||
LogFile,
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
|
||||
0655,
|
||||
)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Failed to log to file %s: %s", cfg.Config.Application.LogFile, err)
|
||||
Log.Warning(msg)
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
mw := io.MultiWriter(os.Stdout, file)
|
||||
Log.SetOutput(mw)
|
||||
|
||||
configJson, err := json.Marshal(cfg.Config)
|
||||
if err == nil {
|
||||
Info(fmt.Sprintf("Using config: %s", configJson))
|
||||
}
|
||||
}
|
||||
103
app/main.go
Normal file
103
app/main.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Package main an example on how to naming your routes & use the custom 'url path' Jet Template Engine.
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
firstName string
|
||||
lastName string
|
||||
email string
|
||||
}
|
||||
|
||||
var users = []User{
|
||||
{
|
||||
firstName: "Pero",
|
||||
lastName: "Perić",
|
||||
email: "pero@gmail.com",
|
||||
},
|
||||
{
|
||||
firstName: "Mirko",
|
||||
lastName: "Mirković",
|
||||
email: "mirko@gmail.com",
|
||||
},
|
||||
{
|
||||
firstName: "Ivo",
|
||||
lastName: "Ivić",
|
||||
email: "ivo@gmail.com",
|
||||
},
|
||||
{
|
||||
firstName: "Slavko",
|
||||
lastName: "Slavković",
|
||||
email: "slavko@gmail.com",
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
app.RegisterView(iris.Jet("./app/templates", ".jet").Reload(true))
|
||||
|
||||
// mypathRoute := app.Get("/mypath", writePathHandler)
|
||||
// mypathRoute.Name = "my-page1"
|
||||
|
||||
// mypath2Route := app.Get("/mypath2/{paramfirst}/{paramsecond}", writePathHandler)
|
||||
// mypath2Route.Name = "my-page2"
|
||||
|
||||
// mypath3Route := app.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", writePathHandler)
|
||||
// mypath3Route.Name = "my-page3"
|
||||
|
||||
// mypath4Route := app.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", writePathHandler)
|
||||
// // same as: app.Get("/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something", writePathHandler)
|
||||
// mypath4Route.Name = "my-page4"
|
||||
|
||||
// // same with Handle/Func
|
||||
// mypath5Route := app.Handle("GET", "/mypath5/{paramfirst:int}/statichere/{paramsecond}/{otherparam}/anything/{something:path}", writePathHandlerPage5)
|
||||
// mypath5Route.Name = "my-page5"
|
||||
|
||||
// mypath6Route := app.Get("/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}", writePathHandler)
|
||||
// mypath6Route.Name = "my-page6"
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
// templateContext := map[string]string{}
|
||||
|
||||
params1 := []string{"param 1", "param 2", "param 3"}
|
||||
ctx.ViewData("params1", params1)
|
||||
ctx.ViewData("users", users)
|
||||
|
||||
if err := ctx.View("pages/index.jet"); err != nil {
|
||||
ctx.HTML("<h3>%s</h3>", err.Error())
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
app.Get("/redirect/{namedRoute}", func(ctx iris.Context) {
|
||||
routeName := ctx.Params().Get("namedRoute")
|
||||
r := app.GetRoute(routeName)
|
||||
if r == nil {
|
||||
ctx.StatusCode(iris.StatusNotFound)
|
||||
ctx.Writef("Route with name %s not found", routeName)
|
||||
return
|
||||
}
|
||||
|
||||
println("The path of " + routeName + "is: " + r.Path)
|
||||
// if routeName == "my-page1"
|
||||
// prints: The path of of my-page1 is: /mypath
|
||||
// if it's a path which takes named parameters
|
||||
// then use "r.ResolvePath(paramValuesHere)"
|
||||
ctx.Redirect(r.Path)
|
||||
// http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath
|
||||
})
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/redirect/my-page1
|
||||
app.Listen(":8000")
|
||||
}
|
||||
|
||||
func writePathHandler(ctx iris.Context) {
|
||||
ctx.Writef("Hello from %s.", ctx.Path())
|
||||
}
|
||||
|
||||
func writePathHandlerPage5(ctx iris.Context) {
|
||||
ctx.Writef("Hello from %s.\nparamfirst(int)=%d", ctx.Path(), ctx.Params().GetIntDefault("paramfirst", 0))
|
||||
}
|
||||
21
app/templates/base/base.jet
Normal file
21
app/templates/base/base.jet
Normal file
@ -0,0 +1,21 @@
|
||||
{{ title := "Hello world" }}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
<main>
|
||||
{{ yield mainContent() }}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
15
app/templates/components/table_component.jet
Normal file
15
app/templates/components/table_component.jet
Normal file
@ -0,0 +1,15 @@
|
||||
{{ block usersTable(users) }}
|
||||
<p>blablabla</p>
|
||||
|
||||
<table class="table">
|
||||
<tbody>
|
||||
{{ range users }}
|
||||
<tr>
|
||||
<td>{{ .firstName }}</td>
|
||||
<td>{{ .lastName }}</td>
|
||||
<td>{{ .email }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
18
app/templates/pages/index.jet
Normal file
18
app/templates/pages/index.jet
Normal file
@ -0,0 +1,18 @@
|
||||
{{ extends "/base/base.jet" }}
|
||||
{{ import "/components/table_component.jet" }}
|
||||
|
||||
|
||||
{{ block mainContent() }}
|
||||
{{ title = "Hello world from index" }}
|
||||
|
||||
<ul>
|
||||
{{ range params1 }}
|
||||
<li>{{ . }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
<h3>{{ title }}</h3>
|
||||
|
||||
{{ yield usersTable(users=users) }}
|
||||
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user