commit 2ff4b8dfbb2e4335c03766e8d7f21ffb1f11bb15 Author: Torjus HÃ¥kestad Date: Sat Jan 15 19:07:33 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cde0123 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..65eed5c --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,50 @@ +# This is an example .goreleaser.yml file with some sensible defaults. +# Make sure to check the documentation at https://goreleaser.com +before: + hooks: + # You may remove this if you don't use go modules. + - go mod tidy + # you may remove this if you don't need go generate + - go generate ./... +builds: + - id: "gpaste-client" + binary: "gpaste" + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + goarch: + - amd64 + main: ./cmd/client/client.go + + - id: "gpaste-server" + binary: "gpaste-server" + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + main: ./cmd/server/server.go +archives: + - format_overrides: + - goos: windows + format: zip +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' +gitea_urls: + api: https://git.t-juice.club/api/v1/ + download: https://git.t-juice.club +env_files: + gitea_token: gitea_token diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..12d7148 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,49 @@ +pipeline: + test: + image: golang:latest + commands: + - go build ./cmd/client/client.go + - go build ./cmd/server/server.go + - go test -v ./... + - go vet ./... + when: + branch: master + event: [push, pull_request, tag, deployment] + + image-latest: + image: plugins/docker + settings: + repo: registry.t-juice.club/gpaste + registry: registry.t-juice.club + username: woodpecker + password: + from_secret: registry_password + tags: + - latest + - "${CI_COMMIT_SHA:0:8}" + when: + branch: master + event: push + + image-tagged: + image: plugins/docker + settings: + repo: registry.t-juice.club/gpaste + registry: registry.t-juice.club + username: woodpecker + password: + from_secret: registry_password + tags: + - "${CI_COMMIT_TAG}" + when: + event: [tag] + + goreleaser-tagged: + image: goreleaser/goreleaser + commands: + - git fetch --tags + - echo "$GITEA_TOKEN" > gitea_token + - goreleaser release + when: + event: [tag] + secrets: [gitea_token] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..344fafe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:alpine AS builder +WORKDIR /src +COPY go.mod . +COPY go.sum . +RUN go mod download +COPY . . +RUN go build -o gpaste ./cmd/client/client.go +RUN go build -o gpaste-server ./cmd/server/server.go + +FROM alpine:latest +COPY --from=builder /src/gpaste /bin/gpaste +COPY --from=builder /src/gpaste-server /bin/gpaste-server +CMD ["/bin/gpaste-server"] \ No newline at end of file diff --git a/cmd/client/client.go b/cmd/client/client.go new file mode 100644 index 0000000..54611f9 --- /dev/null +++ b/cmd/client/client.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Starting gpaste client") +} diff --git a/cmd/server/server.go b/cmd/server/server.go new file mode 100644 index 0000000..5d4b70e --- /dev/null +++ b/cmd/server/server.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Starting gpaste server") +} diff --git a/file.go b/file.go new file mode 100644 index 0000000..94ed297 --- /dev/null +++ b/file.go @@ -0,0 +1,21 @@ +package gpaste + +import ( + "io" + "time" +) + +type File struct { + ID string + Body io.ReadCloser + + MaxViews uint + ExpiresOn time.Time +} + +type FileStore interface { + Store(f *File) error + Get(id string) (*File, error) + Delete(id string) error + List() ([]string, error) +} diff --git a/filestore_memory.go b/filestore_memory.go new file mode 100644 index 0000000..8576fd7 --- /dev/null +++ b/filestore_memory.go @@ -0,0 +1,82 @@ +package gpaste + +import ( + "bytes" + "fmt" + "io" + "sync" + "time" +) + +type fileData struct { + ID string + Body bytes.Buffer + + MaxViews uint + ExpiresOn time.Time +} + +type MemoryFileStore struct { + lock sync.RWMutex + data map[string]*fileData +} + +func NewMemoryFileStore() *MemoryFileStore { + return &MemoryFileStore{ + data: make(map[string]*fileData), + } +} + +func (s *MemoryFileStore) Store(f *File) error { + + data := &fileData{ + ID: f.ID, + MaxViews: f.MaxViews, + ExpiresOn: f.ExpiresOn, + } + + _, err := io.Copy(&data.Body, f.Body) + _ = f.Body.Close() + + s.lock.Lock() + defer s.lock.Unlock() + + s.data[f.ID] = data + return err +} + +func (s *MemoryFileStore) Get(id string) (*File, error) { + s.lock.RLock() + defer s.lock.RUnlock() + + fd, ok := s.data[id] + if !ok { + return nil, fmt.Errorf("no such item") + } + f := &File{ + ID: fd.ID, + MaxViews: fd.MaxViews, + ExpiresOn: fd.ExpiresOn, + Body: io.NopCloser(&fd.Body), + } + + return f, nil +} + +func (s *MemoryFileStore) Delete(id string) error { + s.lock.Lock() + defer s.lock.RUnlock() + delete(s.data, id) + return nil +} + +func (s *MemoryFileStore) List() ([]string, error) { + var ids []string + + s.lock.RLock() + defer s.lock.RUnlock() + for id := range s.data { + ids = append(ids, id) + } + return ids, nil +} diff --git a/filestore_memory_test.go b/filestore_memory_test.go new file mode 100644 index 0000000..f0cd698 --- /dev/null +++ b/filestore_memory_test.go @@ -0,0 +1,13 @@ +package gpaste_test + +import ( + "testing" + + "git.t-juice.club/torjus/gpaste" +) + +func TestMemoryFileStore(t *testing.T) { + s := gpaste.NewMemoryFileStore() + + RunFilestoreTest(s, t) +} diff --git a/filestore_test.go b/filestore_test.go new file mode 100644 index 0000000..ed70c55 --- /dev/null +++ b/filestore_test.go @@ -0,0 +1,63 @@ +package gpaste_test + +import ( + "bytes" + "io" + "testing" + + "git.t-juice.club/torjus/gpaste" + "github.com/google/uuid" +) + +func RunFilestoreTest(s gpaste.FileStore, t *testing.T) { + t.Run("Basic", func(t *testing.T) { + // Create + dataString := "TEST_LOL_OMG" + id := uuid.Must(uuid.NewRandom()).String() + bodyBuf := &bytes.Buffer{} + bodyBuf.Write([]byte(dataString)) + body := io.NopCloser(bodyBuf) + f := &gpaste.File{ + ID: id, + MaxViews: 0, + Body: body, + } + + err := s.Store(f) + if err != nil { + t.Fatalf("Unable to store file: %s", err) + } + + // Retrieve + retrieved, err := s.Get(id) + if err != nil { + t.Fatalf("Unable to retrieve file: %s", err) + } + + retrievedBuf := &bytes.Buffer{} + _, err = retrievedBuf.ReadFrom(retrieved.Body) + if err != nil { + t.Fatalf("Unable to read retrieved body: %s", err) + } + _ = retrieved.Body.Close() + if err != nil { + t.Fatalf("Error reading from retrieved file: %s", err) + } + + if retrievedBuf.String() != dataString { + t.Fatalf("Data from retrieved body mismatch. Got %s want %s", retrievedBuf.String(), dataString) + } + + // List + ids, err := s.List() + if err != nil { + t.Fatalf("Error doing list: %s", err) + } + if len(ids) != 1 { + t.Fatalf("List has wrong length: %d", len(ids)) + } + if ids[0] != id { + t.Fatalf("ID is wrong. Got %s want %s", ids[0], id) + } + }) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7184081 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.t-juice.club/torjus/gpaste + +go 1.17 + +require github.com/google/uuid v1.3.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3dfe1c9 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/http.go b/http.go new file mode 100644 index 0000000..cb3bae9 --- /dev/null +++ b/http.go @@ -0,0 +1 @@ +package gpaste