This repository has been archived by the owner on Apr 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
/
main.go
153 lines (124 loc) · 5.12 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package baseproject
import (
"embed"
"fmt"
"github.com/BurntSushi/toml"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/uberswe/golang-base-project/middleware"
"github.com/uberswe/golang-base-project/routes"
"golang.org/x/text/language"
"html/template"
"io/fs"
"log"
"math/rand"
"net/http"
"time"
)
// staticFS is an embedded file system
//go:embed dist/*
var staticFS embed.FS
// Run is the main function that runs the entire package and starts the webserver, this is called by /cmd/base/main.go
func Run() {
// When generating random strings we need to provide a seed otherwise we always get the same strings the next time our application starts
rand.Seed(time.Now().UnixNano())
// We load environment variables, these are only read when the application launches
conf := loadEnvVariables()
// Translations
bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
languages := []string{
"en",
"sv",
}
for _, l := range languages {
_, err := bundle.LoadMessageFile(fmt.Sprintf("active.%s.toml", l))
if err != nil {
log.Fatalln(err)
}
}
// We connect to the database using the configuration generated from the environment variables.
db, err := connectToDatabase(conf)
if err != nil {
log.Fatalln(err)
}
// Once a database connection is established we run any needed migrations
err = migrateDatabase(db)
if err != nil {
log.Fatalln(err)
}
// t will hold all our html templates used to render pages
var t *template.Template
// We parse and load the html files into our t variable
t, err = loadTemplates()
if err != nil {
log.Fatalln(err)
}
// A gin Engine instance with the default configuration
r := gin.Default()
// We create a new cookie store with a key used to secure cookies with HMAC
store := cookie.NewStore([]byte(conf.CookieSecret))
// We define our session middleware to be used globally on all routes
r.Use(sessions.Sessions("golang_base_project_session", store))
// We pase our template variable t to the gin engine so it can be used to render html pages
r.SetHTMLTemplate(t)
// our assets are only located in a section of our file system. so we create a sub file system.
subFS, err := fs.Sub(staticFS, "dist/assets")
if err != nil {
log.Fatalln(err)
}
// All static assets are under the /assets path so we make this its own group called assets
assets := r.Group("/assets")
// This middleware sets the Cache-Control header and is applied to the assets group only
assets.Use(middleware.Cache(conf.CacheMaxAge))
// All requests to /assets will use the sub fil system which contains all our static assets
assets.StaticFS("/", http.FS(subFS))
// Session middleware is applied to all groups after this point.
r.Use(middleware.Session(db))
// A General middleware is defined to add default headers to improve site security
r.Use(middleware.General())
// A new instance of the routes controller is created
controller := routes.New(db, conf, bundle)
// Any request to / will call controller.Index
r.GET("/", controller.Index)
// We want to handle both POST and GET requests on the /search route. We define both but use the same function to handle the requests.
r.GET("/search", controller.Search)
r.POST("/search", controller.Search)
r.Any("/search/:page", controller.Search)
r.Any("/search/:page/:query", controller.Search)
// We define our 404 handler for when a page can not be found
r.NoRoute(controller.NoRoute)
// noAuth is a group for routes which should only be accessed if the user is not authenticated
noAuth := r.Group("/")
noAuth.Use(middleware.NoAuth())
noAuth.GET("/login", controller.Login)
noAuth.GET("/register", controller.Register)
noAuth.GET("/activate/resend", controller.ResendActivation)
noAuth.GET("/activate/:token", controller.Activate)
noAuth.GET("/user/password/forgot", controller.ForgotPassword)
noAuth.GET("/user/password/reset/:token", controller.ResetPassword)
// We make a separate group for our post requests on the same endpoints so that we can define our throttling middleware on POST requests only.
noAuthPost := noAuth.Group("/")
noAuthPost.Use(middleware.Throttle(conf.RequestsPerMinute))
noAuthPost.POST("/login", controller.LoginPost)
noAuthPost.POST("/register", controller.RegisterPost)
noAuthPost.POST("/activate/resend", controller.ResendActivationPost)
noAuthPost.POST("/user/password/forgot", controller.ForgotPasswordPost)
noAuthPost.POST("/user/password/reset/:token", controller.ResetPasswordPost)
// the admin group handles routes that should only be accessible to authenticated users
admin := r.Group("/")
admin.Use(middleware.Auth())
admin.Use(middleware.Sensitive())
admin.GET("/admin", controller.Admin)
// We need to handle post from the login redirect
admin.POST("/admin", controller.Admin)
admin.GET("/logout", controller.Logout)
// This starts our webserver, our application will not stop running or go past this point unless
// an error occurs or the web server is stopped for some reason. It is designed to run forever.
err = r.Run(conf.Port)
if err != nil {
log.Fatalln(err)
}
}