database & CD bucket
This commit is contained in:
parent
f5013e5f9e
commit
abd805b9cd
@ -9,3 +9,9 @@ simple object storage
|
||||
- Standalone
|
||||
- Head
|
||||
- StorageNode
|
||||
|
||||
## create openapi
|
||||
|
||||
``` go
|
||||
oapi-codegen -config openapi/server.cfg.yml openapi/openapi.yml
|
||||
```
|
||||
|
@ -466,12 +466,6 @@ type GetBucketsResponseObject interface {
|
||||
type GetBuckets200JSONResponse struct {
|
||||
Items *[]Bucket `json:"items,omitempty"`
|
||||
|
||||
// Limit Maximum number of items returned in the response
|
||||
Limit *int `json:"limit,omitempty"`
|
||||
|
||||
// Offset Number of items skipped before starting the response
|
||||
Offset *int `json:"offset,omitempty"`
|
||||
|
||||
// Total Total number of buckets available
|
||||
Total *int `json:"total,omitempty"`
|
||||
}
|
||||
@ -945,26 +939,26 @@ func (sh *strictHandler) PutBucketsBucketNameObjectsObjectKey(w http.ResponseWri
|
||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
var swaggerSpec = []string{
|
||||
|
||||
"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",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
42
config/env.go
Normal file
42
config/env.go
Normal file
@ -0,0 +1,42 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Config) ReadEnv() (err error) {
|
||||
|
||||
// General Configuration
|
||||
|
||||
stringVar(&c.BaseUrl, "BASE_URL")
|
||||
stringVar(&c.SavePath, "SAVE_PATH")
|
||||
intVar(&c.Port, "PORT")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func stringVar(x *string, key string) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
*x = v
|
||||
}
|
||||
}
|
||||
func float64Var(x *float64, key string) (err error) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
*x, err = strconv.ParseFloat(v, 64)
|
||||
}
|
||||
return
|
||||
}
|
||||
func intVar(x *int, key string) (err error) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
*x, err = strconv.Atoi(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
func durationVar(x *time.Duration, key string) (err error) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
*x, err = time.ParseDuration(v)
|
||||
}
|
||||
return
|
||||
}
|
134
config/env_test.go
Normal file
134
config/env_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestReadEnv(t *testing.T) {
|
||||
// Set up environment variables
|
||||
os.Setenv("CONFIG_PATH", "/path/to/config")
|
||||
os.Setenv("ENV", "dev")
|
||||
os.Setenv("PORT", "80")
|
||||
os.Setenv("BASE_URL", "https://example.com")
|
||||
os.Setenv("ACCESS_ORIGIN", "lou-taylor.ch")
|
||||
os.Setenv("DB_HOST", "localhost")
|
||||
os.Setenv("DB_PORT", "5432")
|
||||
os.Setenv("DB_USER", "testuser")
|
||||
os.Setenv("DB_PASSWORD", "password")
|
||||
os.Setenv("DB_NAME", "testdb")
|
||||
os.Setenv("ADMIN_NAME", "admin")
|
||||
os.Setenv("ADMIN_PASSWORD", "adminpass")
|
||||
os.Setenv("JWT_SECRET", "supersecret")
|
||||
os.Setenv("JWT_VALID_PERIOD", "24h")
|
||||
os.Setenv("IMAGE_QUALITY", "85.5")
|
||||
os.Setenv("IMAGE_MAX_WIDTH", "1920")
|
||||
os.Setenv("IMAGE_SAVE_PATH", "/path/to/images")
|
||||
|
||||
// Initialize an empty Config struct
|
||||
c := New()
|
||||
|
||||
// Call ReadEnv to populate the config from environment variables
|
||||
err := c.ReadEnv()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if c.BaseUrl != "https://example.com" {
|
||||
t.Errorf("expected BaseUrl to be 'https://example.com', got %v", c.BaseUrl)
|
||||
}
|
||||
if c.Port != 80 {
|
||||
t.Errorf("expected Port to be 80, got %d", c.Port)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHelperFunctions(t *testing.T) {
|
||||
t.Run("stringVar", func(t *testing.T) {
|
||||
// Set up environment variable
|
||||
os.Setenv("TEST_STRING", "test_value")
|
||||
|
||||
var result string
|
||||
stringVar(&result, "TEST_STRING")
|
||||
|
||||
if result != "test_value" {
|
||||
t.Errorf("expected 'test_value', got '%v'", result)
|
||||
}
|
||||
|
||||
// Test empty environment variable case
|
||||
os.Setenv("TEST_STRING_EMPTY", "")
|
||||
stringVar(&result, "TEST_STRING_EMPTY")
|
||||
|
||||
if result != "test_value" { // result shouldn't change
|
||||
t.Errorf("expected 'test_value', got '%v'", result)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("float64Var", func(t *testing.T) {
|
||||
// Set up valid float environment variable
|
||||
os.Setenv("TEST_FLOAT", "42.42")
|
||||
|
||||
var result float64
|
||||
err := float64Var(&result, "TEST_FLOAT")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if result != 42.42 {
|
||||
t.Errorf("expected 42.42, got '%v'", result)
|
||||
}
|
||||
|
||||
// Test invalid float value
|
||||
os.Setenv("TEST_FLOAT_INVALID", "invalid_float")
|
||||
err = float64Var(&result, "TEST_FLOAT_INVALID")
|
||||
if err == nil {
|
||||
t.Errorf("expected error for invalid float, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("intVar", func(t *testing.T) {
|
||||
// Set up valid integer environment variable
|
||||
os.Setenv("TEST_INT", "123")
|
||||
|
||||
var result int
|
||||
err := intVar(&result, "TEST_INT")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if result != 123 {
|
||||
t.Errorf("expected 123, got '%v'", result)
|
||||
}
|
||||
|
||||
// Test invalid int value
|
||||
os.Setenv("TEST_INT_INVALID", "invalid_int")
|
||||
err = intVar(&result, "TEST_INT_INVALID")
|
||||
if err == nil {
|
||||
t.Errorf("expected error for invalid int, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("durationVar", func(t *testing.T) {
|
||||
// Set up valid duration environment variable
|
||||
os.Setenv("TEST_DURATION", "2h")
|
||||
|
||||
var result time.Duration
|
||||
err := durationVar(&result, "TEST_DURATION")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expectedDuration := 2 * time.Hour
|
||||
if result != expectedDuration {
|
||||
t.Errorf("expected %v, got '%v'", expectedDuration, result)
|
||||
}
|
||||
|
||||
// Test invalid duration value
|
||||
os.Setenv("TEST_DURATION_INVALID", "invalid_duration")
|
||||
err = durationVar(&result, "TEST_DURATION_INVALID")
|
||||
if err == nil {
|
||||
t.Errorf("expected error for invalid duration, but got nil")
|
||||
}
|
||||
})
|
||||
}
|
13
config/flag.go
Normal file
13
config/flag.go
Normal file
@ -0,0 +1,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
func (c *Config) ReadFlags() {
|
||||
flag.StringVar(&c.ConfigPath, "config", c.ConfigPath, "path of the toml configuration file")
|
||||
flag.StringVar(&c.BaseUrl, "base-ulr", c.BaseUrl, "base url of the api")
|
||||
flag.IntVar(&c.Port, "port", c.Port, "port of the api")
|
||||
|
||||
flag.Parse()
|
||||
}
|
39
config/resource.go
Normal file
39
config/resource.go
Normal file
@ -0,0 +1,39 @@
|
||||
package config
|
||||
|
||||
type Environment string
|
||||
|
||||
const (
|
||||
Dev Environment = "dev"
|
||||
Staging = "staging"
|
||||
Production = "prod"
|
||||
)
|
||||
|
||||
// Config holds all configuration values
|
||||
type Config struct {
|
||||
ConfigPath string `toml:"-"`
|
||||
|
||||
SavePath string `toml:"savePath"`
|
||||
|
||||
BaseUrl string `toml:"base_url"`
|
||||
Port int `toml:"port"`
|
||||
}
|
||||
|
||||
// New returns a pointer to a default configuration with all empty values
|
||||
func New() *Config {
|
||||
return &Config{
|
||||
ConfigPath: "",
|
||||
Port: 0,
|
||||
SavePath: "",
|
||||
BaseUrl: "",
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns a pointer to a default configuration
|
||||
func Default() *Config {
|
||||
return &Config{
|
||||
ConfigPath: "./config.toml",
|
||||
BaseUrl: "http://localhost:8080",
|
||||
Port: 8080,
|
||||
SavePath: "./storage",
|
||||
}
|
||||
}
|
26
config/toml.go
Normal file
26
config/toml.go
Normal file
@ -0,0 +1,26 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
func (c *Config) ReadFile(name string) error {
|
||||
file, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = toml.NewDecoder(file).Decode(c)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Config) TOML() string {
|
||||
b, err := toml.Marshal(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
22
config/toml_test.go
Normal file
22
config/toml_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
c := Default()
|
||||
|
||||
err := c.ReadFile("../config.example.toml")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read config file: %v", err)
|
||||
}
|
||||
|
||||
// Validate the values are correctly set based on the TOML file
|
||||
expectedBaseURL := "http://localhost:8080"
|
||||
if c.BaseUrl != expectedBaseURL {
|
||||
t.Errorf("expected BaseUrl to be '%v', got '%v'", expectedBaseURL, c.BaseUrl)
|
||||
}
|
||||
|
||||
}
|
60
controller/controller.go
Normal file
60
controller/controller.go
Normal file
@ -0,0 +1,60 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/api"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (c *Controller) GetBuckets(ctx context.Context, request api.GetBucketsRequestObject) (api.GetBucketsResponseObject, error) {
|
||||
|
||||
buckets, err := c.db.FindApiBuckets(*request.Params.Limit, *request.Params.Offset)
|
||||
if err != nil {
|
||||
return api.GetBuckets500Response{}, nil
|
||||
}
|
||||
|
||||
t := len(buckets)
|
||||
|
||||
return api.GetBuckets200JSONResponse{
|
||||
Items: &buckets,
|
||||
Total: &t,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) PostBuckets(ctx context.Context, request api.PostBucketsRequestObject) (api.PostBucketsResponseObject, error) {
|
||||
|
||||
b := &model.Bucket{}
|
||||
b.Name = *request.Body.Name
|
||||
|
||||
c.db.InsertBucket(b)
|
||||
|
||||
return api.PostBuckets409Response{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteBucketsBucketName(ctx context.Context, request api.DeleteBucketsBucketNameRequestObject) (api.DeleteBucketsBucketNameResponseObject, error) {
|
||||
if err := c.db.DeleteBucketByName(request.BucketName); errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return api.DeleteBucketsBucketName404Response{}, nil
|
||||
} else if err != nil {
|
||||
return api.DeleteBucketsBucketName500Response{}, err
|
||||
}
|
||||
return api.DeleteBucketsBucketName204Response{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) GetBucketsBucketNameObjects(ctx context.Context, request api.GetBucketsBucketNameObjectsRequestObject) (api.GetBucketsBucketNameObjectsResponseObject, error) {
|
||||
return api.GetBucketsBucketNameObjects404Response{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteBucketsBucketNameObjectsObjectKey(ctx context.Context, request api.DeleteBucketsBucketNameObjectsObjectKeyRequestObject) (api.DeleteBucketsBucketNameObjectsObjectKeyResponseObject, error) {
|
||||
return api.DeleteBucketsBucketNameObjectsObjectKey500Response{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) GetBucketsBucketNameObjectsObjectKey(ctx context.Context, request api.GetBucketsBucketNameObjectsObjectKeyRequestObject) (api.GetBucketsBucketNameObjectsObjectKeyResponseObject, error) {
|
||||
return api.GetBucketsBucketNameObjectsObjectKey404Response{}, nil
|
||||
}
|
||||
|
||||
func (c *Controller) PutBucketsBucketNameObjectsObjectKey(ctx context.Context, request api.PutBucketsBucketNameObjectsObjectKeyRequestObject) (api.PutBucketsBucketNameObjectsObjectKeyResponseObject, error) {
|
||||
return api.PutBucketsBucketNameObjectsObjectKey500Response{}, nil
|
||||
}
|
24
controller/resource.go
Normal file
24
controller/resource.go
Normal file
@ -0,0 +1,24 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/api"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/model"
|
||||
)
|
||||
|
||||
// Implement the interface
|
||||
type Controller struct {
|
||||
db database
|
||||
}
|
||||
|
||||
func New(db database) *Controller {
|
||||
return &Controller{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type database interface {
|
||||
InsertBucket(bucket *model.Bucket) error
|
||||
FindApiBuckets(limit, offset int) (buckets []api.Bucket, err error)
|
||||
FindBucketIdByName(name string) (id uint, err error)
|
||||
DeleteBucketByName(name string) error
|
||||
}
|
30
database/controller.go
Normal file
30
database/controller.go
Normal file
@ -0,0 +1,30 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/api"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/model"
|
||||
)
|
||||
|
||||
func (db *DB) InsertBucket(bucket *model.Bucket) error {
|
||||
return db.conn.Create(bucket).Error
|
||||
}
|
||||
|
||||
func (db *DB) FindApiBuckets(limit, offset int) (buckets []api.Bucket, err error) {
|
||||
buckets = make([]api.Bucket, 0)
|
||||
err = db.conn.Model(&model.Bucket{}).Limit(limit).Offset(offset).Find(&buckets).Error
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) FindBucketIdByName(name string) (id uint, err error) {
|
||||
b := &model.Bucket{}
|
||||
err = db.conn.Select("id").First(b).Error
|
||||
|
||||
return b.ID, err
|
||||
}
|
||||
|
||||
func (db *DB) DeleteBucket(id uint) error {
|
||||
return db.conn.Delete(&model.Bucket{}, id).Error
|
||||
}
|
||||
func (db *DB) DeleteBucketByName(name string) error {
|
||||
return db.conn.Delete(&model.Bucket{}).Where("name = ?", name).Error
|
||||
}
|
20
database/resource.go
Normal file
20
database/resource.go
Normal file
@ -0,0 +1,20 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/model"
|
||||
"gorm.io/driver/sqlite" // Sqlite driver based on CGO
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
conn *gorm.DB
|
||||
}
|
||||
|
||||
func Init(file string) (*DB, error) {
|
||||
if db, err := gorm.Open(sqlite.Open(file), &gorm.Config{}); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
db.AutoMigrate(&model.Bucket{}, &model.Object{})
|
||||
return &DB{conn: db}, nil
|
||||
}
|
||||
}
|
8
go.mod
8
go.mod
@ -5,7 +5,11 @@ go 1.23.2
|
||||
require (
|
||||
github.com/getkin/kin-openapi v0.128.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/pelletier/go-toml/v2 v2.2.3
|
||||
gorm.io/driver/sqlite v1.5.6
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
@ -14,9 +18,13 @@ require (
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/invopop/yaml v0.3.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
16
go.sum
16
go.sum
@ -19,6 +19,12 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
|
||||
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
@ -28,10 +34,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -45,8 +55,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
||||
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
|
52
main.go
Normal file
52
main.go
Normal file
@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/api"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/config"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/controller"
|
||||
"git.schreifuchs.ch/schreifuchs/warehouse/database"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func readCfg() *config.Config {
|
||||
cfg := config.Default()
|
||||
|
||||
if err := godotenv.Overload(); err != nil {
|
||||
fmt.Println("no .env file loaded")
|
||||
} else {
|
||||
fmt.Println(".env file loaded")
|
||||
}
|
||||
cfg.ReadEnv()
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := readCfg()
|
||||
|
||||
fmt.Println(cfg.TOML())
|
||||
|
||||
db, err := database.Init("./warehouse.db")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
controller := controller.New(db)
|
||||
|
||||
apiHandler := api.Handler(api.NewStrictHandler(controller, nil))
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.PathPrefix("/").Handler(apiHandler)
|
||||
|
||||
if err := http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), r); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
16
model/resource.go
Normal file
16
model/resource.go
Normal file
@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Bucket struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"uniqueIndex"`
|
||||
Objects []Object
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"uniqueIndex"`
|
||||
Size uint
|
||||
BucketId uint
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
|
||||
openapi: 3.0.3
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Object Storage API
|
||||
description: API for managing objects in an object storage service.
|
||||
@ -21,7 +21,7 @@ paths:
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 10
|
||||
default: 100
|
||||
description: The maximum number of items to return
|
||||
- name: offset
|
||||
in: query
|
||||
@ -40,12 +40,6 @@ paths:
|
||||
total:
|
||||
type: integer
|
||||
description: Total number of buckets available
|
||||
limit:
|
||||
type: integer
|
||||
description: Maximum number of items returned in the response
|
||||
offset:
|
||||
type: integer
|
||||
description: Number of items skipped before starting the response
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
|
BIN
warehouse.db
Normal file
BIN
warehouse.db
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user