mirror of
https://git.joinplu.me/Plume/Plume.git
synced 2024-11-26 05:21:00 +00:00
commit
00fe11fcbb
12 changed files with 424 additions and 300 deletions
93
Cargo.lock
generated
93
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
51
docs/CLI.md
Normal 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.
|
|
@ -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
|
||||
```
|
||||
|
||||
|
@ -196,7 +225,7 @@ server {
|
|||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name blog.example.org;
|
||||
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /home/plume/Plume/ ;
|
||||
|
||||
|
@ -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*.
|
||||
|
|
|
@ -4,11 +4,12 @@ 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
|
||||
|
||||
# If you are using systemd
|
||||
# If you are using systemd
|
||||
sudo systemctl restart plume
|
||||
```
|
||||
|
||||
|
|
24
plume-cli/Cargo.toml
Normal file
24
plume-cli/Cargo.toml
Normal 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
63
plume-cli/src/instance.rs
Normal 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
41
plume-cli/src/main.rs
Normal 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
77
plume-cli/src/users.rs
Normal 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);
|
||||
}
|
13
src/main.rs
13
src/main.rs
|
@ -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,
|
||||
|
|
269
src/setup.rs
269
src/setup.rs
|
@ -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");
|
||||
}
|
Loading…
Reference in a new issue