Skip to content

Commit 57ed934

Browse files
committed
Initial commit
0 parents  commit 57ed934

File tree

24 files changed

+656
-0
lines changed

24 files changed

+656
-0
lines changed

.github/workflows/deploy-mdbook.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Deploy mdBook
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
pages: write
10+
id-token: write
11+
12+
jobs:
13+
build-and-deploy:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
- name: Install Rust (rustup)
19+
run: rustup update stable --no-self-update && rustup default stable
20+
- name: Install mdBook
21+
run: cargo install mdbook
22+
- name: Build doc
23+
run: mdbook build doc
24+
- name: Configure GitHub Pages
25+
uses: actions/configure-pages@v5
26+
- name: Upload Pages artifact
27+
uses: actions/upload-pages-artifact@v3
28+
with:
29+
path: doc/book
30+
- name: Deploy to GitHub Pages
31+
uses: actions/deploy-pages@v4

.github/workflows/lint.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
9+
jobs:
10+
lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
- name: Run golangci-lint
16+
uses: golangci/golangci-lint-action@v8

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"go.testFlags": ["-v", "-count=1"],
3+
"[markdown]": {
4+
"editor.formatOnSave": true,
5+
"editor.formatOnPaste": true
6+
}
7+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 slhmy
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# GWM (Golang Web Modules)
2+
3+
Solution collection for quickly setting up
4+
Golang web applications (Currently especially for Gin framework).
5+
6+
## Why GWM?
7+
8+
GWM maintains the best practices and patterns for building web applications in Go.
9+
If painlessly building a web application in Go is your goal, GWM is the solution.
10+
It provides a solid foundation for your application,
11+
allowing you to focus on building features rather than boilerplate code.
12+
13+
### Application Core
14+
15+
- **Configuration Store** depends on [viper](https://github.com/spf13/viper) for managing application configuration.
16+
- **Slog Logger** with contextual logging and support (using [tint](github.com/lmittmann/tint) for colorized output).
17+
18+
### Clients
19+
20+
- **MongoDB Driver** for MongoDB database operations.
21+
- **Redis Clients** for Redis operations and other Redis-based services (For example, distributed locks & caching).
22+
23+
### Utilities
24+
25+
- **Gin Utils** for Gin framework, including middleware and helpers.
26+
27+
See more in [documentation](https://slhmy.github.io/go-webmods).

app/config.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package gwm_app
2+
3+
import (
4+
"os"
5+
"path"
6+
"strings"
7+
8+
"github.com/spf13/viper"
9+
)
10+
11+
const (
12+
envRelativeConfigPath = "GWM_RELATIVE_CONFIG_PATH"
13+
envOverrideConfigName = "GWM_OVERRIDE_CONFIG_NAME"
14+
15+
defaultConfigName = "config"
16+
defaultOverrideConfigName = "override"
17+
)
18+
19+
var (
20+
config *viper.Viper
21+
)
22+
23+
func Config() *viper.Viper {
24+
return config
25+
}
26+
27+
func init() {
28+
cwd, _ := os.Getwd()
29+
relativeConfigPath := os.Getenv(envRelativeConfigPath)
30+
viper.AddConfigPath(path.Join(cwd, relativeConfigPath))
31+
viper.SetConfigName(defaultConfigName)
32+
err := viper.ReadInConfig()
33+
if err != nil {
34+
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
35+
panic(err)
36+
}
37+
}
38+
overrideConfigName := os.Getenv(envOverrideConfigName)
39+
if len(overrideConfigName) == 0 {
40+
overrideConfigName = defaultOverrideConfigName
41+
}
42+
viper.SetConfigName(overrideConfigName)
43+
err = viper.MergeInConfig()
44+
if err != nil {
45+
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
46+
panic(err)
47+
}
48+
}
49+
viper.AutomaticEnv()
50+
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
51+
config = viper.GetViper()
52+
}

app/error.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package gwm_app
2+
3+
type ServiceError struct {
4+
Err error
5+
HttpStatusCode int
6+
}
7+
8+
func (e ServiceError) Error() string {
9+
return e.Err.Error()
10+
}

clients/gorm/db.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package gwm_gorm
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
7+
gwm_app "github.com/slhmy/go-webmods/app"
8+
"github.com/slhmy/go-webmods/internal"
9+
"gorm.io/driver/postgres"
10+
"gorm.io/gorm"
11+
)
12+
13+
var (
14+
initMutx sync.Mutex
15+
db *gorm.DB
16+
)
17+
18+
func GetDB() *gorm.DB {
19+
if db == nil {
20+
initMutx.Lock()
21+
defer initMutx.Unlock()
22+
if db != nil {
23+
return db
24+
}
25+
26+
driver := gwm_app.Config().GetString(internal.ConfigKeyDatabaseDriver)
27+
switch driver {
28+
case "postgres":
29+
db, err := openPostgres()
30+
if err != nil {
31+
panic(err)
32+
}
33+
return db
34+
default:
35+
panic(fmt.Sprintf("unsupported database driver: %s", driver))
36+
}
37+
}
38+
return db
39+
}
40+
41+
func openPostgres() (db *gorm.DB, err error) {
42+
dsn := fmt.Sprintf(
43+
"host=%s port=%s user=%s dbname=%s password=%s sslmode=%s",
44+
gwm_app.Config().GetString(internal.ConfigKeyDatabaseHost),
45+
gwm_app.Config().GetString(internal.ConfigKeyDatabasePort),
46+
gwm_app.Config().GetString(internal.ConfigKeyDatabaseUsername),
47+
gwm_app.Config().GetString(internal.ConfigKeyDatabaseName),
48+
gwm_app.Config().GetString(internal.ConfigKeyDatabasePassword),
49+
gwm_app.Config().GetString(internal.ConfigKeyDatabaseSSLMode),
50+
)
51+
db, err = gorm.Open(postgres.Open(dsn))
52+
if err != nil {
53+
return nil, err
54+
}
55+
return db, nil
56+
}

clients/redis/rdb.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package gwm_redis
2+
3+
import (
4+
"sync"
5+
6+
"github.com/redis/go-redis/v9"
7+
gwm_app "github.com/slhmy/go-webmods/app"
8+
"github.com/slhmy/go-webmods/internal"
9+
)
10+
11+
var (
12+
rdbInitMutex sync.Mutex
13+
rdbClient redis.UniversalClient
14+
)
15+
16+
func GetRDB() redis.UniversalClient {
17+
if rdbClient == nil {
18+
rdbInitMutex.Lock()
19+
defer rdbInitMutex.Unlock()
20+
if rdbClient != nil {
21+
return rdbClient
22+
}
23+
24+
addrs := gwm_app.Config().GetStringSlice(internal.ConfigKeyRedisAddrs)
25+
password := gwm_app.Config().GetString(internal.ConfigKeyRedisPassword)
26+
if len(addrs) == 0 {
27+
panic("No redis hosts configured")
28+
}
29+
if len(addrs) == 1 {
30+
rdbClient = redis.NewClient(&redis.Options{
31+
Addr: addrs[0],
32+
Password: password,
33+
})
34+
}
35+
if len(addrs) > 1 {
36+
rdbClient = redis.NewClusterClient(&redis.ClusterOptions{
37+
Addrs: addrs,
38+
Password: password,
39+
})
40+
}
41+
}
42+
return rdbClient
43+
}

doc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
book

doc/book.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[book]
2+
authors = ["slhmy"]
3+
language = "en"
4+
src = "src"
5+
title = "Golang Web Modules"

doc/src/SUMMARY.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!-- markdownlint-enable MD025 -->
2+
3+
# Summary
4+
5+
# Application Core
6+
7+
- [Config](app/config.md)
8+
- [Service Error](app/service_error.md)
9+
10+
# Utilities
11+
12+
- [Gin Error Handler](utils/gin/error_handler.md)

doc/src/app/config.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Config
2+
3+
Read configuration from config files or environment variables.
4+
5+
Config namings are recommended to be in snake_case and separated by dots.
6+
7+
You can set configs in `conifg` file and override them in `override` file
8+
(multiple file extensions are supported, such as `.yaml`, `.toml`, `.json`, etc.).
9+
10+
In some cases, using environment variables is more convenient.
11+
You can override the final config by making every character uppercase and replacing `.` with `_`
12+
(e.g. `server.port` becomes `SERVER_PORT`),
13+
environment variables have the highest priority.
14+
15+
## Example
16+
17+
```go
18+
os.Setenv("SERVER_PORT", "443")
19+
port = gwm_app.Config().GetInt("server.port")
20+
// port = 443
21+
```
22+
23+
## Customize
24+
25+
Customize the config file path by setting the `GWM_RELATIVE_CONFIG_PATH` environment variable.
26+
Set the override config file name by setting the `GWM_OVERRIDE_CONFIG_NAME` environment variable.
27+
28+
The following environment variables should be set before running the application.

doc/src/app/service_error.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Service Error

doc/src/utils/gin/error_handler.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Gin Error Handler

go.mod

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
module github.com/slhmy/go-webmods
2+
3+
go 1.24.3
4+
5+
require (
6+
github.com/gin-gonic/gin v1.10.0
7+
github.com/redis/go-redis/v9 v9.8.0
8+
github.com/spf13/viper v1.20.1
9+
gorm.io/driver/postgres v1.5.11
10+
gorm.io/gorm v1.26.1
11+
)
12+
13+
require (
14+
github.com/bytedance/sonic v1.11.6 // indirect
15+
github.com/bytedance/sonic/loader v0.1.1 // indirect
16+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
17+
github.com/cloudwego/base64x v0.1.4 // indirect
18+
github.com/cloudwego/iasm v0.2.0 // indirect
19+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
20+
github.com/fsnotify/fsnotify v1.8.0 // indirect
21+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
22+
github.com/gin-contrib/sse v0.1.0 // indirect
23+
github.com/go-playground/locales v0.14.1 // indirect
24+
github.com/go-playground/universal-translator v0.18.1 // indirect
25+
github.com/go-playground/validator/v10 v10.20.0 // indirect
26+
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
27+
github.com/goccy/go-json v0.10.2 // indirect
28+
github.com/jackc/pgpassfile v1.0.0 // indirect
29+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
30+
github.com/jackc/pgx/v5 v5.5.5 // indirect
31+
github.com/jackc/puddle/v2 v2.2.1 // indirect
32+
github.com/jinzhu/inflection v1.0.0 // indirect
33+
github.com/jinzhu/now v1.1.5 // indirect
34+
github.com/json-iterator/go v1.1.12 // indirect
35+
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
36+
github.com/leodido/go-urn v1.4.0 // indirect
37+
github.com/mattn/go-isatty v0.0.20 // indirect
38+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
39+
github.com/modern-go/reflect2 v1.0.2 // indirect
40+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
41+
github.com/sagikazarmark/locafero v0.7.0 // indirect
42+
github.com/sourcegraph/conc v0.3.0 // indirect
43+
github.com/spf13/afero v1.12.0 // indirect
44+
github.com/spf13/cast v1.7.1 // indirect
45+
github.com/spf13/pflag v1.0.6 // indirect
46+
github.com/subosito/gotenv v1.6.0 // indirect
47+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
48+
github.com/ugorji/go/codec v1.2.12 // indirect
49+
go.uber.org/atomic v1.9.0 // indirect
50+
go.uber.org/multierr v1.9.0 // indirect
51+
golang.org/x/arch v0.8.0 // indirect
52+
golang.org/x/crypto v0.32.0 // indirect
53+
golang.org/x/net v0.33.0 // indirect
54+
golang.org/x/sync v0.10.0 // indirect
55+
golang.org/x/sys v0.29.0 // indirect
56+
golang.org/x/text v0.21.0 // indirect
57+
google.golang.org/protobuf v1.36.1 // indirect
58+
gopkg.in/yaml.v3 v3.0.1 // indirect
59+
)

0 commit comments

Comments
 (0)