mirror of
https://github.com/encounter/decomp.dev.git
synced 2026-07-10 03:18:48 -07:00
Add README.md, local dev mode & project visibility
This commit is contained in:
+9
-3
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
-12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+9
-3
@@ -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"
|
||||
}
|
||||
-12
@@ -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"
|
||||
}
|
||||
+3
-3
@@ -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"
|
||||
}
|
||||
+13
-7
@@ -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"
|
||||
}
|
||||
Generated
+18
-3
@@ -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"
|
||||
|
||||
@@ -13,6 +13,7 @@ members = [
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0"
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
+3
-3
@@ -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
|
||||
|
||||
+50
-38
@@ -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<StoredOAuth>,
|
||||
pub data: CurrentUserResponse,
|
||||
#[serde(skip, default)]
|
||||
pub super_admin: bool,
|
||||
}
|
||||
|
||||
impl CurrentUser {
|
||||
pub fn client(&self) -> Result<Octocrab> {
|
||||
Octocrab::builder()
|
||||
.oauth(self.oauth.clone().into())
|
||||
.build()
|
||||
.context("Failed to create GitHub client")
|
||||
pub fn client(&self, config: &GitHubConfig) -> Result<Octocrab> {
|
||||
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<Current
|
||||
} else {
|
||||
tracing::info!("Logged in as @{} ({} repos)", data.login, data.repositories.len());
|
||||
}
|
||||
Ok(CurrentUser { oauth, data, super_admin })
|
||||
Ok(CurrentUser { oauth: Some(oauth), data, super_admin })
|
||||
}
|
||||
|
||||
async fn refresh_access_token(
|
||||
@@ -229,12 +239,12 @@ async fn refresh_access_token(
|
||||
let oauth = StoredOAuth::from(oauth);
|
||||
let super_admin = config.super_admin_ids.contains(&prev_auth.data.id);
|
||||
tracing::info!("Refreshed token for @{}", prev_auth.data.login);
|
||||
Ok(CurrentUser { oauth, data: prev_auth.data.clone(), super_admin })
|
||||
Ok(CurrentUser { oauth: Some(oauth), data: prev_auth.data.clone(), super_admin })
|
||||
}
|
||||
|
||||
impl<S> FromRequestParts<S> for CurrentUser
|
||||
where
|
||||
GitHubConfig: FromRef<S>,
|
||||
Config: FromRef<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
@@ -268,7 +278,7 @@ where
|
||||
|
||||
impl<S> OptionalFromRequestParts<S> for CurrentUser
|
||||
where
|
||||
GitHubConfig: FromRef<S>,
|
||||
Config: FromRef<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, &'static str);
|
||||
@@ -278,7 +288,7 @@ where
|
||||
state: &S,
|
||||
) -> Result<Option<Self>, 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::<CurrentUser>(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))
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct Project {
|
||||
pub workflow_id: Option<String>,
|
||||
pub enable_pr_comments: bool,
|
||||
pub header_image_id: Option<ImageId>,
|
||||
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<str> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("") {
|
||||
|
||||
+22
-8
@@ -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<u8> { COMPRESSOR.with_borrow_mut(|z| z.compress(data).unwrap()) }
|
||||
|
||||
fn decompress(data: &[u8]) -> Result<Cow<[u8]>> {
|
||||
fn decompress(data: &[u8]) -> Result<Cow<'_, [u8]>> {
|
||||
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))?))
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user