Skip to content

Commit

Permalink
api: Integrate Docker + implement s3 store
Browse files Browse the repository at this point in the history
  • Loading branch information
beesaferoot committed Dec 31, 2023
1 parent de746c3 commit 92cef4e
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: '1.21'

- name: Test
run: make test
6 changes: 3 additions & 3 deletions .todo
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ Backend check list
[+] implement /upload_batch_track endpoint
[+] add some auth protection on /upload_batch_track endpoint
[-] serve audio files (stream from client)
[-] support downloading
[-] support downloading

UI check list
[-] search interface
[-] audio pane display
[+] search interface
[+] audio pane display
[-] audio player interface
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Build stage
FROM golang:1.21-alpine3.19 as builder
WORKDIR /app
COPY . .

RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

RUN go build -o auxstream

# Run stage
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/auxstream .
COPY --from=builder /go/bin/migrate ./migrate
COPY app.env .
COPY start.sh .
COPY db/migration ./db/migration

EXPOSE 5009

CMD ["/app/auxstream"]
ENTRYPOINT ["/app/start.sh"]
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ test:
run:
go run main.go


build:
go build -o build/auxstream

.PHONY: test run createdb setup-db teardown-db
4 changes: 2 additions & 2 deletions api/track.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func AddTrackHandler(c *gin.Context) {
}
raw_bytes := make([]byte, file.Size)
_, err = raw_file.Read(raw_bytes)
fileName, err := fs.Store.Save(raw_bytes)
fileName, err := fs.LStore.Save(raw_bytes)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, errorResponse(fmt.Sprintf("(store) audio upload failed: %s", err.Error())))
return
Expand Down Expand Up @@ -146,7 +146,7 @@ func processFiles(files []*multipart.FileHeader) (fileNames []string, err error)
buf_channel := make(chan string, len(groupfiles))

// concurrently write files to disk
fs.Store.BulkSave(buf_channel, groupfiles)
fs.LStore.BulkSave(buf_channel, groupfiles)

for fileName := range buf_channel {
fileNames = append(fileNames, fileName)
Expand Down
2 changes: 1 addition & 1 deletion app.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
DATABASE_URL=postgresql://root:secret@localhost:5432/auxstreamdb?sslmode=disable
DATABASE_URL=postgresql://postgres:secret@postgres:5432/auxstreamdb?sslmode=disable
SESSION_STRING=test_random_string
21 changes: 21 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: "3.9"
services:
postgres:
restart: always
image: postgres:14-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=auxstreamdb
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "5009:5009"
environment:
- DATABASE_URL=postgresql://postgres:secret@postgres:5432/auxstreamdb?sslmode=disable

restart: on-failure
depends_on:
- postgres
13 changes: 11 additions & 2 deletions file_system/local_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type LocalStore struct {
writes int
reads int
baseLocation string
mu sync.Mutex
}

func NewStore(path string) *LocalStore {
func NewLocalStore(path string) *LocalStore {
_, err := os.Stat(path)
if os.IsNotExist(err) {
// Create the directory
Expand Down Expand Up @@ -46,13 +47,17 @@ func (l *LocalStore) Save(raw []byte) (filename string, err error) {
return "", err
}
_, err = file.Write(raw)
l.mu.Lock()
l.writes++
l.mu.Unlock()
return
}

func (l *LocalStore) Read(fileName string) (file File, err error) {
file, err = OpenFile(filepath.Join(l.baseLocation, fileName))
l.mu.Lock()
l.reads++
l.mu.Unlock()
return
}

Expand Down Expand Up @@ -119,10 +124,14 @@ func (f *LocalFile) Write(p []byte) (n int, err error) {
return f.content.Write(p)
}

func (f *LocalFile) WriteAt(p []byte, off int64) (n int, err error) {
return f.content.WriteAt(p, off)
}

func genFileName() string {
return "aud_file_" + uuid.New().String() + ".mp3"
}

var cwd, _ = os.Getwd()
var rootDir, _ = filepath.Abs(cwd)
var Store = NewStore(filepath.Join(rootDir, "uploads"))
var LStore = NewLocalStore(filepath.Join(rootDir, "uploads"))
126 changes: 126 additions & 0 deletions file_system/s3_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package filesystem

import (
"bytes"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
s3API "github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"log"
"sync"
)

// S3Store a FileSystem interface def over s3 storage
type S3Store struct {
session *session.Session
bucketId string
uploads int
downloads int
mu sync.Mutex
}

func NewS3Store(bucketId string) *S3Store {
sess := session.Must(session.NewSession())
return &S3Store{session: sess, bucketId: bucketId}
}

func (s3 *S3Store) Reads() int {
return s3.downloads
}

func (s3 *S3Store) Writes() int {
return s3.uploads
}

func (s3 *S3Store) Save(raw []byte) (filename string, err error) {
if len(raw) < 1 {
return filename, fmt.Errorf("empty file")
}
filename = genFileName()

freader := bytes.NewReader(raw)

// Create an uploader with the session
uploader := s3manager.NewUploader(s3.session)

// Upload the file to S3.
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s3.bucketId),
Key: aws.String(filename),
Body: freader,
})

if err != nil {
return "", fmt.Errorf("failed to upload file, %v", err)
}
s3.mu.Lock()
s3.uploads++
s3.mu.Unlock()
return
}

func (s3 *S3Store) Read(fileName string) (file File, err error) {
// Create a downloader with the session=
downloader := s3manager.NewDownloader(s3.session)
// Create a file to write the S3 Object contents to.
lfile, err := NewFile(fileName)
file = lfile

if err != nil {
return nil, fmt.Errorf("failed to create file %q, %v", fileName, err)
}

_, err = downloader.Download(lfile, &s3API.GetObjectInput{
Bucket: aws.String(s3.bucketId),
Key: aws.String(fileName),
})

if err != nil {
return nil, fmt.Errorf("failed to download file, %v", err)
}

s3.mu.Lock()
s3.downloads++
s3.mu.Unlock()

return
}

func (s3 *S3Store) BulkSave(buf chan<- string, listOfRaw [][]byte) {
var wg sync.WaitGroup
for _, raw := range listOfRaw {
wg.Add(1)
go func(raw []byte) {
defer wg.Done()
fileName, err := s3.Save(raw)
if err != nil {
log.Println(err)
buf <- ""
return
}
buf <- fileName
}(raw)
}
wg.Wait()
close(buf)
}

func (s3 *S3Store) Remove(fileName string) error {
deleteObjectInput := &s3API.DeleteObjectInput{
Bucket: aws.String(s3.bucketId),
Key: aws.String(fileName),
}

s3Client := s3API.New(s3.session)

// Delete the object
_, err := s3Client.DeleteObject(deleteObjectInput)
if err != nil {
log.Println("Error deleting object:", err)
return err
}

log.Printf("Object '%s' deleted from bucket '%s'\n", fileName, s3.bucketId)
return nil
}
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module auxstream
go 1.20

require (
github.com/aws/aws-sdk-go v1.49.9
github.com/gin-contrib/cors v1.4.0
github.com/gin-contrib/sessions v0.0.5
github.com/gin-gonic/gin v1.9.0
Expand All @@ -12,7 +13,7 @@ require (
github.com/pashagolub/pgxmock/v2 v2.7.0
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.3
golang.org/x/crypto v0.9.0
golang.org/x/crypto v0.14.0
gopkg.in/validator.v2 v2.0.1
)

Expand All @@ -33,6 +34,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
Expand All @@ -52,10 +54,10 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
24 changes: 16 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/aws/aws-sdk-go v1.49.9 h1:4xoyi707rsifB1yMsd5vGbAH21aBzwpL3gNRMSmjIyc=
github.com/aws/aws-sdk-go v1.49.9/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
Expand Down Expand Up @@ -173,6 +175,10 @@ github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk=
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
Expand Down Expand Up @@ -271,8 +277,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -338,8 +344,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -400,8 +406,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -411,8 +417,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -569,6 +575,8 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
9 changes: 9 additions & 0 deletions start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

set -e

echo "run db migration"
/app/migrate -path /app/db/migration -database "$DATABASE_URL" -verbose up

echo "start app"
exec "$@"
Loading

0 comments on commit 92cef4e

Please sign in to comment.