[WIP] A (somewhat barebones) atproto app for creating custom sites without hosting!

upload: form an index of all files to be uploaded

todo: detect mime type from file extension

vielle.dev f5f3239a 51173549

verified
Changed files
+308 -4
upload
+184 -2
upload/Cargo.lock
··· 3 version = 4 4 5 [[package]] 6 name = "anstream" 7 version = "0.6.21" 8 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 38 source = "registry+https://github.com/rust-lang/crates.io-index" 39 checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 40 dependencies = [ 41 - "windows-sys", 42 ] 43 44 [[package]] ··· 49 dependencies = [ 50 "anstyle", 51 "once_cell_polyfill", 52 - "windows-sys", 53 ] 54 55 [[package]] ··· 57 version = "0.1.0" 58 dependencies = [ 59 "clap", 60 ] 61 62 [[package]] ··· 106 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 107 108 [[package]] 109 name = "heck" 110 version = "0.5.0" 111 source = "registry+https://github.com/rust-lang/crates.io-index" 112 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 113 114 [[package]] 115 name = "is_terminal_polyfill" 116 version = "1.70.1" 117 source = "registry+https://github.com/rust-lang/crates.io-index" 118 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 119 120 [[package]] 121 name = "once_cell_polyfill" 122 version = "1.70.1" 123 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 142 ] 143 144 [[package]] 145 name = "strsim" 146 version = "0.11.1" 147 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 171 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 172 173 [[package]] 174 name = "windows-link" 175 version = "0.2.1" 176 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 183 checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 184 dependencies = [ 185 "windows-targets", 186 ] 187 188 [[package]]
··· 3 version = 4 4 5 [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.3" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 name = "anstream" 16 version = "0.6.21" 17 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 47 source = "registry+https://github.com/rust-lang/crates.io-index" 48 checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 49 dependencies = [ 50 + "windows-sys 0.60.2", 51 ] 52 53 [[package]] ··· 58 dependencies = [ 59 "anstyle", 60 "once_cell_polyfill", 61 + "windows-sys 0.60.2", 62 ] 63 64 [[package]] ··· 66 version = "0.1.0" 67 dependencies = [ 68 "clap", 69 + "ignore", 70 + "regex", 71 + ] 72 + 73 + [[package]] 74 + name = "bstr" 75 + version = "1.12.0" 76 + source = "registry+https://github.com/rust-lang/crates.io-index" 77 + checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 78 + dependencies = [ 79 + "memchr", 80 + "serde", 81 ] 82 83 [[package]] ··· 127 checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 128 129 [[package]] 130 + name = "crossbeam-deque" 131 + version = "0.8.6" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 134 + dependencies = [ 135 + "crossbeam-epoch", 136 + "crossbeam-utils", 137 + ] 138 + 139 + [[package]] 140 + name = "crossbeam-epoch" 141 + version = "0.9.18" 142 + source = "registry+https://github.com/rust-lang/crates.io-index" 143 + checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 144 + dependencies = [ 145 + "crossbeam-utils", 146 + ] 147 + 148 + [[package]] 149 + name = "crossbeam-utils" 150 + version = "0.8.21" 151 + source = "registry+https://github.com/rust-lang/crates.io-index" 152 + checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 153 + 154 + [[package]] 155 + name = "globset" 156 + version = "0.4.16" 157 + source = "registry+https://github.com/rust-lang/crates.io-index" 158 + checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 159 + dependencies = [ 160 + "aho-corasick", 161 + "bstr", 162 + "log", 163 + "regex-automata", 164 + "regex-syntax", 165 + ] 166 + 167 + [[package]] 168 name = "heck" 169 version = "0.5.0" 170 source = "registry+https://github.com/rust-lang/crates.io-index" 171 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 172 173 [[package]] 174 + name = "ignore" 175 + version = "0.4.23" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" 178 + dependencies = [ 179 + "crossbeam-deque", 180 + "globset", 181 + "log", 182 + "memchr", 183 + "regex-automata", 184 + "same-file", 185 + "walkdir", 186 + "winapi-util", 187 + ] 188 + 189 + [[package]] 190 name = "is_terminal_polyfill" 191 version = "1.70.1" 192 source = "registry+https://github.com/rust-lang/crates.io-index" 193 checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 194 195 [[package]] 196 + name = "log" 197 + version = "0.4.28" 198 + source = "registry+https://github.com/rust-lang/crates.io-index" 199 + checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 200 + 201 + [[package]] 202 + name = "memchr" 203 + version = "2.7.6" 204 + source = "registry+https://github.com/rust-lang/crates.io-index" 205 + checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 206 + 207 + [[package]] 208 name = "once_cell_polyfill" 209 version = "1.70.1" 210 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 229 ] 230 231 [[package]] 232 + name = "regex" 233 + version = "1.12.2" 234 + source = "registry+https://github.com/rust-lang/crates.io-index" 235 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 236 + dependencies = [ 237 + "aho-corasick", 238 + "memchr", 239 + "regex-automata", 240 + "regex-syntax", 241 + ] 242 + 243 + [[package]] 244 + name = "regex-automata" 245 + version = "0.4.13" 246 + source = "registry+https://github.com/rust-lang/crates.io-index" 247 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 248 + dependencies = [ 249 + "aho-corasick", 250 + "memchr", 251 + "regex-syntax", 252 + ] 253 + 254 + [[package]] 255 + name = "regex-syntax" 256 + version = "0.8.8" 257 + source = "registry+https://github.com/rust-lang/crates.io-index" 258 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 259 + 260 + [[package]] 261 + name = "same-file" 262 + version = "1.0.6" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 265 + dependencies = [ 266 + "winapi-util", 267 + ] 268 + 269 + [[package]] 270 + name = "serde" 271 + version = "1.0.228" 272 + source = "registry+https://github.com/rust-lang/crates.io-index" 273 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 274 + dependencies = [ 275 + "serde_core", 276 + ] 277 + 278 + [[package]] 279 + name = "serde_core" 280 + version = "1.0.228" 281 + source = "registry+https://github.com/rust-lang/crates.io-index" 282 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 283 + dependencies = [ 284 + "serde_derive", 285 + ] 286 + 287 + [[package]] 288 + name = "serde_derive" 289 + version = "1.0.228" 290 + source = "registry+https://github.com/rust-lang/crates.io-index" 291 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 292 + dependencies = [ 293 + "proc-macro2", 294 + "quote", 295 + "syn", 296 + ] 297 + 298 + [[package]] 299 name = "strsim" 300 version = "0.11.1" 301 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 325 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 326 327 [[package]] 328 + name = "walkdir" 329 + version = "2.5.0" 330 + source = "registry+https://github.com/rust-lang/crates.io-index" 331 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 332 + dependencies = [ 333 + "same-file", 334 + "winapi-util", 335 + ] 336 + 337 + [[package]] 338 + name = "winapi-util" 339 + version = "0.1.11" 340 + source = "registry+https://github.com/rust-lang/crates.io-index" 341 + checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 342 + dependencies = [ 343 + "windows-sys 0.61.2", 344 + ] 345 + 346 + [[package]] 347 name = "windows-link" 348 version = "0.2.1" 349 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 356 checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 357 dependencies = [ 358 "windows-targets", 359 + ] 360 + 361 + [[package]] 362 + name = "windows-sys" 363 + version = "0.61.2" 364 + source = "registry+https://github.com/rust-lang/crates.io-index" 365 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 366 + dependencies = [ 367 + "windows-link", 368 ] 369 370 [[package]]
+2
upload/Cargo.toml
··· 5 6 [dependencies] 7 clap = { version = "4.5.49", features = ["derive"] }
··· 5 6 [dependencies] 7 clap = { version = "4.5.49", features = ["derive"] } 8 + ignore = "0.4.23" 9 + regex = "1.12.2"
+20 -2
upload/src/main.rs
··· 1 - use clap::Parser; 2 use std::path::PathBuf; 3 4 #[derive(Parser, Debug)] 5 #[command(version, about, long_about = None)] ··· 12 #[arg(verbatim_doc_comment, short, long)] 13 password: String, 14 15 /// Directory to upload 16 #[arg(verbatim_doc_comment)] 17 dir: PathBuf, ··· 24 println!( 25 "user: {} 26 pword: {} 27 - dir: {:?}", 28 config.user, 29 config.password, 30 config.dir.to_str(), 31 ); 32 33 // get local site info 34 35 // create session 36
··· 1 + use clap::{ArgAction, Parser}; 2 use std::path::PathBuf; 3 + 4 + mod sitemap; 5 6 #[derive(Parser, Debug)] 7 #[command(version, about, long_about = None)] ··· 14 #[arg(verbatim_doc_comment, short, long)] 15 password: String, 16 17 + /// Include dotfiles in upload 18 + /// Default: false 19 + #[arg(verbatim_doc_comment, short = 'a', long = "all")] 20 + all_files: bool, 21 + 22 + /// Respect gitignore files 23 + /// Note: gitignore files are not uploaded unless --all is set 24 + /// Default: true 25 + #[arg(verbatim_doc_comment, short = 'g', long = "no-gitignore", action = ArgAction::SetFalse, )] 26 + git_ignore: bool, 27 + 28 /// Directory to upload 29 #[arg(verbatim_doc_comment)] 30 dir: PathBuf, ··· 37 println!( 38 "user: {} 39 pword: {} 40 + dir: {:?} 41 + all: {} 42 + gitignore: {}", 43 config.user, 44 config.password, 45 config.dir.to_str(), 46 + config.all_files, 47 + config.git_ignore 48 ); 49 50 // get local site info 51 + let sitemap = sitemap::local_sitemap(config.dir, config.all_files, config.git_ignore); 52 53 // create session 54
+102
upload/src/sitemap.rs
···
··· 1 + use ignore::WalkBuilder; 2 + use std::{collections::HashMap, error::Error, fmt, path::PathBuf}; 3 + 4 + pub type Sitemap = HashMap<String, SitemapNode>; 5 + 6 + #[derive(Debug)] 7 + pub struct SitemapNode { 8 + pub mime_type: String, 9 + pub blob: BlobRef, 10 + } 11 + 12 + #[derive(Debug)] 13 + pub enum BlobRef { 14 + Local(PathBuf), 15 + Remote(String), 16 + } 17 + 18 + #[derive(Debug)] 19 + pub enum SitemapErr { 20 + IoErr(std::io::Error), 21 + IgnoreErr(ignore::Error), 22 + } 23 + 24 + impl From<std::io::Error> for SitemapErr { 25 + fn from(value: std::io::Error) -> Self { 26 + SitemapErr::IoErr(value) 27 + } 28 + } 29 + 30 + impl From<ignore::Error> for SitemapErr { 31 + fn from(value: ignore::Error) -> Self { 32 + SitemapErr::IgnoreErr(value) 33 + } 34 + } 35 + 36 + impl Error for SitemapErr {} 37 + 38 + impl fmt::Display for SitemapErr { 39 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 + match self { 41 + SitemapErr::IoErr(err) => write!(f, "IO Error: {}", err), 42 + SitemapErr::IgnoreErr(err) => write!(f, "Ignore Error: {}", err), 43 + } 44 + } 45 + } 46 + 47 + pub fn local_sitemap<'a>( 48 + dir: PathBuf, 49 + include_dotfiles: bool, 50 + use_gitignore: bool, 51 + ) -> Result<Sitemap, SitemapErr> { 52 + let res = WalkBuilder::new(dir) 53 + .hidden(!include_dotfiles) 54 + .git_ignore(use_gitignore) 55 + .git_exclude(use_gitignore) 56 + .git_global(use_gitignore) 57 + .follow_links(true) 58 + .build() 59 + .filter_map(|x| { 60 + let x = x 61 + .or_else(|err| { 62 + println!("Warning: {} ", err); 63 + Err(()) 64 + }) 65 + .ok()?; 66 + 67 + let metadata = x 68 + .metadata() 69 + .or_else(|err| { 70 + println!("Warning on file {}:\n{} ", x.path().display(), err); 71 + Err(()) 72 + }) 73 + .ok()?; 74 + 75 + if !metadata.is_file() { 76 + return None; 77 + }; 78 + 79 + let key = x.path().to_str()?; 80 + let key = String::from(key); 81 + 82 + Some(( 83 + key, 84 + SitemapNode { 85 + mime_type: String::from("application/octet-stream"), 86 + blob: BlobRef::Local(x.path().to_owned()), 87 + }, 88 + )) 89 + }) 90 + .collect::<HashMap<_, _>>(); 91 + 92 + println!( 93 + " - {}", 94 + res 95 + .iter() 96 + .map(|(k, v)| format!("{}: {:?}", k, v)) 97 + .collect::<Vec<_>>() 98 + .join("\n - ") 99 + ); 100 + 101 + Ok(res) 102 + }