Merge branch 'main' into better-caching
This commit is contained in:
		
						commit
						d44c034f6a
					
				| @ -10,7 +10,7 @@ executors: | ||||
|         type: boolean | ||||
|         default: false | ||||
|     docker: | ||||
|     - image: plumeorg/plume-buildenv:v0.0.9 | ||||
|     - image: plumeorg/plume-buildenv:v0.2.0 | ||||
|     - image: <<#parameters.postgres>>circleci/postgres:9.6-alpine<</parameters.postgres>><<^parameters.postgres>>alpine:latest<</parameters.postgres>> | ||||
|       environment: | ||||
|         POSTGRES_USER: postgres | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| FROM debian:stretch-20190326 | ||||
| FROM debian:buster-20201117 | ||||
| ENV PATH="/root/.cargo/bin:${PATH}" | ||||
| 
 | ||||
| #install native/circleci/build dependancies | ||||
|  | ||||
| @ -45,3 +45,12 @@ ROCKET_ADDRESS=127.0.0.1 | ||||
| #PLUME_LOGO_192=icons/trwnh/paragraphs/plumeParagraphs192.png | ||||
| #PLUME_LOGO_256=icons/trwnh/paragraphs/plumeParagraphs256.png | ||||
| #PLUME_LOGO_512=icons/trwnh/paragraphs/plumeParagraphs512.png | ||||
| 
 | ||||
| ## LDAP CONFIG ## | ||||
| # the object that will be bound is "${USER_NAME_ATTR}=${username},${BASE_DN}" | ||||
| #LDAP_ADDR=ldap://127.0.0.1:1389 | ||||
| #LDAP_BASE_DN="ou=users,dc=your-org,dc=eu" | ||||
| #LDAP_USER_NAME_ATTR=cn | ||||
| #LDAP_USER_MAIL_ATTR=mail | ||||
| #LDAP_TLS=false | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -18,3 +18,4 @@ tags.* | ||||
| search_index | ||||
| .buildconfig | ||||
| __pycache__ | ||||
| .vscode/ | ||||
|  | ||||
							
								
								
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | ||||
| # Changelog | ||||
| 
 | ||||
| <!-- next-header --> | ||||
| 
 | ||||
| ## [Unreleased] - No release date | ||||
| 
 | ||||
| ## [0.5.0] - 2020-06-21 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Email blocklisting (#718) | ||||
| - Syntax highlighting (#691) | ||||
| - Persian localization (#782) | ||||
| - Switchable tokenizer - enables Japanese full-text search (#776) | ||||
| - Make database connections configurable by environment variables (#768) | ||||
| 
 | ||||
| ### Changed | ||||
| 
 | ||||
| - Display likes and boost on post cards (#744) | ||||
| - Rust 2018 (#726) | ||||
| - Bump to LLVM to 9.0.0 to fix ARM builds (#737) | ||||
| - Remove dependency on runtime-fmt (#773) | ||||
| - Drop the -alpha suffix in release names, it is implied that Plume is not stable yet because of the 0 major version (Plume 1.0.0 will be the first stable release). | ||||
| 
 | ||||
| ### Fixed | ||||
| 
 | ||||
| - Fix parsing of mentions inside a Markdown code block (be430c6) | ||||
| - Fix RSS issues (#720) | ||||
| - Fix Atom feed (#764) | ||||
| - Fix default theme (#746) | ||||
| - Fix shown password on remote interact pages (#741) | ||||
| - Allow unicode hashtags (#757) | ||||
| - Fix French grammar for for 0 (#760) | ||||
| - Don't show boosts and likes for "all" and "local" in timelines (#781) | ||||
| - Fix liking and boosting posts on remote instances (#762) | ||||
| 
 | ||||
| ## [0.4.0] - 2019-12-23 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Add support for generic timeline (#525) | ||||
| - Federate user deletion (#551) | ||||
| - import migrations and don't require diesel_cli for admins (#555) | ||||
| - Cache local instance (#572) | ||||
| - Initial RTL support #575 (#577) | ||||
| - Confirm deletion of blog (#602) | ||||
| - Make a distinction between moderators and admins (#619) | ||||
| - Theming (#624) | ||||
| - Add clap to plume in order to print help and version (#631) | ||||
| - Add Snapcraft metadata and install/maintenance hooks (#666) | ||||
| - Add environmental variable to control path of media (#683) | ||||
| - Add autosaving to the editor (#688) | ||||
| - CI: Upload artifacts to pull request deploy environment (#539) | ||||
| - CI: Upload artifact of wasm binary (#571) | ||||
| 
 | ||||
| ### Changed | ||||
| 
 | ||||
| - Update follow_remote.rs.html grammar (#548) | ||||
| - Add some feedback when performing some actions (#552) | ||||
| - Theme update (#553) | ||||
| - Remove the new index lock tantivy uses (#556) | ||||
| - Reduce reqwest timeout to 5s (#557) | ||||
| - Improve notification management (#561) | ||||
| - Fix occurrences of 'have been' to 'has been' (#578) + Direct follow-up to #578 (#603) | ||||
| - Store password reset requests in database (#610) | ||||
| - Use futures and tokio to send activities (#620) | ||||
| - Don't ignore dotenv errors (#630) | ||||
| - Replace the input! macro with an Input builder (#646) | ||||
| - Update default license (#659) | ||||
| - Paginate the outbox responses. Fixes #669 (#681) | ||||
| - Use the "classic" editor by default (#697) | ||||
| - Fix issue #705 (#708) | ||||
| - Make comments in styleshhets a bit clearer (#545) | ||||
| - Rewrite circleci config (#558) | ||||
| - Use openssl instead of sha256sum for build.rs (#568) | ||||
| - Update dependencies (#574) | ||||
| - Refactor code to use Shrinkwraprs and diesel-derive-newtype (#598) | ||||
| - Add enum containing all successful route returns (#614) | ||||
| - Update dependencies which depended on nix -- fixes arm32 builds (#615) | ||||
| - Update some documents (#616) | ||||
| - Update dependencies (#643) | ||||
| - Make the comment syntax consistent across all CSS (#487) | ||||
| 
 | ||||
| ### Fixed | ||||
| 
 | ||||
| - Remove r (#535) | ||||
| - Fix certain improper rendering of forms (#560) | ||||
| - make hashtags work in profile summary (#562) | ||||
| - Fix some federation issue (#573) | ||||
| - Prevent comment form submit button distortion on iOS (#592) | ||||
| - Update textarea overflow to scroll (#609) | ||||
| - Fix arm builds (#612) | ||||
| - Fix theme caching (#647) | ||||
| - Fix issue #642, frontend not in English if the user language does not exist (#648) | ||||
| - Don't index drafts (#656) | ||||
| - Fill entirely user on creation (#657) | ||||
| - Delete notification on user deletion (#658) | ||||
| - Order media so that latest added are top (#660) | ||||
| - Fix logo URL (#664) | ||||
| - Snap: Ensure cargo-web doesn't erroneously adopt our workspace. (#667) | ||||
| - Snap: Another fix for building (#668) | ||||
| - Snap: Fix build for non-Tier-1 Rust platforms (#672) | ||||
| - Don't split sentences for translations (#677) | ||||
| - Escape href quotation marks (#678) | ||||
| - Re-add empty strings in translation (#682) | ||||
| - Make the search index creation during migration respect SEARCH_INDEX (#689) | ||||
| - Fix the navigation menu not opening on touch (#690) | ||||
| - Make search items optional (#693) | ||||
| - Various snap fixes (#698) | ||||
| - Fix #637 : Markdown footnotes (#700) | ||||
| - Fix lettre (#706) | ||||
| - CI: Fix Crowdin upload (#576) | ||||
| 
 | ||||
| ### Removed | ||||
| 
 | ||||
| - Remove the Canapi dependency (#540) | ||||
| - Remove use of Rust in migrations (#704) | ||||
| 
 | ||||
| ## [0.3.0] - 2019-04-19 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Cover for articles (#299, #387) | ||||
| - Password reset (#448) | ||||
| - New editor (#293, #458, #482, #483, #486, #530) | ||||
| - Search (#324, #375, #445) | ||||
| - Edit blogs (#460, #494, #497) | ||||
| - Hashtags in articles (#283, #295) | ||||
| - API endpoints (#245, #285, #307) | ||||
| - A bunch of new translations! (#479, #501, #506, #510, #512, #514) | ||||
| 
 | ||||
| ### Changed | ||||
| 
 | ||||
| - Federation improvements (#216, #217, #357, #364, #399, #443, #446, #455, #502, #519) | ||||
| - Improved build process (#281, #374, #392, #402, #489, #498, #503, #511, #513, #515, #528) | ||||
| 
 | ||||
| ### Fixes | ||||
| 
 | ||||
| - UI usability fixes (#370, #386, #401, #417, #418, #444, #452, #480, #516, #518, #522, #532) | ||||
| 
 | ||||
| ## [0.2.0] - 2018-09-12 | ||||
| 
 | ||||
| ### Added | ||||
| 
 | ||||
| - Article publishing, or save as a draft | ||||
| - Like, or boost an article | ||||
| - Basic Markdown editor | ||||
| - Federated commenting system | ||||
| - User account creation | ||||
| - Limited federation on other platforms and subscribing to users | ||||
| - Ability to create multiple blogs | ||||
| 
 | ||||
| <!-- next-url --> | ||||
| [Unreleased]: https://github.com/Plume-org/Plume/compare/0.5.0...HEAD | ||||
| [0.5.0]: https://github.com/Plume-org/Plume/compare/0.4.0-alpha-4...0.5.0 | ||||
| [0.4.0]: https://github.com/Plume-org/Plume/compare/0.3.0-alpha-2...0.4.0-alpha-4 | ||||
| [0.3.0]: https://github.com/Plume-org/Plume/compare/0.2.0-alpha-1...0.3.0-alpha-2 | ||||
| [0.2.0]: https://github.com/Plume-org/Plume/releases/tag/0.2.0-alpha-1 | ||||
							
								
								
									
										1952
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1952
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,7 +1,7 @@ | ||||
| [package] | ||||
| authors = ["Plume contributors"] | ||||
| name = "plume" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| repository = "https://github.com/Plume-org/Plume" | ||||
| edition = "2018" | ||||
| 
 | ||||
| @ -20,8 +20,8 @@ heck = "0.3.0" | ||||
| lettre = "0.9.2" | ||||
| lettre_email = "0.9.2" | ||||
| num_cpus = "1.10" | ||||
| rocket = "0.4.2" | ||||
| rocket_contrib = { version = "0.4.2", features = ["json"] } | ||||
| rocket = "0.4.5" | ||||
| rocket_contrib = { version = "0.4.5", features = ["json"] } | ||||
| rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" } | ||||
| rpassword = "4.0" | ||||
| scheduled-thread-pool = "0.2.2" | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| FROM rust:1-stretch as builder | ||||
| FROM rust:1-buster as builder | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     ca-certificates \ | ||||
| @ -28,7 +28,7 @@ RUN cargo install --path ./ --force --no-default-features --features postgres | ||||
| RUN cargo install --path plume-cli --force --no-default-features --features postgres | ||||
| RUN cargo clean | ||||
| 
 | ||||
| FROM debian:stretch-slim | ||||
| FROM debian:buster-slim | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     ca-certificates \ | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| FROM rust:1-stretch | ||||
| FROM rust:1-buster | ||||
| 
 | ||||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||||
|     ca-certificates \ | ||||
|  | ||||
| @ -40,7 +40,7 @@ main header.article { | ||||
| 
 | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     justify-content: end; | ||||
|     justify-content: flex-end; | ||||
| 
 | ||||
|     h1, .article-info { | ||||
|       text-align: center; | ||||
| @ -490,3 +490,30 @@ input:checked ~ .cw-container > .cw-text { | ||||
|     display: inline; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Small screens | ||||
| @media screen and (max-width: 600px) { | ||||
|   #plume-editor header { | ||||
|     flex-direction: column-reverse; | ||||
| 
 | ||||
|     button { | ||||
|       flex: 0 0 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .popup { | ||||
|     top: 10vh; | ||||
|     bottom: 10vh; | ||||
|     left: 1vw; | ||||
|     right: 1vw; | ||||
|   } | ||||
| 
 | ||||
|   main article { | ||||
|     margin: 2.5em .5em; | ||||
|     max-width: none; | ||||
|   } | ||||
| 
 | ||||
|   main .article-meta > *, main .article-meta .comments, main .article-meta > .banner > * { | ||||
|     margin: 0 5%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -490,6 +490,10 @@ figure { | ||||
| 
 | ||||
| /// Small screens | ||||
| @media screen and (max-width: 600px) { | ||||
|   body > main > *, .h-feed > * { | ||||
|     margin: 1em; | ||||
|   } | ||||
| 
 | ||||
|   main .article-meta { | ||||
|     > *, .comments { | ||||
|       margin: 0 5%; | ||||
| @ -535,7 +539,7 @@ figure { | ||||
|     margin: 0; | ||||
| 
 | ||||
|     & > * { | ||||
|       max-width: 100%; | ||||
|       max-width: 100% !important; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -205,6 +205,7 @@ body > header { | ||||
|    		position: absolute; | ||||
|    		left: 50%; | ||||
|    		transform: translate(-50%, 0); | ||||
|    		transform: translateZ(0); | ||||
|    		opacity: 0; | ||||
|    		font-size: 0.9em; | ||||
|    		white-space: nowrap; | ||||
| @ -221,3 +222,93 @@ body > header { | ||||
|     } | ||||
|  	} | ||||
| } | ||||
| 
 | ||||
| // Small screens | ||||
| @media screen and (max-width: 600px) { | ||||
|   @keyframes menuOpening { | ||||
|     from { | ||||
|       transform: scaleX(0); | ||||
|       transform-origin: left; | ||||
|       opacity: 0; | ||||
|     } | ||||
|     to { | ||||
|       transform: scaleX(1); | ||||
|       transform-origin: left; | ||||
|       opacity: 1; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   body > header { | ||||
|     flex-direction: column; | ||||
| 
 | ||||
|     nav#menu { | ||||
|       display: inline-flex; | ||||
|       z-index: 21; | ||||
|     } | ||||
| 
 | ||||
|     #content { | ||||
|       display: none; | ||||
|       appearance: none; | ||||
|       text-align: center; | ||||
|       z-index: 20; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   body > header:focus-within #content, #content.show { | ||||
|     position: fixed; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     justify-content: flex-start; | ||||
| 
 | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     box-sizing: border-box; | ||||
| 
 | ||||
|     animation: 0.2s menuOpening; | ||||
| 
 | ||||
|     &::before { | ||||
|       content: ""; | ||||
|       position: absolute; | ||||
|       transform: skewX(-10deg); | ||||
|       top: 0; | ||||
|       left: -20%; | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
| 
 | ||||
|       z-index: -10; | ||||
| 
 | ||||
|       background: $primary; | ||||
|     } | ||||
| 
 | ||||
|     > nav { | ||||
|       flex-direction: column; | ||||
|       align-items: flex-start; | ||||
| 
 | ||||
|       a { | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         align-items: center; | ||||
|         margin: 0; | ||||
|         padding: 1rem 1.5rem; | ||||
|         color: $background; | ||||
|         font-size: 1.4em; | ||||
|         font-weight: 300; | ||||
| 
 | ||||
|         &.title { font-size: 1.8em; } | ||||
| 
 | ||||
|         > *:first-child { width: 3rem; } | ||||
|         > img:first-child { height: 3rem; } | ||||
|         > *:last-child { margin-left: 1rem; } | ||||
|         > nav hr { | ||||
|           display: block; | ||||
|           margin: 0; | ||||
|           width: 100%; | ||||
|           border: solid $background 0.1rem; | ||||
|         } | ||||
|         .mobile-label { display: initial; } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| "project_identifier": "plume" | ||||
| "api_key_env": CROWDIN_API_KEY | ||||
| "project_id": 352097 | ||||
| "api_token_env": "CROWDIN_API_KEY" | ||||
| preserve_hierarchy: true | ||||
| files: | ||||
|   - source: /po/plume/plume.pot | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-api" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Plume contributors"] | ||||
| edition = "2018" | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								plume-api/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-api/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-cli" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Plume contributors"] | ||||
| edition = "2018" | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								plume-cli/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-cli/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -132,7 +132,7 @@ fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) { | ||||
|         role, | ||||
|         &bio, | ||||
|         email, | ||||
|         User::hash_pass(&password).expect("Couldn't hash password"), | ||||
|         Some(User::hash_pass(&password).expect("Couldn't hash password")), | ||||
|     ) | ||||
|     .expect("Couldn't save new user"); | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-common" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Plume contributors"] | ||||
| edition = "2018" | ||||
| 
 | ||||
| @ -14,7 +14,7 @@ heck = "0.3.0" | ||||
| hex = "0.3" | ||||
| hyper = "0.12.33" | ||||
| openssl = "0.10.22" | ||||
| rocket = "0.4.0" | ||||
| rocket = "0.4.5" | ||||
| reqwest = "0.9" | ||||
| serde = "1.0" | ||||
| serde_derive = "1.0" | ||||
|  | ||||
							
								
								
									
										1
									
								
								plume-common/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-common/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -294,8 +294,7 @@ pub fn md_to_html<'a>( | ||||
|                                     } | ||||
|                                     let hashtag = text_acc; | ||||
|                                     let link = Tag::Link( | ||||
|                                         format!("{}tag/{}", base_url, &hashtag.to_camel_case()) | ||||
|                                             .into(), | ||||
|                                         format!("{}tag/{}", base_url, &hashtag).into(), | ||||
|                                         hashtag.to_owned().into(), | ||||
|                                     ); | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-front" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Plume contributors"] | ||||
| edition = "2018" | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								plume-front/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-front/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -269,7 +269,13 @@ pub fn init() -> Result<(), EditorError> { | ||||
|                 let editor_button = document().create_element("a")?; | ||||
|                 js! { @{&editor_button}.href = "#"; } | ||||
|                 editor_button.add_event_listener(|_: ClickEvent| { | ||||
|                     window().local_storage().remove("basic-editor"); | ||||
|                     if window() | ||||
|                         .local_storage() | ||||
|                         .insert("basic-editor", "false") | ||||
|                         .is_err() | ||||
|                     { | ||||
|                         console!(log, "Failed to write into local storage"); | ||||
|                     } | ||||
|                     window().history().go(0).ok(); // refresh
 | ||||
|                 }); | ||||
|                 editor_button.append_child( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-macro" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Trinity Pointard <trinity.pointard@insa-rennes.fr>"] | ||||
| edition = "2018" | ||||
| description = "Plume procedural macros" | ||||
|  | ||||
							
								
								
									
										1
									
								
								plume-macro/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-macro/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -1,6 +1,6 @@ | ||||
| [package] | ||||
| name = "plume-models" | ||||
| version = "0.4.0" | ||||
| version = "0.5.0" | ||||
| authors = ["Plume contributors"] | ||||
| edition = "2018" | ||||
| 
 | ||||
| @ -13,9 +13,10 @@ guid-create = "0.1" | ||||
| heck = "0.3.0" | ||||
| itertools = "0.8.0" | ||||
| lazy_static = "1.0" | ||||
| ldap3 = "0.7.1" | ||||
| migrations_internals= "1.4.0" | ||||
| openssl = "0.10.22" | ||||
| rocket = "0.4.0" | ||||
| rocket = "0.4.5" | ||||
| rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" } | ||||
| reqwest = "0.9" | ||||
| scheduled-thread-pool = "0.2.2" | ||||
| @ -30,7 +31,7 @@ whatlang = "0.7.1" | ||||
| shrinkwraprs = "0.2.1" | ||||
| diesel-derive-newtype = "0.1.2" | ||||
| glob = "0.3.0" | ||||
| lindera-tantivy = { version = "0.1.2", optional = true } | ||||
| lindera-tantivy = { version = "0.1.3", optional = true } | ||||
| 
 | ||||
| [dependencies.chrono] | ||||
| features = ["serde"] | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								plume-models/plume.db-journal
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								plume-models/plume.db-journal
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								plume-models/plume_tests.sqlite-journal
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								plume-models/plume_tests.sqlite-journal
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								plume-models/release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								plume-models/release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pre-release-replacements = [] | ||||
| @ -20,6 +20,7 @@ pub struct Config { | ||||
|     pub logo: LogoConfig, | ||||
|     pub default_theme: String, | ||||
|     pub media_directory: String, | ||||
|     pub ldap: Option<LdapConfig>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| @ -240,6 +241,42 @@ impl SearchTokenizerConfig { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct LdapConfig { | ||||
|     pub addr: String, | ||||
|     pub base_dn: String, | ||||
|     pub tls: bool, | ||||
|     pub user_name_attr: String, | ||||
|     pub mail_attr: String, | ||||
| } | ||||
| 
 | ||||
| fn get_ldap_config() -> Option<LdapConfig> { | ||||
|     let addr = var("LDAP_ADDR").ok(); | ||||
|     let base_dn = var("LDAP_BASE_DN").ok(); | ||||
|     match (addr, base_dn) { | ||||
|         (Some(addr), Some(base_dn)) => { | ||||
|             let tls = var("LDAP_TLS").unwrap_or_else(|_| "false".to_owned()); | ||||
|             let tls = match tls.as_ref() { | ||||
|                 "1" | "true" | "TRUE" => true, | ||||
|                 "0" | "false" | "FALSE" => false, | ||||
|                 _ => panic!("Invalid LDAP configuration : tls"), | ||||
|             }; | ||||
|             let user_name_attr = var("LDAP_USER_NAME_ATTR").unwrap_or_else(|_| "cn".to_owned()); | ||||
|             let mail_attr = var("LDAP_USER_MAIL_ATTR").unwrap_or_else(|_| "mail".to_owned()); | ||||
|             Some(LdapConfig { | ||||
|                 addr, | ||||
|                 base_dn, | ||||
|                 tls, | ||||
|                 user_name_attr, | ||||
|                 mail_attr, | ||||
|             }) | ||||
|         } | ||||
|         (None, None) => None, | ||||
|         (_, _) => { | ||||
|             panic!("Invalid LDAP configuration : both LDAP_ADDR and LDAP_BASE_DN must be set") | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| lazy_static! { | ||||
|     pub static ref CONFIG: Config = Config { | ||||
|         base_url: var("BASE_URL").unwrap_or_else(|_| format!( | ||||
| @ -267,5 +304,6 @@ lazy_static! { | ||||
|         default_theme: var("DEFAULT_THEME").unwrap_or_else(|_| "default-light".to_owned()), | ||||
|         media_directory: var("MEDIA_UPLOAD_DIRECTORY") | ||||
|             .unwrap_or_else(|_| "static/media".to_owned()), | ||||
|         ldap: get_ldap_config(), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @ -11,7 +11,7 @@ use activitypub::{ | ||||
| }; | ||||
| use chrono::{NaiveDateTime, TimeZone, Utc}; | ||||
| use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; | ||||
| use heck::{CamelCase, KebabCase}; | ||||
| use heck::KebabCase; | ||||
| use plume_common::{ | ||||
|     activity_pub::{ | ||||
|         inbox::{AsObject, FromId}, | ||||
| @ -622,7 +622,6 @@ impl FromId<PlumeRocket> for Post { | ||||
|         let mut hashtags = md_to_html(&post.source, None, false, None) | ||||
|             .2 | ||||
|             .into_iter() | ||||
|             .map(|s| s.to_camel_case()) | ||||
|             .collect::<HashSet<_>>(); | ||||
|         if let Some(serde_json::Value::Array(tags)) = article.object_props.tag { | ||||
|             for tag in tags { | ||||
| @ -762,7 +761,6 @@ impl AsObject<User, Update, &PlumeRocket> for PostUpdate { | ||||
|         let mut txt_hashtags = md_to_html(&post.source, None, false, None) | ||||
|             .2 | ||||
|             .into_iter() | ||||
|             .map(|s| s.to_camel_case()) | ||||
|             .collect::<HashSet<_>>(); | ||||
|         if let Some(serde_json::Value::Array(mention_tags)) = self.tags { | ||||
|             let mut mentions = vec![]; | ||||
|  | ||||
| @ -5,10 +5,10 @@ use crate::{ | ||||
| use chrono::Datelike; | ||||
| use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; | ||||
| use itertools::Itertools; | ||||
| use std::{cmp, fs::create_dir_all, path::Path, sync::Mutex}; | ||||
| use std::{cmp, fs::create_dir_all, io, path::Path, sync::Mutex}; | ||||
| use tantivy::{ | ||||
|     collector::TopDocs, directory::MmapDirectory, schema::*, Index, IndexReader, IndexWriter, | ||||
|     ReloadPolicy, Term, | ||||
|     ReloadPolicy, TantivyError, Term, | ||||
| }; | ||||
| use whatlang::{detect as detect_lang, Lang}; | ||||
| 
 | ||||
| @ -18,6 +18,7 @@ pub enum SearcherError { | ||||
|     WriteLockAcquisitionError, | ||||
|     IndexOpeningError, | ||||
|     IndexEditionError, | ||||
|     InvalidIndexDataError, | ||||
| } | ||||
| 
 | ||||
| pub struct Searcher { | ||||
| @ -135,7 +136,19 @@ impl Searcher { | ||||
|                 .reader_builder() | ||||
|                 .reload_policy(ReloadPolicy::Manual) | ||||
|                 .try_into() | ||||
|                 .map_err(|_| SearcherError::IndexCreationError)?, | ||||
|                 .map_err(|e| { | ||||
|                     if let TantivyError::IOError(err) = e { | ||||
|                         let err: io::Error = err.into(); | ||||
|                         if err.kind() == io::ErrorKind::InvalidData { | ||||
|                             // Search index was created in older Tantivy format.
 | ||||
|                             SearcherError::InvalidIndexDataError | ||||
|                         } else { | ||||
|                             SearcherError::IndexCreationError | ||||
|                         } | ||||
|                     } else { | ||||
|                         SearcherError::IndexCreationError | ||||
|                     } | ||||
|                 })?, | ||||
|             index, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| use crate::{ | ||||
|     ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, db_conn::DbConn, follows::Follow, | ||||
|     instance::*, medias::Media, notifications::Notification, post_authors::PostAuthor, posts::Post, | ||||
|     safe_string::SafeString, schema::users, search::Searcher, timeline::Timeline, Connection, | ||||
|     Error, PlumeRocket, Result, ITEMS_PER_PAGE, | ||||
|     ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, config::CONFIG, db_conn::DbConn, | ||||
|     follows::Follow, instance::*, medias::Media, notifications::Notification, | ||||
|     post_authors::PostAuthor, posts::Post, safe_string::SafeString, schema::users, | ||||
|     search::Searcher, timeline::Timeline, Connection, Error, PlumeRocket, Result, ITEMS_PER_PAGE, | ||||
| }; | ||||
| use activitypub::{ | ||||
|     activity::Delete, | ||||
| @ -14,6 +14,7 @@ use activitypub::{ | ||||
| use bcrypt; | ||||
| use chrono::{NaiveDateTime, Utc}; | ||||
| use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; | ||||
| use ldap3::{LdapConn, Scope, SearchEntry}; | ||||
| use openssl::{ | ||||
|     hash::MessageDigest, | ||||
|     pkey::{PKey, Private}, | ||||
| @ -292,11 +293,116 @@ impl User { | ||||
|         bcrypt::hash(pass, 10).map_err(Error::from) | ||||
|     } | ||||
| 
 | ||||
|     pub fn auth(&self, pass: &str) -> bool { | ||||
|         self.hashed_password | ||||
|             .clone() | ||||
|             .map(|hashed| bcrypt::verify(pass, hashed.as_ref()).unwrap_or(false)) | ||||
|             .unwrap_or(false) | ||||
|     fn ldap_register(conn: &Connection, name: &str, password: &str) -> Result<User> { | ||||
|         if CONFIG.ldap.is_none() { | ||||
|             return Err(Error::NotFound); | ||||
|         } | ||||
|         let ldap = CONFIG.ldap.as_ref().unwrap(); | ||||
| 
 | ||||
|         let mut ldap_conn = LdapConn::new(&ldap.addr).map_err(|_| Error::NotFound)?; | ||||
|         let ldap_name = format!("{}={},{}", ldap.user_name_attr, name, ldap.base_dn); | ||||
|         let bind = ldap_conn | ||||
|             .simple_bind(&ldap_name, password) | ||||
|             .map_err(|_| Error::NotFound)?; | ||||
| 
 | ||||
|         if bind.success().is_err() { | ||||
|             return Err(Error::NotFound); | ||||
|         } | ||||
| 
 | ||||
|         let search = ldap_conn | ||||
|             .search( | ||||
|                 &ldap_name, | ||||
|                 Scope::Base, | ||||
|                 "(|(objectClass=person)(objectClass=user))", | ||||
|                 vec![&ldap.mail_attr], | ||||
|             ) | ||||
|             .map_err(|_| Error::NotFound)? | ||||
|             .success() | ||||
|             .map_err(|_| Error::NotFound)?; | ||||
|         for entry in search.0 { | ||||
|             let entry = SearchEntry::construct(entry); | ||||
|             let email = entry.attrs.get("mail").and_then(|vec| vec.first()); | ||||
|             if let Some(email) = email { | ||||
|                 let _ = ldap_conn.unbind(); | ||||
|                 return NewUser::new_local( | ||||
|                     conn, | ||||
|                     name.to_owned(), | ||||
|                     name.to_owned(), | ||||
|                     Role::Normal, | ||||
|                     "", | ||||
|                     email.to_owned(), | ||||
|                     None, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         let _ = ldap_conn.unbind(); | ||||
|         Err(Error::NotFound) | ||||
|     } | ||||
| 
 | ||||
|     fn ldap_login(&self, password: &str) -> bool { | ||||
|         if let Some(ldap) = CONFIG.ldap.as_ref() { | ||||
|             let mut conn = if let Ok(conn) = LdapConn::new(&ldap.addr) { | ||||
|                 conn | ||||
|             } else { | ||||
|                 return false; | ||||
|             }; | ||||
|             let name = format!( | ||||
|                 "{}={},{}", | ||||
|                 ldap.user_name_attr, &self.username, ldap.base_dn | ||||
|             ); | ||||
|             if let Ok(bind) = conn.simple_bind(&name, password) { | ||||
|                 bind.success().is_ok() | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } else { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn login(conn: &Connection, ident: &str, password: &str) -> Result<User> { | ||||
|         let local_id = Instance::get_local()?.id; | ||||
|         let user = match User::find_by_email(conn, ident) { | ||||
|             Ok(user) => Ok(user), | ||||
|             _ => User::find_by_name(conn, ident, local_id), | ||||
|         } | ||||
|         .and_then(|u| { | ||||
|             if u.instance_id == local_id { | ||||
|                 Ok(u) | ||||
|             } else { | ||||
|                 Err(Error::NotFound) | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         match user { | ||||
|             Ok(user) if user.hashed_password.is_some() => { | ||||
|                 if bcrypt::verify(password, user.hashed_password.as_ref().unwrap()).unwrap_or(false) | ||||
|                 { | ||||
|                     Ok(user) | ||||
|                 } else { | ||||
|                     Err(Error::NotFound) | ||||
|                 } | ||||
|             } | ||||
|             Ok(user) => { | ||||
|                 if user.ldap_login(password) { | ||||
|                     Ok(user) | ||||
|                 } else { | ||||
|                     Err(Error::NotFound) | ||||
|                 } | ||||
|             } | ||||
|             e => { | ||||
|                 if let Ok(user) = User::ldap_register(conn, ident, password) { | ||||
|                     return Ok(user); | ||||
|                 } | ||||
|                 // if no user was found, and we were unable to auto-register from ldap
 | ||||
|                 // fake-verify a password, and return an error.
 | ||||
|                 let other = User::get(&*conn, 1) | ||||
|                     .expect("No user is registered") | ||||
|                     .hashed_password; | ||||
|                 other.map(|pass| bcrypt::verify(password, &pass)); | ||||
|                 e | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn reset_password(&self, conn: &Connection, pass: &str) -> Result<()> { | ||||
| @ -983,7 +1089,7 @@ impl NewUser { | ||||
|         role: Role, | ||||
|         summary: &str, | ||||
|         email: String, | ||||
|         password: String, | ||||
|         password: Option<String>, | ||||
|     ) -> Result<User> { | ||||
|         let (pub_key, priv_key) = gen_keypair(); | ||||
|         let instance = Instance::get_local()?; | ||||
| @ -1001,7 +1107,7 @@ impl NewUser { | ||||
|                 summary: summary.to_owned(), | ||||
|                 summary_html: SafeString::new(&utils::md_to_html(&summary, None, false, None).0), | ||||
|                 email: Some(email), | ||||
|                 hashed_password: Some(password), | ||||
|                 hashed_password: password, | ||||
|                 instance_id: instance.id, | ||||
|                 public_key: String::from_utf8(pub_key).or(Err(Error::Signature))?, | ||||
|                 private_key: Some(String::from_utf8(priv_key).or(Err(Error::Signature))?), | ||||
| @ -1043,7 +1149,7 @@ pub(crate) mod tests { | ||||
|             Role::Admin, | ||||
|             "Hello there, I'm the admin", | ||||
|             "admin@example.com".to_owned(), | ||||
|             "invalid_admin_password".to_owned(), | ||||
|             Some("invalid_admin_password".to_owned()), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         let user = NewUser::new_local( | ||||
| @ -1053,7 +1159,7 @@ pub(crate) mod tests { | ||||
|             Role::Normal, | ||||
|             "Hello there, I'm no one", | ||||
|             "user@example.com".to_owned(), | ||||
|             "invalid_user_password".to_owned(), | ||||
|             Some("invalid_user_password".to_owned()), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         let other = NewUser::new_local( | ||||
| @ -1063,7 +1169,7 @@ pub(crate) mod tests { | ||||
|             Role::Normal, | ||||
|             "Hello there, I'm someone else", | ||||
|             "other@example.com".to_owned(), | ||||
|             "invalid_other_password".to_owned(), | ||||
|             Some("invalid_other_password".to_owned()), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|         vec![admin, user, other] | ||||
| @ -1082,7 +1188,7 @@ pub(crate) mod tests { | ||||
|                 Role::Normal, | ||||
|                 "Hello I'm a test", | ||||
|                 "test@example.com".to_owned(), | ||||
|                 User::hash_pass("test_password").unwrap(), | ||||
|                 Some(User::hash_pass("test_password").unwrap()), | ||||
|             ) | ||||
|             .unwrap(); | ||||
|             assert_eq!( | ||||
| @ -1165,12 +1271,15 @@ pub(crate) mod tests { | ||||
|                 Role::Normal, | ||||
|                 "Hello I'm a test", | ||||
|                 "test@example.com".to_owned(), | ||||
|                 User::hash_pass("test_password").unwrap(), | ||||
|                 Some(User::hash_pass("test_password").unwrap()), | ||||
|             ) | ||||
|             .unwrap(); | ||||
| 
 | ||||
|             assert!(test_user.auth("test_password")); | ||||
|             assert!(!test_user.auth("other_password")); | ||||
|             assert_eq!( | ||||
|                 User::login(conn, "test", "test_password").unwrap().id, | ||||
|                 test_user.id | ||||
|             ); | ||||
|             assert!(User::login(conn, "test", "other_password").is_err()); | ||||
|             Ok(()) | ||||
|         }); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										17
									
								
								release.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								release.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| # we don't have a crate yet, so | ||||
| disable-publish = true | ||||
| # change when we all have gpg keys | ||||
| sign-commit = false | ||||
| dev-version-ext = 'dev' | ||||
| # update all crates in plume at once: | ||||
| consolidate-commits = true | ||||
| 
 | ||||
| pre-release-hook = ["crowdin", "pull"] | ||||
| 
 | ||||
| pre-release-replacements = [ | ||||
|   {file="CHANGELOG.md", search="Unreleased", replace="[{{version}}]"}, | ||||
|   {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, | ||||
|   {file="CHANGELOG.md", search="No release date", replace="{{date}}"}, | ||||
|   {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n\n## [Unreleased] - ReleaseDate", exactly=1}, | ||||
|   {file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/Plume-org/Plume/compare/{{tag_name}}...HEAD", exactly=1}, | ||||
| ] | ||||
| @ -62,30 +62,20 @@ pub fn oauth( | ||||
|     let conn = &*rockets.conn; | ||||
|     let app = App::find_by_client_id(conn, &query.client_id)?; | ||||
|     if app.client_secret == query.client_secret { | ||||
|         if let Ok(user) = User::find_by_fqn(&rockets, &query.username) { | ||||
|             if user.auth(&query.password) { | ||||
|                 let token = ApiToken::insert( | ||||
|                     conn, | ||||
|                     NewApiToken { | ||||
|                         app_id: app.id, | ||||
|                         user_id: user.id, | ||||
|                         value: random_hex(), | ||||
|                         scopes: query.scopes.clone(), | ||||
|                     }, | ||||
|                 )?; | ||||
|                 Ok(Json(json!({ | ||||
|                     "token": token.value | ||||
|                 }))) | ||||
|             } else { | ||||
|                 Ok(Json(json!({ | ||||
|                     "error": "Invalid credentials" | ||||
|                 }))) | ||||
|             } | ||||
|         if let Ok(user) = User::login(conn, &query.username, &query.password) { | ||||
|             let token = ApiToken::insert( | ||||
|                 conn, | ||||
|                 NewApiToken { | ||||
|                     app_id: app.id, | ||||
|                     user_id: user.id, | ||||
|                     value: random_hex(), | ||||
|                     scopes: query.scopes.clone(), | ||||
|                 }, | ||||
|             )?; | ||||
|             Ok(Json(json!({ | ||||
|                 "token": token.value | ||||
|             }))) | ||||
|         } else { | ||||
|             // Making fake password verification to avoid different
 | ||||
|             // response times that would make it possible to know
 | ||||
|             // if a username is registered or not.
 | ||||
|             User::get(conn, 1)?.auth(&query.password); | ||||
|             Ok(Json(json!({ | ||||
|                 "error": "Invalid credentials" | ||||
|             }))) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| use chrono::NaiveDateTime; | ||||
| use heck::{CamelCase, KebabCase}; | ||||
| use heck::KebabCase; | ||||
| use rocket_contrib::json::Json; | ||||
| 
 | ||||
| use crate::api::{authorization::*, Api}; | ||||
| @ -181,7 +181,7 @@ pub fn create( | ||||
|         Tag::insert( | ||||
|             conn, | ||||
|             NewTag { | ||||
|                 tag: hashtag.to_camel_case(), | ||||
|                 tag: hashtag, | ||||
|                 is_hashtag: true, | ||||
|                 post_id: post.id, | ||||
|             }, | ||||
|  | ||||
							
								
								
									
										27
									
								
								src/main.rs
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										27
									
								
								src/main.rs
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -10,6 +10,7 @@ extern crate serde_json; | ||||
| #[macro_use] | ||||
| extern crate validator_derive; | ||||
| 
 | ||||
| use chrono::Utc; | ||||
| use clap::App; | ||||
| use diesel::r2d2::ConnectionManager; | ||||
| use plume_models::{ | ||||
| @ -21,6 +22,8 @@ use plume_models::{ | ||||
| }; | ||||
| use rocket_csrf::CsrfFairingBuilder; | ||||
| use scheduled_thread_pool::ScheduledThreadPool; | ||||
| use std::fs; | ||||
| use std::path::Path; | ||||
| use std::process::exit; | ||||
| use std::sync::{Arc, Mutex}; | ||||
| use std::time::Duration; | ||||
| @ -98,8 +101,30 @@ Then try to restart Plume. | ||||
|     } | ||||
|     let workpool = ScheduledThreadPool::with_name("worker {}", num_cpus::get()); | ||||
|     // we want a fast exit here, so
 | ||||
|     let mut open_searcher = | ||||
|         UnmanagedSearcher::open(&CONFIG.search_index, &CONFIG.search_tokenizers); | ||||
|     if let Err(Error::Search(SearcherError::InvalidIndexDataError)) = open_searcher { | ||||
|         if UnmanagedSearcher::create(&CONFIG.search_index, &CONFIG.search_tokenizers).is_err() { | ||||
|             let current_path = Path::new(&CONFIG.search_index); | ||||
|             let backup_path = format!("{}.{}", ¤t_path.display(), Utc::now().timestamp()); | ||||
|             let backup_path = Path::new(&backup_path); | ||||
|             fs::rename(current_path, backup_path) | ||||
|                 .expect("main: error on backing up search index directory for recreating"); | ||||
|             if UnmanagedSearcher::create(&CONFIG.search_index, &CONFIG.search_tokenizers).is_ok() { | ||||
|                 if fs::remove_dir_all(backup_path).is_err() { | ||||
|                     eprintln!( | ||||
|                         "error on removing backup directory: {}. it remains", | ||||
|                         backup_path.display() | ||||
|                     ); | ||||
|                 } | ||||
|             } else { | ||||
|                 panic!("main: error on recreating search index in new index format. remove search index and run `plm search init` manually"); | ||||
|             } | ||||
|         } | ||||
|         open_searcher = UnmanagedSearcher::open(&CONFIG.search_index, &CONFIG.search_tokenizers); | ||||
|     } | ||||
|     #[allow(clippy::match_wild_err_arm)] | ||||
|     let searcher = match UnmanagedSearcher::open(&CONFIG.search_index, &CONFIG.search_tokenizers) { | ||||
|     let searcher = match open_searcher { | ||||
|         Err(Error::Search(e)) => match e { | ||||
|             SearcherError::WriteLockAcquisitionError => panic!( | ||||
|                 r#" | ||||
|  | ||||
| @ -210,11 +210,19 @@ pub fn add_email_blocklist( | ||||
|     form: LenientForm<NewBlocklistedEmail>, | ||||
|     rockets: PlumeRocket, | ||||
| ) -> Result<Flash<Redirect>, ErrorPage> { | ||||
|     BlocklistedEmail::insert(&*rockets.conn, form.0)?; | ||||
|     Ok(Flash::success( | ||||
|         Redirect::to(uri!(admin_email_blocklist: page = None)), | ||||
|         i18n!(rockets.intl.catalog, "Email Blocked"), | ||||
|     )) | ||||
|     let result = BlocklistedEmail::insert(&*rockets.conn, form.0); | ||||
| 
 | ||||
|     if let Err(Error::Db(_)) = result { | ||||
|         Ok(Flash::error( | ||||
|             Redirect::to(uri!(admin_email_blocklist: page = None)), | ||||
|             i18n!(rockets.intl.catalog, "Email already blocked"), | ||||
|         )) | ||||
|     } else { | ||||
|         Ok(Flash::success( | ||||
|             Redirect::to(uri!(admin_email_blocklist: page = None)), | ||||
|             i18n!(rockets.intl.catalog, "Email Blocked"), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| #[get("/admin/emails?<page>")] | ||||
| pub fn admin_email_blocklist( | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| use chrono::Utc; | ||||
| use heck::{CamelCase, KebabCase}; | ||||
| use heck::KebabCase; | ||||
| use rocket::request::LenientForm; | ||||
| use rocket::response::{Flash, Redirect}; | ||||
| use rocket_i18n::I18n; | ||||
| @ -314,18 +314,17 @@ pub fn update( | ||||
|             let tags = form | ||||
|                 .tags | ||||
|                 .split(',') | ||||
|                 .map(|t| t.trim().to_camel_case()) | ||||
|                 .map(|t| t.trim()) | ||||
|                 .filter(|t| !t.is_empty()) | ||||
|                 .collect::<HashSet<_>>() | ||||
|                 .into_iter() | ||||
|                 .filter_map(|t| Tag::build_activity(t).ok()) | ||||
|                 .filter_map(|t| Tag::build_activity(t.to_string()).ok()) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             post.update_tags(&conn, tags) | ||||
|                 .expect("post::update: tags error"); | ||||
| 
 | ||||
|             let hashtags = hashtags | ||||
|                 .into_iter() | ||||
|                 .map(|h| h.to_camel_case()) | ||||
|                 .collect::<HashSet<_>>() | ||||
|                 .into_iter() | ||||
|                 .filter_map(|t| Tag::build_activity(t).ok()) | ||||
| @ -489,14 +488,14 @@ pub fn create( | ||||
|         let tags = form | ||||
|             .tags | ||||
|             .split(',') | ||||
|             .map(|t| t.trim().to_camel_case()) | ||||
|             .map(|t| t.trim()) | ||||
|             .filter(|t| !t.is_empty()) | ||||
|             .collect::<HashSet<_>>(); | ||||
|         for tag in tags { | ||||
|             Tag::insert( | ||||
|                 &*conn, | ||||
|                 NewTag { | ||||
|                     tag, | ||||
|                     tag: tag.to_string(), | ||||
|                     is_hashtag: false, | ||||
|                     post_id: post.id, | ||||
|                 }, | ||||
| @ -507,7 +506,7 @@ pub fn create( | ||||
|             Tag::insert( | ||||
|                 &*conn, | ||||
|                 NewTag { | ||||
|                     tag: hashtag.to_camel_case(), | ||||
|                     tag: hashtag, | ||||
|                     is_hashtag: true, | ||||
|                     post_id: post.id, | ||||
|                 }, | ||||
|  | ||||
| @ -48,38 +48,19 @@ pub fn create( | ||||
|     rockets: PlumeRocket, | ||||
| ) -> RespondOrRedirect { | ||||
|     let conn = &*rockets.conn; | ||||
|     let user = User::find_by_email(&*conn, &form.email_or_name) | ||||
|         .or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name)); | ||||
|     let mut errors = match form.validate() { | ||||
|         Ok(_) => ValidationErrors::new(), | ||||
|         Err(e) => e, | ||||
|     }; | ||||
| 
 | ||||
|     let user = User::login(conn, &form.email_or_name, &form.password); | ||||
|     let user_id = if let Ok(user) = user { | ||||
|         if !user.auth(&form.password) { | ||||
|             let mut err = ValidationError::new("invalid_login"); | ||||
|             err.message = Some(Cow::from("Invalid username, or password")); | ||||
|             errors.add("email_or_name", err); | ||||
|             String::new() | ||||
|         } else { | ||||
|             user.id.to_string() | ||||
|         } | ||||
|         user.id.to_string() | ||||
|     } else { | ||||
|         // Fake password verification, only to avoid different login times
 | ||||
|         // that could be used to see if an email adress is registered or not
 | ||||
|         User::get(&*conn, 1) | ||||
|             .map(|u| u.auth(&form.password)) | ||||
|             .expect("No user is registered"); | ||||
| 
 | ||||
|         let mut err = ValidationError::new("invalid_login"); | ||||
|         err.message = Some(Cow::from("Invalid username, or password")); | ||||
|         errors.add("email_or_name", err); | ||||
|         String::new() | ||||
|     }; | ||||
| 
 | ||||
|     if !errors.is_empty() { | ||||
|         return render!(session::login(&rockets.to_context(), None, &*form, errors)).into(); | ||||
|     } | ||||
|     }; | ||||
| 
 | ||||
|     cookies.add_private( | ||||
|         Cookie::build(AUTH_COOKIE, user_id) | ||||
|  | ||||
| @ -541,7 +541,7 @@ pub fn create( | ||||
|                 Role::Normal, | ||||
|                 "", | ||||
|                 form.email.to_string(), | ||||
|                 User::hash_pass(&form.password).map_err(to_validation)?, | ||||
|                 Some(User::hash_pass(&form.password).map_err(to_validation)?), | ||||
|             ).map_err(to_validation)?; | ||||
|             Ok(Flash::success( | ||||
|                 Redirect::to(uri!(super::session::new: m = _)), | ||||
|  | ||||
| @ -10,9 +10,6 @@ | ||||
| @:base(ctx, tl.name.clone(), {}, {}, { | ||||
| 	<section class="flex wrap" dir="auto"> | ||||
| 		<h1 class="grow">@i18n_timeline_name(ctx.1, &tl.name)</h1> | ||||
|         @if ctx.clone().2.map(|u| (u.is_admin() && tl.user_id.is_none()) || Some(u.id) == tl.user_id).unwrap_or(false) { | ||||
| 		  <a href="@uri!(timelines::edit: _id = tl.id)" class="button inline-block">@i18n!(ctx.1, "Edit")</a> | ||||
|         } | ||||
| 	</section> | ||||
| 
 | ||||
|     @tabs(&vec![(format!("{}", uri!(instance::index)), i18n!(ctx.1, "Latest articles"), false)] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user