UPDATE: Add super admin

This commit is contained in:
2026-03-30 23:48:37 +07:00
parent 44d0d0c973
commit 62d4b889d1
18 changed files with 1061 additions and 16 deletions

View File

@@ -3,12 +3,14 @@ package database
import (
"context"
"history-api/pkg/config"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
func NewPostgresqlDB() (*pgxpool.Pool, error) {
ctx := context.Background()
connectionURI, err := config.GetConfig("PGX_CONNECTION_URI")
if err != nil {
return nil, err
@@ -19,14 +21,21 @@ func NewPostgresqlDB() (*pgxpool.Pool, error) {
return nil, err
}
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
if err != nil {
return nil, err
var pool *pgxpool.Pool
for i := 0; i < 5; i++ {
pool, err = pgxpool.NewWithConfig(ctx, poolConfig)
if err != nil {
time.Sleep(2 * time.Second)
continue
}
if err = pool.Ping(ctx); err == nil {
return pool, nil
}
time.Sleep(2 * time.Second)
}
if err := pool.Ping(ctx); err != nil {
return nil, err
}
return pool, nil
}
return nil, err
}

75
pkg/database/seed.go Normal file
View File

@@ -0,0 +1,75 @@
package database
import (
"context"
"database/sql"
"errors"
"history-api/internal/gen/sqlc"
"history-api/pkg/config"
"history-api/pkg/constants"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool"
"golang.org/x/crypto/bcrypt"
)
func SeedSuperAdmin(pool *pgxpool.Pool) error {
ctx := context.Background()
displayName, err := config.GetConfig("ADMIN_DISPLAY_NAME")
if err != nil {
return err
}
email, err := config.GetConfig("ADMIN_EMAIL")
if err != nil {
return err
}
password, err := config.GetConfig("ADMIN_PASSWORD")
if err != nil {
return err
}
q := sqlc.New(pool)
_, err = q.GetUserByEmail(ctx, email)
if err == nil {
return nil
}
if !errors.Is(err, sql.ErrNoRows) {
return err
}
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
user, err := q.UpsertUser(ctx, sqlc.UpsertUserParams{
Email: email,
PasswordHash: pgtype.Text{
String: string(hashed),
Valid: len(hashed) != 0,
},
AuthProvider: constants.LocalProvider.String(),
})
if err != nil {
return err
}
_, err = q.CreateUserProfile(ctx, sqlc.CreateUserProfileParams{
UserID: user.ID,
DisplayName: pgtype.Text{
String: displayName,
Valid: displayName != "",
},
})
if err != nil {
return err
}
return nil
}

139
pkg/storage/rustfs.go Normal file
View File

@@ -0,0 +1,139 @@
package storage
import (
"context"
"io"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/rs/zerolog/log"
ffconfig "history-api/pkg/config"
)
type UploadOptions struct {
ContentType string
ContentDisposition string
Metadata map[string]string
}
type Storage interface {
Upload(ctx context.Context, key string, body io.Reader, size int64, opts UploadOptions) error
PresignUpload(ctx context.Context, key string, expire time.Duration, opts UploadOptions) (string, error)
GetURL(ctx context.Context, key string, expire time.Duration) (string, error)
Delete(ctx context.Context, key string) error
}
type s3Storage struct {
client *s3.Client
ps *s3.PresignClient
bucket string
endPoint string
}
func NewS3Storage() (Storage, error) {
accessKey, err := ffconfig.GetConfig("STORAGE_ACCESS_KEY")
if err != nil {
return nil, err
}
secretAccessKey, err := ffconfig.GetConfig("STORAGE_SECRET_KEY")
if err != nil {
return nil, err
}
bucketName, err := ffconfig.GetConfig("STORAGE_BUCKET_NAME")
if err != nil {
return nil, err
}
endpoint, err := ffconfig.GetConfig("STORAGE_ENDPOINT")
if err != nil {
return nil, err
}
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKey, secretAccessKey, "")),
config.WithRegion("auto"),
)
if err != nil {
log.Error().Msgf("unable to load AWS SDK config, %v", err)
return nil, err
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String(endpoint)
})
return &s3Storage{
client: client,
ps: s3.NewPresignClient(client),
bucket: bucketName,
endPoint: endpoint,
}, nil
}
func (s *s3Storage) Upload(ctx context.Context, key string, body io.Reader, size int64, opts UploadOptions) error {
input := &s3.PutObjectInput{
Bucket: &s.bucket,
Key: &key,
Body: body,
}
if opts.ContentType != "" {
input.ContentType = aws.String(opts.ContentType)
}
if opts.ContentDisposition != "" {
input.ContentDisposition = aws.String(opts.ContentDisposition)
}
if len(opts.Metadata) > 0 {
input.Metadata = opts.Metadata
}
_, err := s.client.PutObject(ctx, input)
return err
}
func (s *s3Storage) PresignUpload(ctx context.Context, key string, expire time.Duration, opts UploadOptions) (string, error) {
input := &s3.PutObjectInput{
Bucket: &s.bucket,
Key: &key,
}
if opts.ContentType != "" {
input.ContentType = aws.String(opts.ContentType)
}
if opts.ContentDisposition != "" {
input.ContentDisposition = aws.String(opts.ContentDisposition)
}
if len(opts.Metadata) > 0 {
input.Metadata = opts.Metadata
}
req, err := s.ps.PresignPutObject(ctx, input, s3.WithPresignExpires(expire))
if err != nil {
return "", err
}
return req.URL, nil
}
func (s *s3Storage) GetURL(ctx context.Context, key string, expire time.Duration) (string, error) {
req, err := s.ps.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: &s.bucket,
Key: &key,
}, s3.WithPresignExpires(expire))
if err != nil {
return "", err
}
return req.URL, nil
}
func (s *s3Storage) Delete(ctx context.Context, key string) error {
_, err := s.client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: &s.bucket,
Key: &key,
})
return err
}