Add media cache, improve default styles
This commit is contained in:
		
							parent
							
								
									0a42450801
								
							
						
					
					
						commit
						9ada30626b
					
				
					 18 changed files with 613 additions and 65 deletions
				
			
		
							
								
								
									
										178
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										178
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -364,6 +364,21 @@ dependencies = [ | ||||||
|  "memchr", |  "memchr", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ammonia" | ||||||
|  | version = "3.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "89eac85170f4b3fb3dc5e442c1cfb036cb8eecf9dbbd431a161ffad15d90ea3b" | ||||||
|  | dependencies = [ | ||||||
|  |  "html5ever", | ||||||
|  |  "lazy_static", | ||||||
|  |  "maplit", | ||||||
|  |  "markup5ever_rcdom", | ||||||
|  |  "matches", | ||||||
|  |  "tendril", | ||||||
|  |  "url", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ansi_term" | name = "ansi_term" | ||||||
| version = "0.11.0" | version = "0.11.0" | ||||||
|  | @ -930,6 +945,16 @@ version = "0.3.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "futf" | ||||||
|  | version = "0.1.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" | ||||||
|  | dependencies = [ | ||||||
|  |  "mac", | ||||||
|  |  "new_debug_unreachable", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "futures" | name = "futures" | ||||||
| version = "0.3.4" | version = "0.3.4" | ||||||
|  | @ -1118,6 +1143,20 @@ dependencies = [ | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.8", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "html5ever" | ||||||
|  | version = "0.25.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b" | ||||||
|  | dependencies = [ | ||||||
|  |  "log", | ||||||
|  |  "mac", | ||||||
|  |  "markup5ever", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "http" | name = "http" | ||||||
| version = "0.2.0" | version = "0.2.0" | ||||||
|  | @ -1339,6 +1378,47 @@ dependencies = [ | ||||||
|  "linked-hash-map 0.5.2", |  "linked-hash-map 0.5.2", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "mac" | ||||||
|  | version = "0.1.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "maplit" | ||||||
|  | version = "1.0.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "markup5ever" | ||||||
|  | version = "0.10.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab" | ||||||
|  | dependencies = [ | ||||||
|  |  "log", | ||||||
|  |  "phf", | ||||||
|  |  "phf_codegen", | ||||||
|  |  "serde 1.0.105", | ||||||
|  |  "serde_derive", | ||||||
|  |  "serde_json", | ||||||
|  |  "string_cache", | ||||||
|  |  "string_cache_codegen", | ||||||
|  |  "tendril", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "markup5ever_rcdom" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b" | ||||||
|  | dependencies = [ | ||||||
|  |  "html5ever", | ||||||
|  |  "markup5ever", | ||||||
|  |  "tendril", | ||||||
|  |  "xml5ever", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "match_cfg" | name = "match_cfg" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
|  | @ -1437,6 +1517,12 @@ dependencies = [ | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.8", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "new_debug_unreachable" | ||||||
|  | version = "1.0.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "nodrop" | name = "nodrop" | ||||||
| version = "0.1.14" | version = "0.1.14" | ||||||
|  | @ -1600,6 +1686,26 @@ dependencies = [ | ||||||
|  "phf_shared", |  "phf_shared", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "phf_codegen" | ||||||
|  | version = "0.8.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" | ||||||
|  | dependencies = [ | ||||||
|  |  "phf_generator", | ||||||
|  |  "phf_shared", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "phf_generator" | ||||||
|  | version = "0.8.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" | ||||||
|  | dependencies = [ | ||||||
|  |  "phf_shared", | ||||||
|  |  "rand", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "phf_shared" | name = "phf_shared" | ||||||
| version = "0.8.0" | version = "0.8.0" | ||||||
|  | @ -1680,6 +1786,12 @@ version = "0.2.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "precomputed-hash" | ||||||
|  | version = "0.1.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pretty_env_logger" | name = "pretty_env_logger" | ||||||
| version = "0.4.0" | version = "0.4.0" | ||||||
|  | @ -1763,6 +1875,7 @@ dependencies = [ | ||||||
|  "rand_chacha", |  "rand_chacha", | ||||||
|  "rand_core", |  "rand_core", | ||||||
|  "rand_hc", |  "rand_hc", | ||||||
|  |  "rand_pcg", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
|  | @ -1793,6 +1906,15 @@ dependencies = [ | ||||||
|  "rand_core", |  "rand_core", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand_pcg" | ||||||
|  | version = "0.2.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" | ||||||
|  | dependencies = [ | ||||||
|  |  "rand_core", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "redox_syscall" | name = "redox_syscall" | ||||||
| version = "0.1.56" | version = "0.1.56" | ||||||
|  | @ -1826,12 +1948,14 @@ dependencies = [ | ||||||
|  "actix-rt", |  "actix-rt", | ||||||
|  "actix-web", |  "actix-web", | ||||||
|  "actix-webfinger", |  "actix-webfinger", | ||||||
|  |  "ammonia", | ||||||
|  "anyhow", |  "anyhow", | ||||||
|  "async-trait", |  "async-trait", | ||||||
|  "background-jobs", |  "background-jobs", | ||||||
|  "background-jobs-core", |  "background-jobs-core", | ||||||
|  "base64 0.12.0", |  "base64 0.12.0", | ||||||
|  "bb8-postgres", |  "bb8-postgres", | ||||||
|  |  "bytes", | ||||||
|  "config", |  "config", | ||||||
|  "dotenv", |  "dotenv", | ||||||
|  "env_logger", |  "env_logger", | ||||||
|  | @ -2246,6 +2370,31 @@ version = "0.1.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" | checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "string_cache" | ||||||
|  | version = "0.8.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" | ||||||
|  | dependencies = [ | ||||||
|  |  "lazy_static", | ||||||
|  |  "new_debug_unreachable", | ||||||
|  |  "phf_shared", | ||||||
|  |  "precomputed-hash", | ||||||
|  |  "serde 1.0.105", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "string_cache_codegen" | ||||||
|  | version = "0.5.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" | ||||||
|  | dependencies = [ | ||||||
|  |  "phf_generator", | ||||||
|  |  "phf_shared", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "stringprep" | name = "stringprep" | ||||||
| version = "0.1.2" | version = "0.1.2" | ||||||
|  | @ -2332,6 +2481,17 @@ dependencies = [ | ||||||
|  "unicode-xid", |  "unicode-xid", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tendril" | ||||||
|  | version = "0.4.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" | ||||||
|  | dependencies = [ | ||||||
|  |  "futf", | ||||||
|  |  "mac", | ||||||
|  |  "utf-8", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "termcolor" | name = "termcolor" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
|  | @ -2636,6 +2796,12 @@ dependencies = [ | ||||||
|  "percent-encoding", |  "percent-encoding", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "utf-8" | ||||||
|  | version = "0.7.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "uuid" | name = "uuid" | ||||||
| version = "0.8.1" | version = "0.8.1" | ||||||
|  | @ -2815,6 +2981,18 @@ dependencies = [ | ||||||
|  "winapi-build", |  "winapi-build", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "xml5ever" | ||||||
|  | version = "0.16.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59" | ||||||
|  | dependencies = [ | ||||||
|  |  "log", | ||||||
|  |  "mac", | ||||||
|  |  "markup5ever", | ||||||
|  |  "time 0.1.42", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "yaml-rust" | name = "yaml-rust" | ||||||
| version = "0.4.3" | version = "0.4.3" | ||||||
|  |  | ||||||
|  | @ -19,9 +19,11 @@ actix-rt = "1.0.0" | ||||||
| actix-web = { version = "3.0.0-alpha.1", features = ["rustls"] } | actix-web = { version = "3.0.0-alpha.1", features = ["rustls"] } | ||||||
| actix-webfinger = "0.3.0-alpha.3" | actix-webfinger = "0.3.0-alpha.3" | ||||||
| activitystreams = "0.5.0-alpha.11" | activitystreams = "0.5.0-alpha.11" | ||||||
|  | ammonia = "3.1.0" | ||||||
| async-trait = "0.1.24" | async-trait = "0.1.24" | ||||||
| background-jobs = { version = "0.8.0-alpha.0", git = "https://git.asonix.dog/Aardwolf/background-jobs", default-features = false, features = ["background-jobs-actix"] } | background-jobs = { version = "0.8.0-alpha.0", git = "https://git.asonix.dog/Aardwolf/background-jobs", default-features = false, features = ["background-jobs-actix"] } | ||||||
| background-jobs-core = { version = "0.7.0", git = "https://git.asonix.dog/Aardwolf/background-jobs" } | background-jobs-core = { version = "0.7.0", git = "https://git.asonix.dog/Aardwolf/background-jobs" } | ||||||
|  | bytes = "0.5.4" | ||||||
| base64 = "0.12" | base64 = "0.12" | ||||||
| bb8-postgres = { version = "0.4.0", features = ["with-serde_json-1", "with-uuid-0_8", "with-chrono-0_4"] } | bb8-postgres = { version = "0.4.0", features = ["with-serde_json-1", "with-uuid-0_8", "with-chrono-0_4"] } | ||||||
| config = "0.10.1" | config = "0.10.1" | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								scss/index.scss
									
										
									
									
									
								
							
							
						
						
									
										134
									
								
								scss/index.scss
									
										
									
									
									
								
							|  | @ -7,32 +7,69 @@ body { | ||||||
|     padding-bottom: 96px; |     padding-bottom: 96px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ul { | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  |     list-style: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| body, | body, | ||||||
| body * { | body * { | ||||||
|     box-sizing: border-box; |     box-sizing: border-box; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| header { | header { | ||||||
|     padding: 32px 0; |  | ||||||
|     background-color: #333; |     background-color: #333; | ||||||
|     color: #f5f5f5; |     color: #f5f5f5; | ||||||
|     text-align: center; | 
 | ||||||
|  |     .header-text { | ||||||
|  |         max-width: 700px; | ||||||
|  |         margin: auto; | ||||||
|  |         padding: 24px 0; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     h1 { |     h1 { | ||||||
|         margin: 0px; |         margin: 0px; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     p { | ||||||
|  |         margin: 0; | ||||||
|  |         margin-top: 8px; | ||||||
|  |         font-style: italic; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| section { | section { | ||||||
|     padding: 24px; |  | ||||||
|     background-color: #fff; |     background-color: #fff; | ||||||
|     border: 1px solid #e5e5e5; |     border: 1px solid #e5e5e5; | ||||||
|  |     box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); | ||||||
|     border-radius: 3px; |     border-radius: 3px; | ||||||
|     margin: 32px auto 0; |     margin: 32px auto 0; | ||||||
|     max-width: 700px; |     max-width: 700px; | ||||||
|  |     padding-bottom: 32px; | ||||||
|  | 
 | ||||||
|  |     > p:first-child { | ||||||
|  |         margin-top: 0; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     h3 { |     h3 { | ||||||
|         margin-top: 0px; |         padding: 24px; | ||||||
|  |         margin: 0px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     li { | ||||||
|  |         padding-top: 24px; | ||||||
|  |         padding-bottom: 24px; | ||||||
|  |         border-top: 1px solid #e5e5e5; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .padded { | ||||||
|  |         padding: 0 24px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .joining { | ||||||
|  |         padding: 24px; | ||||||
|  |         border-top: 1px solid #e5e5e5; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -57,6 +94,15 @@ pre { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | a { | ||||||
|  |     &, | ||||||
|  |     &:focus, | ||||||
|  |     &:hover, | ||||||
|  |     &:active { | ||||||
|  |         color: #ea7fbc; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| footer { | footer { | ||||||
|     background-color: #333; |     background-color: #333; | ||||||
|     color: #f5f5f5; |     color: #f5f5f5; | ||||||
|  | @ -67,16 +113,78 @@ footer { | ||||||
|     right: 0; |     right: 0; | ||||||
|     text-align: center; |     text-align: center; | ||||||
| 
 | 
 | ||||||
|     a { |  | ||||||
|         &, |  | ||||||
|         &:focus, |  | ||||||
|         &:hover, |  | ||||||
|         &:active { |  | ||||||
|             color: #ea7fbc; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     p { |     p { | ||||||
|         margin: 0; |         margin: 0; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .instance, | ||||||
|  | .info { | ||||||
|  |     h4 { | ||||||
|  |         font-size: 20px; | ||||||
|  |         margin: 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .instance-info { | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  |         border-top: 1px solid #e5e5e5; | ||||||
|  |         border-bottom: 1px solid #e5e5e5; | ||||||
|  |         padding: 32px; | ||||||
|  | 
 | ||||||
|  |         .instance-description { | ||||||
|  |             margin: 0; | ||||||
|  |             margin-bottom: 24px; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     a { | ||||||
|  |         text-decoration: none; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .admin { | ||||||
|  |     margin-top: 32px; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     background-color: #fff; | ||||||
|  |     border: 1px solid #e5e5e5; | ||||||
|  |     border-radius: 3px; | ||||||
|  |     box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); | ||||||
|  | 
 | ||||||
|  |     .display-name { | ||||||
|  |         font-weight: 600; | ||||||
|  |         font-size: 16px; | ||||||
|  |         margin: 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .username { | ||||||
|  |         font-size: 14px; | ||||||
|  |         color: #777; | ||||||
|  |         margin: 0; | ||||||
|  |         margin-top: 8px; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .avatar { | ||||||
|  |     width: 80px; | ||||||
|  |     height: 80px; | ||||||
|  | 
 | ||||||
|  |     img { | ||||||
|  |         width: 100%; | ||||||
|  |         border-radius: 40px; | ||||||
|  |         border: 1px solid #333; | ||||||
|  |         background-color: #f5f5f5; | ||||||
|  |         box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @media(max-width: 700px) { | ||||||
|  |     header .header-text { | ||||||
|  |         padding: 24px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     section { | ||||||
|  |         border-left: none; | ||||||
|  |         border-right: none; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ pub enum UrlKind { | ||||||
|     Inbox, |     Inbox, | ||||||
|     Index, |     Index, | ||||||
|     MainKey, |     MainKey, | ||||||
|  |     Media(Uuid), | ||||||
|     NodeInfo, |     NodeInfo, | ||||||
|     Outbox, |     Outbox, | ||||||
| } | } | ||||||
|  | @ -136,6 +137,7 @@ impl Config { | ||||||
|             UrlKind::Inbox => format!("{}://{}/inbox", scheme, self.hostname), |             UrlKind::Inbox => format!("{}://{}/inbox", scheme, self.hostname), | ||||||
|             UrlKind::Index => format!("{}://{}/", scheme, self.hostname), |             UrlKind::Index => format!("{}://{}/", scheme, self.hostname), | ||||||
|             UrlKind::MainKey => format!("{}://{}/actor#main-key", scheme, self.hostname), |             UrlKind::MainKey => format!("{}://{}/actor#main-key", scheme, self.hostname), | ||||||
|  |             UrlKind::Media(uuid) => format!("{}://{}/media/{}", scheme, self.hostname, uuid), | ||||||
|             UrlKind::NodeInfo => format!("{}://{}/nodeinfo/2.0.json", scheme, self.hostname), |             UrlKind::NodeInfo => format!("{}://{}/nodeinfo/2.0.json", scheme, self.hostname), | ||||||
|             UrlKind::Outbox => format!("{}://{}/outbox", scheme, self.hostname), |             UrlKind::Outbox => format!("{}://{}/outbox", scheme, self.hostname), | ||||||
|         } |         } | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								src/data/media.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/data/media.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | use activitystreams::primitives::XsdAnyUri; | ||||||
|  | use bytes::Bytes; | ||||||
|  | use lru::LruCache; | ||||||
|  | use std::{collections::HashMap, sync::Arc, time::Duration}; | ||||||
|  | use tokio::sync::{Mutex, RwLock}; | ||||||
|  | use ttl_cache::TtlCache; | ||||||
|  | use uuid::Uuid; | ||||||
|  | 
 | ||||||
|  | static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2); | ||||||
|  | 
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct Media { | ||||||
|  |     inverse: Arc<Mutex<HashMap<XsdAnyUri, Uuid>>>, | ||||||
|  |     url_cache: Arc<Mutex<LruCache<Uuid, XsdAnyUri>>>, | ||||||
|  |     byte_cache: Arc<RwLock<TtlCache<Uuid, (String, Bytes)>>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Media { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         Media { | ||||||
|  |             inverse: Arc::new(Mutex::new(HashMap::new())), | ||||||
|  |             url_cache: Arc::new(Mutex::new(LruCache::new(128))), | ||||||
|  |             byte_cache: Arc::new(RwLock::new(TtlCache::new(128))), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn get_uuid(&self, url: &XsdAnyUri) -> Option<Uuid> { | ||||||
|  |         let uuid = self.inverse.lock().await.get(url).cloned()?; | ||||||
|  | 
 | ||||||
|  |         if self.url_cache.lock().await.contains(&uuid) { | ||||||
|  |             return Some(uuid); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         self.inverse.lock().await.remove(url); | ||||||
|  | 
 | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn get_url(&self, uuid: Uuid) -> Option<XsdAnyUri> { | ||||||
|  |         self.url_cache.lock().await.get(&uuid).cloned() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn get_bytes(&self, uuid: Uuid) -> Option<(String, Bytes)> { | ||||||
|  |         self.byte_cache.read().await.get(&uuid).cloned() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn store_url(&self, url: &XsdAnyUri) -> Uuid { | ||||||
|  |         let uuid = Uuid::new_v4(); | ||||||
|  |         self.inverse.lock().await.insert(url.clone(), uuid); | ||||||
|  |         self.url_cache.lock().await.put(uuid, url.clone()); | ||||||
|  |         uuid | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn store_bytes(&self, uuid: Uuid, content_type: String, bytes: Bytes) { | ||||||
|  |         self.byte_cache | ||||||
|  |             .write() | ||||||
|  |             .await | ||||||
|  |             .insert(uuid, (content_type, bytes), MEDIA_DURATION); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| mod actor; | mod actor; | ||||||
|  | mod media; | ||||||
| mod node; | mod node; | ||||||
| mod state; | mod state; | ||||||
| 
 | 
 | ||||||
| pub use self::{ | pub use self::{ | ||||||
|     actor::{Actor, ActorCache}, |     actor::{Actor, ActorCache}, | ||||||
|     node::{Node, NodeCache}, |     media::Media, | ||||||
|  |     node::{Contact, Info, Instance, Node, NodeCache}, | ||||||
|     state::State, |     state::State, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -70,6 +70,9 @@ pub enum MyError { | ||||||
|     #[error("Hosts don't match, {0}, {1}")] |     #[error("Hosts don't match, {0}, {1}")] | ||||||
|     HostMismatch(String, String), |     HostMismatch(String, String), | ||||||
| 
 | 
 | ||||||
|  |     #[error("Invalid or missing content type")] | ||||||
|  |     ContentType, | ||||||
|  | 
 | ||||||
|     #[error("Couldn't flush buffer")] |     #[error("Couldn't flush buffer")] | ||||||
|     FlushBuffer, |     FlushBuffer, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| use crate::jobs::JobState; | use crate::{config::UrlKind, jobs::JobState}; | ||||||
| use activitystreams::primitives::XsdAnyUri; | use activitystreams::primitives::XsdAnyUri; | ||||||
| use anyhow::Error; | use anyhow::Error; | ||||||
| use background_jobs::{Job, Processor}; | use background_jobs::{Job, Processor}; | ||||||
|  | @ -44,7 +44,14 @@ impl QueryInstance { | ||||||
|             instance.description |             instance.description | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if let Some(contact) = instance.contact { |         if let Some(mut contact) = instance.contact { | ||||||
|  |             if let Some(uuid) = state.media.get_uuid(&contact.avatar).await { | ||||||
|  |                 contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).parse()?; | ||||||
|  |             } else { | ||||||
|  |                 let uuid = state.media.store_url(&contact.avatar).await; | ||||||
|  |                 contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).parse()?; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             state |             state | ||||||
|                 .node_cache |                 .node_cache | ||||||
|                 .set_contact( |                 .set_contact( | ||||||
|  | @ -57,6 +64,8 @@ impl QueryInstance { | ||||||
|                 .await?; |                 .await?; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         let description = ammonia::clean(&description); | ||||||
|  | 
 | ||||||
|         state |         state | ||||||
|             .node_cache |             .node_cache | ||||||
|             .set_instance( |             .set_instance( | ||||||
|  |  | ||||||
|  | @ -9,7 +9,8 @@ pub use self::{ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::{ActorCache, NodeCache, State}, |     config::Config, | ||||||
|  |     data::{ActorCache, Media, NodeCache, State}, | ||||||
|     db::Db, |     db::Db, | ||||||
|     error::MyError, |     error::MyError, | ||||||
|     jobs::{ |     jobs::{ | ||||||
|  | @ -33,17 +34,31 @@ pub fn create_server(db: Db) -> JobServer { | ||||||
|     JobServer::new(shared) |     JobServer::new(shared) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn create_workers(state: State, actors: ActorCache, job_server: JobServer) { | pub fn create_workers( | ||||||
|  |     state: State, | ||||||
|  |     actors: ActorCache, | ||||||
|  |     job_server: JobServer, | ||||||
|  |     media: Media, | ||||||
|  |     config: Config, | ||||||
|  | ) { | ||||||
|     let remote_handle = job_server.remote.clone(); |     let remote_handle = job_server.remote.clone(); | ||||||
| 
 | 
 | ||||||
|     WorkerConfig::new(move || JobState::new(state.clone(), actors.clone(), job_server.clone())) |     WorkerConfig::new(move || { | ||||||
|         .register(DeliverProcessor) |         JobState::new( | ||||||
|         .register(DeliverManyProcessor) |             state.clone(), | ||||||
|         .register(NodeinfoProcessor) |             actors.clone(), | ||||||
|         .register(InstanceProcessor) |             job_server.clone(), | ||||||
|         .register(ListenersProcessor) |             media.clone(), | ||||||
|         .set_processor_count("default", 4) |             config.clone(), | ||||||
|         .start(remote_handle); |         ) | ||||||
|  |     }) | ||||||
|  |     .register(DeliverProcessor) | ||||||
|  |     .register(DeliverManyProcessor) | ||||||
|  |     .register(NodeinfoProcessor) | ||||||
|  |     .register(InstanceProcessor) | ||||||
|  |     .register(ListenersProcessor) | ||||||
|  |     .set_processor_count("default", 4) | ||||||
|  |     .start(remote_handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
|  | @ -51,6 +66,8 @@ pub struct JobState { | ||||||
|     requests: Requests, |     requests: Requests, | ||||||
|     state: State, |     state: State, | ||||||
|     actors: ActorCache, |     actors: ActorCache, | ||||||
|  |     config: Config, | ||||||
|  |     media: Media, | ||||||
|     node_cache: NodeCache, |     node_cache: NodeCache, | ||||||
|     job_server: JobServer, |     job_server: JobServer, | ||||||
| } | } | ||||||
|  | @ -61,11 +78,19 @@ pub struct JobServer { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl JobState { | impl JobState { | ||||||
|     fn new(state: State, actors: ActorCache, job_server: JobServer) -> Self { |     fn new( | ||||||
|  |         state: State, | ||||||
|  |         actors: ActorCache, | ||||||
|  |         job_server: JobServer, | ||||||
|  |         media: Media, | ||||||
|  |         config: Config, | ||||||
|  |     ) -> Self { | ||||||
|         JobState { |         JobState { | ||||||
|             requests: state.requests(), |             requests: state.requests(), | ||||||
|             node_cache: state.node_cache(), |             node_cache: state.node_cache(), | ||||||
|             actors, |             actors, | ||||||
|  |             config, | ||||||
|  |             media, | ||||||
|             state, |             state, | ||||||
|             job_server, |             job_server, | ||||||
|         } |         } | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								src/main.rs
									
										
									
									
									
								
							|  | @ -16,7 +16,7 @@ mod routes; | ||||||
| use self::{ | use self::{ | ||||||
|     args::Args, |     args::Args, | ||||||
|     config::Config, |     config::Config, | ||||||
|     data::{ActorCache, State}, |     data::{ActorCache, Media, State}, | ||||||
|     db::Db, |     db::Db, | ||||||
|     jobs::{create_server, create_workers}, |     jobs::{create_server, create_workers}, | ||||||
|     middleware::RelayResolver, |     middleware::RelayResolver, | ||||||
|  | @ -62,6 +62,7 @@ async fn main() -> Result<(), anyhow::Error> { | ||||||
|         return Ok(()); |         return Ok(()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     let media = Media::new(); | ||||||
|     let state = State::hydrate(config.clone(), &db).await?; |     let state = State::hydrate(config.clone(), &db).await?; | ||||||
|     let actors = ActorCache::new(db.clone()); |     let actors = ActorCache::new(db.clone()); | ||||||
|     let job_server = create_server(db.clone()); |     let job_server = create_server(db.clone()); | ||||||
|  | @ -84,9 +85,11 @@ async fn main() -> Result<(), anyhow::Error> { | ||||||
|             let state = state.clone(); |             let state = state.clone(); | ||||||
|             let actors = actors.clone(); |             let actors = actors.clone(); | ||||||
|             let job_server = job_server.clone(); |             let job_server = job_server.clone(); | ||||||
|  |             let media = media.clone(); | ||||||
|  |             let config = config.clone(); | ||||||
| 
 | 
 | ||||||
|             Arbiter::new().exec_fn(move || { |             Arbiter::new().exec_fn(move || { | ||||||
|                 create_workers(state, actors, job_server); |                 create_workers(state, actors, job_server, media, config); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|         actix_rt::signal::ctrl_c().await?; |         actix_rt::signal::ctrl_c().await?; | ||||||
|  | @ -98,7 +101,13 @@ async fn main() -> Result<(), anyhow::Error> { | ||||||
|     let bind_address = config.bind_address(); |     let bind_address = config.bind_address(); | ||||||
|     HttpServer::new(move || { |     HttpServer::new(move || { | ||||||
|         if !no_jobs { |         if !no_jobs { | ||||||
|             create_workers(state.clone(), actors.clone(), job_server.clone()); |             create_workers( | ||||||
|  |                 state.clone(), | ||||||
|  |                 actors.clone(), | ||||||
|  |                 job_server.clone(), | ||||||
|  |                 media.clone(), | ||||||
|  |                 config.clone(), | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         App::new() |         App::new() | ||||||
|  | @ -109,7 +118,9 @@ async fn main() -> Result<(), anyhow::Error> { | ||||||
|             .data(actors.clone()) |             .data(actors.clone()) | ||||||
|             .data(config.clone()) |             .data(config.clone()) | ||||||
|             .data(job_server.clone()) |             .data(job_server.clone()) | ||||||
|  |             .data(media.clone()) | ||||||
|             .service(web::resource("/").route(web::get().to(index))) |             .service(web::resource("/").route(web::get().to(index))) | ||||||
|  |             .service(web::resource("/media/{path}").route(web::get().to(routes::media))) | ||||||
|             .service( |             .service( | ||||||
|                 web::resource("/inbox") |                 web::resource("/inbox") | ||||||
|                     .wrap(config.digest_middleware()) |                     .wrap(config.digest_middleware()) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| use crate::error::MyError; | use crate::error::MyError; | ||||||
| use activitystreams::primitives::XsdAnyUri; | use activitystreams::primitives::XsdAnyUri; | ||||||
| use actix_web::client::Client; | use actix_web::client::Client; | ||||||
|  | use bytes::Bytes; | ||||||
| use http_signature_normalization_actix::prelude::*; | use http_signature_normalization_actix::prelude::*; | ||||||
| use log::error; | use log::error; | ||||||
| use rsa::{hash::Hashes, padding::PaddingScheme, RSAPrivateKey}; | use rsa::{hash::Hashes, padding::PaddingScheme, RSAPrivateKey}; | ||||||
|  | @ -63,6 +64,55 @@ impl Requests { | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pub async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), MyError> { | ||||||
|  |         let mut res = self | ||||||
|  |             .client | ||||||
|  |             .get(url) | ||||||
|  |             .header("Accept", "application/activity+json") | ||||||
|  |             .header("User-Agent", self.user_agent.as_str()) | ||||||
|  |             .signature(&self.config, &self.key_id, |signing_string| { | ||||||
|  |                 self.sign(signing_string) | ||||||
|  |             })? | ||||||
|  |             .send() | ||||||
|  |             .await | ||||||
|  |             .map_err(|e| { | ||||||
|  |                 error!("Couldn't send request to {}, {}", url, e); | ||||||
|  |                 MyError::SendRequest | ||||||
|  |             })?; | ||||||
|  | 
 | ||||||
|  |         let content_type = if let Some(content_type) = res.headers().get("content-type") { | ||||||
|  |             if let Ok(s) = content_type.to_str() { | ||||||
|  |                 s.to_owned() | ||||||
|  |             } else { | ||||||
|  |                 return Err(MyError::ContentType); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             return Err(MyError::ContentType); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if !res.status().is_success() { | ||||||
|  |             if let Ok(bytes) = res.body().await { | ||||||
|  |                 if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { | ||||||
|  |                     if !s.is_empty() { | ||||||
|  |                         error!("Response, {}", s); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return Err(MyError::Status(res.status())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let bytes = match res.body().limit(1024 * 1024 * 4).await { | ||||||
|  |             Err(e) => { | ||||||
|  |                 error!("Coudn't fetch json from {}, {}", url, e); | ||||||
|  |                 return Err(MyError::ReceiveResponse); | ||||||
|  |             } | ||||||
|  |             Ok(bytes) => bytes, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Ok((content_type, bytes)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     pub async fn deliver<T>(&self, inbox: XsdAnyUri, item: &T) -> Result<(), MyError> |     pub async fn deliver<T>(&self, inbox: XsdAnyUri, item: &T) -> Result<(), MyError> | ||||||
|     where |     where | ||||||
|         T: serde::ser::Serialize, |         T: serde::ser::Serialize, | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ pub async fn route( | ||||||
|     config: web::Data<Config>, |     config: web::Data<Config>, | ||||||
| ) -> Result<HttpResponse, MyError> { | ) -> Result<HttpResponse, MyError> { | ||||||
|     let nodes = state.node_cache().nodes().await; |     let nodes = state.node_cache().nodes().await; | ||||||
| 
 |  | ||||||
|     let mut buf = BufWriter::new(Vec::new()); |     let mut buf = BufWriter::new(Vec::new()); | ||||||
| 
 | 
 | ||||||
|     crate::templates::index(&mut buf, &nodes, &config)?; |     crate::templates::index(&mut buf, &nodes, &config)?; | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								src/routes/media.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/routes/media.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | use crate::{data::Media, error::MyError, requests::Requests}; | ||||||
|  | use actix_web::{web, HttpResponse}; | ||||||
|  | use uuid::Uuid; | ||||||
|  | 
 | ||||||
|  | pub async fn route( | ||||||
|  |     media: web::Data<Media>, | ||||||
|  |     requests: web::Data<Requests>, | ||||||
|  |     uuid: web::Path<Uuid>, | ||||||
|  | ) -> Result<HttpResponse, MyError> { | ||||||
|  |     let uuid = uuid.into_inner(); | ||||||
|  | 
 | ||||||
|  |     if let Some((content_type, bytes)) = media.get_bytes(uuid).await { | ||||||
|  |         return Ok(HttpResponse::Ok().content_type(content_type).body(bytes)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if let Some(url) = media.get_url(uuid).await { | ||||||
|  |         let (content_type, bytes) = requests.fetch_bytes(url.as_str()).await?; | ||||||
|  | 
 | ||||||
|  |         media | ||||||
|  |             .store_bytes(uuid, content_type.clone(), bytes.clone()) | ||||||
|  |             .await; | ||||||
|  | 
 | ||||||
|  |         return Ok(HttpResponse::Ok().content_type(content_type).body(bytes)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Ok(HttpResponse::NotFound().finish()) | ||||||
|  | } | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| mod actor; | mod actor; | ||||||
| mod inbox; | mod inbox; | ||||||
| mod index; | mod index; | ||||||
|  | mod media; | ||||||
| mod nodeinfo; | mod nodeinfo; | ||||||
| mod statics; | mod statics; | ||||||
| 
 | 
 | ||||||
|  | @ -8,6 +9,7 @@ pub use self::{ | ||||||
|     actor::route as actor, |     actor::route as actor, | ||||||
|     inbox::route as inbox, |     inbox::route as inbox, | ||||||
|     index::route as index, |     index::route as index, | ||||||
|  |     media::route as media, | ||||||
|     nodeinfo::{route as nodeinfo, well_known as nodeinfo_meta}, |     nodeinfo::{route as nodeinfo, well_known as nodeinfo_meta}, | ||||||
|     statics::route as statics, |     statics::route as statics, | ||||||
| }; | }; | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								templates/admin.rs.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								templates/admin.rs.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | @use crate::data::Contact; | ||||||
|  | 
 | ||||||
|  | @(contact: &Contact) | ||||||
|  | 
 | ||||||
|  | <div class="admin"> | ||||||
|  |     <div class="left"> | ||||||
|  |         <figure class="avatar"> | ||||||
|  |             <img src="@contact.avatar" alt="@contact.display_name's avatar"> | ||||||
|  |         </figure> | ||||||
|  |     </div> | ||||||
|  |     <div class="right"> | ||||||
|  |         <p class="display-name"><a href="@contact.url">@contact.display_name</a></p> | ||||||
|  |         <p class="username">@@@contact.username</p> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
| @use crate::{config::{Config, UrlKind}, templates::statics::index_css, data::Node}; | @use crate::{ | ||||||
|  |     config::{Config, UrlKind}, | ||||||
|  |     data::Node, | ||||||
|  |     templates::{info, instance, statics::index_css}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| @(nodes: &[Node], config: &Config) | @(nodes: &[Node], config: &Config) | ||||||
| 
 | 
 | ||||||
|  | @ -11,28 +15,29 @@ | ||||||
|     </head> |     </head> | ||||||
|     <body> |     <body> | ||||||
|         <header> |         <header> | ||||||
|             <h1>Welcome to @config.software_name() on @config.hostname()</h1> |             <div class="header-text"> | ||||||
|  |                 <h1>@config.software_name()</h1> | ||||||
|  |                 <p>on @config.hostname()</p> | ||||||
|  |             </div> | ||||||
|         </header> |         </header> | ||||||
|         <main> |         <main> | ||||||
|             <section> |             <section> | ||||||
|                 <h3>Connected Servers:</h3> |                 <h3>Connected Servers</h3> | ||||||
|                 @if nodes.is_empty() { |                 @if nodes.is_empty() { | ||||||
|                     <p>There are no connected servers at this time.</p> |                     <p>There are no connected servers at this time.</p> | ||||||
|                 } else { |                 } else { | ||||||
|                     <ul> |                     <ul> | ||||||
|                         @for node in nodes { |                         @for node in nodes { | ||||||
|                             @if let Some(domain) = node.base.as_url().domain() { |                             @if let Some(inst) = node.instance.as_ref() { | ||||||
|                                 <li> |                                 <li> | ||||||
|                                     <p><a href="@node.base">@domain</a></p> |                                     @:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(), &node.base) | ||||||
|                                     @if let Some(info) = node.info.as_ref() { |  | ||||||
|                                         <p>Running @info.software version @info.version</p> |  | ||||||
|                                         @if info.reg { |  | ||||||
|                                             <p>Registration is open</p> |  | ||||||
|                                         } else { |  | ||||||
|                                             <p>Registration is closed</p> |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                 </li> |                                 </li> | ||||||
|  |                             } else { | ||||||
|  |                                 @if let Some(inf) = node.info.as_ref() { | ||||||
|  |                                     <li> | ||||||
|  |                                         @:info(&inf, &node.base) | ||||||
|  |                                     </li> | ||||||
|  |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     </ul> |                     </ul> | ||||||
|  | @ -40,27 +45,29 @@ | ||||||
|             </section> |             </section> | ||||||
|             <section> |             <section> | ||||||
|                 <h3>Joining</h3> |                 <h3>Joining</h3> | ||||||
|                 <p> |                 <article class="joining"> | ||||||
|                     If you are the admin of a server that supports activitypub relays, you can add |                     <p> | ||||||
|                     this relay to your server. |                         If you are the admin of a server that supports activitypub relays, you can add | ||||||
|                 </p> |                         this relay to your server. | ||||||
|                 <h4>Mastodon</h4> |                     </p> | ||||||
|                 <p> |                     <h4>Mastodon</h4> | ||||||
|                     Mastodon admins can add this relay by adding |                     <p> | ||||||
|                     <pre>@config.generate_url(UrlKind::Inbox)</pre> in their relay settings. |                         Mastodon admins can add this relay by adding | ||||||
|                 </p> |                         <pre>@config.generate_url(UrlKind::Inbox)</pre> in their relay settings. | ||||||
|                 <h4>Pleroma</h4> |                     </p> | ||||||
|                 <p> |                     <h4>Pleroma</h4> | ||||||
|                     Pleroma admins can add this relay by adding |                     <p> | ||||||
|                     <pre>@config.generate_url(UrlKind::Actor)</pre> |                         Pleroma admins can add this relay by adding | ||||||
|                     to their relay settings (I don't actually know how pleroma handles adding |                         <pre>@config.generate_url(UrlKind::Actor)</pre> | ||||||
|                     relays, is it still a mix command?). |                         to their relay settings (I don't actually know how pleroma handles adding | ||||||
|                 </p> |                         relays, is it still a mix command?). | ||||||
|                 <h4>Others</h4> |                     </p> | ||||||
|                 <p> |                     <h4>Others</h4> | ||||||
|                     Consult the documentation for your server. It's likely that it follows either |                     <p> | ||||||
|                     Mastodon or Pleroma's relay formatting. |                         Consult the documentation for your server. It's likely that it follows either | ||||||
|                 </p> |                         Mastodon or Pleroma's relay formatting. | ||||||
|  |                     </p> | ||||||
|  |                 </article> | ||||||
|             </section> |             </section> | ||||||
|         </main> |         </main> | ||||||
|         <footer> |         <footer> | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								templates/info.rs.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								templates/info.rs.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | @use crate::data::Info; | ||||||
|  | @use activitystreams::primitives::XsdAnyUri; | ||||||
|  | 
 | ||||||
|  | @(info: &Info, base: &XsdAnyUri) | ||||||
|  | 
 | ||||||
|  | <article class="info"> | ||||||
|  |     @if let Some(domain) = base.as_url().domain() { | ||||||
|  |         <h4 class="padded"><a href="@base">@domain</a></h4> | ||||||
|  |     } | ||||||
|  |     <p class="padded"> | ||||||
|  |         Running @info.software, version @info.version. | ||||||
|  |         @if info.reg { | ||||||
|  |             Registration is open | ||||||
|  |         } | ||||||
|  |     </p> | ||||||
|  | </article> | ||||||
							
								
								
									
										32
									
								
								templates/instance.rs.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								templates/instance.rs.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | @use crate::{data::{Contact, Instance}, templates::admin}; | ||||||
|  | @use activitystreams::primitives::XsdAnyUri; | ||||||
|  | 
 | ||||||
|  | @(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &XsdAnyUri) | ||||||
|  | 
 | ||||||
|  | <article class="instance"> | ||||||
|  |     <h4 class="padded"><a href="@base">@instance.title</a></h4> | ||||||
|  |     <p class="padded"> | ||||||
|  |         @if let Some(software) = software { | ||||||
|  |             Running @software, version @instance.version. | ||||||
|  |         } | ||||||
|  |         @if instance.reg { | ||||||
|  |             <br>Registration is open. | ||||||
|  |             @if instance.requires_approval { | ||||||
|  |                 Accounts must be approved by an admin. | ||||||
|  |             } | ||||||
|  |         } else{ | ||||||
|  |             Registration is closed | ||||||
|  |         } | ||||||
|  |     </p> | ||||||
|  |     <div class="instance-info"> | ||||||
|  |         <h4 class="instance-description">Description:</h4> | ||||||
|  |         <div class="description"> | ||||||
|  |             <div class="please-stay"> | ||||||
|  |                 @Html(&instance.description) | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         @if let Some(contact) = contact { | ||||||
|  |             @:admin(contact) | ||||||
|  |         } | ||||||
|  |     </div> | ||||||
|  | </article> | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue