Skip to content

Commit a510a94

Browse files
committed
Implement filedrop as a standalone server
Also improve README.md and library documentation. Closes #3.
1 parent fce5c8d commit a510a94

File tree

9 files changed

+200
-34
lines changed

9 files changed

+200
-34
lines changed

README.md

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,62 @@ filedrop
66
[![Issues](https://img.shields.io/github/issues-raw/foxcpp/filedrop.svg?style=flat-square)](https://github.com/foxcpp/filedrop/issues)
77
[![License](https://img.shields.io/github/license/foxcpp/filedrop.svg?style=flat-square)](https://github.com/foxcpp/filedrop/blob/master/LICENSE)
88

9-
Too lightweight file storage server with HTTP API.
9+
Lightweight file storage server with HTTP API.
1010

11-
**Currently filedrop is implemented only as a library. Sections below also
12-
document ideas for standalone server. See issue #3.**
11+
### Features
12+
- Painless configuration! You don't even have to `rewrite` requests on your reverse proxy!
13+
- Limits support! Link usage count, file size and storage time.
14+
- Embeddable! Can run as part of your application.
15+
16+
You can use filedrop either as a standalone server or as a part of your application.
17+
In former case you want to check `filedropd` subpackage, in later case just
18+
import `filedrop` package and pass config stucture to `filedrop.New`, returned
19+
object implements `http.Handler` so you can use it how you like.
20+
21+
### Installation
22+
23+
This repository uses Go 1.11 modules. Things may work with old `GOPATH`
24+
approach but we don't support it so don't report cryptic compilation errors
25+
caused by wrong dependency version.
26+
27+
`master` branch contains code from latest (pre-)release. `dev` branch
28+
contains bleeding-edge code. You probably want to use one of [tagged
29+
releases](https://github.com/foxcpp/filedrop/releases).
30+
31+
#### SQL drivers
32+
33+
filedrop uses SQL database as a meta-information storage so you need a
34+
SQL driver for it to use.
35+
36+
When building standalone server you may want to enable one of the
37+
supported SQL DBMS using build tags:
38+
* `postgres` for PostgreSQL
39+
* `sqlite3` for SQLite3
40+
* `mysql` for MySQL
41+
42+
**Note:** No SQL server support is planned. However if you would like
43+
to see it - PRs are welcome.
44+
45+
When using filedrop as a library you are given more freedom. Just make
46+
sure that you import driver you use.
47+
48+
#### Library
49+
50+
Just use `github.com/foxcpp/filedrop` as any other library. Documentation
51+
is here: [godoc.org](https://godoc.org/github.com/foxcpp/filedrop).
52+
53+
#### Standalone server
54+
55+
See `fildropd` subdirectory. To start server you need a configuration
56+
file. See example [here](filedrop.example.yml). It should be pretty
57+
straightforward. Then just pass path to configuration file in
58+
command-line arguments.
59+
60+
```
61+
filedropd /etc/filedropd.yml
62+
```
63+
64+
systemd unit file is included for your convenience.
1365

1466
### HTTP API
1567

@@ -49,27 +101,7 @@ and allow it to be downloaded not more than 10 times.
49101

50102
### Authorization
51103

52-
filedrop supports very basic access control. Basically, it can execute SQL
53-
query with contents of `Authorization` header and file name. If query returns 1 - access
54-
will be allowed, if query returns 0 or fails - client will get 403.
55-
56-
Also if you are using filedrop as a library, you can instead just pass custom
57-
authorization callback.
58-
59-
See [configuration example](filedrop.example.yml) for details.
60-
61-
### Installation
62-
63-
`go get` or clone this repo and build binary from `filedropd` package. Pass
64-
path to configuration in first argument. You are perfect.
65-
66-
### Configuration
67-
68-
[Documented example](filedrop.example.yml) is included in repo, check it out.
69-
70-
### Embedding
104+
When using filedrop as a library you can setup custom callbacks
105+
for access control.
71106

72-
If your backend server is written in Golang or you are not a big fan of
73-
microservices architecture - you can run filedrop server as part of your
74-
program. See [documentation](godocs.org/github.com/foxcpp/filedrop) for
75-
details.
107+
See `filedrop.AuthConfig` documentation.

config.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,49 @@ package filedrop
33
import "net/http"
44

55
type LimitsConfig struct {
6-
MaxUses uint `yaml:"max_uses"`
6+
// MaxUses is how much much times file can be accessed. Note that it also counts HEAD requests
7+
// and incomplete downloads (byte-range requests).
8+
// Per-file max-uses parameter can't exceed this value but can be smaller.
9+
MaxUses uint `yaml:"max_uses"`
10+
11+
// MaxStoreSecs specifies max time for which files will be stored on
12+
// filedrop server. Per-file store-secs parameter can't exceed this value but
13+
// can be smaller.
714
MaxStoreSecs uint `yaml:"max_store_secs"`
15+
16+
// MaxFileSize is a maximum file size in bytes that can uploaded to filedrop.
817
MaxFileSize uint `yaml:"max_file_size"`
918
}
1019

1120
type DBConfig struct {
21+
// Driver is a database/sql driver name.
1222
Driver string `yaml:"driver"`
13-
DSN string `yaml:"dsn"`
23+
24+
// Data Source Name.
25+
DSN string `yaml:"dsn"`
1426
}
1527

1628
type AuthConfig struct {
17-
//Query string TODO
18-
19-
// Used only in embedded interface.
29+
// Callback is called to check access before processing any request.
30+
// If Callback is null, no check will be performed.
2031
Callback func(*http.Request) bool `yaml:"omitempty"`
2132
}
2233

2334
type Config struct {
35+
// ListenOn specifies endpoint to listen on in format ADDR:PORT. Used only by filedropd.
36+
ListenOn string `yaml:"listen_on"`
37+
2438
Limits LimitsConfig `yaml:"limits"`
2539
DB DBConfig `yaml:"db"`
2640
DownloadAuth AuthConfig `yaml:"download_auth"`
2741
UploadAuth AuthConfig `yaml:"upload_auth"`
28-
StorageDir string `yaml:"storage_dir"`
29-
HTTPSDownstream bool `yaml:"https_downstream"`
42+
43+
// StorageDir is where files will be saved on disk.
44+
StorageDir string `yaml:"storage_dir"`
45+
46+
// HTTPSDownstream specifies whether filedrop should return links with https scheme or not.
47+
// Overridden by X-HTTPS-Downstream header.
48+
HTTPSDownstream bool `yaml:"https_downstream"`
3049

3150
// Internal, used only for testing. Always 60 secs in production.
3251
CleanupIntervalSecs int `yaml:"-"`

filedropd/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
filedropd
2+
filedropd.yml
3+
test.yml
4+
*.db*
5+
filedrop

filedropd/filedropd.example.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# IP:PORT to listen on.
2+
# Use 0.0.0.0 to listen on all interfaces, however we recommend using
3+
# reverse proxy for caching and stuff.
4+
listen_on: "127.0.0.1:8000"
5+
6+
limits:
7+
# How much much times file can be accessed. Note that it also counts HEAD requests
8+
# and incomplete downloads (byte-range requests).
9+
# Per-file max-uses parameter can't exceed this value but can be smaller.
10+
max_uses: 60
11+
12+
# Max time for which files will be stored on filedrop server. Per-file store-secs
13+
# parameter can't exceed this value but can be smaller.
14+
max_store_secs: 3600
15+
16+
# Maximum size of file which can be uploaded to filedrop, in bytes.
17+
max_file_size: 1073741824
18+
19+
db:
20+
# Driver to use for SQL DB (same as build tag you used to enable it).
21+
driver: sqlite3
22+
23+
# Data Source Name, see underlying driver documentation for exact format you should use:
24+
# - PostgreSQL https://godoc.org/github.com/lib/pq
25+
# TLDR: `postgres://user:password@address/dbname`
26+
# - MySQL https://github.com/go-sql-driver/mysql
27+
# TLDR: `username:password@protocol(address)/dbname`
28+
# - SQLite3 https://github.com/mattn/go-sqlite3
29+
# TLDR: `filepath`
30+
dsn: /var/lib/filedrop/index.db
31+
32+
# Where files will be saved on disk.
33+
storage_dir: /var/lib/filedrop
34+
35+
# Specifies whether filedrop should return links with https scheme or not.
36+
# Overridden by X-HTTPS-Downstream header.
37+
https_downstream: true

filedropd/filedropd.service

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[Unit]
2+
Description=filedrop standalone server
3+
After=network.target
4+
5+
[Service]
6+
Type=simple
7+
ExecStart=/usr/bin/filedropd /etc/filedropd.yml
8+
Restart=on-failure
9+
DynamicUser=true
10+
StateDirectory=filedrop
11+
12+
[Install]
13+
WantedBy=multi-user.target

filedropd/main.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"log"
7+
"net/http"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
12+
"github.com/foxcpp/filedrop"
13+
"gopkg.in/yaml.v2"
14+
)
15+
16+
func main() {
17+
if len(os.Args) != 2 {
18+
fmt.Println("Usage:", os.Args[0], "<config file>")
19+
os.Exit(1)
20+
}
21+
22+
confBlob, err := ioutil.ReadFile(os.Args[1])
23+
if err != nil {
24+
log.Fatalln("Failed to read config file:", err)
25+
}
26+
27+
config := filedrop.Config{}
28+
if err := yaml.Unmarshal(confBlob, &config); err != nil {
29+
log.Fatalln("Failed to parse config file:", err)
30+
}
31+
32+
serv, err := filedrop.New(config)
33+
if err != nil {
34+
log.Fatalln("Failed to start server:", err)
35+
}
36+
37+
go func() {
38+
log.Println("Listening on", config.ListenOn + "...")
39+
http.ListenAndServe(config.ListenOn, serv)
40+
}()
41+
42+
sig := make(chan os.Signal, 1)
43+
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGHUP)
44+
<-sig
45+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ require (
77
github.com/mattn/go-sqlite3 v1.9.0
88
github.com/pkg/errors v0.8.0
99
google.golang.org/appengine v1.2.0 // indirect
10+
gopkg.in/yaml.v2 v2.2.1
1011
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
1313
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1414
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
1515
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
16+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
17+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18+
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
19+
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

server.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
var ErrFileDoesntExists = errors.New("file doesn't exists")
2121

22-
// filedrop server structure, implements http.Handler.
22+
// Main filedrop server structure, implements http.Handler.
2323
type Server struct {
2424
DB *db
2525
Conf Config
@@ -29,6 +29,10 @@ type Server struct {
2929
fileCleanerStopChan chan bool
3030
}
3131

32+
// Create and initialize new server instance using passed configuration.
33+
//
34+
// serv.Logger will be redirected to os.Stderr by default.
35+
// Created instances should be closed by using serv.Close.
3236
func New(conf Config) (*Server, error) {
3337
s := new(Server)
3438
var err error
@@ -138,6 +142,8 @@ func (s *Server) removeFile(tx *sql.Tx, fileUUID string) error {
138142
return nil
139143
}
140144

145+
// OpenFile opens file for reading without any other side-effects
146+
// applied (such as "link" usage counting).
141147
func (s *Server) OpenFile(fileUUID string) (io.ReadSeeker, error) {
142148
// Just to check validity.
143149
_, err := uuid.FromString(fileUUID)
@@ -334,6 +340,10 @@ func (s *Server) serveFile(w http.ResponseWriter, r *http.Request) {
334340
http.ServeContent(w, r, fileUUID, time.Time{}, reader)
335341
}
336342

343+
// ServeHTTP implements http.Handler for filedrop.Server.
344+
//
345+
// Note that filedrop code is URL prefix-agnostic, so request URI doesn't
346+
// matters much.
337347
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
338348
if r.Method == http.MethodPost {
339349
s.acceptFile(w, r)

0 commit comments

Comments
 (0)