This commit is contained in:
commit
2ff4b8dfbb
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
dist/
|
50
.goreleaser.yaml
Normal file
50
.goreleaser.yaml
Normal file
@ -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
|
49
.woodpecker.yml
Normal file
49
.woodpecker.yml
Normal file
@ -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]
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -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"]
|
7
cmd/client/client.go
Normal file
7
cmd/client/client.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Starting gpaste client")
|
||||||
|
}
|
7
cmd/server/server.go
Normal file
7
cmd/server/server.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Starting gpaste server")
|
||||||
|
}
|
21
file.go
Normal file
21
file.go
Normal file
@ -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)
|
||||||
|
}
|
82
filestore_memory.go
Normal file
82
filestore_memory.go
Normal file
@ -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
|
||||||
|
}
|
13
filestore_memory_test.go
Normal file
13
filestore_memory_test.go
Normal file
@ -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)
|
||||||
|
}
|
63
filestore_test.go
Normal file
63
filestore_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module git.t-juice.club/torjus/gpaste
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require github.com/google/uuid v1.3.0
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -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=
|
Loading…
Reference in New Issue
Block a user