Merge pull request #261 from Plume-org/setup-tools

CLI tools
This commit is contained in:
Baptiste Gelez 2018-10-06 14:28:13 +01:00 committed by GitHub
commit 00fe11fcbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 424 additions and 300 deletions

93
Cargo.lock generated
View File

@ -71,6 +71,14 @@ dependencies = [
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "antidote"
version = "1.0.0"
@ -104,6 +112,16 @@ dependencies = [
"quick-xml 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.1.8"
@ -281,6 +299,20 @@ name = "chunked_transfer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -1518,6 +1550,17 @@ dependencies = [
"serde_derive 1.0.77 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "plume-cli"
version = "0.2.0"
dependencies = [
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"plume-models 0.2.0",
"rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "plume-common"
version = "0.2.0"
@ -1722,6 +1765,14 @@ name = "redox_syscall"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.2.11"
@ -2148,6 +2199,11 @@ name = "string_cache_shared"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
@ -2260,6 +2316,24 @@ dependencies = [
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.3.6"
@ -2580,6 +2654,11 @@ name = "unicode-segmentation"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
@ -2673,6 +2752,11 @@ name = "vcpkg"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.4"
@ -2781,11 +2865,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
"checksum ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8b93ecb80665873703bf3b0a77f369c96b183d8e0afaf30a3ff5ff07dfc6409"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
"checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum ascii 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14"
"checksum atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9a7ab83635ff7a3b04856f4ad95324dccc9b947ab1e790fc5c769ee6d6f60c"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum backtrace 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "150ae7828afa7afb6d474f909d64072d21de1f3365b6e8ad8029bf7b1c6350a0"
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
@ -2808,6 +2894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f74ad218e66339b11fd23f693fb8f1d621e80ba6ac218297be26073365d163d"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc0a60679001b62fb628c4da80e574b9645ab4646056d7c9018885efffe45533"
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
@ -2962,6 +3049,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
"checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341"
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
@ -3008,6 +3096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
"checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191"
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5"
"checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b"
@ -3019,6 +3108,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508"
"checksum tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e815b67d44c26feb06630011fb58b5b243f4e9585aac1ed0592c5795de64cd75"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
"checksum tiny_http 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442681f9f72e440be192700eeb2861e4174b9983f16f4877c93a134cb5e5f63"
@ -3051,6 +3142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
@ -3063,6 +3155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum validator 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a3f0d7368156daa506d6e42bd110857ee42d320dd1edd8e77f81b44fb00bb844"
"checksum validator_derive 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b50b766d1588742f67db0d5df7d5394473234fbc60a382da81bd666a99db41e"
"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "af464bc7be7b785c7ac72e266a6b67c4c9070155606f51655a650a6686204e35"

View File

@ -24,6 +24,10 @@ validator_derive = "0.7"
webfinger = "0.3"
workerpool = "1.1"
[[bin]]
name = "plume"
path = "src/main.rs"
[dependencies.chrono]
features = ["serde"]
version = "0.4"
@ -68,4 +72,4 @@ postgres = ["plume-models/postgres"]
sqlite = ["plume-models/sqlite"]
[workspace]
members = ["plume-api", "plume-models", "plume-common"]
members = ["plume-api", "plume-cli", "plume-models", "plume-common"]

View File

@ -14,7 +14,8 @@ WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN cargo install diesel_cli --no-default-features --features postgres --version '=1.2.0'
COPY . .
RUN cargo build
RUN cargo install --force
RUN cargo install --path plume-cli --force
RUN rm -rf target/debug/incremental
CMD ["cargo", "run"]
CMD ["plume"]
EXPOSE 7878

51
docs/CLI.md Normal file
View File

@ -0,0 +1,51 @@
# `plm` CLI reference
If any required argument is ommitted, you will be asked to input manually.
## `plm instance`
Manage instances.
### `plm instance new`
Create a new local instance.
**Example:**
```bash
plm instance new --private --domain plu.me --name 'My Plume Instance' -l 'CC-BY'
```
**Arguments:**
- `--domain`, `-d`: the domain name on which your instance will be available.
- `--name`, `-n`: The name of your instance. It will be displayed on the homepage.
- `--default-license`, `-l`: the license to use for articles written on this instance, if no other license is explicitely specified. Optional, defaults to CC-0.
- `--private`, `-p`: if this argument is present, registering on this instance won't be possible. Optional, off by default.
**Environment variables:**
- `BASE_URL` will be used as a fallback if `--domain` is not specified.
## `plm users`
Manage users.
### `plm users new`
Creates a new user on this instance.
**Example:**
```bash
plm users new --admin -n 'kate' -N 'Kate' --bio "I'm Kate." --email 'kate@plu.me'
```
**Arguments:**
- `--name`, `--username`, `-n`: the name of this user. It will be used an human-readable identifier in URLs, for federation and when mentioning this person. It can't be changed afterwards.
- `--display-name`, `-N`: the display name of this user, that will be shown everywhere on the interface.
- `--bio`, `--biography`, `-b`: the biography of the user. Optional, empty by default.
- `--email`, `-e`: the email adress of the user.
- `--password`, `-p`: the password of the user. You probably want to use this option in shell scipts only, since if you don't specify it, the prompt won't show your password.
- `--admin`, `-a`: makes the user an admin of the instance. Optional, off by default.

View File

@ -133,44 +133,73 @@ This command may be useful if you decided to use a separate database server.
## Starting Plume
When you launch Plume for the first time, it will ask you a few questions to setup your instance before it actually launches. To start it, run these commands.
First, you'll need to install Plume and the CLI tools to manage your instance.
```
# Optional, only do it if the database URL is not
# postgres://plume:plume@localhost/plume
export DB_URL=postgres://plume:PASSWORD@DBSERVERIP:DBPORT/DATABASE_NAME
cargo install && cargo install --path plume-cli
```
# Create the media directory, where uploads will be stored
Before starting Plume, you'll need to create a configuration file, called `.env`. Here is a sample of what you should put inside.
```bash
# The address of the database
# (replace USER, PASSWORD, PORT and DATABASE_NAME with your values)
DB_URL=postgres://USER:PASSWORD@IP:PORT/DATABASE_NAME
# The domain on which your instance will be available
BASE_URL=plu.me
# Secret key used for private cookies and CSRF protection
# You can generate one with `openssl rand -base64 32`
ROCKET_SECRET_KEY=
```
For more information about what you can put in your `.env`, see [the documentation about environment variables](ENV-VARS.md).
After that, you'll need to setup your instance, and the admin's account.
```
plm instance new
plm users new --admin
```
For more information about these commands, and the arguments you can give them, check out [their documentaion](CLI.md).
After that, you are almost done, the last thing to do is to create the media directory, where uploads will be stored:
```bash
mkdir media
```
# Actually start Plume
cargo run
Finally, you can start Plume with:
```bash
plume
```
## Docker install
You can use `docker` and `docker-compose` in order to manage your Plume instance and
have it isolated from your host:
You can use `docker` and `docker-compose` in order to manage your Plume instance and have it isolated from your host:
```
```bash
git clone git@github.com:Plume-org/Plume.git
cd Plume
cp docs/docker-compose.sample.yml docker-compose.yml
cp docs/docker.sample.env .env
# build the containers
# Build the containers
docker-compose build
# launch the database
# Launch the database
docker-compose up -d postgres
# run the migrations
# Run the migrations
docker-compose run --rm plume diesel migration run
# run interactive setup
docker-compose run --rm plume bash
cargo run
# copy the env file and paste it in your host .env file
cat .env
# leave the container
exit
# launch your instance for good
# Setup your instance
docker-compose run --rm plume plume instance new
docker-compose run --rm plume plume users new --admin
# Launch your instance for good
docker-compose up -d
```
@ -295,7 +324,7 @@ Description=plume
Type=simple
User=plume
WorkingDirectory=/home/plume/Plume
ExecStart=/home/plume/.cargo/bin/cargo run
ExecStart=/home/plume/.cargo/bin/plume
TimeoutSec=30
Restart=always
@ -339,7 +368,7 @@ This script can also be useful if you are using SysVinit.
### END INIT INFO
dir="/home/plume/Plume"
cmd="/home/plume/.cargo/bin/cargo run"
cmd="/home/plume/.cargo/bin/plume"
user="plume"
name=`basename $0`
@ -437,4 +466,4 @@ exit 0
## Acknowledgements
Most of this documentation has been written by *gled-rs*. The systemd unit file, Nginx and Apache configurations have been written by *nonbinaryanargeek*. Some parts (especially the instructions to install native dependencies) are from the [Aardwolf project](https://github.com/Aardwolf-Social/aardwolf).
Most of this documentation has been written by *gled-rs*. The systemd unit file, Nginx and Apache configurations have been written by *nonbinaryanargeek*. Some parts (especially the instructions to install native dependencies) are from the [Aardwolf project](https://github.com/Aardwolf-Social/aardwolf). The docker instructions, and files have been added by *Eliot Berriot*.

View File

@ -4,6 +4,7 @@ To update your instance, run these commands with `plume` user if you created it,
```
git pull origin master
cargo install --force && cargo install --path plume-cli --force
# If you are using sysvinit
sudo service plume restart

24
plume-cli/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "plume-cli"
version = "0.2.0"
authors = ["Bat' <baptiste@gelez.xyz>"]
[[bin]]
name = "plm"
path = "src/main.rs"
[dependencies]
clap = "2.32"
dotenv = "0.13"
rpassword = "2.0"
[dependencies.diesel]
features = ["postgres", "r2d2", "chrono"]
version = "*"
[dependencies.plume-models]
path = "../plume-models"
[features]
postgres = ["plume-models/postgres"]
sqlite = ["plume-models/sqlite"]

63
plume-cli/src/instance.rs Normal file
View File

@ -0,0 +1,63 @@
use clap::{Arg, ArgMatches, App, SubCommand};
use std::env;
use plume_models::{
Connection,
instance::*,
safe_string::SafeString,
};
pub fn command<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("instance")
.about("Manage instances")
.subcommand(SubCommand::with_name("new")
.arg(Arg::with_name("domain")
.short("d")
.long("domain")
.takes_value(true)
.help("The domain name of your instance")
).arg(Arg::with_name("name")
.short("n")
.long("name")
.takes_value(true)
.help("The name of your instance")
).arg(Arg::with_name("default-license")
.short("l")
.long("default-license")
.takes_value(true)
.help("The license that will be used by default for new articles on this instance")
).arg(Arg::with_name("private")
.short("p")
.long("private")
.help("Closes the registrations on this instance")
).about("Create a new local instance"))
}
pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
let conn = conn;
match args.subcommand() {
("new", Some(x)) => new(x, conn),
_ => println!("Unknwon subcommand"),
}
}
fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
let domain = args.value_of("domain").map(String::from)
.unwrap_or_else(|| env::var("BASE_URL")
.unwrap_or_else(|_| super::ask_for("Domain name")));
let name = args.value_of("name").map(String::from).unwrap_or_else(|| super::ask_for("Instance name"));
let license = args.value_of("default-license").map(String::from).unwrap_or(String::from("CC-0"));
let open_reg = !args.is_present("private");
Instance::insert(conn, NewInstance {
public_domain: domain,
name: name,
local: true,
long_description: SafeString::new(""),
short_description: SafeString::new(""),
default_license: license,
open_registrations: open_reg,
short_description_html: String::new(),
long_description_html: String::new()
});
}

41
plume-cli/src/main.rs Normal file
View File

@ -0,0 +1,41 @@
extern crate clap;
extern crate diesel;
extern crate dotenv;
extern crate plume_models;
extern crate rpassword;
use clap::App;
use diesel::Connection;
use std::io::{self, prelude::*};
use plume_models::{DB_URL, Connection as Conn};
mod instance;
mod users;
fn main() {
let mut app = App::new("Plume CLI")
.bin_name("plm")
.version("0.2.0")
.about("Collection of tools to manage your Plume instance.")
.subcommand(instance::command())
.subcommand(users::command());
let matches = app.clone().get_matches();
dotenv::dotenv().ok();
let conn = Conn::establish(DB_URL.as_str());
match matches.subcommand() {
("instance", Some(args)) => instance::run(args, &conn.expect("Couldn't connect to the database.")),
("users", Some(args)) => users::run(args, &conn.expect("Couldn't connect to the database.")),
_ => app.print_help().unwrap()
};
}
pub fn ask_for(something: &str) -> String {
print!("{}: ", something);
io::stdout().flush().expect("Couldn't flush STDOUT");
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Unable to read line");
input.retain(|c| c != '\n');
input
}

77
plume-cli/src/users.rs Normal file
View File

@ -0,0 +1,77 @@
use clap::{Arg, ArgMatches, App, SubCommand};
use rpassword;
use std::io::{self, Write};
use plume_models::{
Connection,
users::*,
};
pub fn command<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("users")
.about("Manage users")
.subcommand(SubCommand::with_name("new")
.arg(Arg::with_name("name")
.short("n")
.long("name")
.alias("username")
.takes_value(true)
.help("The username of the new user")
).arg(Arg::with_name("display-name")
.short("N")
.long("display-name")
.takes_value(true)
.help("The display name of the new user")
).arg(Arg::with_name("biography")
.short("b")
.long("bio")
.alias("biography")
.takes_value(true)
.help("The biography of the new user")
).arg(Arg::with_name("email")
.short("e")
.long("email")
.takes_value(true)
.help("Email address of the new user")
).arg(Arg::with_name("password")
.short("p")
.long("password")
.takes_value(true)
.help("The password of the new user")
).arg(Arg::with_name("admin")
.short("a")
.long("admin")
.help("Makes the user an administrator of the instance")
).about("Create a new user on this instance"))
}
pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
let conn = conn;
match args.subcommand() {
("new", Some(x)) => new(x, conn),
_ => println!("Unknwon subcommand"),
}
}
fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
let username = args.value_of("name").map(String::from).unwrap_or_else(|| super::ask_for("Username"));
let display_name = args.value_of("display-name").map(String::from).unwrap_or_else(|| super::ask_for("Display name"));
let admin = args.is_present("admin");
let bio = args.value_of("biography").unwrap_or("").to_string();
let email = args.value_of("email").map(String::from).unwrap_or_else(|| super::ask_for("Email address"));
let password = args.value_of("password").map(String::from).unwrap_or_else(|| {
print!("Password: ");
io::stdout().flush().expect("Couldn't flush STDOUT");
rpassword::read_password().expect("Couldn't read your password.")
});
NewUser::new_local(
conn,
username,
display_name,
admin,
bio,
email,
User::hash_pass(password),
).update_boxes(conn);
}

View File

@ -34,20 +34,29 @@ extern crate validator_derive;
extern crate webfinger;
extern crate workerpool;
use diesel::r2d2::ConnectionManager;
use rocket::State;
use rocket_contrib::Template;
use rocket_csrf::CsrfFairingBuilder;
use plume_models::{DB_URL, Connection, db_conn::DbPool};
use workerpool::{Pool, thunk::ThunkWorker};
mod api;
mod inbox;
mod setup;
mod routes;
type Worker<'a> = State<'a, Pool<ThunkWorker<()>>>;
/// Initializes a database pool.
fn init_pool() -> Option<DbPool> {
dotenv::dotenv().ok();
let manager = ConnectionManager::<Connection>::new(DB_URL.as_str());
DbPool::new(manager).ok()
}
fn main() {
let pool = setup::check();
let pool = init_pool();
rocket::ignite()
.mount("/", routes![
routes::blogs::paginated_details,

View File

@ -1,269 +0,0 @@
use colored::Colorize;
use diesel::r2d2::{ConnectionManager, Pool};
use dotenv::dotenv;
use std::fs::{self, File};
use std::io;
use std::path::Path;
use std::process::{exit, Command};
use rpassword;
use plume_models::safe_string::SafeString;
use plume_models::{
Connection,
DB_URL,
db_conn::{DbConn, DbPool},
instance::*,
users::*
};
/// Initializes a database pool.
fn init_pool() -> Option<DbPool> {
dotenv().ok();
let manager = ConnectionManager::<Connection>::new(DB_URL.as_str());
Pool::new(manager).ok()
}
pub fn check() -> DbPool {
if let Some(pool) = init_pool() {
match pool.get() {
Ok(conn) => {
let db_conn = DbConn(conn);
if Instance::get_local(&*db_conn).is_none() {
run_setup(Some(db_conn));
}
}
Err(_) => panic!("Couldn't connect to database")
}
migrate();
pool
} else {
run_setup(None);
init_pool().unwrap()
}
}
fn run_setup(conn: Option<DbConn>) {
println!("\n\n");
println!("{}\n{}\n{}\n\n{}",
"Welcome in the Plume setup tool.".magenta(),
"It will help you setup your new instance, by asking you a few questions.".magenta(),
"Then you'll be able to enjoy Plume!".magenta(),
"First let's check that you have all the required dependencies. Press Enter to start."
);
read_line();
check_native_deps();
let conn = setup_db(conn);
setup_type(conn);
dotenv().ok();
println!("{}\n{}\n{}",
"Your Plume instance is now ready to be used.".magenta(),
"We hope you will enjoy it.".magenta(),
"If you ever encounter a problem, feel free to report it at https://github.com/Plume-org/Plume/issues/".magenta(),
);
println!("\nPress Enter to start it.\n");
}
fn setup_db(conn: Option<DbConn>) -> DbConn {
write_to_dotenv("DB_URL", DB_URL.as_str().to_string());
match conn {
Some(conn) => conn,
None => {
println!("\n{}\n", "We are going to setup the database.".magenta());
println!("{}\n", "About to create a new PostgreSQL user named 'plume'".blue());
Command::new("createuser")
.arg("-d")
.arg("-P")
.arg("plume")
.status()
.map(|s| {
if s.success() {
println!("{}\n", " ✔️ Done".green());
}
})
.expect("Couldn't create new user");
println!("{}\n", "About to create a new PostgreSQL database named 'plume'".blue());
Command::new("createdb")
.arg("-O")
.arg("plume")
.arg("plume")
.status()
.map(|s| {
if s.success() {
println!("{}\n", " ✔️ Done".green());
}
})
.expect("Couldn't create new table");
migrate();
init_pool()
.expect("Couldn't init DB pool")
.get()
.map(|c| DbConn(c))
.expect("Couldn't connect to the database")
}
}
}
fn migrate() {
println!("{}\n", "Running migrations…".blue());
Command::new("diesel")
.arg("migration")
.arg("run")
.arg("--database-url")
.arg(DB_URL.as_str())
.status()
.map(|s| {
if s.success() {
println!("{}\n", " ✔️ Done".green());
}
})
.expect("Couldn't run migrations");
}
fn setup_type(conn: DbConn) {
println!("\nDo you prefer a simple setup, or to customize everything?\n");
println!(" 1 - Simple setup");
println!(" 2 - Complete setup");
match read_line().as_ref() {
"Simple" | "simple" | "s" | "S" |
"1" => quick_setup(conn),
"Complete" | "complete" | "c" | "C" |
"2" => complete_setup(conn),
x => {
println!("Invalid choice. Choose between '1' or '2'. {}", x);
setup_type(conn);
}
}
}
fn quick_setup(conn: DbConn) {
println!("What is your instance domain?");
let domain = read_line();
write_to_dotenv("BASE_URL", domain.clone());
println!("\nWhat is your instance name?");
let name = read_line();
let instance = Instance::insert(&*conn, NewInstance {
public_domain: domain,
name: name,
local: true,
long_description: SafeString::new(""),
short_description: SafeString::new(""),
default_license: String::from("CC-0"),
open_registrations: true,
short_description_html: String::new(),
long_description_html: String::new()
});
println!("{}\n", " ✔️ Your instance was succesfully created!".green());
// Generate Rocket secret key.
let key = Command::new("openssl")
.arg("rand")
.arg("-base64")
.arg("32")
.output()
.map(|o| String::from_utf8(o.stdout).expect("Invalid output from openssl"))
.expect("Couldn't generate secret key.");
write_to_dotenv("ROCKET_SECRET_KEY", key);
create_admin(instance, conn);
}
fn complete_setup(conn: DbConn) {
quick_setup(conn);
println!("\nOn which port should Plume listen? (default: 7878)");
let port = read_line_or("7878");
write_to_dotenv("ROCKET_PORT", port);
println!("\nOn which address should Plume listen? (default: 0.0.0.0)");
let address = read_line_or("0.0.0.0");
write_to_dotenv("ROCKET_ADDRESS", address);
}
fn create_admin(instance: Instance, conn: DbConn) {
println!("{}\n\n", "You are now about to create your admin account".magenta());
println!("What is your username? (default: admin)");
let name = read_line_or("admin");
println!("What is your email?");
let email = read_line();
println!("What is your password?");
let password = rpassword::read_password().expect("Couldn't read your password.");
NewUser::new_local(
&*conn,
name.clone(),
name,
true,
format!("Admin of {}", instance.name),
email,
User::hash_pass(password),
).update_boxes(&*conn);
println!("{}\n", " ✔️ Your account was succesfully created!".green());
}
fn check_native_deps() {
let mut not_found = Vec::new();
if !try_run("psql") {
not_found.push(("PostgreSQL", "sudo apt install postgresql"));
}
if !try_run("gettext") {
not_found.push(("GetText", "sudo apt install gettext"))
}
if !try_run("diesel") {
not_found.push(("Diesel CLI", "cargo install diesel_cli"))
}
if not_found.len() > 0 {
println!("{}\n", "Some native dependencies are missing:".red());
for (dep, install) in not_found.into_iter() {
println!("{}", format!(" - {} (can be installed with `{}`, on Debian based distributions)", dep, install).red())
}
println!("\nRetry once you have installed them.");
exit(1);
} else {
println!("{}", " ✔️ All native dependencies are present.".green())
}
}
fn try_run(command: &'static str) -> bool {
Command::new(command)
.output()
.is_ok()
}
fn read_line() -> String {
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Unable to read line");
input.retain(|c| c != '\n');
input
}
fn read_line_or(or: &str) -> String {
let input = read_line();
if input.len() == 0 {
or.to_string()
} else {
input
}
}
fn write_to_dotenv(var: &'static str, val: String) {
if !Path::new(".env").exists() {
File::create(".env").expect("Error while creating .env file");
}
fs::write(".env", format!("{}\n{}={}", fs::read_to_string(".env").expect("Unable to read .env"), var, val)).expect("Unable to write .env");
}