標準コマンドだけでファイルを送りあえるサーバーを作ってみました

## はじめに

他のデバイスへのファイル転送の煩わしさを少しでも解消できたらと思い作りました
MacユーザーならAirDropでとすんなりいけるのですが、他のユーザーだと、Slack、LINEとかを使ってのファイル転送で、それもなければメールで... と面倒です

ターミナルと使ってると、他のブラウザなりGUIを立ち上げるの面倒なのです。なのでコマンドで送れるように考えました
加えて、新しくコマンドをインストールせずに、標準的なコマンドだけで送れる方法を考えました

## 特徴

* 標準的なコマンド`curl`か`wget`があれば、ファイル送受信できる
* アカウントを作る必要がない
* ブラウザからでも送受信できる

## ファイルを送る

以下は`./test.txt`を送る例です

```bash
cat ./test.txt | curl -T - https://trans-akka.herokuapp.com/
```

![Dec-03-2017 18-45-48.gif](https://qiita-image-store.s3.amazonaws.com/0/84451/f1bd04eb-37a6-8b3a-f26b-f8e34d0d635a.gif)


## ファイルを受け取る


以下の方法でダウンロードできます。
`cwm`はFile IDです。送信したときのレスポンスのランダムな文字列です（後述のオプションでセキュリティのレベルを向上させることができます）。

```bash
curl https://trans-akka.herokuapp.com/cwm > myfile
```

![Dec-03-2017 18-46-59.gif](https://qiita-image-store.s3.amazonaws.com/0/84451/4873f2d9-e54d-0107-e03c-e2015ac5cc24.gif)

（上記のURLにアクセスすれば、ブラウザからでもダウンロードできます）

## その他の使い方

詳しい説明は、下の方の「その他の特徴」をご覧ください。


## GitHubリポジトリ

GitHub: [nwtgck/trans-server-akka](https://github.com/nwtgck/trans-server-akka)

## 公開サーバー


* https://trans-akka.herokuapp.com/


## Dockerでサーバーを立てる

`docker run`一発ですぐに立てれます。

```bash
docker run -it -p 8080:80 nwtgck/trans-server-akka:v1.19.6 --http-port=80
```

これで、http://localhost:8080 にサーバーが立ちます。


### データの永続化 - Docker

`/trans/db`内にファイルデータもデータベースのデータもあるので、以下のコマンドで永続化するようなサーバーが立てれます

```bash
docker run -d -p 8080:80 -v $PWD/trans-db:/trans/db --restart=always nwtgck/trans-server-akka:v1.19.6 --http-port=80
```

現在(ver 1.19.6)では、
* ファイルは圧縮され、暗号化されて保存されます
* データベースはファイルベースの[H2 Database](http://www.h2database.com/html/main.html)を使用しています


## Let's encryptでHTTPS化 - Docker

ファイル送信は安全が重要なので、[Let's encrypt](https://letsencrypt.org/)を使った、HTTPS化の方法の紹介です。
（Let's encryptに限らず使えるとは思いますが）

```bash
FULLCHAIN=/etc/letsencrypt/live/hogehoge.io/fullchain.pem
PRIVKEY=/etc/letsencrypt/live/hogehoge.io/privkey.pem

# pkcs.p12を生成する
sudo openssl pkcs12 -export -in $FULLCHAIN -inkey $PRIVKEY -out pkcs.p12 -name mytlskeyalias -passout pass:changeit

# trans.keystoreを生成する
keytool -importkeystore -srckeystore pkcs.p12 -srcstoretype PKCS12 -destkeystore trans.keystore -deststoretype JKS -srcstorepass changeit -deststorepass changeit

# transサーバーを起動
sudo docker run -it -p 80:80 -p 443:443 -v $PWD/trans.keystore:/trans/trans.keystore  nwtgck/trans-server-akka:v1.19.6 --http-port=80 --https-port=443
```

（`FULLCHAIN`と`PRIVKEY`はご自身のドメインに合わせて変更してください）

## その他の特徴

### ヘルプ/ サーバーバージョン

`wget`とか`curl`のオプションとかを忘れたときに使えそうな`/help`です

```
curl https://trans-akka.herokuapp.com/help
```

```
curl https://trans-akka.herokuapp.com/version
```

(`curl`を使うとデフォルトで標準出力にレスポンスが出るので、`curl`があるときはこっちが便利です)



### ファイル削除

```bash
curl -X DELETE https://trans-akka.herokuapp.com/cwm
```

## ダウンロード回数制限付きで送信

以下の例だと1回ダウンロードされると、自動的にファイルが消滅するになります

```bash
curl 'https://trans-akka.herokuapp.com/?get-times=1' -T ./test.txt
```

## File IDを長くして送信

覚えやすさ優先で、デフォルトが3文字なので、長くしてセキュアにしたいときは、長さが指定できます。

```bash
curl 'https://trans-akka.herokuapp.com/?id-length=16' -T ./test.txt
```

## 削除キー付きで送信

デフォルトだと削除するときにキーがないので、File IDを知っている人はだれでも削除できるので、以下で削除用のキーを設定できます

```bash
curl 'https://trans-akka.herokuapp.com/?delete-key=mykey1234' -T ./test.txt
```

## 誰も削除できないようにする

```bash
curl 'https://trans-akka.herokuapp.com/?deletable=false' -T ./test.txt
```

## URLを送る

HTTPのPOST送信ボディー部を内容にしているだけなので、以下のようにファイルに保存されている内容でなくても送れます

```bash
 echo "https://example.com" | curl -T - https://trans-akka.herokuapp.com
```

`https://trans-akka.herokuapp.com/r/YOUR FILE_ID` のように `/r/YOUR FILE_ID`を指定することでリダイレクトしてくれます。
この機能で、一定時間で消滅する短縮リンクみたいになってくれます。

## `zip` & `curl` でディレクトリ送信

上のURL送信と同じように、`zip`でも一度圧縮したファイルを生成しなくても送れます

```bash
zip -q -r - ./mydir | curl -T - https://trans-akka.herokuapp.com
```

## `tar` & `curl` でディレクトリ送信

以下は`.tar.gz`版です

```bash
tar zfcp - ./mydir/ | curl -T - https://trans-akka.herokuapp.com
```



## Range access

[高速でダウンロードできるツールを Go で実装した。](https://qiita.com/Kei-Kamikawa/items/d0a500ac387d39a34401)で有名な[`pget`](https://github.com/Code-Hex/pget)にも対応してます

## （追記）　専用のCLI

Transのコンセプトは、標準的なコマンドで送ったり受け取ったりすることです。
ただし、専用のCLIがあったほうが便利なので、Go言語でCLIを作りました。
これで`trans`コマンドを使える人は`trans`で送って、持っていない人は`wget`、`curl`やブラウザから送受信ができるようになり便利です。


### インストール

```bash
brew install nwtgck/homebrew-trans/trans
```

### 使用法

```bash
trans send test.txt
```


```bash
trans get cwm
```

```bash
trans delete cwm
```


どのサーバーを使うかは以下のコマンドで設定できます。

```bash
trans use https://trans-akka.herokuapp.com
```

詳しい使い方は <https://github.com/nwtgck/trans-cli-go> や、`trans -h`をみてください。


# サーバーサイド

サーバーサイドは、[Akka HTTP](https://doc.akka.io/docs/akka-http/current/scala/http/introduction.html)データベースは、[Slick](http://slick.lightbend.com/)で書いています。

ストリームな感じがとてもよく、
以下のような処理が、

```
ファイルのバイト列 => 圧縮 => 暗号化
              => バイト数を数える（Content-Lengthで使うため）
```

思ったように以下のようなコードになります

```scala
RunnableGraph.fromGraph(GraphDSL.create(fileStoreSink, lengthSink)(_.zip(_)){implicit builder => (fileStoreSink, lengthSink) =>
	import GraphDSL.Implicits._
	val bcast = builder.add(Broadcast[ByteString](2))

	// * compress ~> encrypt ~> store
	// * calc length
	byteSource ~> bcast ~> Compression.gzip ~> CipherFlow.flow(genEncryptCipher()) ~> fileStoreSink
	              bcast ~> lengthSink
	ClosedShape
})
```
https://github.com/nwtgck/trans-server-akka/blob/c21b764b235e444d30d42cec8b6239a87dbee752/src/main/scala/io/github/nwtgck/trans_server/Core.scala#L90

特に凝ったこともしていないですし、[Akka HTTPのHigh-Levelなルーティング](https://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/index.html)で、とても快適にルーティングできました。

# 最後に

少しでもファイルを送り合う煩わしさから開放されたら嬉しいです
あと、AkkaとかAkka StreamとかAkka HTTPとかの日本語の記事が増えてくれると嬉しいです

# 参考

* [Enabling HTTPS in your Java server using a free certificate](https://vaadin.com/blog/enabling-https-in-your-java-server-using-a-free-certificate)
* [zip / unzip コマンド | コマンドの使い方(Linux) | hydroculのメモ](https://hydrocul.github.io/wiki/commands/zip.html)
* [tarの標準出力や標準入力を使いこなす方法](http://kaworu.jpn.org/kaworu/2008-07-10-1.php)