diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 95a4ae0..9f80cbd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,7 +21,7 @@ jobs: ref: ${{ github.event.release.tag_name }} - name: Install pnpm package manager - uses: pnpm/action-setup@v2.4.0 + uses: pnpm/action-setup@v3.0.0 with: version: latest diff --git a/.github/workflows/git-flow.yml b/.github/workflows/git-flow.yml new file mode 100644 index 0000000..85f42f7 --- /dev/null +++ b/.github/workflows/git-flow.yml @@ -0,0 +1,44 @@ +# Name of the workflow +name: Git Flow + +# Defines the event that triggers the workflow +on: + push: + branches: + - 'feature/*' # Trigger for pushes to feature branches + - 'bugfix/*' # Trigger for pushes to bugfix branches + - 'release/*' # Trigger for pushes to release branches + - 'hotfix/*' # Trigger for pushes to hotfix branches + +# Jobs are a set of steps that execute on the same runner +jobs: + create-pull-request: + # Specifies the runner environment, using the latest Ubuntu + runs-on: ubuntu-latest + name: Create Pull Request + permissions: write-all + + # Steps are individual tasks that run commands in a job + steps: + # Checks out the repository code under $GITHUB_WORKSPACE, so the job can access it + - name: Checkout Repository Code + uses: actions/checkout@v4.1.1 + + # This step uses the Git Flow Action to create PRs based on branch types + - name: Execute Git Flow Action + uses: nekofar/git-flow-action@develop # Specifies the Git Flow Action to use and the branch + with: + # The GitHub Token for authentication with GitHub API + github-token: ${{ secrets.GITHUB_TOKEN }} + # The branch to target for release and hotfix PRs + master-branch: 'master' + # The branch to target for feature and bugfix PRs + develop-branch: 'develop' + # Prefix for feature branches + feature-prefix: 'feature/' + # Prefix for bugfix branches + bugfix-prefix: 'bugfix/' + # Prefix for release branches + release-prefix: 'release/' + # Prefix for hotfix branches + hotfix-prefix: 'hotfix/' diff --git a/Cargo.lock b/Cargo.lock index a8a5b55..a9e82fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -946,9 +946,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64", "bytes", @@ -968,9 +968,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -1010,6 +1012,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + [[package]] name = "ryu" version = "1.0.15" @@ -1176,6 +1187,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 8b96f23..eb544c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ html-minifier = { version = "5.0.0", default-features = false } log = { version = "0.4.20", features = [] } percent-encoding = "2.3.1" regex = "1.10.3" -reqwest = "0.11.23" +reqwest = "0.11.24" serde = { version = "1.0.196", features = ["derive"] } sqids = "0.3.0" unidecode = "0.3.0" diff --git a/package.json b/package.json index ef4a52a..52d65ef 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,6 @@ "dev": "wrangler dev" }, "devDependencies": { - "wrangler": "3.25.0" + "wrangler": "3.28.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2a4d89..dbabf5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,19 +6,19 @@ settings: devDependencies: wrangler: - specifier: 3.25.0 - version: 3.25.0 + specifier: 3.28.2 + version: 3.28.2 packages: - /@cloudflare/kv-asset-handler@0.2.0: - resolution: {integrity: sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==} + /@cloudflare/kv-asset-handler@0.3.1: + resolution: {integrity: sha512-lKN2XCfKCmpKb86a1tl4GIwsJYDy9TGuwjhDELLmpKygQhw8X2xR4dusgpC5Tg7q1pB96Eb0rBo81kxSILQMwA==} dependencies: mime: 3.0.0 dev: true - /@cloudflare/workerd-darwin-64@1.20231218.0: - resolution: {integrity: sha512-547gOmTIVmRdDy7HNAGJUPELa+fSDm2Y0OCxqAtQOz0GLTDu1vX61xYmsb2rn91+v3xW6eMttEIpbYokKjtfJA==} + /@cloudflare/workerd-darwin-64@1.20240129.0: + resolution: {integrity: sha512-DfVVB5IsQLVcWPJwV019vY3nEtU88c2Qu2ST5SQxqcGivZ52imagLRK0RHCIP8PK4piSiq90qUC6ybppUsw8eg==} engines: {node: '>=16'} cpu: [x64] os: [darwin] @@ -26,8 +26,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-darwin-arm64@1.20231218.0: - resolution: {integrity: sha512-b39qrU1bKolCfmKFDAnX4vXcqzISkEUVE/V8sMBsFzxrIpNAbcUHBZAQPYmS/OHIGB94KjOVokvDi7J6UNurPw==} + /@cloudflare/workerd-darwin-arm64@1.20240129.0: + resolution: {integrity: sha512-t0q8ABkmumG1zRM/MZ/vIv/Ysx0vTAXnQAPy/JW5aeQi/tqrypXkO9/NhPc0jbF/g/hIPrWEqpDgEp3CB7Da7Q==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] @@ -35,8 +35,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-linux-64@1.20231218.0: - resolution: {integrity: sha512-dMUF1wA+0mybm6hHNOCgY/WMNMwomPPs4I7vvYCgwHSkch0Q2Wb7TnxQZSt8d1PK/myibaBwadrlIxpjxmpz3w==} + /@cloudflare/workerd-linux-64@1.20240129.0: + resolution: {integrity: sha512-sFV1uobHgDI+6CKBS/ZshQvOvajgwl6BtiYaH4PSFSpvXTmRx+A9bcug+6BnD+V4WgwxTiEO2iR97E1XuwDAVw==} engines: {node: '>=16'} cpu: [x64] os: [linux] @@ -44,8 +44,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-linux-arm64@1.20231218.0: - resolution: {integrity: sha512-2s5uc8IHt0QmWyKxAr1Fy+4b8Xy0b/oUtlPnm5MrKi2gDRlZzR7JvxENPJCpCnYENydS8lzvkMiAFECPBccmyQ==} + /@cloudflare/workerd-linux-arm64@1.20240129.0: + resolution: {integrity: sha512-O7q7htHaFRp8PgTqNJx1/fYc3+LnvAo6kWWB9a14C5OWak6AAZk42PNpKPx+DXTmGvI+8S1+futBGUeJ8NPDXg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] @@ -53,8 +53,8 @@ packages: dev: true optional: true - /@cloudflare/workerd-windows-64@1.20231218.0: - resolution: {integrity: sha512-oN5hz6TXUDB5YKUN5N3QWAv6cYz9JjTZ9g16HVyoegVFEL6/zXU3tV19MBX2IvlE11ab/mRogEv9KXVIrHfKmA==} + /@cloudflare/workerd-windows-64@1.20240129.0: + resolution: {integrity: sha512-YqGno0XSqqqkDmNoGEX6M8kJlI2lEfWntbTPVtHaZlaXVR9sWfoD7TEno0NKC95cXFz+ioyFLbgbOdnfWwmVAA==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -522,8 +522,8 @@ packages: hasBin: true dev: true - /miniflare@3.20231218.4: - resolution: {integrity: sha512-2mpxvDiRBxGGGVnTKC0SZy0FtTXxFs3tM1ol67EoIJABGzvWFf33GThwh+/dRmaHSjKKId/FI8rEl5JxXXXZgQ==} + /miniflare@3.20240129.2: + resolution: {integrity: sha512-BPUg8HsPmWQlRFUeiQk274i8M9L0gOvzbkjryuTvCX+M53EwBpP0gM2wyrRr/HokQoJcxWGh3InBu6L8+0bbPw==} engines: {node: '>=16.13'} hasBin: true dependencies: @@ -535,7 +535,7 @@ packages: glob-to-regexp: 0.4.1 stoppable: 1.1.0 undici: 5.28.2 - workerd: 1.20231218.0 + workerd: 1.20240129.0 ws: 8.14.2 youch: 3.3.1 zod: 3.22.3 @@ -681,31 +681,36 @@ packages: '@fastify/busboy': 2.0.0 dev: true - /workerd@1.20231218.0: - resolution: {integrity: sha512-AGIsDvqCrcwhoA9kb1hxOhVAe53/xJeaGZxL4FbYI9FvO17DZwrnqGq+6eqItJ6Cfw1ZLmf3BM+QdMWaL2bFWQ==} + /workerd@1.20240129.0: + resolution: {integrity: sha512-t4pnsmjjk/u+GdVDgH2M1AFmJaBUABshYK/vT/HNrAXsHSwN6VR8Yqw0JQ845OokO34VLkuUtYQYyxHHKpdtsw==} engines: {node: '>=16'} hasBin: true requiresBuild: true optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20231218.0 - '@cloudflare/workerd-darwin-arm64': 1.20231218.0 - '@cloudflare/workerd-linux-64': 1.20231218.0 - '@cloudflare/workerd-linux-arm64': 1.20231218.0 - '@cloudflare/workerd-windows-64': 1.20231218.0 + '@cloudflare/workerd-darwin-64': 1.20240129.0 + '@cloudflare/workerd-darwin-arm64': 1.20240129.0 + '@cloudflare/workerd-linux-64': 1.20240129.0 + '@cloudflare/workerd-linux-arm64': 1.20240129.0 + '@cloudflare/workerd-windows-64': 1.20240129.0 dev: true - /wrangler@3.25.0: - resolution: {integrity: sha512-eU47Ez1QLu1B/wutm5ow+VwZnY4OqA+D/iy6BORAu5tABujoDr9p1yBxY/1DS/DxxDWqqY3sBBS6TzcC4NSLUQ==} + /wrangler@3.28.2: + resolution: {integrity: sha512-hlD4f2avBZuR1+qo9Um6D1prdWrSRtGTo9h6o/AKce+bHQEJWoJgJKHeLmrpZlLtHg/gGR1Xa1xzrexhuIzeJw==} engines: {node: '>=16.17.0'} hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20230914.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true dependencies: - '@cloudflare/kv-asset-handler': 0.2.0 + '@cloudflare/kv-asset-handler': 0.3.1 '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) blake3-wasm: 2.1.5 chokidar: 3.5.3 esbuild: 0.17.19 - miniflare: 3.20231218.4 + miniflare: 3.20240129.2 nanoid: 3.3.6 path-to-regexp: 6.2.1 resolve: 1.22.8 diff --git a/src/lib.rs b/src/lib.rs index 2802755..5dce6b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,15 +7,17 @@ mod utils; #[event(fetch)] async fn main(req: Request, env: Env, _ctx: Context) -> Result { - let router = Router::new(); + let router = Router::new(); - router - .get("/", |_, _| { - Response::redirect(Url::parse("https://lilnouns.wtf")?) - }) - .get_async("/:sqid", routes::handle_redirect) - .get_async("/:sqid/og.png", routes::handle_og_image) - .post_async("/", routes::handle_creation) - .run(req, env) - .await + router + .get("/", |_, _| { + Response::redirect(Url::parse( + "https://lilnouns.wtf?utm_source=farcaster&utm_medium=social", + )?) + }) + .get_async("/:sqid", routes::handle_redirect) + .get_async("/:sqid/og.png", routes::handle_og_image) + .post_async("/", routes::handle_creation) + .run(req, env) + .await } diff --git a/src/routes.rs b/src/routes.rs index da2c66f..4e4d0f7 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -6,88 +6,99 @@ use url::Url; use worker::{Request, Response, ResponseBody, RouteContext}; use crate::{ - queries::{fetch_lil_nouns_data, fetch_meta_gov_data, fetch_prop_lot_data}, - routes::{ - Community::LilNouns, - Platform::{Ethereum, MetaGov, PropLot}, - }, - utils::create_og_image, + queries::{fetch_lil_nouns_data, fetch_meta_gov_data, fetch_prop_lot_data}, + routes::{ + Community::LilNouns, + Platform::{Ethereum, MetaGov, PropLot}, + }, + utils::create_og_image, }; #[derive(Debug, Serialize, Deserialize)] struct UrlPayload { - pub url: String, - pub sqid: Option, + pub url: String, + pub sqid: Option, } #[derive(Debug, PartialEq)] pub enum Community { - LilNouns = 1, + LilNouns = 1, } #[derive(Debug, PartialEq)] pub enum Platform { - Ethereum = 1, - PropLot = 2, - MetaGov = 3, + Ethereum = 1, + PropLot = 2, + MetaGov = 3, } pub async fn handle_redirect(req: Request, ctx: RouteContext) -> worker::Result { - if let Some(sqid) = ctx.param("sqid") { - let sqids = Sqids::default(); - let numbers = sqids.decode(&sqid); - - let community = match numbers[0] { - 1 => Some(LilNouns), - _ => None, - }; - - let platform = match numbers[1] { - 1 => Some(Ethereum), - 2 => Some(PropLot), - 3 => Some(MetaGov), - _ => None, - }; - - let (url, title, description, image) = match (community, platform) { - (Some(LilNouns), Some(Ethereum)) => { - let url = format!("{}/{}", "https://lilnouns.wtf/vote", numbers[2]); - let (title, description) = fetch_lil_nouns_data(&ctx.env, numbers[2]).await?; - let image = req - .url() - .unwrap() - .join(format!("{}/og.png", sqid).as_str()) - .unwrap() - .to_string(); - (url, title, description, image) - } - (Some(LilNouns), Some(PropLot)) => { - let url = format!("{}/{}", "https://lilnouns.proplot.wtf/idea", numbers[2]); - let (title, description) = fetch_prop_lot_data(&ctx.env, numbers[2]).await?; - let image = req - .url() - .unwrap() - .join(format!("{}/og.png", sqid).as_str()) - .unwrap() - .to_string(); - (url, title, description, image) - } - (Some(LilNouns), Some(MetaGov)) => { - let url = format!("{}/{}", "https://lilnouns.wtf/vote/nounsdao", numbers[2]); - let (title, description) = fetch_meta_gov_data(&ctx.env, numbers[2]).await?; - let image = req - .url() - .unwrap() - .join(format!("{}/og.png", sqid).as_str()) - .unwrap() - .to_string(); - (url, title, description, image) - } - _ => (String::new(), String::new(), String::new(), String::new()), - }; - - let html_doc = format!( - r#" + if let Some(sqid) = ctx.param("sqid") { + let sqids = Sqids::default(); + let numbers = sqids.decode(&sqid); + + let community = match numbers[0] { + 1 => Some(LilNouns), + _ => None, + }; + + let platform = match numbers[1] { + 1 => Some(Ethereum), + 2 => Some(PropLot), + 3 => Some(MetaGov), + _ => None, + }; + + let (url, title, description, image) = match (community, platform) { + (Some(LilNouns), Some(Ethereum)) => { + let url = format!( + "{}/{}?utm_source=farcaster&utm_medium=social&utm_campaign=governor&\ + utm_content=proposal_{}", + "https://lilnouns.wtf/vote", numbers[2], numbers[2] + ); + let (title, description) = fetch_lil_nouns_data(&ctx.env, numbers[2]).await?; + let image = req + .url() + .unwrap() + .join(format!("{}/og.png", sqid).as_str()) + .unwrap() + .to_string(); + (url, title, description, image) + } + (Some(LilNouns), Some(PropLot)) => { + let url = format!( + "{}/{}?utm_source=farcaster&utm_medium=social&utm_campaign=proplot&utm_content=idea_{}", + "https://lilnouns.proplot.wtf/idea", numbers[2], numbers[2] + ); + let (title, description) = fetch_prop_lot_data(&ctx.env, numbers[2]).await?; + let image = req + .url() + .unwrap() + .join(format!("{}/og.png", sqid).as_str()) + .unwrap() + .to_string(); + (url, title, description, image) + } + (Some(LilNouns), Some(MetaGov)) => { + let url = format!( + "{}/{}?utm_source=farcaster&utm_medium=social&utm_campaign=metagov&\ + utm_content=proposal_{}", + "https://lilnouns.wtf/vote/nounsdao", numbers[2], numbers[2] + ); + let (title, description) = fetch_meta_gov_data(&ctx.env, numbers[2]).await?; + let image = req + .url() + .unwrap() + .join(format!("{}/og.png", sqid).as_str()) + .unwrap() + .to_string(); + (url, title, description, image) + } + _ => (String::new(), String::new(), String::new(), String::new()), + }; + + let html_doc = format!( + r#" @@ -139,142 +150,142 @@ pub async fn handle_redirect(req: Request, ctx: RouteContext) -> worker::R "#, - url, // OpenGraph URL - encode_safe(&title), // OpenGraph Title - encode_safe(&description), // OpenGraph Description - image, // OpenGraph Image URL - image, // OpenGraph Image Secure URL - encode_safe(&title), // OpenGraph Image Alt - url, // Twitter URL - encode_safe(&title), // Twitter Title - encode_safe(&description), // Twitter Description - image, // Twitter Image - image, // Farcaster Image - "Show Result", // Farcaster Button #1 - url, // Farcaster Post URL - url, // Page Refresh URL - encode_safe(&title), // Page Title - encode_safe(&description), // Page Description - url, // Page Content Link URL - encode_safe(&title), // Page Content Link Title - ); - - let minified_html = minify(html_doc).expect("Failed to minify HTML"); - - let response = Response::from_body(ResponseBody::Body(minified_html.as_bytes().to_vec())); - - return match response { - Ok(mut res) => { - res.headers_mut().set("Content-Type", "text/html").unwrap(); - return Ok(res); - } - Err(e) => Err(e), - }; - } - - Response::error("Bad Request", 400) + url, // OpenGraph URL + encode_safe(&title), // OpenGraph Title + encode_safe(&description), // OpenGraph Description + image, // OpenGraph Image URL + image, // OpenGraph Image Secure URL + encode_safe(&title), // OpenGraph Image Alt + url, // Twitter URL + encode_safe(&title), // Twitter Title + encode_safe(&description), // Twitter Description + image, // Twitter Image + image, // Farcaster Image + "Show Result", // Farcaster Button #1 + req.url().unwrap().as_str(), // Farcaster Post URL + url, // Page Refresh URL + encode_safe(&title), // Page Title + encode_safe(&description), // Page Description + url, // Page Content Link URL + encode_safe(&title), // Page Content Link Title + ); + + let minified_html = minify(html_doc).expect("Failed to minify HTML"); + + let response = Response::from_body(ResponseBody::Body(minified_html.as_bytes().to_vec())); + + return match response { + Ok(mut res) => { + res.headers_mut().set("Content-Type", "text/html").unwrap(); + return Ok(res); + } + Err(e) => Err(e), + }; + } + + Response::error("Bad Request", 400) } pub async fn handle_og_image(_req: Request, ctx: RouteContext) -> worker::Result { - if let Some(sqid) = ctx.param("sqid") { - let sqids = Sqids::default(); - let numbers = sqids.decode(&sqid); - - let community = match numbers[0] { - 1 => Some(LilNouns), - _ => None, - }; - - let platform = match numbers[1] { - 1 => Some(Ethereum), - 2 => Some(PropLot), - 3 => Some(MetaGov), - _ => None, - }; - - let image = match (community, platform) { - (Some(LilNouns), Some(Ethereum)) => { - let (title, description) = fetch_lil_nouns_data(&ctx.env, numbers[2]).await?; - create_og_image(&title, &description, "7CC4F2") - } - (Some(LilNouns), Some(PropLot)) => { - let (title, description) = fetch_prop_lot_data(&ctx.env, numbers[2]).await?; - create_og_image(&title, &description, "8C8D92") - } - (Some(LilNouns), Some(MetaGov)) => { - let (title, description) = fetch_meta_gov_data(&ctx.env, numbers[2]).await?; - create_og_image(&title, &description, "EFC950") - } - _ => String::new(), - }; - - return Response::redirect(Url::parse(&*image)?); - } - - Response::error("Bad Request", 400) + if let Some(sqid) = ctx.param("sqid") { + let sqids = Sqids::default(); + let numbers = sqids.decode(&sqid); + + let community = match numbers[0] { + 1 => Some(LilNouns), + _ => None, + }; + + let platform = match numbers[1] { + 1 => Some(Ethereum), + 2 => Some(PropLot), + 3 => Some(MetaGov), + _ => None, + }; + + let image = match (community, platform) { + (Some(LilNouns), Some(Ethereum)) => { + let (title, description) = fetch_lil_nouns_data(&ctx.env, numbers[2]).await?; + create_og_image(&title, &description, "7CC4F2") + } + (Some(LilNouns), Some(PropLot)) => { + let (title, description) = fetch_prop_lot_data(&ctx.env, numbers[2]).await?; + create_og_image(&title, &description, "8C8D92") + } + (Some(LilNouns), Some(MetaGov)) => { + let (title, description) = fetch_meta_gov_data(&ctx.env, numbers[2]).await?; + create_og_image(&title, &description, "EFC950") + } + _ => String::new(), + }; + + return Response::redirect(Url::parse(&*image)?); + } + + Response::error("Bad Request", 400) } pub async fn handle_creation( - mut req: Request, - _ctx: RouteContext, + mut req: Request, + _ctx: RouteContext, ) -> worker::Result { - let sqids = Sqids::default(); - let mut numbers: Vec = Vec::new(); - - if let Ok(payload) = req.json::().await { - let url = Url::parse(&*payload.url).expect("Invalid URL"); - - return match url.host_str() { - Some("lilnouns.wtf") | Some("www.lilnouns.wtf") => { - let segments: Vec<_> = url - .path_segments() - .expect("Cannot get path segments") - .filter(|segment| !segment.is_empty()) - .collect(); - - if segments.is_empty() || segments[0] != "vote" { - return Response::error("Bad Request", 400); - } - - if segments[1] == "nounsdao" { - numbers.push(LilNouns as u64); - numbers.push(MetaGov as u64); - numbers.push(segments[2].parse::().unwrap().try_into().unwrap()); - } else { - numbers.push(LilNouns as u64); - numbers.push(Ethereum as u64); - numbers.push(segments[1].parse::().unwrap().try_into().unwrap()); - } - - Response::from_json(&UrlPayload { - url: url.into(), - sqid: Some(sqids.encode(&*numbers).unwrap()), - }) - } - Some("lilnouns.proplot.wtf") | Some("www.lilnouns.proplot.wtf") => { - numbers.push(LilNouns as u64); - - let segments: Vec<_> = url - .path_segments() - .expect("Cannot get path segments") - .filter(|segment| !segment.is_empty()) - .collect(); - - if segments[0] == "idea" { - numbers.push(PropLot as u64); - numbers.push(segments[1].parse::().unwrap().try_into().unwrap()); - } else { - return Response::error("Bad Request", 400); - } - - Response::from_json(&UrlPayload { - url: url.into(), - sqid: Some(sqids.encode(&*numbers).unwrap()), - }) - } - _ => Response::error("Bad Request", 400), - }; - } - - Response::error("Bad Request", 400) + let sqids = Sqids::default(); + let mut numbers: Vec = Vec::new(); + + if let Ok(payload) = req.json::().await { + let url = Url::parse(&*payload.url).expect("Invalid URL"); + + return match url.host_str() { + Some("lilnouns.wtf") | Some("www.lilnouns.wtf") => { + let segments: Vec<_> = url + .path_segments() + .expect("Cannot get path segments") + .filter(|segment| !segment.is_empty()) + .collect(); + + if segments.is_empty() || segments[0] != "vote" { + return Response::error("Bad Request", 400); + } + + if segments[1] == "nounsdao" { + numbers.push(LilNouns as u64); + numbers.push(MetaGov as u64); + numbers.push(segments[2].parse::().unwrap().try_into().unwrap()); + } else { + numbers.push(LilNouns as u64); + numbers.push(Ethereum as u64); + numbers.push(segments[1].parse::().unwrap().try_into().unwrap()); + } + + Response::from_json(&UrlPayload { + url: url.into(), + sqid: Some(sqids.encode(&*numbers).unwrap()), + }) + } + Some("lilnouns.proplot.wtf") | Some("www.lilnouns.proplot.wtf") => { + numbers.push(LilNouns as u64); + + let segments: Vec<_> = url + .path_segments() + .expect("Cannot get path segments") + .filter(|segment| !segment.is_empty()) + .collect(); + + if segments[0] == "idea" { + numbers.push(PropLot as u64); + numbers.push(segments[1].parse::().unwrap().try_into().unwrap()); + } else { + return Response::error("Bad Request", 400); + } + + Response::from_json(&UrlPayload { + url: url.into(), + sqid: Some(sqids.encode(&*numbers).unwrap()), + }) + } + _ => Response::error("Bad Request", 400), + }; + } + + Response::error("Bad Request", 400) }