From 935a8e6868600ffe04982343ee211479eca8d37f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 7 Jul 2025 23:13:29 -0600 Subject: [PATCH] Add README.md, local dev mode & project visibility --- ...f2689efdbd0af3397ebdc9f4dec0a8709bd3.json} | 12 +- ...e2511d3f1668d07956ac60a1450ddd8901135.json | 12 ++ ...899e71f5f491f72864b134b4debe85bb57fa9.json | 12 -- ...8ef17184498e031c3f19aae4b27af4ab9fb0d.json | 12 ++ ...b87cb3bf82646329034caeea9c48d9586823.json} | 12 +- ...cb6a8c6627de9d8e5f4a823c30cc52abe57d6.json | 12 -- ...5c50ae1bb982ce29a63073f89a7723fc6f11.json} | 6 +- ...54cc07efb5d4496d80d489f23a39f0e6b032.json} | 20 +- Cargo.lock | 21 +- Cargo.toml | 1 + README.md | 60 ++++++ config.example.yml | 6 +- crates/auth/src/lib.rs | 88 +++++---- crates/cli/src/cmd/changes.rs | 4 +- crates/core/src/config.rs | 2 + crates/core/src/lib.rs | 2 +- crates/core/src/models.rs | 24 ++- crates/core/src/util.rs | 6 +- crates/db/src/lib.rs | 30 ++- crates/github/Cargo.toml | 2 +- crates/github/src/lib.rs | 55 ++++-- crates/github/src/webhook.rs | 12 +- crates/images/src/badge.rs | 6 +- crates/web/src/cron.rs | 8 + crates/web/src/frogress/mod.rs | 10 +- crates/web/src/handlers/auth.rs | 25 ++- crates/web/src/handlers/common.rs | 6 +- crates/web/src/handlers/csp.rs | 2 +- crates/web/src/handlers/images.rs | 28 +-- crates/web/src/handlers/manage.rs | 183 +++++++++++++----- crates/web/src/handlers/mod.rs | 2 +- crates/web/src/handlers/project.rs | 17 +- crates/web/src/handlers/report.rs | 138 +++++++------ crates/web/src/main.rs | 2 +- css/main.scss | 9 + docs/manage_new.png | Bin 0 -> 93336 bytes migrations/19_project_enabled.sql | 1 + 37 files changed, 580 insertions(+), 268 deletions(-) rename .sqlx/{query-e679ddb4ba6c9a8e4e460b2654312956d174591989dec05c4cfe42eaedfebd21.json => query-415ca8dbe5fb5b2d573200257e96f2689efdbd0af3397ebdc9f4dec0a8709bd3.json} (80%) create mode 100644 .sqlx/query-5bdd491e42bf700a271a5fc8431e2511d3f1668d07956ac60a1450ddd8901135.json delete mode 100644 .sqlx/query-5de3235fb918d2e691289b998a5899e71f5f491f72864b134b4debe85bb57fa9.json create mode 100644 .sqlx/query-c0ffba953513be9fccf5427dd818ef17184498e031c3f19aae4b27af4ab9fb0d.json rename .sqlx/{query-36261e887badca80fc0c19ac0a368951387da8f8a42623be91ca2d251c745364.json => query-d66c20bc973f3199c05ca1e7f2e3b87cb3bf82646329034caeea9c48d9586823.json} (81%) delete mode 100644 .sqlx/query-e9bddc3272d50fb0ca0c4375deecb6a8c6627de9d8e5f4a823c30cc52abe57d6.json rename .sqlx/{query-c6e1281fcba9403a87315a09483b92c86d27bc26d6db402c0039026be5ade978.json => query-f7bf7260f20ecc8e216bfcf1c62d5c50ae1bb982ce29a63073f89a7723fc6f11.json} (58%) rename .sqlx/{query-927b8e137db05ddd2996e9b2d04b80f8af3f6e2113654f3dd4a24ff5efb0855a.json => query-fc441fa93f0d46f6a0a541ae625154cc07efb5d4496d80d489f23a39f0e6b032.json} (72%) create mode 100644 README.md create mode 100644 docs/manage_new.png create mode 100644 migrations/19_project_enabled.sql diff --git a/.sqlx/query-e679ddb4ba6c9a8e4e460b2654312956d174591989dec05c4cfe42eaedfebd21.json b/.sqlx/query-415ca8dbe5fb5b2d573200257e96f2689efdbd0af3397ebdc9f4dec0a8709bd3.json similarity index 80% rename from .sqlx/query-e679ddb4ba6c9a8e4e460b2654312956d174591989dec05c4cfe42eaedfebd21.json rename to .sqlx/query-415ca8dbe5fb5b2d573200257e96f2689efdbd0af3397ebdc9f4dec0a8709bd3.json index c7416d5..c2f3252 100644 --- a/.sqlx/query-e679ddb4ba6c9a8e4e460b2654312956d174591989dec05c4cfe42eaedfebd21.json +++ b/.sqlx/query-415ca8dbe5fb5b2d573200257e96f2689efdbd0af3397ebdc9f4dec0a8709bd3.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT id AS \"id!\", owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id\n FROM projects\n WHERE owner = ? COLLATE NOCASE AND repo = ? COLLATE NOCASE\n ", + "query": "\n SELECT id AS \"id!\", owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled\n FROM projects\n WHERE owner = ? COLLATE NOCASE AND repo = ? COLLATE NOCASE\n ", "describe": { "columns": [ { @@ -57,6 +57,11 @@ "name": "header_image_id", "ordinal": 10, "type_info": "Blob" + }, + { + "name": "enabled", + "ordinal": 11, + "type_info": "Bool" } ], "parameters": { @@ -73,8 +78,9 @@ true, true, false, - true + true, + false ] }, - "hash": "e679ddb4ba6c9a8e4e460b2654312956d174591989dec05c4cfe42eaedfebd21" + "hash": "415ca8dbe5fb5b2d573200257e96f2689efdbd0af3397ebdc9f4dec0a8709bd3" } diff --git a/.sqlx/query-5bdd491e42bf700a271a5fc8431e2511d3f1668d07956ac60a1450ddd8901135.json b/.sqlx/query-5bdd491e42bf700a271a5fc8431e2511d3f1668d07956ac60a1450ddd8901135.json new file mode 100644 index 0000000..8695018 --- /dev/null +++ b/.sqlx/query-5bdd491e42bf700a271a5fc8431e2511d3f1668d07956ac60a1450ddd8901135.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 12 + }, + "nullable": [] + }, + "hash": "5bdd491e42bf700a271a5fc8431e2511d3f1668d07956ac60a1450ddd8901135" +} diff --git a/.sqlx/query-5de3235fb918d2e691289b998a5899e71f5f491f72864b134b4debe85bb57fa9.json b/.sqlx/query-5de3235fb918d2e691289b998a5899e71f5f491f72864b134b4debe85bb57fa9.json deleted file mode 100644 index 497ee2a..0000000 --- a/.sqlx/query-5de3235fb918d2e691289b998a5899e71f5f491f72864b134b4debe85bb57fa9.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n ON CONFLICT (id) DO NOTHING\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 7 - }, - "nullable": [] - }, - "hash": "5de3235fb918d2e691289b998a5899e71f5f491f72864b134b4debe85bb57fa9" -} diff --git a/.sqlx/query-c0ffba953513be9fccf5427dd818ef17184498e031c3f19aae4b27af4ab9fb0d.json b/.sqlx/query-c0ffba953513be9fccf5427dd818ef17184498e031c3f19aae4b27af4ab9fb0d.json new file mode 100644 index 0000000..845a245 --- /dev/null +++ b/.sqlx/query-c0ffba953513be9fccf5427dd818ef17184498e031c3f19aae4b27af4ab9fb0d.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n ON CONFLICT (id) DO NOTHING\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 12 + }, + "nullable": [] + }, + "hash": "c0ffba953513be9fccf5427dd818ef17184498e031c3f19aae4b27af4ab9fb0d" +} diff --git a/.sqlx/query-36261e887badca80fc0c19ac0a368951387da8f8a42623be91ca2d251c745364.json b/.sqlx/query-d66c20bc973f3199c05ca1e7f2e3b87cb3bf82646329034caeea9c48d9586823.json similarity index 81% rename from .sqlx/query-36261e887badca80fc0c19ac0a368951387da8f8a42623be91ca2d251c745364.json rename to .sqlx/query-d66c20bc973f3199c05ca1e7f2e3b87cb3bf82646329034caeea9c48d9586823.json index 171f246..5946f66 100644 --- a/.sqlx/query-36261e887badca80fc0c19ac0a368951387da8f8a42623be91ca2d251c745364.json +++ b/.sqlx/query-d66c20bc973f3199c05ca1e7f2e3b87cb3bf82646329034caeea9c48d9586823.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id\n FROM projects\n WHERE id = ?\n ", + "query": "\n SELECT owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled\n FROM projects\n WHERE id = ?\n ", "describe": { "columns": [ { @@ -52,6 +52,11 @@ "name": "header_image_id", "ordinal": 9, "type_info": "Blob" + }, + { + "name": "enabled", + "ordinal": 10, + "type_info": "Bool" } ], "parameters": { @@ -67,8 +72,9 @@ true, true, false, - true + true, + false ] }, - "hash": "36261e887badca80fc0c19ac0a368951387da8f8a42623be91ca2d251c745364" + "hash": "d66c20bc973f3199c05ca1e7f2e3b87cb3bf82646329034caeea9c48d9586823" } diff --git a/.sqlx/query-e9bddc3272d50fb0ca0c4375deecb6a8c6627de9d8e5f4a823c30cc52abe57d6.json b/.sqlx/query-e9bddc3272d50fb0ca0c4375deecb6a8c6627de9d8e5f4a823c30cc52abe57d6.json deleted file mode 100644 index e2ba8e1..0000000 --- a/.sqlx/query-e9bddc3272d50fb0ca0c4375deecb6a8c6627de9d8e5f4a823c30cc52abe57d6.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 10 - }, - "nullable": [] - }, - "hash": "e9bddc3272d50fb0ca0c4375deecb6a8c6627de9d8e5f4a823c30cc52abe57d6" -} diff --git a/.sqlx/query-c6e1281fcba9403a87315a09483b92c86d27bc26d6db402c0039026be5ade978.json b/.sqlx/query-f7bf7260f20ecc8e216bfcf1c62d5c50ae1bb982ce29a63073f89a7723fc6f11.json similarity index 58% rename from .sqlx/query-c6e1281fcba9403a87315a09483b92c86d27bc26d6db402c0039026be5ade978.json rename to .sqlx/query-f7bf7260f20ecc8e216bfcf1c62d5c50ae1bb982ce29a63073f89a7723fc6f11.json index 31a80c6..878cef0 100644 --- a/.sqlx/query-c6e1281fcba9403a87315a09483b92c86d27bc26d6db402c0039026be5ade978.json +++ b/.sqlx/query-f7bf7260f20ecc8e216bfcf1c62d5c50ae1bb982ce29a63073f89a7723fc6f11.json @@ -1,12 +1,12 @@ { "db_name": "SQLite", - "query": "\n UPDATE projects\n SET owner = ?, repo = ?, name = ?, short_name = ?, default_category = ?, default_version = ?, platform = ?, workflow_id = ?, enable_pr_comments = ?, header_image_id = ?, updated_at = CURRENT_TIMESTAMP\n WHERE id = ?\n ", + "query": "\n UPDATE projects\n SET owner = ?, repo = ?, name = ?, short_name = ?, default_category = ?, default_version = ?, platform = ?, workflow_id = ?, enable_pr_comments = ?, header_image_id = ?, enabled = ?, updated_at = CURRENT_TIMESTAMP\n WHERE id = ?\n ", "describe": { "columns": [], "parameters": { - "Right": 11 + "Right": 12 }, "nullable": [] }, - "hash": "c6e1281fcba9403a87315a09483b92c86d27bc26d6db402c0039026be5ade978" + "hash": "f7bf7260f20ecc8e216bfcf1c62d5c50ae1bb982ce29a63073f89a7723fc6f11" } diff --git a/.sqlx/query-927b8e137db05ddd2996e9b2d04b80f8af3f6e2113654f3dd4a24ff5efb0855a.json b/.sqlx/query-fc441fa93f0d46f6a0a541ae625154cc07efb5d4496d80d489f23a39f0e6b032.json similarity index 72% rename from .sqlx/query-927b8e137db05ddd2996e9b2d04b80f8af3f6e2113654f3dd4a24ff5efb0855a.json rename to .sqlx/query-fc441fa93f0d46f6a0a541ae625154cc07efb5d4496d80d489f23a39f0e6b032.json index 696cb6b..91f0fe5 100644 --- a/.sqlx/query-927b8e137db05ddd2996e9b2d04b80f8af3f6e2113654f3dd4a24ff5efb0855a.json +++ b/.sqlx/query-fc441fa93f0d46f6a0a541ae625154cc07efb5d4496d80d489f23a39f0e6b032.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT\n projects.id AS \"project_id!\",\n owner AS \"owner!\",\n repo AS \"repo!\",\n name,\n short_name,\n default_category,\n default_version,\n platform,\n workflow_id,\n enable_pr_comments AS \"enable_pr_comments!\",\n header_image_id,\n git_commit,\n git_commit_message,\n MAX(timestamp) AS \"timestamp: time::OffsetDateTime\",\n JSON_GROUP_ARRAY(version ORDER BY version)\n FILTER (WHERE version IS NOT NULL) AS versions\n FROM projects LEFT JOIN reports ON (\n reports.project_id = projects.id\n AND reports.timestamp = (\n SELECT MAX(timestamp)\n FROM reports\n WHERE project_id = projects.id\n )\n )\n GROUP BY projects.id\n ORDER BY MAX(timestamp) DESC\n ", + "query": "\n SELECT\n projects.id AS \"project_id!\",\n owner AS \"owner!\",\n repo AS \"repo!\",\n name,\n short_name,\n default_category,\n default_version,\n platform,\n workflow_id,\n enable_pr_comments AS \"enable_pr_comments!\",\n header_image_id,\n enabled AS \"enabled!\",\n git_commit,\n git_commit_message,\n MAX(timestamp) AS \"timestamp: time::OffsetDateTime\",\n JSON_GROUP_ARRAY(version ORDER BY version)\n FILTER (WHERE version IS NOT NULL) AS versions\n FROM projects LEFT JOIN reports ON (\n reports.project_id = projects.id\n AND reports.timestamp = (\n SELECT MAX(timestamp)\n FROM reports\n WHERE project_id = projects.id\n )\n )\n GROUP BY projects.id\n ORDER BY MAX(timestamp) DESC\n ", "describe": { "columns": [ { @@ -59,24 +59,29 @@ "type_info": "Blob" }, { - "name": "git_commit", + "name": "enabled!", "ordinal": 11, - "type_info": "Text" + "type_info": "Bool" }, { - "name": "git_commit_message", + "name": "git_commit", "ordinal": 12, "type_info": "Text" }, { - "name": "timestamp: time::OffsetDateTime", + "name": "git_commit_message", "ordinal": 13, "type_info": "Text" }, { - "name": "versions", + "name": "timestamp: time::OffsetDateTime", "ordinal": 14, "type_info": "Text" + }, + { + "name": "versions", + "ordinal": 15, + "type_info": "Text" } ], "parameters": { @@ -97,8 +102,9 @@ true, true, true, + true, true ] }, - "hash": "927b8e137db05ddd2996e9b2d04b80f8af3f6e2113654f3dd4a24ff5efb0855a" + "hash": "fc441fa93f0d46f6a0a541ae625154cc07efb5d4496d80d489f23a39f0e6b032" } diff --git a/Cargo.lock b/Cargo.lock index 3d335ee..c99ed21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1208,6 +1208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-rs-sys", "miniz_oxide", ] @@ -2186,6 +2187,15 @@ dependencies = [ "glob", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -5676,18 +5686,23 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.1" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" dependencies = [ "arbitrary", "crc32fast", - "crossbeam-utils", "flate2", "indexmap", "memchr", ] +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + [[package]] name = "zstd" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index c41ae11..68a5766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ [workspace.package] version = "0.1.0" edition = "2024" +rust-version = "1.88" [workspace.dependencies] anyhow = "1.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..740c503 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# [decomp.dev](https://decomp.dev) + +Decompilation progress website & GitHub bot. + +For more information, see the [wiki](https://wiki.decomp.dev/tools/decomp-dev). + +## Backend Setup + +1. Install Rust via [rustup](https://rustup.rs/): + ```bash + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + ``` +2. Clone the repository: + ```bash + git clone https://github.com/encounter/decomp.dev.git + cd decomp.dev + ``` +3. Copy `config.example.yml` to `config.yml`: + ```bash + cp config.example.yml config.yml + ``` +4. [Create a new GitHub personal access token](https://github.com/settings/tokens/new?description=decomp.dev&scopes=workflow,write:discussion) with scopes `workflow`, `write:discussion`. Set it in `config.yml`: + ```yaml + github: + token: ghp_abcd1234abcd1234abcd1234abcd1234abcd + ``` +5. Install [bacon](https://dystroy.org/bacon/): + ```bash + cargo install --locked bacon + ``` +6. Run the backend with automatic rebuild on changes: + ```bash + bacon + ``` + +When modifying Rust code, it will take a few seconds to build and restart the server. +Once the server updates, the browser page will automatically reload. + +## Frontend Setup + +1. Install dependencies: + ```bash + npm install + ``` +2. Start the frontend development server: + ```bash + npm start + ``` + +When the frontend development server is running, changes to files under `css/`, `js/` and `assets/` will live reload in the browser. + +## Testing + +When running both the backend and frontend, the site will be accessible at http://localhost:3000. + +By default, `server.dev_mode` in `config.yml` is enabled, allowing any visitor to log in as a superuser without authentication. + +To add projects to the site, visit http://localhost:3000/manage/new, using an existing project from [decomp.dev](https://decomp.dev/) as reference: + +![New project page](/docs/manage_new.png) diff --git a/config.example.yml b/config.example.yml index 6fd1f82..3df1072 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,5 +1,8 @@ server: port: 3000 + # Enable development mode + # WARNING: Allows anyone to log in as a super admin + dev_mode: true db: url: sqlite:./db.sqlite @@ -7,6 +10,3 @@ db: github: # Personal access token for public repo access token: ghp_abcd1234abcd1234abcd1234abcd1234abcd - -openai: - api_key: sk-proj-abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234 diff --git a/crates/auth/src/lib.rs b/crates/auth/src/lib.rs index 65b5b84..3076391 100644 --- a/crates/auth/src/lib.rs +++ b/crates/auth/src/lib.rs @@ -6,7 +6,10 @@ use axum::{ response::{IntoResponse, Redirect, Response}, }; use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD}; -use decomp_dev_core::{AppError, config::GitHubConfig}; +use decomp_dev_core::{ + AppError, + config::{Config, GitHubConfig}, +}; use decomp_dev_github::graphql::{ CurrentUserResponse, RepositoryPermission, fetch_current_user, fetch_simple_current_user, }; @@ -46,18 +49,25 @@ pub type Profile = Author; #[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct CurrentUser { - pub oauth: StoredOAuth, + pub oauth: Option, pub data: CurrentUserResponse, #[serde(skip, default)] pub super_admin: bool, } impl CurrentUser { - pub fn client(&self) -> Result { - Octocrab::builder() - .oauth(self.oauth.clone().into()) - .build() - .context("Failed to create GitHub client") + pub fn client(&self, config: &GitHubConfig) -> Result { + if let Some(oauth) = &self.oauth { + Octocrab::builder() + .oauth(oauth.clone().into()) + .build() + .context("Failed to create GitHub client") + } else { + Octocrab::builder() + .personal_token(config.token.clone()) + .build() + .context("Failed to create GitHub client") + } } pub fn permissions_for_repo(&self, id: u64) -> RepositoryPermission { @@ -202,7 +212,7 @@ async fn fetch_access_token(config: &GitHubConfig, code: &str) -> Result FromRequestParts for CurrentUser where - GitHubConfig: FromRef, + Config: FromRef, S: Send + Sync, { type Rejection = Response; @@ -268,7 +278,7 @@ where impl OptionalFromRequestParts for CurrentUser where - GitHubConfig: FromRef, + Config: FromRef, S: Send + Sync, { type Rejection = (StatusCode, &'static str); @@ -278,7 +288,7 @@ where state: &S, ) -> Result, Self::Rejection> { let session = Session::from_request_parts(parts, state).await?; - let config = GitHubConfig::from_ref(state); + let config = Config::from_ref(state); let mut user = match session.get::(CURRENT_USER).await { Ok(Some(user)) => user, Ok(None) => return Ok(None), @@ -289,7 +299,7 @@ where }; // Refresh user data if the ID is 0 if user.data.id == 0 { - match user.client() { + match user.client(&config.github) { Ok(client) => match fetch_simple_current_user(&client).await { Ok(data) => { user.data = @@ -315,35 +325,37 @@ where } } } - user.super_admin = config.super_admin_ids.contains(&user.data.id); - if let Some(expires_at) = user.oauth.expires_at { - if (UtcDateTime::now() + Duration::seconds(30)) > expires_at { - // Access token expired, attempt to refresh - if let Err(e) = session.remove_value(CURRENT_USER).await { - tracing::error!("Failed to remove user from session: {}", e); - }; - if let Some(refresh_token) = &user.oauth.refresh_token { - if let Some(refresh_token_expires_at) = user.oauth.refresh_token_expires_at { - if UtcDateTime::now() >= refresh_token_expires_at { - // Refresh token expired + user.super_admin = (config.server.dev_mode && user.data.id == u64::MAX) + || config.github.super_admin_ids.contains(&user.data.id); + if let Some(oauth) = &user.oauth + && let Some(expires_at) = oauth.expires_at + && (UtcDateTime::now() + Duration::seconds(30)) > expires_at + { + // Access token expired, attempt to refresh + if let Err(e) = session.remove_value(CURRENT_USER).await { + tracing::error!("Failed to remove user from session: {}", e); + }; + if let Some(refresh_token) = &oauth.refresh_token { + if let Some(refresh_token_expires_at) = oauth.refresh_token_expires_at + && UtcDateTime::now() >= refresh_token_expires_at + { + // Refresh token expired + return Ok(None); + } + let current_user = + match refresh_access_token(&config.github, refresh_token, &user).await { + Ok(current_user) => current_user, + Err(e) => { + tracing::error!("Failed to refresh access token: {:?}", e); return Ok(None); } - } - let current_user = - match refresh_access_token(&config, refresh_token, &user).await { - Ok(current_user) => current_user, - Err(e) => { - tracing::error!("Failed to refresh access token: {:?}", e); - return Ok(None); - } - }; - if let Err(e) = session.insert(CURRENT_USER, current_user.clone()).await { - tracing::error!("Failed to insert user into session: {}", e); - } - return Ok(Some(current_user)); + }; + if let Err(e) = session.insert(CURRENT_USER, current_user.clone()).await { + tracing::error!("Failed to insert user into session: {}", e); } - return Ok(None); + return Ok(Some(current_user)); } + return Ok(None); } Ok(Some(user)) } diff --git a/crates/cli/src/cmd/changes.rs b/crates/cli/src/cmd/changes.rs index ca7eb8d..44227ee 100644 --- a/crates/cli/src/cmd/changes.rs +++ b/crates/cli/src/cmd/changes.rs @@ -35,9 +35,9 @@ pub fn run(args: Args) -> Result<()> { let comment = generate_comment(&previous_report, ¤t_report, None, None, None, changes); if let Some(out_path) = &args.output { std::fs::write(out_path.with_platform_encoding(), comment) - .with_context(|| format!("Failed to write output file '{}'", out_path))?; + .with_context(|| format!("Failed to write output file '{out_path}'"))?; } else { - println!("{}", comment); + println!("{comment}"); } Ok(()) } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 624fbc2..12a9d29 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -11,6 +11,8 @@ pub struct Config { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ServerConfig { pub port: u16, + #[serde(default)] + pub dev_mode: bool, } #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 1fe0856..0b92e1c 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -25,7 +25,7 @@ impl IntoResponse for AppError { Self::Status(status) => status.into_response(), Self::Internal(err) => { tracing::error!("{:?}", err); - (StatusCode::INTERNAL_SERVER_ERROR, format!("Something went wrong: {}", err)) + (StatusCode::INTERNAL_SERVER_ERROR, format!("Something went wrong: {err}")) .into_response() } } diff --git a/crates/core/src/models.rs b/crates/core/src/models.rs index 26c7e07..f72605c 100644 --- a/crates/core/src/models.rs +++ b/crates/core/src/models.rs @@ -20,6 +20,7 @@ pub struct Project { pub workflow_id: Option, pub enable_pr_comments: bool, pub header_image_id: Option, + pub enabled: bool, } impl Default for Project { @@ -36,12 +37,13 @@ impl Default for Project { workflow_id: None, enable_pr_comments: true, header_image_id: None, + enabled: true, } } } impl Project { - pub fn name(&self) -> Cow { + pub fn name(&self) -> Cow<'_, str> { if let Some(name) = self.name.as_ref() { Cow::Borrowed(name) } else { @@ -54,6 +56,8 @@ impl Project { } pub fn repo_url(&self) -> String { format!("https://github.com/{}/{}", self.owner, self.repo) } + + pub fn default_category(&self) -> &str { self.default_category.as_deref().unwrap_or("all") } } #[derive(Debug, Clone, Eq, PartialEq, Serialize)] @@ -225,3 +229,21 @@ impl FromStr for Platform { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProjectVisibility { + Visible, + Hidden, + Disabled, +} + +pub fn project_visibility(project: &Project, measures: Option<&Measures>) -> ProjectVisibility { + // Hide projects with less than 0.5% matched code or if the project is disabled + if !project.enabled { + ProjectVisibility::Disabled + } else if measures.is_none_or(|m| m.matched_code_percent < 0.5) { + ProjectVisibility::Hidden + } else { + ProjectVisibility::Visible + } +} diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs index a747b6d..b8fb147 100644 --- a/crates/core/src/util.rs +++ b/crates/core/src/util.rs @@ -31,10 +31,8 @@ impl UrlExt for Url { pairs.append_pair(&k, &v); } } - if !updated { - if let Some(value) = value { - pairs.append_pair(key, value); - } + if !updated && let Some(value) = value { + pairs.append_pair(key, value); } drop(pairs); if out.query() == Some("") { diff --git a/crates/db/src/lib.rs b/crates/db/src/lib.rs index 381ef00..3936711 100644 --- a/crates/db/src/lib.rs +++ b/crates/db/src/lib.rs @@ -86,10 +86,11 @@ impl Database { ) -> Result<()> { let mut tx = self.pool.begin().await?; let project_id = project.id as i64; + let header_image_id = project.header_image_id.as_ref().map(|b| b.as_slice()); sqlx::query!( r#" - INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT (id) DO NOTHING "#, project_id, @@ -99,6 +100,11 @@ impl Database { project.short_name, project.default_category, project.default_version, + project.platform, + project.workflow_id, + project.enable_pr_comments, + header_image_id, + project.enabled, ) .execute(&mut *tx) .await?; @@ -358,7 +364,7 @@ impl Database { let mut conn = self.pool.acquire().await?; let project = match sqlx::query!( r#" - SELECT id AS "id!", owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id + SELECT id AS "id!", owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled FROM projects WHERE owner = ? COLLATE NOCASE AND repo = ? COLLATE NOCASE "#, @@ -380,6 +386,7 @@ impl Database { workflow_id: row.workflow_id, enable_pr_comments: row.enable_pr_comments, header_image_id: row.header_image_id.and_then(|b| b.try_into().ok()), + enabled: row.enabled, }, None => return Ok(None), }; @@ -395,7 +402,7 @@ impl Database { let project_id_db = project_id as i64; let project = match sqlx::query!( r#" - SELECT owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id + SELECT owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled FROM projects WHERE id = ? "#, @@ -416,6 +423,7 @@ impl Database { workflow_id: row.workflow_id, enable_pr_comments: row.enable_pr_comments, header_image_id: row.header_image_id.and_then(|b| b.try_into().ok()), + enabled: row.enabled, }, None => return Ok(None), }; @@ -550,6 +558,7 @@ impl Database { workflow_id, enable_pr_comments AS "enable_pr_comments!", header_image_id, + enabled AS "enabled!", git_commit, git_commit_message, MAX(timestamp) AS "timestamp: time::OffsetDateTime", @@ -583,6 +592,7 @@ impl Database { workflow_id: row.workflow_id, enable_pr_comments: row.enable_pr_comments, header_image_id: row.header_image_id.and_then(|b| b.try_into().ok()), + enabled: row.enabled, }, commit: match (row.git_commit, row.timestamp) { (Some(sha), Some(timestamp)) => Some(Commit { @@ -964,7 +974,7 @@ impl Database { sqlx::query!( r#" UPDATE projects - SET owner = ?, repo = ?, name = ?, short_name = ?, default_category = ?, default_version = ?, platform = ?, workflow_id = ?, enable_pr_comments = ?, header_image_id = ?, updated_at = CURRENT_TIMESTAMP + SET owner = ?, repo = ?, name = ?, short_name = ?, default_category = ?, default_version = ?, platform = ?, workflow_id = ?, enable_pr_comments = ?, header_image_id = ?, enabled = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? "#, project.owner, @@ -977,6 +987,7 @@ impl Database { project.workflow_id, project.enable_pr_comments, header_image_id, + project.enabled, project_id, ) .execute(&mut *conn) @@ -987,10 +998,11 @@ impl Database { pub async fn create_project(&self, project: &Project) -> Result<()> { let mut conn = self.pool.acquire().await?; let project_id = project.id as i64; + let header_image_id = project.header_image_id.as_ref().map(|b| b.as_slice()); sqlx::query!( r#" - INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) + INSERT INTO projects (id, owner, repo, name, short_name, default_category, default_version, platform, workflow_id, enable_pr_comments, header_image_id, enabled, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) "#, project_id, project.owner, @@ -1002,6 +1014,8 @@ impl Database { project.platform, project.workflow_id, project.enable_pr_comments, + header_image_id, + project.enabled, ) .execute(&mut *conn) .await?; @@ -1108,7 +1122,7 @@ thread_local! { fn compress(data: &[u8]) -> Vec { COMPRESSOR.with_borrow_mut(|z| z.compress(data).unwrap()) } -fn decompress(data: &[u8]) -> Result> { +fn decompress(data: &[u8]) -> Result> { match zstd::zstd_safe::get_frame_content_size(data) { Ok(Some(size)) => { Ok(Cow::Owned(DECOMPRESSOR.with_borrow_mut(|z| z.decompress(data, size as usize))?)) diff --git a/crates/github/Cargo.toml b/crates/github/Cargo.toml index 9692c3f..636d77b 100644 --- a/crates/github/Cargo.toml +++ b/crates/github/Cargo.toml @@ -24,4 +24,4 @@ sha2 = "0.10" time.workspace = true tokio.workspace = true tracing.workspace = true -zip = { version = "2.6", default-features = false, features = ["flate2", "deflate-flate2"] } +zip = { version = "4.2", default-features = false, features = ["deflate-flate2-zlib-rs"] } diff --git a/crates/github/src/lib.rs b/crates/github/src/lib.rs index d86f885..1e7d575 100644 --- a/crates/github/src/lib.rs +++ b/crates/github/src/lib.rs @@ -127,7 +127,7 @@ async fn list_installation_repositories( page += 1; response = app_client .get( - &format!("/installation/repositories?page={}", page), + &format!("/installation/repositories?page={page}"), Some(&PageParams { per_page: Some(100), page: Some(page) }), ) .await?; @@ -247,13 +247,13 @@ pub async fn refresh_project( .get_project_info_by_id(repo_id, None) .await .context("Failed to fetch project info")? - .with_context(|| format!("Failed to fetch project info for ID {}", repo_id))?; + .with_context(|| format!("Failed to fetch project info for ID {repo_id}"))?; let repo = client_override .unwrap_or(&github.client) .repos_by_id(project_info.project.id) .get() .await - .with_context(|| format!("Failed to fetch repo for ID {}", repo_id))?; + .with_context(|| format!("Failed to fetch repo for ID {repo_id}"))?; let branch = repo.default_branch.as_deref().unwrap_or("main"); let owner = repo.owner.context("Repository has no owner")?; @@ -270,7 +270,7 @@ pub async fn refresh_project( .get_project_info_by_id(repo_id, None) .await .context("Failed to fetch project info")? - .with_context(|| format!("Failed to fetch project info for ID {}", repo_id))?; + .with_context(|| format!("Failed to fetch project info for ID {repo_id}"))?; } let project = &project_info.project; @@ -324,16 +324,15 @@ pub async fn refresh_project( } Err(e) => { return Err(e) - .with_context(|| format!("Failed to fetch workflows page {}", page)); + .with_context(|| format!("Failed to fetch workflows page {page}")); } }; for run in items { - if !full_refresh { - if let Some(commit) = project_info.commit.as_ref() { - if run.head_sha == commit.sha { - break 'outer; - } - } + if !full_refresh + && let Some(commit) = project_info.commit.as_ref() + && run.head_sha == commit.sha + { + break 'outer; } runs.push(run); } @@ -644,3 +643,37 @@ pub async fn check_for_reports( } Err(anyhow!("No workflow runs containing reports found.")) } + +pub fn extract_github_url(url: &str) -> Option<(&str, &str)> { + static REGEX: OnceLock = OnceLock::new(); + let caps = REGEX + .get_or_init(|| { + Regex::new(r"^https?://github\.com/(?P[^/]+)/(?P[^/]+?)(?:\.git)?(?:/|$)") + .unwrap() + }) + .captures(url)?; + let owner = caps.name("owner").map(|m| m.as_str()).unwrap_or_default(); + let repo = caps.name("repo").map(|m| m.as_str()).unwrap_or_default(); + Some((owner, repo)) +} + +#[cfg(test)] +mod tests { + use super::extract_github_url; + + #[test] + fn test_extract_github_url() { + let cases: &[(&str, Option<(&str, &str)>)] = &[ + ("https://github.com/foo/bar", Some(("foo", "bar"))), + ("http://github.com/foo/bar/", Some(("foo", "bar"))), + ("https://github.com/foo/bar.git", Some(("foo", "bar"))), + ("https://github.com/foo/bar/issues/17", Some(("foo", "bar"))), + ("https://gitlab.com/foo/bar", None), + ("https://github.com/foo", None), + ("https://github.com/foo/bar.git/issues", Some(("foo", "bar"))), + ]; + for &(url, expected) in cases { + assert_eq!(extract_github_url(url), expected); + } + } +} diff --git a/crates/github/src/webhook.rs b/crates/github/src/webhook.rs index af57df7..2924b43 100644 --- a/crates/github/src/webhook.rs +++ b/crates/github/src/webhook.rs @@ -118,14 +118,12 @@ pub async fn webhook(GitHubEvent { event, state }: GitHubEvent) -> Result { - if inner.action == PullRequestWebhookEventAction::Opened - || inner.action == PullRequestWebhookEventAction::Synchronize + if (inner.action == PullRequestWebhookEventAction::Opened + || inner.action == PullRequestWebhookEventAction::Synchronize) + && let Err(e) = handle_pull_request_update(&state, client, inner.pull_request).await { - if let Err(e) = handle_pull_request_update(&state, client, inner.pull_request).await - { - tracing::error!("Error handling pull_request event: {e}"); - return Ok((StatusCode::OK, "Internal error").into_response()); - } + tracing::error!("Error handling pull_request event: {e}"); + return Ok((StatusCode::OK, "Internal error").into_response()); } } WebhookEventPayload::Installation(inner) => { diff --git a/crates/images/src/badge.rs b/crates/images/src/badge.rs index 5c2bfbb..759cdf6 100644 --- a/crates/images/src/badge.rs +++ b/crates/images/src/badge.rs @@ -30,14 +30,14 @@ pub struct ShieldResponse { label_color: Option, } -fn format_percent(value: f32) -> String { format!("{:.2}%", value) } +fn format_percent(value: f32) -> String { format!("{value:.2}%") } fn format_bytes(a: u64, b: u64) -> String { let mut a_buf = num_format::Buffer::default(); let mut b_buf = num_format::Buffer::default(); a_buf.write_formatted(&a, &num_format::Locale::en); b_buf.write_formatted(&b, &num_format::Locale::en); - format!("{} B / {} B", a_buf, b_buf) + format!("{a_buf} B / {b_buf} B") } fn format_num(a: u32, b: u32) -> String { @@ -45,7 +45,7 @@ fn format_num(a: u32, b: u32) -> String { let mut b_buf = num_format::Buffer::default(); a_buf.write_formatted(&a, &num_format::Locale::en); b_buf.write_formatted(&b, &num_format::Locale::en); - format!("{} / {}", a_buf, b_buf) + format!("{a_buf} / {b_buf}") } fn format_size(a: u64, b: u64) -> String { format!("{} / {}", size(a), size(b)) } diff --git a/crates/web/src/cron.rs b/crates/web/src/cron.rs index ae348d4..a3e2783 100644 --- a/crates/web/src/cron.rs +++ b/crates/web/src/cron.rs @@ -64,6 +64,14 @@ pub async fn create( pub async fn refresh_projects(state: &AppState, full_refresh: bool) -> Result<()> { for project_info in state.db.get_projects().await? { + if !project_info.project.enabled { + log::debug!( + "Skipping disabled project {}/{}", + project_info.project.owner, + project_info.project.repo + ); + continue; + } if !full_refresh { // Skip projects with active app installations if let Some(installations) = &state.github.installations { diff --git a/crates/web/src/frogress/mod.rs b/crates/web/src/frogress/mod.rs index 257265f..d5ffb1c 100644 --- a/crates/web/src/frogress/mod.rs +++ b/crates/web/src/frogress/mod.rs @@ -41,11 +41,9 @@ pub async fn migrate_data(state: &mut AppState) -> Result<()> { .get_project_info_by_id(project_id, None) .await? .ok_or_else(|| anyhow!("No project ID {}", project_id))?; - let data = reqwest::get(&format!( - "https://progress.decomp.club/data/{}/{}?mode=all", - slug, version - )) - .await?; + let data = + reqwest::get(&format!("https://progress.decomp.club/data/{slug}/{version}?mode=all")) + .await?; let data: FrogressAllData = data.json().await?; let mut data_iter = data.iter(); let (data_slug, version_data) = @@ -196,7 +194,7 @@ pub async fn migrate_data(state: &mut AppState) -> Result<()> { } if report.measures.is_none() { if report.categories.is_empty() { - log::warn!("No measures for {}/{}/{}", slug, version, category); + log::warn!("No measures for {slug}/{version}/{category}"); reports.remove(&entry.git_hash); } else { let mut all_measures = Measures::default(); diff --git a/crates/web/src/handlers/auth.rs b/crates/web/src/handlers/auth.rs index 4291a13..8833e47 100644 --- a/crates/web/src/handlers/auth.rs +++ b/crates/web/src/handlers/auth.rs @@ -3,8 +3,9 @@ use axum::{ http::{HeaderMap, StatusCode, header::REFERER}, response::{IntoResponse, Redirect, Response}, }; -use decomp_dev_auth::{CurrentUser, GITHUB_OAUTH_STATE, RETURN_TO, generate_nonce}; -use decomp_dev_core::{AppError, config::GitHubConfig, util::UrlExt}; +use decomp_dev_auth::{CURRENT_USER, CurrentUser, GITHUB_OAUTH_STATE, RETURN_TO, generate_nonce}; +use decomp_dev_core::{AppError, config::Config, util::UrlExt}; +use decomp_dev_github::graphql::CurrentUserResponse; use maud::{DOCTYPE, html}; use tower_sessions::Session; @@ -19,14 +20,30 @@ pub async fn login( session: Session, headers: HeaderMap, Query(query): Query, - State(config): State, + State(config): State, current_user: Option, mut ctx: TemplateContext, ) -> Result { if current_user.is_some() { return Ok(Redirect::to("/").into_response()); } - let Some(config) = &config.oauth else { + let Some(config) = &config.github.oauth else { + // Dev mode override + if config.server.dev_mode { + session + .insert(CURRENT_USER, CurrentUser { + oauth: None, + data: CurrentUserResponse { + id: u64::MAX, + login: "devuser".to_string(), + url: String::new(), + repositories: vec![], + }, + super_admin: true, + }) + .await?; + return Ok(Redirect::to("/").into_response()); + } tracing::warn!("No GitHub OAuth config found"); return Ok((StatusCode::INTERNAL_SERVER_ERROR, "No GitHub OAuth config").into_response()); }; diff --git a/crates/web/src/handlers/common.rs b/crates/web/src/handlers/common.rs index cbe73a3..c478f4f 100644 --- a/crates/web/src/handlers/common.rs +++ b/crates/web/src/handlers/common.rs @@ -381,9 +381,9 @@ impl ProgressSections { } pub fn push(&mut self, class: &str, percent: f32, tooltip: String) { - let width_class = format!("width-{:.2}", percent).replace('.', "p"); + let width_class = format!("width-{percent:.2}").replace('.', "p"); let rendered = html! { .(class).(width_class) data-tooltip=(tooltip) {} }; - self.width_classes.insert(width_class, format!("width:{}%", percent)); + self.width_classes.insert(width_class, format!("width:{percent}%")); self.rendered.0.push_str(&rendered.0); } } @@ -454,7 +454,7 @@ pub async fn get_robots() -> Result { Ok(text) } -pub fn escape_script(text: &str) -> PreEscaped> { +pub fn escape_script(text: &str) -> PreEscaped> { static REGEX: LazyLock = LazyLock::new(|| Regex::new("<(?i:!--|/?script)").unwrap()); PreEscaped(REGEX.replace_all(text, |caps: ®ex::Captures| format!("\\x3C{}", &caps[0][1..]))) } diff --git a/crates/web/src/handlers/csp.rs b/crates/web/src/handlers/csp.rs index 895209c..e27a13c 100644 --- a/crates/web/src/handlers/csp.rs +++ b/crates/web/src/handlers/csp.rs @@ -58,7 +58,7 @@ pub async fn csp_middleware(mut req: Request, next: Next) -> Response { response.extensions().get::().map(|e| e.0.clone()).unwrap_or_default(); let response_headers = response.headers_mut(); let mut header = "default-src 'none';base-uri 'none';script-src ".to_string(); - let nonce_value = format!("'nonce-{}'", nonce); + let nonce_value = format!("'nonce-{nonce}'"); header.push_str(&nonce_value); #[cfg(debug_assertions)] { diff --git a/crates/web/src/handlers/images.rs b/crates/web/src/handlers/images.rs index 0ca3727..8a7b46d 100644 --- a/crates/web/src/handlers/images.rs +++ b/crates/web/src/handlers/images.rs @@ -54,21 +54,21 @@ pub async fn get_image( let mut transforms = Vec::::new(); let mut current_width = image_width; let mut current_height = image_height; - if let Some(width) = query.width { - if current_width > width { - // Maintaining aspect ratio - let height = (current_height as f32 * (width as f32 / current_width as f32)) as u32; - current_width = width; - current_height = height; - } + if let Some(width) = query.width + && current_width > width + { + // Maintaining aspect ratio + let height = (current_height as f32 * (width as f32 / current_width as f32)) as u32; + current_width = width; + current_height = height; } - if let Some(height) = query.height { - if current_height > height { - // Maintaining aspect ratio - let width = (current_width as f32 * (height as f32 / current_height as f32)) as u32; - current_width = width; - current_height = height; - } + if let Some(height) = query.height + && current_height > height + { + // Maintaining aspect ratio + let width = (current_width as f32 * (height as f32 / current_height as f32)) as u32; + current_width = width; + current_height = height; } if current_width != image_width || current_height != image_height { transforms.push(Transform::Resize(current_width, current_height)); diff --git a/crates/web/src/handlers/manage.rs b/crates/web/src/handlers/manage.rs index 032587a..9c6aa08 100644 --- a/crates/web/src/handlers/manage.rs +++ b/crates/web/src/handlers/manage.rs @@ -1,6 +1,6 @@ use std::io::Cursor; -use anyhow::Result; +use anyhow::{Context, Result}; use axum::{ Form, extract::{Path, State}, @@ -12,9 +12,14 @@ use bytes::Bytes; use decomp_dev_auth::CurrentUser; use decomp_dev_core::{ AppError, - models::{ALL_PLATFORMS, Project, ProjectInfo}, + models::{ + ALL_PLATFORMS, CachedReportFile, Project, ProjectInfo, ProjectVisibility, + project_visibility, + }, +}; +use decomp_dev_github::{ + check_for_reports, extract_github_url, graphql::RepositoryPermission, refresh_project, }; -use decomp_dev_github::{check_for_reports, graphql::RepositoryPermission, refresh_project}; use itertools::Itertools; use maud::{DOCTYPE, Markup, html}; use serde::Deserialize; @@ -82,9 +87,9 @@ pub async fn manage( fn project_fragment(info: &ProjectInfo) -> Markup { let project_path = format!("/manage/{}/{}", info.project.owner, info.project.repo); html! { - article .project { + article.project { .project-header { - h3 .project-title { + h3.project-title { a href=(project_path) { (info.project.name()) } } } @@ -123,6 +128,7 @@ async fn render_new( .sorted_by(|a, b| lexicmp::lexical_cmp(&a.1, &b.1)) .collect::>(); + let current_url = prefill.as_ref().map(|p| p.repo_url()).unwrap_or_default(); let current_name = prefill.as_ref().and_then(|p| p.name.as_deref()).unwrap_or(""); let current_short_name = prefill.as_ref().and_then(|p| p.short_name.as_deref()).unwrap_or(""); let current_platform = prefill.as_ref().and_then(|p| p.platform.as_deref()); @@ -172,11 +178,16 @@ async fn render_new( fieldset { label { "Repository" + @if current_user.super_admin { + input name="repository_url" type="url" required + aria-invalid=[message.map(|_| "true")] + value=(current_url); + } @else { + select name="repository_id" aria-invalid=[message.map(|_| "true")] { (repo_options) } + } @if let Some(message) = message { - select name="repo" aria-invalid="true" { (repo_options) } small { (message) } } @else { - select name="repo" { (repo_options) } small { "Repository must be public. Admin permissions are required." } } } @@ -225,7 +236,8 @@ fn platform_options(current_platform: Option<&str>) -> Markup { #[derive(Deserialize)] pub struct NewForm { - repo: u64, + repository_id: Option, + repository_url: Option, name: String, short_name: String, platform: String, @@ -237,27 +249,54 @@ pub async fn new_save( current_user: CurrentUser, Form(form): Form, ) -> Result { - if let Some(existing) = state.db.get_project_info_by_id(form.repo, None).await? { + let client = current_user.client(&state.config.github)?; + let (repository_id, repo) = match (form.repository_id, form.repository_url) { + (Some(id), _) => (id, None), + (None, Some(url)) => { + let Some((owner, repo)) = extract_github_url(&url) else { + return render_new( + ctx, + &state, + ¤t_user, + Some("Invalid repository URL."), + None, + ) + .await; + }; + let Ok(repo) = client.repos(owner, repo).get().await else { + return render_new(ctx, &state, ¤t_user, Some("Repository not found."), None) + .await; + }; + (repo.id.into_inner(), Some(repo)) + } + (None, None) => { + return render_new(ctx, &state, ¤t_user, Some("Repository is required."), None) + .await; + } + }; + if let Some(existing) = state.db.get_project_info_by_id(repository_id, None).await? { return Ok(Redirect::to(&format!("/{}/{}", existing.project.owner, existing.project.repo)) .into_response()); } let Some(platform) = ALL_PLATFORMS.iter().find(|p| p.to_str() == form.platform) else { return Err(AppError::Status(StatusCode::BAD_REQUEST)); }; - let client = current_user.client()?; - let repo = match client.repos_by_id(form.repo).get().await { - Ok(repo) => repo, - Err(e) => { - tracing::error!("Failed to fetch repository: {:?}", e); - return render_new( - ctx, - &state, - ¤t_user, - Some("Failed to fetch repository information."), - None, - ) - .await; - } + let repo = match repo { + Some(repo) => repo, + None => match client.repos_by_id(repository_id).get().await { + Ok(repo) => repo, + Err(e) => { + tracing::error!("Failed to fetch repository: {:?}", e); + return render_new( + ctx, + &state, + ¤t_user, + Some("Failed to fetch repository information."), + None, + ) + .await; + } + }, }; let name = form.name.trim(); @@ -271,7 +310,7 @@ pub async fn new_save( platform: Some(platform.to_str().to_string()), ..Default::default() }; - if repo.permissions.as_ref().is_none_or(|p| !p.admin) { + if !current_user.super_admin && repo.permissions.as_ref().is_none_or(|p| !p.admin) { return render_new( ctx, &state, @@ -301,15 +340,28 @@ pub async fn manage_project( current_user: CurrentUser, ctx: TemplateContext, ) -> Result { - let Some(project_info) = state.db.get_project_info(¶ms.owner, ¶ms.repo, None).await? - else { + let Some(info) = state.db.get_project_info(¶ms.owner, ¶ms.repo, None).await? else { return Err(AppError::Status(StatusCode::NOT_FOUND)); }; - if !current_user.can_manage_repo(project_info.project.id) { + if !current_user.can_manage_repo(info.project.id) { return Err(AppError::Status(StatusCode::FORBIDDEN)); } - render_manage_project(ctx, &state, &project_info, ¤t_user, Message::None).await + let report = if let (Some(version), Some(commit)) = (info.default_version(), &info.commit) { + state + .db + .get_report(&info.project.owner, &info.project.repo, &commit.sha, version) + .await + .with_context(|| { + format!( + "Failed to fetch report for {}/{} sha {} version {}", + info.project.owner, info.project.repo, commit.sha, version + ) + })? + } else { + None + }; + render_manage_project(ctx, &state, &info, report.as_ref(), ¤t_user, Message::None).await } enum Message { @@ -322,10 +374,10 @@ fn render_message(message: &Message) -> Markup { match message { Message::None => Markup::default(), Message::Info(msg) => html! { - article .info-card { (msg) } + article.info-card { (msg) } }, Message::Error(msg) => html! { - article .error-card { (msg) } + article.error-card { (msg) } }, } } @@ -334,6 +386,7 @@ async fn render_manage_project( mut ctx: TemplateContext, state: &AppState, project_info: &ProjectInfo, + latest_report: Option<&CachedReportFile>, current_user: &CurrentUser, message: Message, ) -> Result { @@ -356,6 +409,10 @@ async fn render_manage_project( None }; + // Check if the project is hidden based on matched code percentage + let visibility = + project_visibility(&project_info.project, latest_report.map(|r| &r.report.measures)); + let rendered = html! { (DOCTYPE) html lang="en" { @@ -386,8 +443,24 @@ async fn render_manage_project( main { h3 { "Edit " (project_short_name) } (render_message(&message)) + @match visibility { + ProjectVisibility::Visible => {}, + ProjectVisibility::Disabled => { + article.warning-card { "This project is disabled." } + } + ProjectVisibility::Hidden => { + article.warning-card { "This project is hidden until it has reached a minimum of 0.5% matched code." } + } + } form method="post" enctype="multipart/form-data" data-loading="Saving..." { fieldset { + label { + input name="enabled" type="checkbox" role="switch" + checked[project_info.project.enabled]; + "Enable project" + br; + small.muted { "Disabled projects will not be listed, and reports will not be fetched." } + } label { "Repository" input type="text" readonly disabled value=(project_info.project.repo_url()); @@ -489,6 +562,7 @@ pub struct ProjectForm { pub enable_pr_comments: Option, pub header_image: Option, pub clear_header_image: Option, + pub enabled: Option, } #[derive(Deserialize)] @@ -553,6 +627,7 @@ pub async fn manage_project_save( project_info.project.enable_pr_comments }, header_image_id, + enabled: form.enabled.is_some_and(|v| v == "on"), }; state.db.update_project(&project).await?; let redirect_url = format!("/{}/{}", params.owner, params.repo); @@ -565,28 +640,38 @@ pub async fn manage_project_refresh( State(state): State, current_user: CurrentUser, ) -> Result { - let Some(project_info) = state.db.get_project_info(¶ms.owner, ¶ms.repo, None).await? - else { + let Some(info) = state.db.get_project_info(¶ms.owner, ¶ms.repo, None).await? else { return Err(AppError::Status(StatusCode::NOT_FOUND)); }; - if !current_user.can_manage_repo(project_info.project.id) { + if !current_user.can_manage_repo(info.project.id) { return Err(AppError::Status(StatusCode::FORBIDDEN)); } - let client = current_user.client()?; - let message = match refresh_project( - &state.github, - &state.db, - project_info.project.id, - Some(&client), - true, - ) - .await - { - Ok(inserted_reports) => Message::Info(format!("Fetched {} new reports", inserted_reports)), - Err(e) => { - tracing::error!("Failed to refresh project: {:?}", e); - Message::Error(format!("Failed to refresh project: {}", e)) - } + let client = current_user.client(&state.config.github)?; + let message = + match refresh_project(&state.github, &state.db, info.project.id, Some(&client), true).await + { + Ok(inserted_reports) => { + Message::Info(format!("Fetched {inserted_reports} new reports")) + } + Err(e) => { + tracing::error!("Failed to refresh project: {:?}", e); + Message::Error(format!("Failed to refresh project: {e}")) + } + }; + + let report = if let (Some(version), Some(commit)) = (info.default_version(), &info.commit) { + state + .db + .get_report(&info.project.owner, &info.project.repo, &commit.sha, version) + .await + .with_context(|| { + format!( + "Failed to fetch report for {}/{} sha {} version {}", + info.project.owner, info.project.repo, commit.sha, version + ) + })? + } else { + None }; - render_manage_project(ctx, &state, &project_info, ¤t_user, message).await + render_manage_project(ctx, &state, &info, report.as_ref(), ¤t_user, message).await } diff --git a/crates/web/src/handlers/mod.rs b/crates/web/src/handlers/mod.rs index 6476a77..89f1256 100644 --- a/crates/web/src/handlers/mod.rs +++ b/crates/web/src/handlers/mod.rs @@ -89,7 +89,7 @@ fn add_charset(response: &mut Response) { return; }; if mime.type_() == mime::TEXT && mime.get_param("charset").is_none() { - existing.insert(format!("{};charset=utf-8", mime).parse().unwrap()); + existing.insert(format!("{mime};charset=utf-8").parse().unwrap()); } } diff --git a/crates/web/src/handlers/project.rs b/crates/web/src/handlers/project.rs index c25011d..465d7ed 100644 --- a/crates/web/src/handlers/project.rs +++ b/crates/web/src/handlers/project.rs @@ -10,7 +10,10 @@ use axum::{ use decomp_dev_auth::CurrentUser; use decomp_dev_core::{ AppError, FullUri, - models::{CachedReportFile, Commit, Platform, ProjectInfo, ReportInner}, + models::{ + CachedReportFile, Commit, Platform, ProjectInfo, ProjectVisibility, ReportInner, + project_visibility, + }, util::{UrlExt, size}, }; use maud::{DOCTYPE, Markup, html}; @@ -55,13 +58,6 @@ const SORT_OPTIONS: &[SortOption] = &[ SortOption { key: "total_code", name: "Total Code" }, ]; -#[derive(Serialize, Clone)] -pub struct ProgressSection { - pub class: String, - pub percent: f32, - pub tooltip: String, -} - #[derive(serde::Serialize)] pub struct ProjectsResponse { pub projects: Vec, @@ -218,6 +214,11 @@ pub async fn get_projects( } } + // Hide projects that are disabled or don't meet visibility criteria + out.retain(|c| { + project_visibility(&c.info.project, Some(&c.measures)) == ProjectVisibility::Visible + }); + let current_sort_key = query.sort.as_deref().unwrap_or("updated"); let current_sort = SORT_OPTIONS .iter() diff --git a/crates/web/src/handlers/report.rs b/crates/web/src/handlers/report.rs index 6f427c9..4bf53c1 100644 --- a/crates/web/src/handlers/report.rs +++ b/crates/web/src/handlers/report.rs @@ -10,7 +10,7 @@ use axum::{ use decomp_dev_auth::CurrentUser; use decomp_dev_core::{ AppError, FullUri, - models::{FullReportFile, ProjectInfo}, + models::{FullReportFile, ProjectInfo, ProjectVisibility, project_visibility}, util::{UrlExt, size}, }; use decomp_dev_images::{ @@ -162,41 +162,37 @@ fn is_valid_extension(ext: &str) -> bool { fn extract_extension(params: ReportParams) -> (ReportParams, Option) { if let Some(commit) = params.commit.as_deref() { - if let Some((commit, ext)) = commit.rsplit_once('.') { - if is_valid_extension(ext) { - return ( - ReportParams { commit: Some(commit.to_string()), ..params }, - Some(ext.to_string()), - ); - } + if let Some((commit, ext)) = commit.rsplit_once('.') + && is_valid_extension(ext) + { + return ( + ReportParams { commit: Some(commit.to_string()), ..params }, + Some(ext.to_string()), + ); } } else if let Some(version) = params.version.as_deref() { - if let Some((version, ext)) = version.rsplit_once('.') { - if is_valid_extension(ext) { - return ( - ReportParams { version: Some(version.to_string()), ..params }, - Some(ext.to_string()), - ); - } + if let Some((version, ext)) = version.rsplit_once('.') + && is_valid_extension(ext) + { + return ( + ReportParams { version: Some(version.to_string()), ..params }, + Some(ext.to_string()), + ); } } else if let Some(repo) = params.repo.as_deref() { - if let Some((repo, ext)) = repo.rsplit_once('.') { - if is_valid_extension(ext) { - return ( - ReportParams { repo: Some(repo.to_string()), ..params }, - Some(ext.to_string()), - ); - } - } - } else if let Some(id) = params.id.as_deref() { - if let Some((id, ext)) = id.rsplit_once('.') { - if is_valid_extension(ext) { - return ( - ReportParams { id: Some(id.to_string()), ..params }, - Some(ext.to_string()), - ); - } + if let Some((repo, ext)) = repo.rsplit_once('.') + && is_valid_extension(ext) + { + return ( + ReportParams { repo: Some(repo.to_string()), ..params }, + Some(ext.to_string()), + ); } + } else if let Some(id) = params.id.as_deref() + && let Some((id, ext)) = id.rsplit_once('.') + && is_valid_extension(ext) + { + return (ReportParams { id: Some(id.to_string()), ..params }, Some(ext.to_string())); } (params, None) } @@ -547,14 +543,13 @@ fn apply_scope<'a>( .units .iter() .filter_map(|unit| { - if let Some(category_id) = &category_id_filter { - if !unit + if let Some(category_id) = &category_id_filter + && !unit .metadata .as_ref() .is_some_and(|m| m.progress_categories.iter().any(|c| c == category_id)) - { - return None; - } + { + return None; } let measures = unit.measures.as_ref()?; if measures.total_code == 0 { @@ -644,9 +639,14 @@ async fn render_report( format!("/manage/{}/{}", project_info.project.owner, project_info.project.repo); let can_manage = current_user.as_ref().is_some_and(|u| u.can_manage_repo(project_info.project.id)); + let default_category = project_info.project.default_category(); let is_default_version = project_info.default_version() == Some(report.version.as_str()); let is_latest_commit = project_info.next_commit.is_none(); + let is_default_category = current_category.is_none_or(|c| c.id == default_category); + let is_primary_view = + is_latest_commit && is_default_version && is_default_category && current_unit.is_none(); + let canonical_url = if is_default_version && is_latest_commit { request_url .with_path(&format!("/{}/{}", project_info.project.owner, project_info.project.repo)) @@ -691,14 +691,8 @@ async fn render_report( }) .collect::>(); - let all_url = canonical_url.query_param( - "category", - if project_info.project.default_category.as_deref().is_none_or(|c| c == "all") { - None - } else { - Some("all") - }, - ); + let all_url = canonical_url + .query_param("category", if default_category == "all" { None } else { Some("all") }); let all_category = ReportCategoryItem { id: "all", name: "All", path: all_url.path_and_query().to_string() }; let current_category = current_category @@ -754,11 +748,18 @@ async fn render_report( }; let project_short_name = project_info.project.short_name(); let project_short_name_with_label = if let Some(label) = label { - Cow::Owned(format!("{} ({})", project_short_name, label)) + Cow::Owned(format!("{project_short_name} ({label})")) } else { Cow::Borrowed(project_short_name) }; + // Only show visibility banners if we're on the primary view + let visibility = if is_primary_view { + project_visibility(&project_info.project, Some(measures)) + } else { + ProjectVisibility::Visible + }; + // Load blocking resources first so we don't duplicate them let header = ctx.header().await; let report_chunks = ctx.chunks("report", Load::Blocking).await; @@ -784,7 +785,7 @@ async fn render_report( meta property="og:description" content=(format!("Decompilation progress report for {project_name}")); meta property="og:image" content=(image_url); meta property="og:url" content=(canonical_url); - @if !is_latest_commit || !is_default_version { + @if !is_primary_view { // Prevent search engines from indexing anything but the primary report meta name="robots" content="noindex"; } @@ -819,6 +820,15 @@ async fn render_report( } } main { + @match visibility { + ProjectVisibility::Visible => {}, + ProjectVisibility::Disabled => { + article.warning-card { "This project is disabled." } + } + ProjectVisibility::Hidden => { + article.warning-card { "This project is hidden until it has reached a minimum of 0.5% matched code." } + } + } .actions { details.dropdown { summary {} @@ -949,18 +959,28 @@ async fn render_report( } } } - canvas #treemap {} - (report_chunks) - script nonce=[ctx.nonce.as_deref()] { - (PreEscaped(r#"window.units="#)) - (escape_script(&serde_json::to_string(&units)?)) - (PreEscaped(r#";drawTreemap("treemap","#)) - (current_unit.is_none()) - (PreEscaped(r#",window.units)"#)) - } - noscript { - style nonce=[ctx.nonce.as_deref()] { "canvas{display:none}" } - img #treemap src=(image_url) alt="Progress graph"; + @if units.is_empty() { + p.muted { + @if current_unit.is_some() { + "No function information available." + } @else { + "No unit information available." + } + } + } @else { + canvas #treemap {} + (report_chunks) + script nonce=[ctx.nonce.as_deref()] { + (PreEscaped(r#"window.units="#)) + (escape_script(&serde_json::to_string(&units)?)) + (PreEscaped(r#";drawTreemap("treemap","#)) + (current_unit.is_none()) + (PreEscaped(r#",window.units)"#)) + } + noscript { + style nonce=[ctx.nonce.as_deref()] { "canvas{display:none}" } + img #treemap src=(image_url) alt="Progress graph"; + } } } } @@ -1036,7 +1056,7 @@ async fn render_history( }; let project_short_name = project_info.project.short_name(); let project_short_name_with_label = if let Some(label) = label { - Cow::Owned(format!("{} ({})", project_short_name, label)) + Cow::Owned(format!("{project_short_name} ({label})")) } else { Cow::Borrowed(project_short_name) }; diff --git a/crates/web/src/main.rs b/crates/web/src/main.rs index 93a4434..91f2788 100644 --- a/crates/web/src/main.rs +++ b/crates/web/src/main.rs @@ -150,7 +150,7 @@ fn app(state: AppState, session_store: impl SessionStore + Clone) -> Router { .make_span_with(MyMakeSpan { level: Level::INFO }) .on_response(DefaultOnResponse::new().level(Level::INFO)), ) - .layer(TimeoutLayer::new(Duration::from_secs(60))) + .layer(TimeoutLayer::new(Duration::from_secs(120))) .layer(CorsLayer::new().allow_methods([Method::GET]).allow_origin(cors::Any)) .layer( SessionManagerLayer::new(session_store) diff --git a/css/main.scss b/css/main.scss index d850aec..6c15605 100644 --- a/css/main.scss +++ b/css/main.scss @@ -533,9 +533,18 @@ body > footer { details.dropdown { clear: right; + + summary + ul { + max-height: 50vh; + overflow-y: auto; + } } .repo-link { float: right; margin-inline-start: 1em; } + +label:has(> input[type="checkbox"]) { + margin-bottom: var(--pico-spacing); +} diff --git a/docs/manage_new.png b/docs/manage_new.png new file mode 100644 index 0000000000000000000000000000000000000000..d891fb27308cf14c62c0717e3e561c1f4152b43b GIT binary patch literal 93336 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

Ut@ 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�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 X))zj1+3w}~1{86gu6{1-oD!M