database & CD bucket

This commit is contained in:
schreifuchs 2024-11-13 21:16:22 +01:00
parent f5013e5f9e
commit abd805b9cd
18 changed files with 530 additions and 34 deletions

View File

@ -9,3 +9,9 @@ simple object storage
- Standalone - Standalone
- Head - Head
- StorageNode - StorageNode
## create openapi
``` go
oapi-codegen -config openapi/server.cfg.yml openapi/openapi.yml
```

View File

@ -466,12 +466,6 @@ type GetBucketsResponseObject interface {
type GetBuckets200JSONResponse struct { type GetBuckets200JSONResponse struct {
Items *[]Bucket `json:"items,omitempty"` 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 Total number of buckets available
Total *int `json:"total,omitempty"` Total *int `json:"total,omitempty"`
} }
@ -945,26 +939,26 @@ func (sh *strictHandler) PutBucketsBucketNameObjectsObjectKey(w http.ResponseWri
// Base64 encoded, gzipped, json marshaled Swagger object // Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{ var swaggerSpec = []string{
"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",
} }
// GetSwagger returns the content of the embedded swagger specification file // GetSwagger returns the content of the embedded swagger specification file

42
config/env.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -5,7 +5,11 @@ go 1.23.2
require ( require (
github.com/getkin/kin-openapi v0.128.0 github.com/getkin/kin-openapi v0.128.0
github.com/gorilla/mux v1.8.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/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 ( require (
@ -14,9 +18,13 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/google/uuid v1.5.0 // indirect github.com/google/uuid v1.5.0 // indirect
github.com/invopop/yaml v0.3.1 // 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/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // 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 gopkg.in/yaml.v3 v3.0.1 // indirect
) )

16
go.sum
View File

@ -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/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 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= 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 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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= 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/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 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= 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 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= 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 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/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 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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
View 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
View 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
}

View File

@ -1,5 +1,5 @@
openapi: 3.0.3 openapi: 3.0.0
info: info:
title: Object Storage API title: Object Storage API
description: API for managing objects in an object storage service. description: API for managing objects in an object storage service.
@ -21,7 +21,7 @@ paths:
in: query in: query
schema: schema:
type: integer type: integer
default: 10 default: 100
description: The maximum number of items to return description: The maximum number of items to return
- name: offset - name: offset
in: query in: query
@ -40,12 +40,6 @@ paths:
total: total:
type: integer type: integer
description: Total number of buckets available 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: items:
type: array type: array
items: items:

BIN
warehouse.db Normal file

Binary file not shown.