1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-26 14:27:04 +00:00

add one more project structure recommendation

This commit is contained in:
Gerasimos (Makis) Maropoulos
2021-11-19 14:13:42 +02:00
parent d62392bebc
commit 226a4cb064
15 changed files with 1166 additions and 1 deletions

View File

@@ -0,0 +1,43 @@
package api
import (
"os"
"github.com/kataras/iris/v12"
"gopkg.in/yaml.v3"
)
// Configuration holds the necessary information
// for our server, including the Iris one.
type Configuration struct {
ServerName string `yaml:"ServerName"`
// The server's host, if empty, defaults to 0.0.0.0
Host string `yaml:"Host"`
// The server's port, e.g. 80
Port int `yaml:"Port"`
// If not empty runs under tls with this domain using letsencrypt.
Domain string `yaml:"Domain"`
// Enables write response and read request compression.
EnableCompression bool `yaml:"EnableCompression"`
// Defines the "Access-Control-Allow-Origin" header of the CORS middleware.
// Many can be separated by comma.
// Defaults to "*" (allow all).
AllowOrigin string `yaml:"AllowOrigin"`
// If not empty a request logger is registered,
// note that this will cost a lot in performance, use it only for debug.
RequestLog string `yaml:"RequestLog"`
// Iris specific configuration.
Iris iris.Configuration `yaml:"Iris"`
}
// BindFile binds the yaml file's contents to this Configuration.
func (c *Configuration) BindFile(filename string) error {
contents, err := os.ReadFile(filename)
if err != nil {
return err
}
return yaml.Unmarshal(contents, c)
}

View File

@@ -0,0 +1,22 @@
package api
import (
"github.com/username/project/api/users"
"github.com/username/project/user"
"github.com/kataras/iris/v12"
)
// buildRouter is the most important part of your server.
// All root endpoints are registered here.
func (srv *Server) buildRouter() {
// Add a simple health route.
srv.Any("/health", func(ctx iris.Context) {
ctx.Writef("%s\n\nOK", srv.String())
})
api := srv.Party("/api")
api.RegisterDependency(user.NewRepository)
api.PartyConfigure("/user", new(users.API))
}

View File

@@ -0,0 +1,165 @@
package api
import (
"context"
"fmt"
"time"
"github.com/username/project/pkg/http/handlers"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/accesslog"
"github.com/kataras/golog"
)
// Server is a wrapper of the main iris application and our project's custom configuration fields.
type Server struct {
*iris.Application
config Configuration
// Here you can keep an instance of the database too.
// db *mydatabase_pkg.DB
closers []func() // See `AddCloser` method.
}
// NewServer initializes a new HTTP/2 server.
// Use its Run/Listen methods to start it based on network options.
func NewServer(c Configuration) *Server {
app := iris.New().SetName(c.ServerName)
app.Configure(iris.WithConfiguration(c.Iris), iris.WithLowercaseRouting)
srv := &Server{
Application: app,
config: c,
}
if err := srv.prepare(); err != nil {
srv.Logger().Fatal(err)
return nil
}
return srv
}
func (srv *Server) prepare() error {
// Here you can register the database instance
// and prepare any project-relative fields.
if srv.Logger().Level == golog.DebugLevel {
srv.registerDebugFeatures()
}
srv.registerMiddlewares()
srv.buildRouter()
return nil
}
// registers application-level middlewares.
func (srv *Server) registerMiddlewares() {
if srv.config.RequestLog != "" {
srv.registerAccessLogger()
}
srv.UseRouter(handlers.CORS(srv.config.AllowOrigin))
if srv.config.EnableCompression {
srv.Use(iris.Compression)
}
}
func (srv *Server) registerDebugFeatures() {}
func (srv *Server) registerAccessLogger() {
// Initialize a new request access log middleware,
// note that we use unbuffered data so we can have the results as fast as possible,
// this has its cost use it only on debug.
// Also, in the future see the iris example to
// enable log rotation (date eand filesize-based files).
ac := accesslog.FileUnbuffered(srv.config.RequestLog)
// The default configuration:
ac.Delim = '|'
ac.TimeFormat = "2006-01-02 15:04:05"
ac.Async = false
ac.IP = true
ac.BytesReceivedBody = true
ac.BytesSentBody = true
ac.BytesReceived = false
ac.BytesSent = false
ac.BodyMinify = false
ac.RequestBody = true
ac.ResponseBody = false
ac.KeepMultiLineError = true
ac.PanicLog = accesslog.LogHandler
// Default line format if formatter is missing:
// Time|Latency|Code|Method|Path|IP|Path Params Query Fields|Bytes Received|Bytes Sent|Request|Response|
//
// Set Custom Formatter:
ac.SetFormatter(&accesslog.JSON{
Indent: " ",
HumanTime: true,
})
// ac.SetFormatter(&accesslog.CSV{})
// ac.SetFormatter(&accesslog.Template{Text: "{{.Code}}"})
srv.UseRouter(ac.Handler)
}
// Start runs the server on the TCP network address "0.0.0.0:port" which
// handles HTTP/1.1 & 2 requests on incoming connections.
func (srv *Server) Start() error {
if srv.config.Domain != "" {
srv.config.Port = 80 // not required but let's force-modify it.
return srv.Application.Run(iris.AutoTLS(
":443",
srv.config.Domain,
"kataras2006@hotmail.com",
))
}
srv.ConfigureHost(func(su *iris.Supervisor) {
// Set timeouts. More than enough, normally we use 20-30 seconds.
su.Server.ReadTimeout = 5 * time.Minute
su.Server.WriteTimeout = 5 * time.Minute
su.Server.IdleTimeout = 10 * time.Minute
su.Server.ReadHeaderTimeout = 2 * time.Minute
})
addr := fmt.Sprintf("%s:%d", srv.config.Host, srv.config.Port)
return srv.Listen(addr)
}
// AddCloser adds one or more function that should be called on
// manual server shutdown or OS interrupt signals.
func (srv *Server) AddCloser(closers ...func()) {
for _, closer := range closers {
if closer == nil {
continue
}
// Terminate any opened connections on OS interrupt signals.
iris.RegisterOnInterrupt(closer)
}
srv.closers = append(srv.closers, closers...)
}
// Close gracefully terminates the HTTP server and calls the closers afterwards.
func (srv *Server) Close() error {
ctx, cancelCtx := context.WithTimeout(context.Background(), 5*time.Second)
err := srv.Shutdown(ctx)
cancelCtx()
for _, closer := range srv.closers {
if closer == nil {
continue
}
closer()
}
return err
}

View File

@@ -0,0 +1,27 @@
package users
import (
"github.com/username/project/user"
"github.com/kataras/iris/v12"
)
type API struct {
Users user.Repository // exported field so api/router.go#api.RegisterDependency can bind it.
}
func (api *API) Configure(r iris.Party) {
r.Post("/signup", api.signUp)
r.Post("/signin", api.signIn)
// Add middlewares such as user verification by bearer token here.
// Authenticated routes...
r.Get("/", api.getInfo)
}
func (api *API) getInfo(ctx iris.Context) {
ctx.WriteString("...")
}
func (api *API) signUp(ctx iris.Context) {}
func (api *API) signIn(ctx iris.Context) {}