init commit
This commit is contained in:
commit
3ccb7519ef
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.log
|
||||||
2
Makefile
Normal file
2
Makefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
run_usersUpdateLoginMethod:
|
||||||
|
go run cmd/main.go -c usersUpdateLoginMethod
|
||||||
49
cmd/main.go
Normal file
49
cmd/main.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"scripts/internal/command"
|
||||||
|
"scripts/internal/config"
|
||||||
|
e "scripts/pkg/errors"
|
||||||
|
"scripts/pkg/logger"
|
||||||
|
l "scripts/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultConfigPath = "config.json"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
commandName string
|
||||||
|
confPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&confPath, "config", defaultConfigPath, "path to config file")
|
||||||
|
flag.StringVar(&commandName, "c", "", "command name")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
config.MustLoad(confPath)
|
||||||
|
|
||||||
|
if commandName == "" {
|
||||||
|
l.Log.Fatal().Msg("flag '-c' required")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.MustInit()
|
||||||
|
command.InitSripts()
|
||||||
|
|
||||||
|
cmd, exists := command.Scripts[commandName]
|
||||||
|
if !exists {
|
||||||
|
l.Log.Warn().Err(e.ErrCommandNotFound).Str("command", commandName).Send()
|
||||||
|
l.Log.Info().Msg("selected command - 'help'")
|
||||||
|
if err := command.Scripts["help"].Exec(); err != nil {
|
||||||
|
l.Log.Fatal().Err(err).Str("command", commandName).Msg("failed to use help command")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log.Info().Msgf("selected command - %s", commandName)
|
||||||
|
if err := cmd.Exec(); err != nil {
|
||||||
|
l.Log.Error().Err(err).Str("command", commandName).Msg("failed to execude command")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
43
config.json
Normal file
43
config.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"SqlSettings": {
|
||||||
|
"DriverName": "postgres",
|
||||||
|
"DataSource": "postgres://mmuser:mostest@localhost/mattermost_test?sslmode=disable\u0026connect_timeout=10\u0026binary_parameters=yes",
|
||||||
|
"DataSourceReplicas": [],
|
||||||
|
"DataSourceSearchReplicas": [],
|
||||||
|
"MaxIdleConns": 20,
|
||||||
|
"ConnMaxLifetimeMilliseconds": 3600000,
|
||||||
|
"ConnMaxIdleTimeMilliseconds": 300000,
|
||||||
|
"MaxOpenConns": 300,
|
||||||
|
"Trace": false,
|
||||||
|
"AtRestEncryptKey": "cirgp8x5eydrr9sjmwawb5zsefhbditq",
|
||||||
|
"QueryTimeout": 30,
|
||||||
|
"DisableDatabaseSearch": false,
|
||||||
|
"MigrationsStatementTimeoutSeconds": 100000,
|
||||||
|
"ReplicaLagSettings": [],
|
||||||
|
"ReplicaMonitorIntervalSeconds": 5
|
||||||
|
},
|
||||||
|
"LogSettings": {
|
||||||
|
"EnableConsole": true,
|
||||||
|
"ConsoleLevel": "DEBUG",
|
||||||
|
"ConsoleJson": true,
|
||||||
|
"EnableColor": false,
|
||||||
|
"EnableFile": true,
|
||||||
|
"FileLevel": "INFO",
|
||||||
|
"FileJson": true,
|
||||||
|
"FileLocation": "./pkg/logger/logs.log",
|
||||||
|
"EnableWebhookDebugging": true,
|
||||||
|
"EnableDiagnostics": true,
|
||||||
|
"VerboseDiagnostics": false,
|
||||||
|
"EnableSentry": true,
|
||||||
|
"EnableCaller": true,
|
||||||
|
"AdvancedLoggingJSON": {},
|
||||||
|
"AdvancedLoggingConfig": "",
|
||||||
|
"MaxFieldSize": 2048
|
||||||
|
},
|
||||||
|
"KeycloakSettings": {
|
||||||
|
"address": "http://localhost:8282",
|
||||||
|
"realm_name": "loop",
|
||||||
|
"admin_name": "admin",
|
||||||
|
"admin_pass": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
go.mod
Normal file
15
go.mod
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module scripts
|
||||||
|
|
||||||
|
go 1.26.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
|
github.com/lib/pq v1.10.9
|
||||||
|
github.com/rs/zerolog v1.34.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
|
)
|
||||||
25
go.sum
Normal file
25
go.sum
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||||
|
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
48
internal/command/common.go
Normal file
48
internal/command/common.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
commandEmailPassToOidc "scripts/internal/command/email_pass_to_oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitSripts() {
|
||||||
|
scripts := map[string]*Commands{
|
||||||
|
commandEmailPassToOidc.Name: {
|
||||||
|
Exec: commandEmailPassToOidc.UsersUpdateLoginMethod,
|
||||||
|
Requirements: commandEmailPassToOidc.Requirement,
|
||||||
|
Description: "переводит авторизацию пользователей с ролью 'system_user' c почта/пароль на keycloak",
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
Exec: Help,
|
||||||
|
Requirements: Requirement,
|
||||||
|
Description: "вывод всех доступных скриптов",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Scripts = scripts
|
||||||
|
}
|
||||||
|
|
||||||
|
var Scripts map[string]*Commands
|
||||||
|
|
||||||
|
type Commands struct {
|
||||||
|
Exec Execute
|
||||||
|
Requirements Requirements
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Execute func() error
|
||||||
|
type Requirements func() string
|
||||||
|
|
||||||
|
func Help() error {
|
||||||
|
fmt.Printf("All commands:\n\n")
|
||||||
|
for k, v := range Scripts {
|
||||||
|
fmt.Printf("name: %s\n", k)
|
||||||
|
fmt.Printf("description: %s\n", v.Description)
|
||||||
|
fmt.Printf("requirements: %s\n\n", v.Requirements())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Requirement() string {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
32
internal/command/email_pass_to_oidc/dto.go
Normal file
32
internal/command/email_pass_to_oidc/dto.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package commandEmailPassToOidc
|
||||||
|
|
||||||
|
type KeycloakUser struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
FirstName string `json:"firstName"`
|
||||||
|
LastName string `json:"lastName"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
EmailVerified bool `json:"emailVerified"`
|
||||||
|
Attributes map[string][]string `json:"attributes"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeycloakTokenResponse struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoopUser struct {
|
||||||
|
Id string `json:"id" db:"id"`
|
||||||
|
Username string `json:"username" db:"username"`
|
||||||
|
Password string `json:"password,omitempty" db:"password"`
|
||||||
|
AuthData *string `json:"auth_data,omitempty" db:"authdata"`
|
||||||
|
AuthService *string `json:"auth_service" db:"authservice"`
|
||||||
|
Email string `json:"email" db:"email"`
|
||||||
|
EmailVerified bool `json:"email_verified,omitempty" db:"emailverified"`
|
||||||
|
Nickname string `json:"nickname" db:"nickname"`
|
||||||
|
FirstName string `json:"first_name" db:"firstname"`
|
||||||
|
LastName string `json:"last_name" db:"lastname"`
|
||||||
|
Roles string `json:"roles" db:"roles"`
|
||||||
|
}
|
||||||
154
internal/command/email_pass_to_oidc/exec.go
Normal file
154
internal/command/email_pass_to_oidc/exec.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package commandEmailPassToOidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
c "scripts/internal/config"
|
||||||
|
l "scripts/pkg/logger"
|
||||||
|
"scripts/pkg/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Name = "usersUpdateLoginMethod"
|
||||||
|
|
||||||
|
func UsersUpdateLoginMethod() error {
|
||||||
|
postgres.InitDB()
|
||||||
|
defer postgres.DB.Close()
|
||||||
|
|
||||||
|
tokenData, err := keycloakAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to get access token from keycloak")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
loopUsers, err := allLoopUsers()
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to get users from loop db")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(loopUsers) == 0 {
|
||||||
|
l.Log.Info().Msg("no users in loop db for change login")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keycloakUsers, err := allKeycloakUsers(tokenData.AccessToken)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to get users from kecloak")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := changeLogin(loopUsers, keycloakUsers); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log.Info().Str("name", Name).Msg("command successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func keycloakAccessToken() (*KeycloakTokenResponse, error) {
|
||||||
|
url := fmt.Sprintf("%s/realms/%s/protocol/openid-connect/token", c.Conf.KeycloakSettings.Address, "master")
|
||||||
|
|
||||||
|
var data bytes.Buffer
|
||||||
|
fmt.Fprintf(&data, "grant_type=password&client_id=admin-cli&username=%s&password=%s", c.Conf.KeycloakSettings.AdminName, c.Conf.KeycloakSettings.AdminPass)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, &data)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to create http request")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to exec http request")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to get access token, status %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenResp KeycloakTokenResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to decode body response")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tokenResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func allLoopUsers() (map[string]LoopUser, error) {
|
||||||
|
ctx := postgres.DB.Context()
|
||||||
|
var users []LoopUser
|
||||||
|
|
||||||
|
if err := postgres.DB.Conn().SelectContext(ctx, &users, allUsersQuery); err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to select query")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]LoopUser, len(users))
|
||||||
|
for _, el := range users {
|
||||||
|
result[el.Email] = el
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func allKeycloakUsers(accessToken string) ([]KeycloakUser, error) {
|
||||||
|
url := fmt.Sprintf("%s/admin/realms/%s/users", c.Conf.KeycloakSettings.Address, c.Conf.KeycloakSettings.RealmName)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to create http request")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to exec http request")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to get users: status %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []KeycloakUser
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to decode body response")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeLogin(loopUsers map[string]LoopUser, keycloakUsers []KeycloakUser) error {
|
||||||
|
|
||||||
|
for _, el := range keycloakUsers {
|
||||||
|
|
||||||
|
user, exists := loopUsers[el.Email]
|
||||||
|
if !exists {
|
||||||
|
l.Log.Warn().Str("email", el.Email).Msg("user not found")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: если в keycloak не были указаны firstname и lastname - при первои входе сам keycloak спросит их и ЗАМЕНИТ данные в БД loop
|
||||||
|
if _, err := postgres.DB.Conn().ExecContext(postgres.DB.Context(), changeLoginQuery, user.Id, el.ID, el.EmailVerified); err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to exec update query")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log.Info().Str("email", el.Email).Msg("successfully update")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
34
internal/command/email_pass_to_oidc/query.go
Normal file
34
internal/command/email_pass_to_oidc/query.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package commandEmailPassToOidc
|
||||||
|
|
||||||
|
const (
|
||||||
|
allUsersQuery = `
|
||||||
|
SELECT
|
||||||
|
users.id,
|
||||||
|
users.username,
|
||||||
|
users.password,
|
||||||
|
users.authdata,
|
||||||
|
users.authservice,
|
||||||
|
users.email,
|
||||||
|
users.emailverified,
|
||||||
|
users.nickname,
|
||||||
|
users.firstname,
|
||||||
|
users.lastname,
|
||||||
|
users.roles
|
||||||
|
FROM users
|
||||||
|
LEFT JOIN bots ON users.id = bots.userid
|
||||||
|
WHERE users.deleteat = 0
|
||||||
|
AND bots.userid IS NULL
|
||||||
|
AND roles LIKE '%system_user%'
|
||||||
|
AND roles NOT LIKE '%system_admin%'
|
||||||
|
AND (users.authservice != 'openid' OR users.authservice IS NULL)
|
||||||
|
`
|
||||||
|
|
||||||
|
changeLoginQuery = `
|
||||||
|
UPDATE users
|
||||||
|
SET password = '',
|
||||||
|
authdata = $2,
|
||||||
|
emailverified = $3,
|
||||||
|
authservice = 'openid'
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
)
|
||||||
20
internal/command/email_pass_to_oidc/requirement.go
Normal file
20
internal/command/email_pass_to_oidc/requirement.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package commandEmailPassToOidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
c "scripts/internal/config"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Requirement() string {
|
||||||
|
var required []string
|
||||||
|
|
||||||
|
t := reflect.TypeFor[c.KeycloakSettings]()
|
||||||
|
|
||||||
|
for field := range t.Fields() {
|
||||||
|
jsonTag := field.Tag.Get("json")
|
||||||
|
required = append(required, jsonTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "необходимо задать в конфигурации ключ KeycloakSettings с ключами в объекте: " + strings.Join(required, "; ")
|
||||||
|
}
|
||||||
67
internal/config/config.go
Normal file
67
internal/config/config.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SqlSettings struct {
|
||||||
|
DriverName string `json:"DriverName"`
|
||||||
|
DataSource string `json:"DataSource"`
|
||||||
|
DataSourceReplicas []string `json:"DataSourceReplicas"`
|
||||||
|
DataSourceSearchReplicas []string `json:"DataSourceSearchReplicas"`
|
||||||
|
MaxIdleConns int `json:"MaxIdleConns"`
|
||||||
|
ConnMaxLifetimeMilliseconds int64 `json:"ConnMaxLifetimeMilliseconds"`
|
||||||
|
ConnMaxIdleTimeMilliseconds int64 `json:"ConnMaxIdleTimeMilliseconds"`
|
||||||
|
MaxOpenConns int `json:"MaxOpenConns"`
|
||||||
|
Trace bool `json:"Trace"`
|
||||||
|
AtRestEncryptKey string `json:"AtRestEncryptKey"`
|
||||||
|
QueryTimeout int `json:"QueryTimeout"`
|
||||||
|
DisableDatabaseSearch bool `json:"DisableDatabaseSearch"`
|
||||||
|
MigrationsStatementTimeoutSeconds int `json:"MigrationsStatementTimeoutSeconds"`
|
||||||
|
ReplicaMonitorIntervalSeconds int `json:"ReplicaMonitorIntervalSeconds"`
|
||||||
|
// ReplicaLagSettings []ReplicaLagSetting `json:"ReplicaLagSettings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogSettings struct {
|
||||||
|
EnableConsole bool `json:"EnableConsole"`
|
||||||
|
ConsoleLevel string `json:"ConsoleLevel"`
|
||||||
|
ConsoleJson bool `json:"ConsoleJson"`
|
||||||
|
EnableColor bool `json:"EnableColor"`
|
||||||
|
EnableFile bool `json:"EnableFile"`
|
||||||
|
FileLevel string `json:"FileLevel"`
|
||||||
|
FileJson bool `json:"FileJson"`
|
||||||
|
FileLocation string `json:"FileLocation"`
|
||||||
|
EnableWebhookDebugging bool `json:"EnableWebhookDebugging"`
|
||||||
|
EnableDiagnostics bool `json:"EnableDiagnostics"`
|
||||||
|
VerboseDiagnostics bool `json:"VerboseDiagnostics"`
|
||||||
|
EnableSentry bool `json:"EnableSentry"`
|
||||||
|
EnableCaller bool `json:"EnableCaller"`
|
||||||
|
AdvancedLoggingConfig string `json:"AdvancedLoggingConfig"`
|
||||||
|
MaxFieldSize int `json:"MaxFieldSize"`
|
||||||
|
// AdvancedLoggingJSON map[string]interface{} `json:"AdvancedLoggingJSON"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var Conf Config
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
SqlSettings SqlSettings `json:"SqlSettings"`
|
||||||
|
LogSettings LogSettings `json:"LogSettings"`
|
||||||
|
KeycloakSettings KeycloakSettings `json:"KeycloakSettings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustLoad(confPath string) {
|
||||||
|
file, err := os.Open(confPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("err", "failed to open file")
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
if err := decoder.Decode(&Conf); err != nil {
|
||||||
|
log.Fatal(err, "failed to decode")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("init config successfully")
|
||||||
|
}
|
||||||
8
internal/config/email_pass_to_oidc.go
Normal file
8
internal/config/email_pass_to_oidc.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type KeycloakSettings struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
RealmName string `json:"realm_name"`
|
||||||
|
AdminName string `json:"admin_name"`
|
||||||
|
AdminPass string `json:"admin_pass"`
|
||||||
|
}
|
||||||
7
pkg/errors/errors.go
Normal file
7
pkg/errors/errors.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCommandNotFound = errors.New("command not found")
|
||||||
|
)
|
||||||
54
pkg/logger/logger.go
Normal file
54
pkg/logger/logger.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
c "scripts/internal/config"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const timeFormat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
var Log zerolog.Logger
|
||||||
|
|
||||||
|
func MustInit() {
|
||||||
|
writers := make([]io.Writer, 0, 2)
|
||||||
|
|
||||||
|
zerolog.TimeFieldFormat = timeFormat
|
||||||
|
|
||||||
|
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: timeFormat}
|
||||||
|
writers = append(writers, consoleWriter)
|
||||||
|
|
||||||
|
if c.Conf.LogSettings.EnableFile {
|
||||||
|
logFile, err := os.OpenFile(c.Conf.LogSettings.FileLocation, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
writers = append(writers, logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
multi := zerolog.MultiLevelWriter(writers...)
|
||||||
|
zCtx := zerolog.New(multi).Level(detectedLogLevel(c.Conf.LogSettings.ConsoleLevel)).With().Timestamp()
|
||||||
|
if c.Conf.LogSettings.EnableCaller {
|
||||||
|
zCtx = zCtx.Caller()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log = zCtx.Logger()
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectedLogLevel(level string) zerolog.Level {
|
||||||
|
switch level {
|
||||||
|
case "TRACE":
|
||||||
|
return zerolog.TraceLevel
|
||||||
|
case "DEBUG":
|
||||||
|
return zerolog.DebugLevel
|
||||||
|
case "WARN":
|
||||||
|
return zerolog.WarnLevel
|
||||||
|
case "INFO":
|
||||||
|
return zerolog.InfoLevel
|
||||||
|
default:
|
||||||
|
return zerolog.InfoLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
68
pkg/postgres/connect.go
Normal file
68
pkg/postgres/connect.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
c "scripts/internal/config"
|
||||||
|
l "scripts/pkg/logger"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *Postgres
|
||||||
|
|
||||||
|
type Option func(*Postgres)
|
||||||
|
|
||||||
|
type Postgres struct {
|
||||||
|
ctx context.Context
|
||||||
|
conn *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Postgres) Conn() *sqlx.DB {
|
||||||
|
return p.conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Postgres) Context() context.Context {
|
||||||
|
return p.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithContext(ctx context.Context) Option {
|
||||||
|
return func(p *Postgres) {
|
||||||
|
p.ctx = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDB(opts ...Option) {
|
||||||
|
p := &Postgres{
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sqlx.ConnectContext(p.ctx, c.Conf.SqlSettings.DriverName, c.Conf.SqlSettings.DataSource)
|
||||||
|
if err != nil {
|
||||||
|
l.Log.Fatal().Err(err).Msg("failed to connect DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetMaxIdleConns(c.Conf.SqlSettings.MaxIdleConns)
|
||||||
|
db.SetMaxOpenConns(c.Conf.SqlSettings.MaxOpenConns)
|
||||||
|
db.SetConnMaxLifetime(time.Duration(c.Conf.SqlSettings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
|
||||||
|
db.SetConnMaxIdleTime(time.Duration(c.Conf.SqlSettings.ConnMaxIdleTimeMilliseconds) * time.Millisecond)
|
||||||
|
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
l.Log.Fatal().Err(err).Msg("failed to ping DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.conn = db
|
||||||
|
DB = p
|
||||||
|
l.Log.Info().Msg("connect to postgres successfully ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Postgres) Close() {
|
||||||
|
if err := p.Conn().Close(); err != nil {
|
||||||
|
l.Log.Error().Err(err).Msg("failed to close connect DB")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user