Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
)
}

if err := t.Send(newFilename, convertedData); err != nil {
if err := t.Send(r.Context(), newFilename, convertedData); err != nil {
slog.Error("target failed", "target", t.Type(), "source", header.Filename, "file", newFilename, "error", err)
http.Error(w, "target forward failed", http.StatusBadGateway)
return
Expand Down
5 changes: 3 additions & 2 deletions server/internal/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"bytes"
"context"
"database/sql"
"fmt"
"mime/multipart"
Expand All @@ -24,15 +25,15 @@ type mockTarget struct {

func (m *mockTarget) Type() string { return "mock" }
func (m *mockTarget) AcceptedFormats() []string { return []string{"gpx_1.1", "geojson"} }
func (m *mockTarget) Send(filename string, data []byte) error { return m.err }
func (m *mockTarget) Send(_ context.Context, filename string, data []byte) error { return m.err }

type countTarget struct {
count *int
}

func (c *countTarget) Type() string { return "count" }
func (c *countTarget) AcceptedFormats() []string { return []string{"gpx_1.1", "geojson"} }
func (c *countTarget) Send(filename string, data []byte) error {
func (c *countTarget) Send(_ context.Context, filename string, data []byte) error {
*c.count++
return nil
}
Expand Down
5 changes: 3 additions & 2 deletions server/internal/target/dawarich/dawarich.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dawarich

import (
"bytes"
"context"
"fmt"
"io"
"mime/multipart"
Expand Down Expand Up @@ -37,7 +38,7 @@ type Dawarich struct {
func (d *Dawarich) Type() string { return "dawarich" }
func (d *Dawarich) AcceptedFormats() []string { return []string{"geojson", "gpx_1.1"} }

func (d *Dawarich) Send(filename string, data []byte) error {
func (d *Dawarich) Send(ctx context.Context, filename string, data []byte) error {
apiKey, err := d.readAPIKey()
if err != nil {
return fmt.Errorf("reading API key: %w", err)
Expand All @@ -57,7 +58,7 @@ func (d *Dawarich) Send(filename string, data []byte) error {
}

url := d.cfg.URL + "/api/v1/imports"
req, err := http.NewRequest("POST", url, &buf)
req, err := http.NewRequestWithContext(ctx, "POST", url, &buf)
if err != nil {
return fmt.Errorf("creating request: %w", err)
}
Expand Down
7 changes: 4 additions & 3 deletions server/internal/target/dawarich/dawarich_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dawarich

import (
"context"
"io"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -31,7 +32,7 @@ func TestSend_Success(t *testing.T) {
client: &http.Client{Timeout: 5 * time.Second},
}

require.NoError(t, d.Send("track.gpx", []byte("<gpx>data</gpx>")))
require.NoError(t, d.Send(context.Background(), "track.gpx", []byte("<gpx>data</gpx>")))
assert.Equal(t, "Bearer test-key", gotAuth)
assert.True(t, strings.HasPrefix(gotContentType, "multipart/form-data"))
assert.Contains(t, gotBody, "track.gpx")
Expand All @@ -50,7 +51,7 @@ func TestSend_PostsToImportsEndpoint(t *testing.T) {
cfg: target.Config{URL: ts.URL, APIKey: "k"},
client: &http.Client{Timeout: 5 * time.Second},
}
_ = d.Send("f.gpx", []byte("data"))
_ = d.Send(context.Background(), "f.gpx", []byte("data"))
assert.Equal(t, "/api/v1/imports", gotPath)
}

Expand All @@ -66,7 +67,7 @@ func TestSend_ErrorOnNon2xx(t *testing.T) {
client: &http.Client{Timeout: 5 * time.Second},
}

err := d.Send("f.gpx", []byte("data"))
err := d.Send(context.Background(), "f.gpx", []byte("data"))
require.Error(t, err)
assert.Contains(t, err.Error(), "500")
}
Expand Down
3 changes: 2 additions & 1 deletion server/internal/target/target.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package target

import (
"context"
"fmt"
"time"
)
Expand All @@ -13,7 +14,7 @@ type Target interface {
// ordered by preference (most preferred first).
AcceptedFormats() []string
// Send forwards a GPS track file to the target.
Send(filename string, data []byte) error
Send(ctx context.Context, filename string, data []byte) error
}

// Constructor creates a new Target from the given config.
Expand Down
6 changes: 4 additions & 2 deletions server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ func main() {

addr := ":" + cfg.Port
httpServer := &http.Server{
Addr: addr,
Handler: srv.Handler(),
Addr: addr,
Handler: srv.Handler(),
ReadTimeout: 30 * time.Second,
WriteTimeout: cfg.TargetTimeout + 10*time.Second,
}

go func() {
Expand Down
9 changes: 5 additions & 4 deletions tracksync/internal/sync/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sync

import (
"bytes"
"context"
"crypto/sha256"
"database/sql"
"encoding/hex"
Expand Down Expand Up @@ -81,7 +82,7 @@ const (
StatusDuplicate // server already had this file
)

func Upload(client *http.Client, serverURL, token, deviceID, sourceFormat, filename string, data []byte) (UploadStatus, error) {
func Upload(ctx context.Context, client *http.Client, serverURL, token, deviceID, sourceFormat, filename string, data []byte) (UploadStatus, error) {
var buf bytes.Buffer
writer := multipart.NewWriter(&buf)
part, err := writer.CreateFormFile("file", filename)
Expand All @@ -95,7 +96,7 @@ func Upload(client *http.Client, serverURL, token, deviceID, sourceFormat, filen
return 0, fmt.Errorf("closing form: %w", err)
}

req, err := http.NewRequest("POST", serverURL+"/upload", &buf)
req, err := http.NewRequestWithContext(ctx, "POST", serverURL+"/upload", &buf)
if err != nil {
return 0, fmt.Errorf("creating request: %w", err)
}
Expand Down Expand Up @@ -133,7 +134,7 @@ type Summary struct {
}

// SyncFiles syncs a list of found files to the server.
func SyncFiles(db *sql.DB, client *http.Client, serverURL, token, deviceID string, files []device.FoundFile) Summary {
func SyncFiles(ctx context.Context, db *sql.DB, client *http.Client, serverURL, token, deviceID string, files []device.FoundFile) Summary {
var summary Summary

for _, ff := range files {
Expand All @@ -160,7 +161,7 @@ func SyncFiles(db *sql.DB, client *http.Client, serverURL, token, deviceID strin
continue
}

status, err := Upload(client, serverURL, token, deviceID, ff.Format, name, data)
status, err := Upload(ctx, client, serverURL, token, deviceID, ff.Format, name, data)
if err != nil {
slog.Error("upload failed", "file", name, "error", err)
summary.Errors++
Expand Down
17 changes: 9 additions & 8 deletions tracksync/internal/sync/sync_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sync

import (
"context"
"database/sql"
"fmt"
"net/http"
Expand Down Expand Up @@ -107,7 +108,7 @@ func TestUpload_Created(t *testing.T) {
defer ts.Close()

client := &http.Client{Timeout: 5 * time.Second}
status, err := Upload(client, ts.URL, "tok", "dev-1", "gpx_1.1", "track.gpx", []byte("<gpx/>"))
status, err := Upload(context.Background(), client, ts.URL, "tok", "dev-1", "gpx_1.1", "track.gpx", []byte("<gpx/>"))
require.NoError(t, err)
assert.Equal(t, StatusUploaded, status)
}
Expand All @@ -120,7 +121,7 @@ func TestUpload_Duplicate(t *testing.T) {
defer ts.Close()

client := &http.Client{Timeout: 5 * time.Second}
status, err := Upload(client, ts.URL, "tok", "dev", "gpx_1.1", "f.gpx", []byte("data"))
status, err := Upload(context.Background(), client, ts.URL, "tok", "dev", "gpx_1.1", "f.gpx", []byte("data"))
require.NoError(t, err)
assert.Equal(t, StatusDuplicate, status)
}
Expand All @@ -133,7 +134,7 @@ func TestUpload_ServerError(t *testing.T) {
defer ts.Close()

client := &http.Client{Timeout: 5 * time.Second}
_, err := Upload(client, ts.URL, "tok", "dev", "gpx_1.1", "f.gpx", []byte("data"))
_, err := Upload(context.Background(), client, ts.URL, "tok", "dev", "gpx_1.1", "f.gpx", []byte("data"))
assert.Error(t, err)
}

Expand All @@ -149,7 +150,7 @@ func TestSyncFiles_Uploaded(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(dir, "track.gpx"), []byte("<gpx/>"), 0644))

files := []device.FoundFile{{Path: filepath.Join(dir, "track.gpx"), Format: "gpx_1.1"}}
summary := SyncFiles(db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)
summary := SyncFiles(context.Background(), db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)

assert.Equal(t, 1, summary.Uploaded)
assert.Equal(t, 0, summary.Duplicate)
Expand All @@ -170,7 +171,7 @@ func TestSyncFiles_Duplicate(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(dir, "track.gpx"), []byte("<gpx/>"), 0644))

files := []device.FoundFile{{Path: filepath.Join(dir, "track.gpx"), Format: "gpx_1.1"}}
summary := SyncFiles(db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)
summary := SyncFiles(context.Background(), db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)

assert.Equal(t, 0, summary.Uploaded)
assert.Equal(t, 1, summary.Duplicate)
Expand All @@ -194,7 +195,7 @@ func TestSyncFiles_SkippedClientSide(t *testing.T) {
require.NoError(t, RecordUpload(db, SHA256Hex(data), "track.gpx", "dev"))

files := []device.FoundFile{{Path: filepath.Join(dir, "track.gpx"), Format: "gpx_1.1"}}
summary := SyncFiles(db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)
summary := SyncFiles(context.Background(), db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)

assert.Equal(t, 0, summary.Uploaded)
assert.Equal(t, 0, summary.Duplicate)
Expand All @@ -214,7 +215,7 @@ func TestSyncFiles_UploadError(t *testing.T) {
require.NoError(t, os.WriteFile(filepath.Join(dir, "track.gpx"), []byte("<gpx/>"), 0644))

files := []device.FoundFile{{Path: filepath.Join(dir, "track.gpx"), Format: "gpx_1.1"}}
summary := SyncFiles(db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)
summary := SyncFiles(context.Background(), db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)

assert.Equal(t, 0, summary.Uploaded)
assert.Equal(t, 0, summary.Duplicate)
Expand Down Expand Up @@ -251,7 +252,7 @@ func TestSyncFiles_MixedResults(t *testing.T) {
{Path: filepath.Join(dir, "dup.gpx"), Format: "gpx_1.1"},
{Path: filepath.Join(dir, "skip.gpx"), Format: "gpx_1.1"},
}
summary := SyncFiles(db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)
summary := SyncFiles(context.Background(), db, &http.Client{Timeout: 5 * time.Second}, ts.URL, "tok", "dev", files)

assert.Equal(t, 1, summary.Uploaded)
assert.Equal(t, 1, summary.Duplicate)
Expand Down
7 changes: 6 additions & 1 deletion tracksync/main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"context"
"encoding/json"
"flag"
"log/slog"
"net/http"
"os"
"os/signal"
"path/filepath"
"time"

Expand Down Expand Up @@ -122,14 +124,17 @@ func main() {
return
}

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()

httpClient := &http.Client{Timeout: *timeout}
slog.Info("starting sync",
"device", *deviceID,
"type", dev.Type(),
"files", len(files),
)

summary := sync.SyncFiles(db, httpClient, *serverURL, token, *deviceID, files)
summary := sync.SyncFiles(ctx, db, httpClient, *serverURL, token, *deviceID, files)

slog.Info("sync complete",
"uploaded", summary.Uploaded,
Expand Down
Loading