Skip to content

Commit

Permalink
finishing API
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredLunde committed Dec 26, 2024
1 parent 831722a commit 4bb78a6
Show file tree
Hide file tree
Showing 21 changed files with 178 additions and 164 deletions.
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ Upload, serve, and process images on Railway. Includes on-the-fly image resizing
To access the key-value API, you must provide an `x-api-key` header with the value of the `SECRET_KEY` environment variable.
Alternatively, you can use a signed URL to access the key-value API. The `/sign/` endpoint always requires the `x-api-key` header.

| Method | Path | Description |
| -------- | ------------------ | -------------------------------------------------- |
| `PUT` | `/files/:key` | Upload a file |
| `GET` | `/files/:key` | Get a file |
| `DELETE` | `/files/:key` | Delete a file |
| `GET` | `/files` | List files with `limit`, `starting_at` parameters. |
| `GET` | `/sign/files/:key` | Create a signed URL for a key value operation |
| Method | Path | Description |
| -------- | ----------------- | -------------------------------------------------- |
| `PUT` | `/blob/:key` | Upload a file |
| `GET` | `/blob/:key` | Get a file |
| `DELETE` | `/blob/:key` | Delete a file |
| `GET` | `/blob` | List files with `limit`, `starting_at` parameters. |
| `GET` | `/sign/blob/:key` | Create a signed URL for a key value operation |

### Image processing API

Expand Down Expand Up @@ -74,58 +74,58 @@ Alternatively, you can use a signed URL to access the key-value API. The `/sign/
### Upload an image

```bash
curl -X PUT -T tmp/gopher.png http://localhost:3000/files/gopher.png \
curl -X PUT -T tmp/gopher.png http://localhost:3000/blob/gopher.png \
-H "x-api-key: $API_KEY"
```

### Upload an image using a signed URL

```bash
# Create a signed URL
curl http://localhost:3000/sign/files/gopher.png \
curl http://localhost:3000/sign/blob/gopher.png \
-H "x-api-key: $API_KEY"
# => http://localhost:3000/files/gopher.png?signature=...&expires=...
# => http://localhost:3000/blob/gopher.png?signature=...&expires=...

# Upload the image
curl -X PUT -T tmp/gopher.png "http://localhost:3000/files/gopher.png?signature=...&expires=..."
curl -X PUT -T tmp/gopher.png "http://localhost:3000/blob/gopher.png?signature=...&expires=..."
```

### Get an image

```bash
curl http://localhost:3000/files/gopher.png \
curl http://localhost:3000/blob/gopher.png \
-H "x-api-key: $API_KEY"
```

### Get an image using a signed URL

```bash
# Create a signed URL
curl http://localhost:3000/sign/files/gopher.png \
curl http://localhost:3000/sign/blob/gopher.png \
-H "x-api-key: $API_KEY"
# => http://localhost:3000/files/gopher.png?signature=...&expires=...
# => http://localhost:3000/blob/gopher.png?signature=...&expires=...

# Get the image
curl "http://localhost:3000/files/gopher.png?signature=...&expires=..."
curl "http://localhost:3000/blob/gopher.png?signature=...&expires=..."
```

### Delete an image

```bash
curl -X DELETE http://localhost:3000/files/gopher.png \
curl -X DELETE http://localhost:3000/blob/gopher.png \
-H "x-api-key: $API_KEY"
```

### Delete an image using a signed URL

```bash
# Create a signed URL
curl http://localhost:3000/sign/files/gopher.png \
curl http://localhost:3000/sign/blob/gopher.png \
-H "x-api-key: $API_KEY"
# => http://localhost:3000/files/gopher.png?signature=...&expires=...
# => http://localhost:3000/blob/gopher.png?signature=...&expires=...

# Delete the image
curl -X DELETE "http://localhost:3000/files/gopher.png?signature=...&expires=..."
curl -X DELETE "http://localhost:3000/blob/gopher.png?signature=...&expires=..."
```

---
Expand All @@ -146,12 +146,12 @@ a comprehensive list of examples.

```bash
# Create a signed URL
curl http://localhost:3000/sign/serve/300x300/files/gopher.png \
curl http://localhost:3000/sign/serve/300x300/blob/gopher.png \
-H "x-api-key: $API_KEY"
# => http://localhost:3000/serve/300x300/files/gopher.png?signature=...
# => http://localhost:3000/serve/300x300/blob/gopher.png?signature=...

# Process the image on the fly
curl http://localhost:3000/serve/300x300/files/gopher.png?signature=...
curl http://localhost:3000/serve/300x300/blob/gopher.png?signature=...
```

### Crop and resize an image from a URL
Expand Down
10 changes: 5 additions & 5 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"net/http"
"net/url"

"github.com/jaredLunde/railway-images/client/sign"
"github.com/jaredLunde/railway-image-service/client/sign"
)

type Options struct {
Expand Down Expand Up @@ -100,7 +100,7 @@ func (c *Client) Sign(path string) (string, error) {
// Get a file from the storage server
func (c *Client) Get(key string) (*http.Response, error) {
u := *c.URL
path, err := url.JoinPath("/files", key)
path, err := url.JoinPath("/blob", key)
if err != nil {
return nil, err
}
Expand All @@ -122,7 +122,7 @@ func (c *Client) Get(key string) (*http.Response, error) {
func (c *Client) Put(key string, r io.Reader) error {
// Create URL
u := *c.URL
u.Path = fmt.Sprintf("/files/%s", key)
u.Path = fmt.Sprintf("/blob/%s", key)

// Create request
req, err := http.NewRequest(http.MethodPut, u.String(), r)
Expand Down Expand Up @@ -157,7 +157,7 @@ func (c *Client) Put(key string, r io.Reader) error {
// Delete a file from the storage server
func (c *Client) Delete(key string) error {
u := *c.URL
path, err := url.JoinPath("/files", key)
path, err := url.JoinPath("/blob", key)
if err != nil {
return err
}
Expand Down Expand Up @@ -199,7 +199,7 @@ type ListOptions struct {
// List files in the storage server
func (c *Client) List(opts ListOptions) (*ListResult, error) {
u := *c.URL
u.Path = "/files"
u.Path = "/blob"

// Build query parameters
q := u.Query()
Expand Down
18 changes: 9 additions & 9 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func TestClient_Sign_Local(t *testing.T) {
}{
{
name: "successful local signing",
path: "/files/test.jpg",
path: "/blob/test.jpg",
signatureSecretKey: "secret",
wantErr: false,
},
Expand All @@ -136,7 +136,7 @@ func TestClient_Sign_Local(t *testing.T) {
},
{
name: "path with query params",
path: "/serve/files/test.jpg",
path: "/serve/blob/test.jpg",
signatureSecretKey: "secret",
wantErr: false,
},
Expand Down Expand Up @@ -179,7 +179,7 @@ func TestClient_Sign_Local(t *testing.T) {
if query.Get("x-signature") == "" {
t.Error("Signed URL missing x-signature parameter")
}
if query.Get("x-expire") == "" && strings.HasPrefix(tt.path, "/files") {
if query.Get("x-expire") == "" && strings.HasPrefix(tt.path, "/blob") {
t.Error("Signed URL missing x-expire parameter")
}

Expand All @@ -199,8 +199,8 @@ func TestClient_Get(t *testing.T) {
if r.Method != http.MethodGet {
t.Errorf("expected GET request, got %s", r.Method)
}
if r.URL.Path != "/files/test.jpg" {
t.Errorf("expected path /files/test.jpg, got %s", r.URL.Path)
if r.URL.Path != "/blob/test.jpg" {
t.Errorf("expected path /blob/test.jpg, got %s", r.URL.Path)
}
w.Write(expectedContent)
}))
Expand Down Expand Up @@ -388,8 +388,8 @@ func TestClient_Delete(t *testing.T) {
if r.Method != http.MethodDelete {
t.Errorf("expected DELETE request, got %s", r.Method)
}
if r.URL.Path != "/files/test.jpg" {
t.Errorf("expected path /files/test.jpg, got %s", r.URL.Path)
if r.URL.Path != "/blob/test.jpg" {
t.Errorf("expected path /blob/test.jpg, got %s", r.URL.Path)
}
w.WriteHeader(http.StatusNoContent)
}))
Expand Down Expand Up @@ -417,8 +417,8 @@ func TestClient_List(t *testing.T) {
if r.Method != http.MethodGet {
t.Errorf("expected GET request, got %s", r.Method)
}
if r.URL.Path != "/files" {
t.Errorf("expected path /files, got %s", r.URL.Path)
if r.URL.Path != "/blob" {
t.Errorf("expected path /blob, got %s", r.URL.Path)
}

q := r.URL.Query()
Expand Down
4 changes: 2 additions & 2 deletions client/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ func SignURL(url *url.URL, secret string) (*string, error) {
path := nextURI.Path
p := strings.TrimPrefix(path, "/sign")
var signature string
if !strings.HasPrefix(p, "/files") && !strings.HasPrefix(p, "/serve") {
if !strings.HasPrefix(p, "/blob") && !strings.HasPrefix(p, "/serve") {
return nil, fmt.Errorf("invalid path")
}
if strings.HasPrefix(p, "/serve") {
signature = Sign(strings.TrimPrefix(p, "/serve"), secret)
}

query := nextURI.Query()
if strings.HasPrefix(p, "/files") {
if strings.HasPrefix(p, "/blob") {
expireAt := time.Now().Add(time.Hour).UnixMilli()
query.Set("x-expire", fmt.Sprintf("%d", expireAt))
nextURI.RawQuery = query.Encode()
Expand Down
2 changes: 1 addition & 1 deletion cmd/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"time"

"github.com/caarlos0/env/v11"
"github.com/jaredLunde/railway-images/internal/pkg/logger"
"github.com/jaredLunde/railway-image-service/internal/pkg/logger"
)

type Config struct {
Expand Down
20 changes: 10 additions & 10 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import (
"github.com/gofiber/fiber/v3/middleware/helmet"
fiberrecover "github.com/gofiber/fiber/v3/middleware/recover"
"github.com/gofiber/fiber/v3/middleware/requestid"
"github.com/jaredLunde/railway-images/internal/app/imagor"
"github.com/jaredLunde/railway-images/internal/app/keyval"
"github.com/jaredLunde/railway-images/internal/app/signature"
"github.com/jaredLunde/railway-images/internal/pkg/logger"
"github.com/jaredLunde/railway-images/internal/pkg/mw"
"github.com/jaredLunde/railway-image-service/internal/app/imagor"
"github.com/jaredLunde/railway-image-service/internal/app/keyval"
"github.com/jaredLunde/railway-image-service/internal/app/signature"
"github.com/jaredLunde/railway-image-service/internal/pkg/logger"
"github.com/jaredLunde/railway-image-service/internal/pkg/mw"
"golang.org/x/sync/errgroup"
)

Expand All @@ -46,7 +46,7 @@ func main() {
})

kvService, err := keyval.New(keyval.Config{
BasePath: "/files",
BasePath: "/blob",
UploadPath: cfg.UploadPath,
LevelDBPath: cfg.LevelDBPath,
SoftDelete: true,
Expand Down Expand Up @@ -140,10 +140,10 @@ func main() {
r.URL.RawQuery = q.Encode()
imagorService.ServeHTTP(w, r)
})))
app.Get("/files", kvService.ServeHTTP, verifyAccess)
app.Get("/files/*", kvService.ServeHTTP, verifyAccess)
app.Put("/files/*", kvService.ServeHTTP, verifyAccess)
app.Delete("/files/*", kvService.ServeHTTP, verifyAccess)
app.Get("/blob", kvService.ServeHTTP, verifyAccess)
app.Get("/blob/*", kvService.ServeHTTP, verifyAccess)
app.Put("/blob/*", kvService.ServeHTTP, verifyAccess)
app.Delete("/blob/*", kvService.ServeHTTP, verifyAccess)
app.Get("/sign/*", signatureService.ServeHTTP, verifyAPIKey)

g := errgroup.Group{}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/jaredLunde/railway-images
module github.com/jaredLunde/railway-image-service

go 1.23.1

Expand Down
2 changes: 1 addition & 1 deletion internal/app/imagor/httploader/httploader.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func New(options ...Option) *HTTPLoader {

// Get implements imagor.Loader interface
func (h *HTTPLoader) Get(r *http.Request, image string) (*imagor.Blob, error) {
if strings.HasPrefix(image, "files/") {
if strings.HasPrefix(image, "blob/") {
return nil, imagor.ErrNotFound
}
if image == "" {
Expand Down
4 changes: 2 additions & 2 deletions internal/app/imagor/imagor.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"github.com/cshum/imagor/imagorpath"
"github.com/cshum/imagor/storage/filestorage"
"github.com/cshum/imagor/vips"
"github.com/jaredLunde/railway-images/internal/app/imagor/httploader"
"github.com/jaredLunde/railway-images/internal/app/keyval"
"github.com/jaredLunde/railway-image-service/internal/app/imagor/httploader"
"github.com/jaredLunde/railway-image-service/internal/app/keyval"
)

type Config struct {
Expand Down
9 changes: 6 additions & 3 deletions internal/app/imagor/kvloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

"github.com/cshum/imagor"
"github.com/cshum/imagor/imagorpath"
"github.com/jaredLunde/railway-images/internal/app/keyval"
"github.com/jaredLunde/railway-image-service/internal/app/keyval"
)

var dotFileRegex = regexp.MustCompile(`/.`)
Expand Down Expand Up @@ -49,10 +49,13 @@ func (s *KVStorage) Path(image string) (string, bool) {
if strings.HasPrefix(image, "/") {
key = []byte(image[1:])
}
if !bytes.HasPrefix(key, []byte("files/")) {
if !bytes.HasPrefix(key, []byte("blob/")) {
return "", false
}
key = bytes.TrimPrefix(key, []byte("blob/"))
if s.KV.GetRecord(key).Deleted != keyval.NO {
return "", false
}
key = bytes.TrimPrefix(key, []byte("files/"))
return filepath.Join(s.PathPrefix, keyval.KeyToPath(key)), true
}

Expand Down
4 changes: 2 additions & 2 deletions internal/app/keyval/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

"github.com/gabriel-vasile/mimetype"
"github.com/gofiber/fiber/v3"
"github.com/jaredLunde/railway-images/client/sign"
"github.com/jaredLunde/railway-images/internal/pkg/ptr"
"github.com/jaredLunde/railway-image-service/client/sign"
"github.com/jaredLunde/railway-image-service/internal/pkg/ptr"
"github.com/syndtr/goleveldb/leveldb/util"
"github.com/valyala/fasthttp"
)
Expand Down
2 changes: 1 addition & 1 deletion internal/app/signature/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"net/url"

"github.com/gofiber/fiber/v3"
"github.com/jaredLunde/railway-images/client/sign"
"github.com/jaredLunde/railway-image-service/client/sign"
)

func New(secret string) *Signature {
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/mw/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

"github.com/gofiber/fiber/v3"
"github.com/jaredLunde/railway-images/client/sign"
"github.com/jaredLunde/railway-image-service/client/sign"
)

func NewVerifyAPIKey(secretKey string) func(c fiber.Ctx) error {
Expand Down
2 changes: 1 addition & 1 deletion js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
## Quick start

```sh
npm install jotai railway-images
npm install jotai railway-image-service
```
4 changes: 2 additions & 2 deletions js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4bb78a6

Please sign in to comment.