oxedyne/fe2o3/fe2o3_steel/src/app/syntax.rs
13.9 KiB, 77 runs
created by r1870400018:959, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //use crate::srv::id; |
| 2 | |
| 3 | use oxedyne_fe2o3_core::prelude::*; |
| 4 | use oxedyne_fe2o3_jdat::{ |
| 5 | prelude::*, |
| 6 | version::SemVer, |
| 7 | }; |
| 8 | use oxedyne_fe2o3_syntax::{ |
| 9 | self, |
| 10 | Syntax, |
| 11 | SyntaxRef, |
| 12 | arg::{ |
| 13 | Arg, |
| 14 | ArgConfig, |
| 15 | }, |
| 16 | cmd::{ |
| 17 | Cmd, |
| 18 | CmdConfig, |
| 19 | }, |
| 20 | }; |
| 21 | |
| 22 | |
| 23 | pub fn new_shell( |
| 24 | name: &str, |
| 25 | ver: &SemVer, |
| 26 | about: &str, |
| 27 | ) |
| 28 | -> Outcome<SyntaxRef> |
| 29 | { |
| 30 | let s = res!(new_shell_raw(name, ver, about)); |
| 31 | Ok(SyntaxRef::new(s)) |
| 32 | } |
| 33 | |
| 34 | pub fn new_shell_raw( |
| 35 | name: &str, |
| 36 | ver: &SemVer, |
| 37 | about: &str, |
| 38 | ) |
| 39 | -> Outcome<Syntax> |
| 40 | { |
| 41 | let mut s = Syntax::new(name).ver(*ver).about(about); |
| 42 | s = res!(s.with_default_help_cmd()); |
| 43 | s = res!(oxedyne_fe2o3_syntax::apps::file::generic_file_system(s)); |
| 44 | |
| 45 | // ┌───────────────────────┐ |
| 46 | // │ CONTROL │ |
| 47 | // └───────────────────────┘ |
| 48 | // --------------------------------------------------------------------------------------------- |
| 49 | // Command: exit |
| 50 | // --------------------------------------------------------------------------------------------- |
| 51 | let cmd = Cmd::from(CmdConfig { |
| 52 | name: fmt!("exit"), |
| 53 | help: Some(fmt!("Shutdown the app and exit, or use Ctrl+C, Ctrl+D")), |
| 54 | cat: fmt!("Control"), |
| 55 | ..Default::default() |
| 56 | }); |
| 57 | s = res!(s.add_cmd(cmd)); |
| 58 | // ============================================================================================= |
| 59 | |
| 60 | // --------------------------------------------------------------------------------------------- |
| 61 | // Command: shell |
| 62 | // --------------------------------------------------------------------------------------------- |
| 63 | let cmd = Cmd::from(CmdConfig { |
| 64 | name: fmt!("shell"), |
| 65 | help: Some(fmt!("Start the app shell")), |
| 66 | cat: fmt!("Control"), |
| 67 | ..Default::default() |
| 68 | }); |
| 69 | s = res!(s.add_cmd(cmd)); |
| 70 | // ============================================================================================= |
| 71 | |
| 72 | // --------------------------------------------------------------------------------------------- |
| 73 | // Command: server |
| 74 | // --------------------------------------------------------------------------------------------- |
| 75 | let mut cmd = Cmd::from(CmdConfig { |
| 76 | name: fmt!("server"), |
| 77 | help: Some(fmt!("Start the app HTTPS server")), |
| 78 | cat: fmt!("Control"), |
| 79 | ..Default::default() |
| 80 | }); |
| 81 | let a1 = Arg::from(ArgConfig { |
| 82 | name: fmt!("dev"), |
| 83 | hyph1: Some(fmt!("d")), |
| 84 | vals: vec![], |
| 85 | reqd: false, |
| 86 | help: Some(fmt!("Run server in developer mode.")), |
| 87 | ..Default::default() |
| 88 | }); |
| 89 | cmd = res!(cmd.add_arg(a1)); |
| 90 | s = res!(s.add_cmd(cmd)); |
| 91 | // ============================================================================================= |
| 92 | |
| 93 | // --------------------------------------------------------------------------------------------- |
| 94 | // Command: cert |
| 95 | // --------------------------------------------------------------------------------------------- |
| 96 | let mut cmd = Cmd::from(CmdConfig { |
| 97 | name: fmt!("cert"), |
| 98 | help: Some(fmt!("Manage TLS certificates")), |
| 99 | cat: fmt!("TLS"), |
| 100 | ..Default::default() |
| 101 | }); |
| 102 | let a1 = Arg::from(ArgConfig { |
| 103 | name: fmt!("create-dev"), |
| 104 | hyph1: Some(fmt!("d")), |
| 105 | vals: vec![], |
| 106 | reqd: false, |
| 107 | help: Some(fmt!("Create self-signed certificates for development.")), |
| 108 | ..Default::default() |
| 109 | }); |
| 110 | cmd = res!(cmd.add_arg(a1)); |
| 111 | s = res!(s.add_cmd(cmd)); |
| 112 | // ============================================================================================= |
| 113 | |
| 114 | // --------------------------------------------------------------------------------------------- |
| 115 | // Command: acme |
| 116 | // --------------------------------------------------------------------------------------------- |
| 117 | let mut cmd = Cmd::from(CmdConfig { |
| 118 | name: fmt!("acme"), |
| 119 | help: Some(fmt!("Manage ACME (Let's Encrypt) certificate state")), |
| 120 | cat: fmt!("TLS"), |
| 121 | ..Default::default() |
| 122 | }); |
| 123 | let a1 = Arg::from(ArgConfig { |
| 124 | name: fmt!("status"), |
| 125 | hyph1: Some(fmt!("s")), |
| 126 | vals: vec![], |
| 127 | reqd: false, |
| 128 | help: Some(fmt!("Print the configured ACME state and vhost hostnames.")), |
| 129 | ..Default::default() |
| 130 | }); |
| 131 | let a2 = Arg::from(ArgConfig { |
| 132 | name: fmt!("renew"), |
| 133 | hyph1: Some(fmt!("r")), |
| 134 | vals: vec![], |
| 135 | reqd: false, |
| 136 | help: Some(fmt!("Clear the ACME cache so certs are re-issued on next start-up.")), |
| 137 | ..Default::default() |
| 138 | }); |
| 139 | cmd = res!(cmd.add_arg(a1)); |
| 140 | cmd = res!(cmd.add_arg(a2)); |
| 141 | s = res!(s.add_cmd(cmd)); |
| 142 | |
| 143 | // ┌───────────────────────┐ |
| 144 | // │ WALLET │ |
| 145 | // └───────────────────────┘ |
| 146 | // --------------------------------------------------------------------------------------------- |
| 147 | // Command: secrets |
| 148 | // --------------------------------------------------------------------------------------------- |
| 149 | let mut cmd = Cmd::from(CmdConfig { |
| 150 | name: fmt!("secrets"), |
| 151 | help: Some(fmt!("Manage wallet encrypted secrets.")), |
| 152 | cat: fmt!("Wallet"), |
| 153 | ..Default::default() |
| 154 | }); |
| 155 | let a1 = Arg::from(ArgConfig { |
| 156 | name: fmt!("create"), |
| 157 | hyph1: Some(fmt!("c")), |
| 158 | vals: vec![(Kind::Str, fmt!("Name of secret for indexing")).into()], |
| 159 | reqd: false, |
| 160 | help: Some(fmt!("Interactively create a new encrypted secret.")), |
| 161 | ..Default::default() |
| 162 | }); |
| 163 | let a2 = Arg::from(ArgConfig { |
| 164 | name: fmt!("recover"), |
| 165 | hyph1: Some(fmt!("r")), |
| 166 | vals: vec![(Kind::Str, fmt!("Name of secret for indexing")).into()], |
| 167 | reqd: false, |
| 168 | help: Some(fmt!("Interactively recover an encrypted secret.")), |
| 169 | ..Default::default() |
| 170 | }); |
| 171 | cmd = res!(cmd.add_arg(a1)); |
| 172 | cmd = res!(cmd.add_arg(a2)); |
| 173 | s = res!(s.add_cmd(cmd)); |
| 174 | // ============================================================================================= |
| 175 | |
| 176 | // --------------------------------------------------------------------------------------------- |
| 177 | // Command: unseal |
| 178 | // --------------------------------------------------------------------------------------------- |
| 179 | let cmd = Cmd::from(CmdConfig { |
| 180 | name: fmt!("unseal"), |
| 181 | help: Some(fmt!("Unwrap the wallet master key with an admin passphrase, so \ |
| 182 | the server opens its databases when it starts. Optional: the server runs \ |
| 183 | sealed as well, serving every static vhost while DB-backed routes answer \ |
| 184 | 503, and can be unsealed from the /admin dashboard afterwards.")), |
| 185 | cat: fmt!("Wallet"), |
| 186 | ..Default::default() |
| 187 | }); |
| 188 | s = res!(s.add_cmd(cmd)); |
| 189 | // ============================================================================================= |
| 190 | |
| 191 | // --------------------------------------------------------------------------------------------- |
| 192 | // Command: wallet |
| 193 | // --------------------------------------------------------------------------------------------- |
| 194 | let mut cmd = Cmd::from(CmdConfig { |
| 195 | name: fmt!("wallet"), |
| 196 | help: Some(fmt!("Manage the wallet file.")), |
| 197 | cat: fmt!("Wallet"), |
| 198 | ..Default::default() |
| 199 | }); |
| 200 | let a1 = Arg::from(ArgConfig { |
| 201 | name: fmt!("migrate"), |
| 202 | hyph1: Some(fmt!("m")), |
| 203 | hyph2: Some(fmt!("migrate")), |
| 204 | vals: vec![], |
| 205 | reqd: false, |
| 206 | help: Some(fmt!("One-shot migrate a pre-admin-user wallet (the legacy \ |
| 207 | app_hashes / wallet_pass_hashes layout) into the multi-admin layout, \ |
| 208 | preserving the existing master key so no Ozone re-encryption is \ |
| 209 | required.")), |
| 210 | ..Default::default() |
| 211 | }); |
| 212 | cmd = res!(cmd.add_arg(a1)); |
| 213 | s = res!(s.add_cmd(cmd)); |
| 214 | // ============================================================================================= |
| 215 | |
| 216 | // --------------------------------------------------------------------------------------------- |
| 217 | // Command: admin |
| 218 | // --------------------------------------------------------------------------------------------- |
| 219 | let mut cmd = Cmd::from(CmdConfig { |
| 220 | name: fmt!("admin"), |
| 221 | help: Some(fmt!("Manage wallet admin entries (add, remove, list).")), |
| 222 | cat: fmt!("Wallet"), |
| 223 | ..Default::default() |
| 224 | }); |
| 225 | let a1 = Arg::from(ArgConfig { |
| 226 | name: fmt!("add"), |
| 227 | hyph1: Some(fmt!("a")), |
| 228 | hyph2: Some(fmt!("add")), |
| 229 | vals: vec![(Kind::Str, fmt!("New admin name")).into()], |
| 230 | reqd: false, |
| 231 | help: Some(fmt!("Add a new admin entry wrapping the wallet master \ |
| 232 | key under a freshly prompted password. The running session must \ |
| 233 | have been unlocked by an admin holding the 'admin' scope.")), |
| 234 | ..Default::default() |
| 235 | }); |
| 236 | let a2 = Arg::from(ArgConfig { |
| 237 | name: fmt!("remove"), |
| 238 | hyph1: Some(fmt!("r")), |
| 239 | hyph2: Some(fmt!("remove")), |
| 240 | vals: vec![(Kind::Str, fmt!("Admin name to remove")).into()], |
| 241 | reqd: false, |
| 242 | help: Some(fmt!("Remove an existing admin entry by name. The last \ |
| 243 | remaining admin cannot be removed.")), |
| 244 | ..Default::default() |
| 245 | }); |
| 246 | let a3 = Arg::from(ArgConfig { |
| 247 | name: fmt!("list"), |
| 248 | hyph1: Some(fmt!("l")), |
| 249 | hyph2: Some(fmt!("list")), |
| 250 | vals: vec![], |
| 251 | reqd: false, |
| 252 | help: Some(fmt!("List the names, scopes and expiry of every admin \ |
| 253 | entry in the wallet.")), |
| 254 | ..Default::default() |
| 255 | }); |
| 256 | let a4 = Arg::from(ArgConfig { |
| 257 | name: fmt!("scopes"), |
| 258 | hyph1: Some(fmt!("s")), |
| 259 | hyph2: Some(fmt!("scopes")), |
| 260 | vals: vec![(Kind::Str, fmt!("Comma-separated verb list")).into()], |
| 261 | reqd: false, |
| 262 | help: Some(fmt!("Scopes for a new admin, comma-separated. Well-known \ |
| 263 | scopes: 'admin' (manage other admin entries, in CLI and dashboard); \ |
| 264 | 'dashboard.view' (log into the admin dashboard, read-only); \ |
| 265 | 'dashboard.admin' (log into the admin dashboard with mutations). \ |
| 266 | Use '*' for operator-level access. Defaults to an empty scope \ |
| 267 | list, which can unlock the wallet but cannot invoke any verb.")), |
| 268 | ..Default::default() |
| 269 | }); |
| 270 | let a5 = Arg::from(ArgConfig { |
| 271 | name: fmt!("expires-in"), |
| 272 | hyph1: Some(fmt!("e")), |
| 273 | hyph2: Some(fmt!("expires-in")), |
| 274 | vals: vec![(Kind::U64, fmt!("Seconds until expiry")).into()], |
| 275 | reqd: false, |
| 276 | help: Some(fmt!("Expire the new admin after N seconds from now. \ |
| 277 | A value of 0 (the default) means 'never expires'.")), |
| 278 | ..Default::default() |
| 279 | }); |
| 280 | let a6 = Arg::from(ArgConfig { |
| 281 | name: fmt!("passwd"), |
| 282 | hyph1: Some(fmt!("p")), |
| 283 | hyph2: Some(fmt!("passwd")), |
| 284 | vals: vec![], |
| 285 | reqd: false, |
| 286 | help: Some(fmt!("Change the caller's own admin password in place. \ |
| 287 | The caller is whichever admin unlocked the wallet at start-up. \ |
| 288 | Scopes and expiry are preserved; only the wrap is replaced. \ |
| 289 | Prompts for the new password twice.")), |
| 290 | ..Default::default() |
| 291 | }); |
| 292 | cmd = res!(cmd.add_arg(a1)); |
| 293 | cmd = res!(cmd.add_arg(a2)); |
| 294 | cmd = res!(cmd.add_arg(a3)); |
| 295 | cmd = res!(cmd.add_arg(a4)); |
| 296 | cmd = res!(cmd.add_arg(a5)); |
| 297 | cmd = res!(cmd.add_arg(a6)); |
| 298 | s = res!(s.add_cmd(cmd)); |
| 299 | // ============================================================================================= |
| 300 | |
| 301 | // ┌───────────────────────┐ |
| 302 | // │ MAIL │ |
| 303 | // └───────────────────────┘ |
| 304 | // --------------------------------------------------------------------------------------------- |
| 305 | // Command: mailpass |
| 306 | // --------------------------------------------------------------------------------------------- |
| 307 | let mut cmd = Cmd::from(CmdConfig { |
| 308 | name: fmt!("mailpass"), |
| 309 | help: Some(fmt!("Hash an interactively-prompted password with Argon2id, ready \ |
| 310 | to paste into the mail users file.")), |
| 311 | cat: fmt!("Mail"), |
| 312 | ..Default::default() |
| 313 | }); |
| 314 | let a1 = Arg::from(ArgConfig { |
| 315 | name: fmt!("address"), |
| 316 | hyph1: Some(fmt!("a")), |
| 317 | vals: vec![(Kind::Str, fmt!("Email address")).into()], |
| 318 | reqd: true, |
| 319 | help: Some(fmt!("Email address (local@domain) the password belongs to.")), |
| 320 | ..Default::default() |
| 321 | }); |
| 322 | let a2 = Arg::from(ArgConfig { |
| 323 | name: fmt!("delivery-dir"), |
| 324 | hyph1: Some(fmt!("d")), |
| 325 | vals: vec![(Kind::Str, fmt!("Relative directory under maildir_root")).into()], |
| 326 | reqd: true, |
| 327 | help: Some(fmt!("Per-user mailbox directory, relative to maildir_root.")), |
| 328 | ..Default::default() |
| 329 | }); |
| 330 | cmd = res!(cmd.add_arg(a1)); |
| 331 | cmd = res!(cmd.add_arg(a2)); |
| 332 | s = res!(s.add_cmd(cmd)); |
| 333 | // ============================================================================================= |
| 334 | |
| 335 | // ┌───────────────────────┐ |
| 336 | // │ WORKSPACE │ |
| 337 | // └───────────────────────┘ |
| 338 | // --------------------------------------------------------------------------------------------- |
| 339 | // Command: vars |
| 340 | // --------------------------------------------------------------------------------------------- |
| 341 | let cmd = Cmd::from(CmdConfig { |
| 342 | name: fmt!("vars"), |
| 343 | help: Some(fmt!("Display variable names and values")), |
| 344 | cat: fmt!("Workspace"), |
| 345 | ..Default::default() |
| 346 | }); |
| 347 | s = res!(s.add_cmd(cmd)); |
| 348 | // ============================================================================================= |
| 349 | |
| 350 | Ok(s) |
| 351 | } |