Compare commits

..

No commits in common. 'd25f03e80c92fe895f50dfd6421cd45ee6de20e4' and 'd1e53c23379f70add4ba59e964c99ca7a0bfd85e' have entirely different histories.

@ -2,16 +2,10 @@ FROM golang:alpine as builder
ENV GO111MODULE=on
ENV USER=appuser
ENV UID=1000
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
"${USER}"
# Create the user and group files to run unprivileged
RUN mkdir /user && \
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
echo 'nobody:x:65534:' > /user/group
RUN apk update && apk add --no-cache git ca-certificates tzdata sqlite build-base
RUN mkdir /build
@ -21,13 +15,11 @@ WORKDIR /build
COPY ./ ./
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o bog .
FROM scratch AS final
FROM golang:alpine AS final
LABEL author="Cajually <me@caj.me>"
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
COPY --from=builder /user/group /user/passwd /etc/
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /build/bog /
COPY --from=builder /build/server/views /server/views
@ -35,7 +27,7 @@ COPY --from=builder /build/default.toml /
WORKDIR /
USER appuser:appuser
USER nobody:nobody
ENTRYPOINT ["/bog"]
EXPOSE 8002

@ -1,7 +1 @@
package dataswamp
import (
// "caj-larsson/bog/dataswamp/namespace"
)
type AdminService struct{}

@ -0,0 +1,7 @@
package dataswamp
type Logger interface {
Debug(format string, a ...interface{})
Info(format string, a ...interface{})
Warn(format string, a ...interface{})
}

@ -11,6 +11,7 @@ type Namespace struct {
LastSeen time.Time
AllowanceDuration time.Duration
FileQuota FileSizeQuota
Usage FileStat
Download FileStat
Upload FileStat
}

@ -27,15 +27,13 @@ func basicNamespaceContract(fac func() Repository, t *testing.T) {
FileSizeQuota{1000, 0},
FileStat{1, 2},
FileStat{3, 4},
FileStat{5, 6},
}
ns1, err := r.Create(ns)
is.NoErr(err)
ns1, _ := r.Create(ns)
ns.Name = "n2"
ns2, err := r.Create(ns)
is.NoErr(err)
is.True(ns1.ID != ns2.ID)
ns2, _ := r.Create(ns)
is.True(ns1 != ns2)
all, err = r.All()

@ -1,111 +0,0 @@
package namespace
import (
"caj-larsson/bog/util"
"fmt"
"time"
)
type Clock interface {
Now() time.Time
}
type NamespaceService struct {
repo Repository
outboxes []func(util.Event)
logger util.Logger
clock Clock
default_ttl time.Duration
default_quota_bytes int64
}
func NewNamespaceService(repo Repository, logger util.Logger, clock Clock, default_ttl time.Duration, default_quota_bytes int64) *NamespaceService {
return &NamespaceService{
repo,
nil,
logger,
clock,
default_ttl,
default_quota_bytes,
}
}
func (s *NamespaceService) GetOrCreateNs(name string) *Namespace {
ns, err := s.repo.GetByName(name)
if err == ErrNotExists {
new_ns := Namespace{
0,
name,
s.clock.Now(),
s.default_ttl,
FileSizeQuota{s.default_quota_bytes, 0},
FileStat{0, 0},
FileStat{0, 0},
}
created_ns, err := s.repo.Create(new_ns)
if err != nil {
panic(err)
}
return created_ns
}
if err != nil {
panic(err)
}
return ns
}
func (s *NamespaceService) Wire(reg func(string, util.EventHandler), outbox func(ev util.Event)) {
reg("FileUsed", s.handleFileUsed)
s.outboxes = append(s.outboxes, outbox)
reg("FileUsed", s.handleFileUsed)
reg("FileDeleted", s.handleFileDeleted)
reg("FileRecieved", s.handleFileRecieved)
}
func (s *NamespaceService) All() []Namespace {
nss, err := s.repo.All()
if err != nil {
panic(err)
}
return nss
}
func (s *NamespaceService) handleFileUsed(payload interface{}) {
var payload_s = payload.(struct {
Name string
Size int64
})
fmt.Printf("file used %v\n", payload_s)
ns := s.GetOrCreateNs(payload_s.Name)
ns.FileQuota = ns.FileQuota.Add(payload_s.Size)
s.repo.Update(ns.ID, *ns)
}
func (s *NamespaceService) handleFileDeleted(payload interface{}) {
var payload_s = payload.(struct {
Name string
Size int64
})
fmt.Printf("file deleted %v\n", payload_s)
ns := s.GetOrCreateNs(payload_s.Name)
ns.FileQuota = ns.FileQuota.Add(-payload_s.Size)
fmt.Printf("file usage %v\n", ns.FileQuota)
s.repo.Update(ns.ID, *ns)
}
func (s *NamespaceService) handleFileRecieved(payload interface{}) {
var payload_s = payload.(struct {
Name string
Size int64
})
fmt.Printf("file recieved %v\n", payload_s)
ns := s.GetOrCreateNs(payload_s.Name)
ns.FileQuota = ns.FileQuota.Add(payload_s.Size)
fmt.Printf("file usage %v\n", ns.FileQuota)
s.repo.Update(ns.ID, *ns)
}

@ -1,38 +0,0 @@
package namespace
import (
"caj-larsson/bog/util"
"testing"
)
func TestEventTest(t *testing.T) {
eb := util.NewEventBus()
svc := NamespaceService{}
svc.Wire(eb.Register, eb.Handle)
events := []util.Event{
*util.NewEvent("FileUsed", struct {
Name string
Size int64
}{
"asd",
int64(12),
}),
*util.NewEvent("FileDeleted", struct {
Name string
Size int64
}{
"asd",
int64(12),
}),
*util.NewEvent("FileRecieved", struct {
Name string
Size int64
}{
"asd",
int64(12),
}),
}
util.AcceptsMessage(t, eb, events)
}

@ -13,18 +13,28 @@ func (f FileSizeQuota) Remaining() int64 {
return f.AllowanceKB - f.CurrentUsage
}
func (f FileSizeQuota) Add(size int64) FileSizeQuota {
return FileSizeQuota{
func (f FileSizeQuota) Add(size int64) (*FileSizeQuota, error) {
if !f.Allows(size) {
return nil, ErrExceedQuota
}
n := FileSizeQuota {
f.AllowanceKB,
f.CurrentUsage + size,
}
return &n, nil
}
func (f FileSizeQuota) Remove(size int64) FileSizeQuota {
return FileSizeQuota{
func (f FileSizeQuota) Remove(size int64) (*FileSizeQuota, error) {
if size > f.CurrentUsage {
return nil, ErrQuotaInvalid
}
n := FileSizeQuota {
f.AllowanceKB,
f.CurrentUsage - size,
}
return &n, nil
}
type FileStat struct {

@ -15,9 +15,27 @@ func TestQuota(t *testing.T) {
func TestQuotaManipulation(t *testing.T) {
is := is.New(t)
quota := FileSizeQuota{1000, 0}
quota = quota.Add(500)
quota := &FileSizeQuota{1000, 0}
quota, err := quota.Add(500)
is.NoErr(err)
is.Equal(quota.CurrentUsage, int64(500))
quota = quota.Remove(1000)
is.Equal(quota.CurrentUsage, int64(-500))
quota, err = quota.Add(500)
is.NoErr(err)
_ , err = quota.Add(1)
is.Equal(err, ErrExceedQuota)
is.Equal(quota.CurrentUsage, int64(1000))
_ , err = quota.Remove(1001)
is.Equal(err, ErrQuotaInvalid)
is.Equal(quota.CurrentUsage, int64(1000))
quota, err = quota.Remove(1000)
is.NoErr(err)
is.Equal(quota.CurrentUsage, int64(0))
}

@ -3,7 +3,6 @@ package dataswamp
import (
"caj-larsson/bog/dataswamp/namespace"
"caj-larsson/bog/dataswamp/swampfile"
"caj-larsson/bog/util"
"io"
"strconv"
"time"
@ -11,29 +10,56 @@ import (
// "fmt"
)
type DataSwampService struct {
ns_svc namespace.NamespaceService
type SwampFileService struct {
namespace_repo namespace.Repository
swamp_file_repo swampfile.Repository
logger util.Logger
eventBus util.EventBus
default_allowance_bytes int64
default_allowance_duration time.Duration
logger Logger
}
func NewDataSwampService(
ns_svc namespace.NamespaceService,
func NewSwampFileService(
namespace_repo namespace.Repository,
swamp_file_repo swampfile.Repository,
logger util.Logger,
) *DataSwampService {
s := DataSwampService{ns_svc, swamp_file_repo, logger, *util.NewEventBus()}
ns_svc.Wire(s.eventBus.Register, s.eventBus.Handle)
return &s
da_bytes int64,
da_duration time.Duration,
logger Logger,
) SwampFileService {
return SwampFileService{namespace_repo, swamp_file_repo, da_bytes, da_duration, logger}
}
func (s DataSwampService) NamespaceStats() []namespace.Namespace {
return s.ns_svc.All()
func (s SwampFileService) getOrCreateNs(namespace_in string) *namespace.Namespace {
ns, err := s.namespace_repo.GetByName(namespace_in)
if err == namespace.ErrNotExists {
new_ns := namespace.Namespace{
0,
namespace_in,
time.Now(),
s.default_allowance_duration,
namespace.FileSizeQuota{s.default_allowance_bytes, 0},
namespace.FileStat{0, 0},
namespace.FileStat{0, 0},
namespace.FileStat{0, 0},
}
created_ns, err := s.namespace_repo.Create(new_ns)
if err != nil {
panic(err)
}
return created_ns
}
if err != nil {
panic(err)
}
return ns
}
func (s DataSwampService) SaveFile(ref swampfile.FileReference, src io.Reader, size int64) error {
ns := s.ns_svc.GetOrCreateNs(ref.UserAgent)
func (s SwampFileService) SaveFile(ref swampfile.FileReference, src io.Reader, size int64) error {
ns := s.getOrCreateNs(ref.UserAgent)
r, err := ref.Clean(true)
@ -48,23 +74,13 @@ func (s DataSwampService) SaveFile(ref swampfile.FileReference, src io.Reader, s
f, err := s.swamp_file_repo.Create(r.Path, strconv.FormatInt(ns.ID, 10))
if err != nil {
// TODO: convert this into a different error.
return err
}
s.eventBus.Handle(*util.NewEvent("FileUsed", struct {
Name string
Size int64
}{
ns.Name,
f.Size(),
}))
// TODO: rewrite this into an interruptable loop that emits downloaded events
written, err := io.CopyN(f, src, size)
if written < size {
s.swamp_file_repo.Delete(r.Path, strconv.FormatInt(ns.ID, 10)) //
s.swamp_file_repo.Delete(r.Path, strconv.FormatInt(ns.ID, 10))
return swampfile.ErrContentSizeExaggerated
}
@ -78,19 +94,20 @@ func (s DataSwampService) SaveFile(ref swampfile.FileReference, src io.Reader, s
}
f.Close()
s.eventBus.Handle(*util.NewEvent("FileRecieved", struct {
Name string
Size int64
}{
ns.Name,
written,
}))
uq, err := ns.FileQuota.Add(size)
if err != nil {
return err
}
ns.FileQuota = *uq
ns.Usage = ns.Usage.Add(size)
ns.Upload = ns.Upload.Add(size)
s.namespace_repo.Update(ns.ID, *ns)
return nil
}
func (s DataSwampService) OpenOutFile(ref swampfile.FileReference) (swampfile.SwampOutFile, error) {
ns := s.ns_svc.GetOrCreateNs(ref.UserAgent)
func (s SwampFileService) OpenOutFile(ref swampfile.FileReference) (swampfile.SwampOutFile, error) {
ns := s.getOrCreateNs(ref.UserAgent)
r, err := ref.Clean(true)
@ -104,31 +121,41 @@ func (s DataSwampService) OpenOutFile(ref swampfile.FileReference) (swampfile.Sw
return nil, err
}
s.eventBus.Handle(*util.NewEvent("FileSent", f.Size()))
ns.Download = ns.Download.Add(f.Size())
s.namespace_repo.Update(ns.ID, *ns)
return f, nil
}
func (s DataSwampService) CleanUpExpiredFiles() error {
func (s SwampFileService) NamespaceStats() ([]namespace.Namespace, error) {
return s.namespace_repo.All()
}
func (s SwampFileService) CleanUpExpiredFiles() error {
s.logger.Info("Cleaning up expired files")
nss, err := s.namespace_repo.All()
if err != nil {
return err
}
for _, ns := range s.ns_svc.All() {
for _, ns := range nss {
expiry := time.Now().Add(-ns.AllowanceDuration)
dfs, err := s.swamp_file_repo.DeleteOlderThan(strconv.FormatInt(ns.ID, 10), expiry)
for _, df := range dfs {
dq, err := ns.FileQuota.Remove(df.Size)
if err != nil {
panic(err)
dq.CurrentUsage = 0
}
ns.FileQuota = *dq
}
for _, df := range dfs {
s.eventBus.Handle(*util.NewEvent("FileDeleted", struct {
Name string
Size int64
}{
ns.Name,
df.Size,
}))
if err != nil {
panic(err)
}
s.namespace_repo.Update(ns.ID, ns)
}
return nil
}

@ -2,16 +2,14 @@ package dataswamp
import (
"bytes"
"caj-larsson/bog/dataswamp/namespace"
"caj-larsson/bog/dataswamp/swampfile"
m_namespace "caj-larsson/bog/infrastructure/memory/namespace"
m_swampfile "caj-larsson/bog/infrastructure/memory/swampfile"
"caj-larsson/bog/infrastructure/system_time"
"fmt"
"github.com/matryer/is"
"github.com/spf13/afero"
"testing"
"time"
// "caj-larsson/bog/dataswamp/namespace"
m_namespace "caj-larsson/bog/infrastructure/memory/namespace"
m_swampfile "caj-larsson/bog/infrastructure/memory/swampfile"
)
type TestLogger struct{}
@ -24,25 +22,15 @@ var file_ref1 = swampfile.FileReference{"/path1", "ns1"}
var file_ref2 = swampfile.FileReference{"/path1", "ns2"}
var file_ref3 = swampfile.FileReference{"/path2", "ns1"}
func NewTestDataSwampService() DataSwampService {
func NewTestSwampFileService() SwampFileService {
file_repo := m_swampfile.NewRepository()
ns_repo := m_namespace.NewRepository()
logger := TestLogger{}
ns_svc := namespace.NewNamespaceService(
ns_repo,
logger,
system_time.Clock{},
time.Hour,
1024,
)
return *NewDataSwampService(*ns_svc, file_repo, logger)
return NewSwampFileService(ns_repo, file_repo, 1024, time.Hour, TestLogger{})
}
func TestFileDontExist(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
outfile, err := s.OpenOutFile(file_ref1)
is.True(err == swampfile.ErrNotExists)
@ -51,7 +39,7 @@ func TestFileDontExist(t *testing.T) {
func TestFileIsStored(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
fakefile := bytes.NewBufferString("My bog data")
@ -72,7 +60,7 @@ func TestFileIsStored(t *testing.T) {
func TestFileIsReadBack(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
infile := bytes.NewBufferString("My bog data")
@ -88,7 +76,7 @@ func TestFileIsReadBack(t *testing.T) {
func TestNSIsolation(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
ns1_file := bytes.NewBufferString("My bog data ns1")
ns2_file := bytes.NewBufferString("My bog data ns2")
@ -106,7 +94,7 @@ func TestNSIsolation(t *testing.T) {
func TestPathStrictMode(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
ns_file := bytes.NewBufferString("My bog data ns1")
@ -126,7 +114,7 @@ func TestPathStrictMode(t *testing.T) {
func TestQuotaWithContenSizeLieOver(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
largefakefile := bytes.NewBufferString("")
@ -141,7 +129,7 @@ func TestQuotaWithContenSizeLieOver(t *testing.T) {
func TestQuotaWithContenSizeLieUnder(t *testing.T) {
is := is.New(t)
s := NewTestDataSwampService()
s := NewTestSwampFileService()
largefakefile := bytes.NewBufferString("small")
@ -156,32 +144,22 @@ func TestCleanUpExpired(t *testing.T) {
fs := afero.NewMemMapFs()
file_repo := m_swampfile.Repository{fs}
ns_repo := m_namespace.NewRepository()
logger := TestLogger{}
ns_svc := namespace.NewNamespaceService(
ns_repo,
logger,
system_time.Clock{},
time.Hour,
1024,
)
s := NewDataSwampService(*ns_svc, file_repo, logger)
fakefile := bytes.NewBufferString("My bog")
s := NewSwampFileService(ns_repo, file_repo, 1024, time.Hour, TestLogger{})
fakefile := bytes.NewBufferString("My bog data")
err := s.SaveFile(file_ref1, fakefile, int64(fakefile.Len()))
is.NoErr(err)
fakefile = bytes.NewBufferString("My bog")
fakefile = bytes.NewBufferString("My bog data")
err = s.SaveFile(file_ref3, fakefile, int64(fakefile.Len()))
is.NoErr(err)
err = fs.Chtimes("1/path1", time.Now(), time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))
is.NoErr(err)
is.NoErr(s.CleanUpExpiredFiles())
ns, err := ns_repo.GetByName("ns1")
fmt.Printf("file final usage %v\n", ns.FileQuota)
is.NoErr(err)
fmt.Printf("file\n")
is.Equal(ns.FileQuota.CurrentUsage, int64(len("My bog")))
is.Equal(ns.FileQuota.CurrentUsage, int64(len("My bog data")))
}

@ -43,7 +43,7 @@ func (fr *FileReference) Clean(strict bool) (*FileReference, error) {
return nil, ErrUnacceptablePath
}
n := FileReference{
n := FileReference {
c,
fr.UserAgent,
}

@ -48,13 +48,6 @@ func (f FileSystemSwampFileData) Modified() time.Time {
return stat.ModTime()
}
func (f FileSystemSwampFileData) Truncate() {
err := f.file.Truncate(0)
if err != nil {
panic(err)
}
}
type Repository struct {
Root string
}
@ -105,7 +98,6 @@ func (f Repository) Create(filename string, namespace_ns string) (swampfile.Swam
if err != nil {
panic(err)
}
file.Truncate(0)
bfd := FileSystemSwampFileData{filename, stat_info.Size(), stat_info.ModTime(), file}

@ -44,13 +44,6 @@ func (f SwampFile) Modified() time.Time {
return stat.ModTime()
}
func (f SwampFile) Truncate() {
err := f.file.Truncate(0)
if err != nil {
panic(err)
}
}
// The actual repository
type Repository struct {
Fs afero.Fs

@ -21,6 +21,7 @@ type Namespace struct {
AllowanceTime sql.NullInt64
QuotaKb sql.NullInt64
QuotaUsageKb sql.NullInt64
UsageID int64
DownloadID int64
UploadID int64
}

@ -6,10 +6,11 @@ INSERT INTO
allowance_time,
quota_kb,
quota_usage_kb,
usage_id,
download_id,
upload_id
)
values(?, ?, ?, ?, ?, ?, ?)
values(?, ?, ?, ?, ?, ?, ?, ?)
returning id;
-- name: CreateFileStats :one
@ -25,11 +26,15 @@ SELECT
ns.allowance_time,
ns.quota_kb,
ns.quota_usage_kb,
u.num as u_num,
u.size_b as u_size_b,
d.num as d_num,
d.size_b as d_size_b,
ul.num as ul_num,
ul.size_b as ul_size_b
FROM namespace as ns
JOIN file_stats as u
ON ns.usage_id = u.id
JOIN file_stats as d
ON ns.download_id = d.id
JOIN file_stats as ul
@ -43,11 +48,15 @@ SELECT
ns.allowance_time,
ns.quota_kb,
ns.quota_usage_kb,
u.num as u_num,
u.size_b as u_size_b,
d.num as d_num,
d.size_b as d_size_b,
ul.num as ul_num,
ul.size_b as ul_size_b
FROM namespace as ns
JOIN file_stats as u
ON ns.usage_id = u.id
JOIN file_stats as d
ON ns.download_id = d.id
JOIN file_stats as ul
@ -69,7 +78,7 @@ UPDATE namespace SET
quota_kb = ?,
quota_usage_kb = ?
WHERE id = ?
RETURNING download_id, upload_id;
RETURNING usage_id, download_id, upload_id;
-- name: DeleteNameSpace :exec
DELETE FROM namespace where id = ?;

@ -18,11 +18,15 @@ SELECT
ns.allowance_time,
ns.quota_kb,
ns.quota_usage_kb,
u.num as u_num,
u.size_b as u_size_b,
d.num as d_num,
d.size_b as d_size_b,
ul.num as ul_num,
ul.size_b as ul_size_b
FROM namespace as ns
JOIN file_stats as u
ON ns.usage_id = u.id
JOIN file_stats as d
ON ns.download_id = d.id
JOIN file_stats as ul
@ -36,6 +40,8 @@ type AllNamespacesRow struct {
AllowanceTime sql.NullInt64
QuotaKb sql.NullInt64
QuotaUsageKb sql.NullInt64
UNum int64
USizeB int64
DNum int64
DSizeB int64
UlNum int64
@ -58,6 +64,8 @@ func (q *Queries) AllNamespaces(ctx context.Context) ([]AllNamespacesRow, error)
&i.AllowanceTime,
&i.QuotaKb,
&i.QuotaUsageKb,
&i.UNum,
&i.USizeB,
&i.DNum,
&i.DSizeB,
&i.UlNum,
@ -102,10 +110,11 @@ INSERT INTO
allowance_time,
quota_kb,
quota_usage_kb,
usage_id,
download_id,
upload_id
)
values(?, ?, ?, ?, ?, ?, ?)
values(?, ?, ?, ?, ?, ?, ?, ?)
returning id
`
@ -115,6 +124,7 @@ type CreateNamespaceParams struct {
AllowanceTime sql.NullInt64
QuotaKb sql.NullInt64
QuotaUsageKb sql.NullInt64
UsageID int64
DownloadID int64
UploadID int64
}
@ -126,6 +136,7 @@ func (q *Queries) CreateNamespace(ctx context.Context, arg CreateNamespaceParams
arg.AllowanceTime,
arg.QuotaKb,
arg.QuotaUsageKb,
arg.UsageID,
arg.DownloadID,
arg.UploadID,
)
@ -162,11 +173,15 @@ SELECT
ns.allowance_time,
ns.quota_kb,
ns.quota_usage_kb,
u.num as u_num,
u.size_b as u_size_b,
d.num as d_num,
d.size_b as d_size_b,
ul.num as ul_num,
ul.size_b as ul_size_b
FROM namespace as ns
JOIN file_stats as u
ON ns.usage_id = u.id
JOIN file_stats as d
ON ns.download_id = d.id
JOIN file_stats as ul
@ -181,6 +196,8 @@ type GetNamespaceByNameRow struct {
AllowanceTime sql.NullInt64
QuotaKb sql.NullInt64
QuotaUsageKb sql.NullInt64
UNum int64
USizeB int64
DNum int64
DSizeB int64
UlNum int64
@ -197,6 +214,8 @@ func (q *Queries) GetNamespaceByName(ctx context.Context, name string) (GetNames
&i.AllowanceTime,
&i.QuotaKb,
&i.QuotaUsageKb,
&i.UNum,
&i.USizeB,
&i.DNum,
&i.DSizeB,
&i.UlNum,
@ -228,7 +247,7 @@ UPDATE namespace SET
quota_kb = ?,
quota_usage_kb = ?
WHERE id = ?
RETURNING download_id, upload_id
RETURNING usage_id, download_id, upload_id
`
type UpdateNamespaceParams struct {
@ -241,6 +260,7 @@ type UpdateNamespaceParams struct {
}
type UpdateNamespaceRow struct {
UsageID int64
DownloadID int64
UploadID int64
}
@ -255,6 +275,6 @@ func (q *Queries) UpdateNamespace(ctx context.Context, arg UpdateNamespaceParams
arg.ID,
)
var i UpdateNamespaceRow
err := row.Scan(&i.DownloadID, &i.UploadID)
err := row.Scan(&i.UsageID, &i.DownloadID, &i.UploadID)
return i, err
}

@ -34,9 +34,10 @@ CREATE TABLE IF NOT EXISTS namespace(
allowance_time BIGINT,
quota_kb BIGINT,
quota_usage_kb BIGINT,
usage_id BIGINT NOT NULL REFERENCES file_stats(Id),
download_id BIGINT NOT NULL REFERENCES file_stats(Id),
upload_id BIGINT NOT NULL REFERENCES file_stats(Id)
);`
);`
_, err = r.db.Exec(query)
return err
@ -63,6 +64,12 @@ func (q *Queries) createFileStats(ctx context.Context, fstat namespace.FileStat)
func (r *Repository) Create(ns namespace.Namespace) (*namespace.Namespace, error) {
ctx := context.Background()
q := New(r.db)
u_id, err := q.createFileStats(ctx, ns.Usage)
if err != nil {
return nil, err
}
dl_id, err := q.createFileStats(ctx, ns.Download)
if err != nil {
@ -83,6 +90,7 @@ func (r *Repository) Create(ns namespace.Namespace) (*namespace.Namespace, error
AllowanceTime: sql.NullInt64{int64(ns.AllowanceDuration.Seconds()), true},
QuotaKb: sql.NullInt64{int64(ns.FileQuota.AllowanceKB), true},
QuotaUsageKb: sql.NullInt64{int64(ns.FileQuota.CurrentUsage), true},
UsageID: u_id,
DownloadID: dl_id,
UploadID: ul_id,
}
@ -114,6 +122,7 @@ func (r *Repository) All() ([]namespace.Namespace, error) {
time.UnixMicro(row.Lastseen),
time.Duration(row.AllowanceTime.Int64 * int64(time.Second)),
namespace.FileSizeQuota{row.QuotaKb.Int64, row.QuotaUsageKb.Int64},
namespace.FileStat{row.UNum, row.USizeB},
namespace.FileStat{row.DNum, row.DSizeB},
namespace.FileStat{row.UlNum, row.UlSizeB},
}
@ -139,6 +148,7 @@ func (r *Repository) GetByName(name string) (*namespace.Namespace, error) {
time.UnixMicro(row.Lastseen),
time.Duration(row.AllowanceTime.Int64 * int64(time.Second)),
namespace.FileSizeQuota{row.QuotaKb.Int64, row.QuotaUsageKb.Int64},
namespace.FileStat{row.UNum, row.USizeB},
namespace.FileStat{row.DNum, row.DSizeB},
namespace.FileStat{row.UlNum, row.UlSizeB},
}
@ -163,6 +173,11 @@ func (r *Repository) Update(id int64, ns namespace.Namespace) (*namespace.Namesp
ns.ID,
})
err = q.updateFileStat(ctx, ids.UsageID, ns.Usage)
if err != nil {
return nil, err
}
err = q.updateFileStat(ctx, ids.DownloadID, ns.Download)
if err != nil {
return nil, err

@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS namespace(
allowance_time BIGINT,
quota_kb BIGINT,
quota_usage_kb BIGINT,
usage_id BIGINT NOT NULL REFERENCES file_stats(Id) ON DELETE CASCADE,
download_id BIGINT NOT NULL REFERENCES file_stats(Id) ON DELETE CASCADE,
upload_id BIGINT NOT NULL REFERENCES file_stats(Id) ON DELETE CASCADE
);

@ -1,9 +0,0 @@
package system_time
import "time"
type Clock struct{}
func (c Clock) Now() time.Time {
return time.Now()
}

@ -6,8 +6,6 @@ import (
"caj-larsson/bog/dataswamp/swampfile"
fs_swampfile "caj-larsson/bog/infrastructure/fs/swampfile"
sql_namespace "caj-larsson/bog/infrastructure/sqlite/namespace"
"caj-larsson/bog/infrastructure/system_time"
"caj-larsson/bog/util"
"net/http"
"strconv"
"text/template"
@ -22,10 +20,10 @@ type Router interface {
type Bog struct {
router Router
adminRouter Router
file_service dataswamp.DataSwampService
file_service dataswamp.SwampFileService
address string
adminAddress string
logger util.Logger
logger dataswamp.Logger
}
func buildFileDataRepository(config FileConfig) swampfile.Repository {
@ -106,7 +104,7 @@ func (b *Bog) dashboardHandler(w http.ResponseWriter, r *http.Request) {
panic(err)
}
stats := b.file_service.NamespaceStats()
stats, _ := b.file_service.NamespaceStats()
err = templ.Execute(w, stats)
if err != nil {
@ -116,7 +114,7 @@ func (b *Bog) dashboardHandler(w http.ResponseWriter, r *http.Request) {
func (b *Bog) routes() {
b.router.HandleFunc("/", b.fileHandler)
// b.adminRouter.HandleFunc("/", b.dashboardHandler)
b.adminRouter.HandleFunc("/", b.dashboardHandler)
}
func (b *Bog) cleanNamespaces() {
@ -135,17 +133,11 @@ func New(config *Configuration) *Bog {
nsRepo := buildNamespaceRepository(config.Database)
logger := ServerLogger{Debug}
ns_svc := namespace.NewNamespaceService(
b.file_service = dataswamp.NewSwampFileService(
nsRepo,
logger,
system_time.Clock{},
config.Quota.ParsedDuration(),
config.Quota.ParsedSizeBytes(),
)
b.file_service = *dataswamp.NewDataSwampService(
*ns_svc,
fsSwampRepo,
config.Quota.ParsedSizeBytes(),
config.Quota.ParsedDuration(),
logger,
)
b.logger = logger
@ -158,6 +150,6 @@ func New(config *Configuration) *Bog {
func (b *Bog) Run() {
b.logger.Info("Starting bog on address: %s", b.address)
go b.cleanNamespaces()
go func() { http.ListenAndServe(b.adminAddress, b.adminRouter) }()
go func(){ http.ListenAndServe(b.adminAddress, b.adminRouter) }()
http.ListenAndServe(b.address, b.router)
}

@ -4,10 +4,8 @@ import (
"testing"
// "fmt"
"caj-larsson/bog/dataswamp"
"caj-larsson/bog/dataswamp/namespace"
ns "caj-larsson/bog/infrastructure/memory/namespace"
"caj-larsson/bog/infrastructure/memory/namespace"
"caj-larsson/bog/infrastructure/memory/swampfile"
"caj-larsson/bog/infrastructure/system_time"
"github.com/matryer/is"
"net/http"
"net/http/httptest"
@ -24,25 +22,17 @@ func (t TestLogger) Warn(format string, a ...interface{}) {}
func TestApplication(t *testing.T) {
is := is.New(t)
logger := TestLogger{}
nsRepo := ns.NewRepository()
ns_svc := namespace.NewNamespaceService(
nsRepo,
logger,
system_time.Clock{},
time.Hour,
1000,
)
file_service := dataswamp.NewDataSwampService(
*ns_svc,
file_service := dataswamp.NewSwampFileService(
namespace.NewRepository(),
swampfile.NewRepository(),
1000,
time.Hour,
logger,
)
bog := Bog{
router: new(http.ServeMux),
file_service: *file_service,
file_service: file_service,
address: "fake",
logger: logger,
}

@ -1,41 +0,0 @@
package util
type Event struct {
eventName string
payload interface{}
}
func NewEvent(eventName string, payload interface{}) *Event {
return &Event{
eventName,
payload,
}
}
func (e *Event) EventName() string {
return e.eventName
}
func (e *Event) Payload() interface{} {
return e.payload
}
type EventHandler func(payload interface{})
type EventBus struct {
handlers map[string][]EventHandler
}
func NewEventBus() *EventBus {
return &EventBus{make(map[string][]EventHandler)}
}
func (eb *EventBus) Register(eventName string, handler EventHandler) {
eb.handlers[eventName] = append(eb.handlers[eventName], handler)
}
func (eb *EventBus) Handle(e Event) {
for _, handler := range eb.handlers[e.EventName()] {
handler(e.Payload())
}
}

@ -1,23 +0,0 @@
package util
import (
"github.com/matryer/is"
"testing"
)
func (eb *EventBus) Handled(e Event) bool {
// TODO: figure out how to verify the event signature here.
handlers, exists := eb.handlers[e.EventName()]
if !exists {
return false
}
return len(handlers) > 0
}
func AcceptsMessage(t *testing.T, eb *EventBus, es []Event) {
is := is.New(t)
for _, e := range es {
is.True(eb.Handled(e))
}
}

@ -1,13 +0,0 @@
package util
type Logger interface {
Debug(format string, a ...interface{})
Info(format string, a ...interface{})
Warn(format string, a ...interface{})
}
type TestLogger struct{}
func (t TestLogger) Debug(format string, a ...interface{}) {}
func (t TestLogger) Info(format string, a ...interface{}) {}
func (t TestLogger) Warn(format string, a ...interface{}) {}
Loading…
Cancel
Save