From 28ebe55193971364522fb5ece402b6098e091815 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Thu, 21 Nov 2024 11:24:29 +0800 Subject: [PATCH 1/2] add test authentication user --- Cargo.toml | 18 ++-- ceres/src/pack/import_repo.rs | 9 +- ceres/src/pack/mod.rs | 4 +- ceres/src/pack/monorepo.rs | 132 ++++++++++++++------------- ceres/src/protocol/smart.rs | 5 +- common/src/config.rs | 22 ++++- docker/config.toml | 18 +++- mega/config.toml | 18 +++- mono/config.toml | 18 +++- mono/src/api/mr/mod.rs | 2 +- mono/src/git_protocol/http.rs | 32 ++++--- mono/src/server/https_server.rs | 3 +- moon/src/app/(dashboard)/mr/page.tsx | 2 +- 13 files changed, 180 insertions(+), 103 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd8ea72a3..c94b9a964 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,19 +35,19 @@ mega = { path = "mega" } mono = { path = "mono" } libra = { path = "libra" } -anyhow = "1.0.90" -serde = "1.0.214" +anyhow = "1.0.93" +serde = "1.0.215" serde_json = "1.0.132" tracing = "0.1.40" tracing-subscriber = "0.3.18" tracing-appender = "0.2" -thiserror = "2.0.0" +thiserror = "2.0.3" rand = "0.8.5" smallvec = "1.13.2" -tokio = "1.41.0" +tokio = "1.41.1" tokio-stream = "0.1.16" tokio-test = "0.4.4" -clap = "4.5.20" +clap = "4.5.21" async-trait = "0.1.83" async-stream = "0.3.6" bytes = "1.8.0" @@ -66,8 +66,8 @@ tower-http = "0.6.1" tower = "0.5.1" hex = "0.4.3" sea-orm = "1.1.1" -flate2 = "1.0.34" -bstr = "1.10.0" +flate2 = "1.0.35" +bstr = "1.11.0" colored = "2.1.0" idgenerator = "2.0.0" num_cpus = "1.16.0" @@ -80,10 +80,10 @@ regex = "1.11.1" ed25519-dalek = "2.1.1" ctrlc = "3.4.4" git2 = "0.19.0" -tempfile = "3.13.0" +tempfile = "3.14.0" home = "0.5.9" ring = "0.17.8" -cedar-policy = "4.2.1" +cedar-policy = "4.2.2" secp256k1 = "0.30.0" oauth2 = "4.4.2" base64 = "0.22.1" diff --git a/ceres/src/pack/import_repo.rs b/ceres/src/pack/import_repo.rs index 8d9c09418..95034db73 100644 --- a/ceres/src/pack/import_repo.rs +++ b/ceres/src/pack/import_repo.rs @@ -55,7 +55,7 @@ impl PackHandler for ImportRepo { self.find_head_hash(refs) } - async fn handle_receiver(&self, receiver: Receiver) -> Result<(), GitError> { + async fn handle_receiver(&self, receiver: Receiver) -> Result { let storage = self.context.services.git_db_storage.clone(); let mut entry_list = vec![]; let mut join_tasks = vec![]; @@ -80,7 +80,7 @@ impl PackHandler for ImportRepo { .await .unwrap(); self.attach_to_monorepo_parent().await.unwrap(); - Ok(()) + Ok(String::new()) } async fn full_pack(&self) -> Result>, GitError> { @@ -294,6 +294,11 @@ impl PackHandler for ImportRepo { .await } + async fn handle_mr(&self, _: &str) -> Result<(), GitError> { + // do nothing in import_repo + Ok(()) + } + async fn update_refs(&self, refs: &RefCommand) -> Result<(), GitError> { let storage = self.context.services.git_db_storage.clone(); match refs.command_type { diff --git a/ceres/src/pack/mod.rs b/ceres/src/pack/mod.rs index dbf6a51a4..87d057ba3 100644 --- a/ceres/src/pack/mod.rs +++ b/ceres/src/pack/mod.rs @@ -38,7 +38,7 @@ pub mod monorepo; pub trait PackHandler: Send + Sync { async fn head_hash(&self) -> (String, Vec); - async fn handle_receiver(&self, rx: Receiver) -> Result<(), GitError>; + async fn handle_receiver(&self, rx: Receiver) -> Result; /// Asynchronously retrieves the full pack data for the specified repository path. /// This function collects commits and nodes from the storage and packs them into @@ -63,6 +63,8 @@ pub trait PackHandler: Send + Sync { hashes: Vec, ) -> Result, MegaError>; + async fn handle_mr(&self, title: &str) -> Result<(), GitError>; + async fn update_refs(&self, refs: &RefCommand) -> Result<(), GitError>; async fn check_commit_exist(&self, hash: &str) -> bool; diff --git a/ceres/src/pack/monorepo.rs b/ceres/src/pack/monorepo.rs index 64acc3d93..fd057a608 100644 --- a/ceres/src/pack/monorepo.rs +++ b/ceres/src/pack/monorepo.rs @@ -15,7 +15,10 @@ use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use callisto::{db_enums::ConvType, raw_blob}; -use common::{errors::MegaError, utils::{self, MEGA_BRANCH_NAME}}; +use common::{ + errors::MegaError, + utils::{self, MEGA_BRANCH_NAME}, +}; use jupiter::{context::Context, storage::mr_storage::MrStorage}; use mercury::internal::{object::ObjectTrait, pack::encode::PackEncoder}; use mercury::{ @@ -119,33 +122,43 @@ impl PackHandler for MonoRepo { self.find_head_hash(refs) } - async fn handle_receiver(&self, receiver: Receiver) -> Result<(), GitError> { - let storage = self.context.mr_stg(); - let path_str = self.path.to_str().unwrap(); - - let unpack_res = self.save_entry(receiver).await; - match unpack_res { - Ok(title) => match storage.get_open_mr_by_path(path_str).await.unwrap() { - Some(mr) => { - let mut mr = mr.into(); - self.handle_existing_mr(&mut mr, &storage).await + async fn handle_receiver(&self, receiver: Receiver) -> Result { + let storage = self.context.services.mono_storage.clone(); + let mut entry_list = Vec::new(); + let mut join_tasks = vec![]; + let mut current_commit_id = String::new(); + let mut mr_title = String::new(); + for entry in receiver { + if current_commit_id.is_empty() { + if entry.obj_type == ObjectType::Commit { + current_commit_id = entry.hash.to_plain_str(); + let commit = Commit::from_bytes(&entry.data, entry.hash).unwrap(); + mr_title = commit.format_message(); + } + } else { + if entry.obj_type == ObjectType::Commit { + return Err(GitError::CustomError( + "only single commit support in each push".to_string(), + )); } - None => { - let link: String = utils::generate_link(); - let mr = MergeRequest { - path: path_str.to_owned(), - from_hash: self.from_hash.clone(), - to_hash: self.to_hash.clone(), - link, - title, - ..Default::default() - }; - storage.save_mr(mr.clone().into()).await.unwrap(); - Ok(()) + if entry_list.len() >= 1000 { + let stg_clone = storage.clone(); + let commit_id = current_commit_id.clone(); + let handle = tokio::spawn(async move { + stg_clone.save_entry(&commit_id, entry_list).await.unwrap(); + }); + join_tasks.push(handle); + entry_list = vec![]; } - }, - Err(err) => Err(err), + } + entry_list.push(entry); } + join_all(join_tasks).await; + storage + .save_entry(¤t_commit_id, entry_list) + .await + .unwrap(); + Ok(mr_title) } // monorepo full pack should follow the shallow clone command 'git clone --depth=1' @@ -305,6 +318,36 @@ impl PackHandler for MonoRepo { .await } + async fn handle_mr(&self, title: &str) -> Result<(), GitError> { + let storage = self.context.mr_stg(); + let path_str = self.path.to_str().unwrap(); + + match storage.get_open_mr_by_path(path_str).await.unwrap() { + Some(mr) => { + let mut mr = mr.into(); + self.handle_existing_mr(&mut mr, &storage).await + } + None => { + if self.from_hash == "0".repeat(40) { + return Err(GitError::CustomError(String::from( + "Can not init directory under monorepo directory!", + ))); + } + let link: String = utils::generate_link(); + let mr = MergeRequest { + path: path_str.to_owned(), + from_hash: self.from_hash.clone(), + to_hash: self.to_hash.clone(), + link, + title: title.to_string(), + ..Default::default() + }; + storage.save_mr(mr.clone().into()).await.unwrap(); + Ok(()) + } + } + } + async fn update_refs(&self, _: &RefCommand) -> Result<(), GitError> { //do nothing in monorepo because we use mr to handle refs update Ok(()) @@ -366,43 +409,4 @@ impl MonoRepo { &to[..6] ) } - - async fn save_entry(&self, receiver: Receiver) -> Result { - let storage = self.context.services.mono_storage.clone(); - let mut entry_list = Vec::new(); - let mut join_tasks = vec![]; - let mut current_commit_id = String::new(); - let mut mr_title = String::new(); - for entry in receiver { - if current_commit_id.is_empty() { - if entry.obj_type == ObjectType::Commit { - current_commit_id = entry.hash.to_plain_str(); - let commit = Commit::from_bytes(&entry.data, entry.hash).unwrap(); - mr_title = commit.format_message(); - } - } else { - if entry.obj_type == ObjectType::Commit { - return Err(GitError::CustomError( - "only single commit support in each push".to_string(), - )); - } - if entry_list.len() >= 1000 { - let stg_clone = storage.clone(); - let commit_id = current_commit_id.clone(); - let handle = tokio::spawn(async move { - stg_clone.save_entry(&commit_id, entry_list).await.unwrap(); - }); - join_tasks.push(handle); - entry_list = vec![]; - } - } - entry_list.push(entry); - } - join_all(join_tasks).await; - storage - .save_entry(¤t_commit_id, entry_list) - .await - .unwrap(); - Ok(mr_title) - } } diff --git a/ceres/src/protocol/smart.rs b/ceres/src/protocol/smart.rs index a8ff3f26f..bef7dda32 100644 --- a/ceres/src/protocol/smart.rs +++ b/ceres/src/protocol/smart.rs @@ -241,7 +241,10 @@ impl SmartProtocol { // b.The reference being pushed could be a non-fast-forward reference and the update hooks or configuration could be set to not allow that, etc. // c.Also, some references can be updated while others can be rejected. match unpack_result { - Ok(_) => { + Ok(ref title) => { + if let Err(e) = pack_handler.handle_mr(title).await { + command.failed(e.to_string()); + } if !default_exist { command.default_branch = true; default_exist = true; diff --git a/common/src/config.rs b/common/src/config.rs index 72605388f..1d16836d0 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -16,6 +16,7 @@ pub struct Config { pub storage: StorageConfig, pub monorepo: MonoConfig, pub pack: PackConfig, + pub authentication: AuthConfig, pub lfs: LFSConfig, // Not used in mega app #[serde(default)] @@ -209,7 +210,6 @@ impl Default for StorageConfig { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct MonoConfig { pub import_dir: PathBuf, - pub enable_http_auth: bool, pub admin: String, pub root_dirs: Vec, } @@ -218,7 +218,6 @@ impl Default for MonoConfig { fn default() -> Self { Self { import_dir: PathBuf::from("/third-part"), - enable_http_auth: false, admin: String::from("admin"), root_dirs: vec![ "third-part".to_string(), @@ -230,6 +229,25 @@ impl Default for MonoConfig { } } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AuthConfig { + pub enable_http_auth: bool, + pub enable_test_user: bool, + pub test_user_name: String, + pub test_user_token: String, +} + +impl Default for AuthConfig { + fn default() -> Self { + Self { + enable_http_auth: false, + enable_test_user: false, + test_user_name: String::from("mega"), + test_user_token: String::from("mega") + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PackConfig { pub pack_decode_mem_size: usize, diff --git a/docker/config.toml b/docker/config.toml index 5166360e3..3e9b3bbb1 100644 --- a/docker/config.toml +++ b/docker/config.toml @@ -47,15 +47,27 @@ obs_region = "cn-east-3" # Override the endpoint URL used for remote storage services obs_endpoint = "https://obs.cn-east-3.myhuaweicloud.com" +[authentication] +# Support http authentication, login in with github and generate token before push +enable_http_auth = false + +# Enable a test user for debugging and development purposes. +# If set to true, the service allows using a predefined test user for authentication. +enable_test_user = true + +# Specify the name of the test user. +# This is only relevant if `enable_test_user` is set to true. +test_user_name = "mega" + +# Specify the token for the test user. +# This is used for authentication when `enable_test_user` is set to true. +test_user_token = "mega" [monorepo] ## Only import directory support multi-branch commit and tag, monorepo only support main branch ## Mega treats files under this directory as import repo and other directories as monorepo import_dir = "/third-part" -# Support http authentication, login in with github and generate token before push -enable_http_auth = false - # Set System Admin in directory init, replace the admin's github username here admin = "admin" diff --git a/mega/config.toml b/mega/config.toml index 1fe5771fe..682c17f28 100644 --- a/mega/config.toml +++ b/mega/config.toml @@ -47,15 +47,27 @@ obs_region = "cn-east-3" # Override the endpoint URL used for remote storage services obs_endpoint = "https://obs.cn-east-3.myhuaweicloud.com" +[authentication] +# Support http authentication, login in with github and generate token before push +enable_http_auth = false + +# Enable a test user for debugging and development purposes. +# If set to true, the service allows using a predefined test user for authentication. +enable_test_user = true + +# Specify the name of the test user. +# This is only relevant if `enable_test_user` is set to true. +test_user_name = "mega" + +# Specify the token for the test user. +# This is used for authentication when `enable_test_user` is set to true. +test_user_token = "mega" [monorepo] ## Only import directory support multi-branch commit and tag, monorepo only support main branch ## Mega treats files under this directory as import repo and other directories as monorepo import_dir = "/third-part" -# Support http authentication, login in with github and generate token before push -enable_http_auth = false - # Set System Admin in directory init, replace the admin's github username here admin = "admin" diff --git a/mono/config.toml b/mono/config.toml index 37a50f38a..714d8584c 100644 --- a/mono/config.toml +++ b/mono/config.toml @@ -47,15 +47,27 @@ obs_region = "cn-east-3" # Override the endpoint URL used for remote storage services obs_endpoint = "https://obs.cn-east-3.myhuaweicloud.com" +[authentication] +# Support http authentication, login in with github and generate token before push +enable_http_auth = false + +# Enable a test user for debugging and development purposes. +# If set to true, the service allows using a predefined test user for authentication. +enable_test_user = true + +# Specify the name of the test user. +# This is only relevant if `enable_test_user` is set to true. +test_user_name = "mega" + +# Specify the token for the test user. +# This is used for authentication when `enable_test_user` is set to true. +test_user_token = "mega" [monorepo] ## Only import directory support multi-branch commit and tag, monorepo only support main branch ## Mega treats files under this directory as import repo and other directories as monorepo import_dir = "/third-part" -# Support http authentication, login in with github and generate token before push -enable_http_auth = false - # Set System Admin in directory init, replace the admin's github username here admin = "admin" diff --git a/mono/src/api/mr/mod.rs b/mono/src/api/mr/mod.rs index bfe28e2a3..22cfd909b 100644 --- a/mono/src/api/mr/mod.rs +++ b/mono/src/api/mr/mod.rs @@ -23,7 +23,7 @@ impl From for MrInfoItem { fn from(value: mega_mr::Model) -> Self { Self { link: value.link, - title: String::new(), + title: value.title, status: value.status.to_string(), open_timestamp: value.created_at.and_utc().timestamp(), merge_timestamp: value.merge_date.map(|dt| dt.and_utc().timestamp()), diff --git a/mono/src/git_protocol/http.rs b/mono/src/git_protocol/http.rs index 735c8aa0c..9b1b36345 100644 --- a/mono/src/git_protocol/http.rs +++ b/mono/src/git_protocol/http.rs @@ -23,15 +23,9 @@ use common::model::InfoRefsParams; // where $servicename MUST be the service name the client wishes to contact to complete the operation. // The request MUST NOT contain additional query parameters. pub async fn git_info_refs( - req: Request, params: InfoRefsParams, mut pack_protocol: SmartProtocol, ) -> Result, ProtocolError> { - if pack_protocol.context.config.monorepo.enable_http_auth - && !http_auth(req.headers(), &pack_protocol.context).await - { - return auth_failed(); - } let service_name = params.service.unwrap(); pack_protocol.service_type = Some(service_name.parse::().unwrap()); @@ -62,11 +56,27 @@ async fn http_auth(header: &HeaderMap, context: &Context) -> bool { let credentials = String::from_utf8(decoded).unwrap_or_default(); let mut parts = credentials.splitn(2, ':'); let username = parts.next().unwrap_or(""); - let password = parts.next().unwrap_or(""); - tracing::debug!("{}, {}", username, password); - match context.user_stg().find_user_by_name(username).await.unwrap() { + let token = parts.next().unwrap_or(""); + tracing::debug!("{}, {}", username, token); + let auth_config = context.config.authentication.clone(); + if auth_config.enable_test_user + && username == auth_config.test_user_name + && token == auth_config.test_user_token + { + return true; + } + match context + .user_stg() + .find_user_by_name(username) + .await + .unwrap() + { Some(user) => { - return context.user_stg().check_token(user.id, password).await.unwrap(); + return context + .user_stg() + .check_token(user.id, token) + .await + .unwrap(); } None => return false, } @@ -175,7 +185,7 @@ pub async fn git_receive_pack( req: Request, mut pack_protocol: SmartProtocol, ) -> Result, ProtocolError> { - if pack_protocol.context.config.monorepo.enable_http_auth + if pack_protocol.context.config.authentication.enable_http_auth && !http_auth(req.headers(), &pack_protocol.context).await { return auth_failed(); diff --git a/mono/src/server/https_server.rs b/mono/src/server/https_server.rs index d14503a01..155f595a3 100644 --- a/mono/src/server/https_server.rs +++ b/mono/src/server/https_server.rs @@ -182,7 +182,6 @@ pub async fn get_method_router( state: State, Query(params): Query, uri: Uri, - req: Request, ) -> Result, ProtocolError> { if INFO_REFS_REGEX.is_match(uri.path()) { let pack_protocol = SmartProtocol::new( @@ -190,7 +189,7 @@ pub async fn get_method_router( state.context.clone(), TransportProtocol::Http, ); - crate::git_protocol::http::git_info_refs(req, params, pack_protocol).await + crate::git_protocol::http::git_info_refs(params, pack_protocol).await } else { Err(ProtocolError::NotFound( "Operation not supported".to_owned(), diff --git a/moon/src/app/(dashboard)/mr/page.tsx b/moon/src/app/(dashboard)/mr/page.tsx index dbfb76a68..e3e682e3d 100644 --- a/moon/src/app/(dashboard)/mr/page.tsx +++ b/moon/src/app/(dashboard)/mr/page.tsx @@ -132,7 +132,7 @@ export default function MergeRequestPage() { avatar={ getStatusIcon(item.status) } - title={{`MR ${item.link} open by Mega automacticlly${item.title}`}{getStatusTag(item.status)}} + title={{`${item.title}`} {getStatusTag(item.status)}} description={getDescription(item)} /> From aad0e261163c51492a612ea0f525004d9f5436d0 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Thu, 21 Nov 2024 15:20:09 +0800 Subject: [PATCH 2/2] update package.json --- moon/package.json | 28 +++++++++---------- moon/src/app/(dashboard)/issue/[id]/page.tsx | 8 +++--- moon/src/app/(dashboard)/issue/page.tsx | 8 +++--- moon/src/app/(dashboard)/mr/[id]/page.tsx | 12 ++++---- moon/src/app/(dashboard)/mr/page.tsx | 9 +++--- .../components/rich-editor/ExampleTheme.ts | 4 ++- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/moon/package.json b/moon/package.json index c462d30b8..1978f7750 100644 --- a/moon/package.json +++ b/moon/package.json @@ -10,33 +10,33 @@ }, "dependencies": { "@ant-design/icons": "^5.5.1", - "@ant-design/nextjs-registry": "^1.0.1", - "@headlessui/react": "^2.1.8", + "@ant-design/nextjs-registry": "^1.0.2", + "@headlessui/react": "^2.2.0", "@headlessui/tailwindcss": "^0.2.1", - "@heroicons/react": "^2.1.5", - "@lexical/react": "^0.19.0", + "@heroicons/react": "^2.2.0", + "@lexical/react": "^0.20.0", "@tailwindcss/forms": "^0.5.9", "clsx": "^2.1.1", "copy-to-clipboard": "^3.3.3", "date-fns": "^4.1.0", - "framer-motion": "^11.11.10", - "github-markdown-css": "^5.7.0", + "framer-motion": "^11.11.17", + "github-markdown-css": "^5.8.0", "highlight.js": "^11.10.0", - "lexical": "^0.19.0", - "next": "^15.0.2", + "lexical": "^0.20.0", + "next": "^15.0.3", "prism-react-renderer": "^2.4.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1" }, "devDependencies": { - "@types/node": "^22", - "@types/react": "^18.3.3", + "@types/node": "^22.9.1", + "@types/react": "^18.3.12", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-config-next": "^15.0.1", - "postcss": "^8.4.40", - "typescript": "^5.5.0" + "eslint": "^9.15.0", + "eslint-config-next": "^15.0.3", + "postcss": "^8.4.49", + "typescript": "^5.6.3" } } diff --git a/moon/src/app/(dashboard)/issue/[id]/page.tsx b/moon/src/app/(dashboard)/issue/[id]/page.tsx index 3d5387295..6cf3bf383 100644 --- a/moon/src/app/(dashboard)/issue/[id]/page.tsx +++ b/moon/src/app/(dashboard)/issue/[id]/page.tsx @@ -1,5 +1,5 @@ 'use client' -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { Card, Button, List, Tabs, TabsProps, Space, Timeline, Flex } from 'antd/lib'; import { CommentOutlined, MergeOutlined, CloseCircleOutlined } from '@ant-design/icons'; import RichEditor from "@/components/rich-editor/RichEditor"; @@ -37,11 +37,11 @@ export default function IssueDetailPage({ params }: { params: Params }) { const [loadings, setLoadings] = useState([]); const router = useRouter(); - const fetchDetail = async () => { + const fetchDetail = useCallback(async () => { const detail = await fetch(`/api/issue/${id}/detail`); const detail_json = await detail.json(); setInfo(detail_json.data.data); - }; + }, [id]); const checkLogin = async () => { const res = await fetch(`/api/auth`); @@ -51,7 +51,7 @@ export default function IssueDetailPage({ params }: { params: Params }) { useEffect(() => { checkLogin() fetchDetail() - }, [id]); + }, [id, fetchDetail]); const set_to_loading = (index: number) => { setLoadings((prevLoadings) => { diff --git a/moon/src/app/(dashboard)/issue/page.tsx b/moon/src/app/(dashboard)/issue/page.tsx index 54c1c8ba9..ce60527a9 100644 --- a/moon/src/app/(dashboard)/issue/page.tsx +++ b/moon/src/app/(dashboard)/issue/page.tsx @@ -1,6 +1,6 @@ 'use client' import { Heading } from '@/components/catalyst/heading' -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import React from 'react'; import { Flex, List, PaginationProps, Tag, Button, Tabs, Space, TabsProps, Segmented } from 'antd/lib'; import { format, formatDistance, fromUnixTime } from 'date-fns' @@ -23,7 +23,7 @@ export default function IssuePage() { const [pageSize, setPageSize] = useState(10); const [status, setStatus] = useState("open") - const fetchData = async (page: number, per_page: number) => { + const fetchData = useCallback(async (page: number, per_page: number) => { try { const res = await fetch(`/api/issue/list`, { method: 'POST', @@ -47,11 +47,11 @@ export default function IssuePage() { } catch (error) { console.error('Error fetching data:', error); } - }; + }, [status]); useEffect(() => { fetchData(1, pageSize); - }, [pageSize, status]); + }, [pageSize, status, fetchData]); const getStatusTag = (status: string) => { switch (status) { diff --git a/moon/src/app/(dashboard)/mr/[id]/page.tsx b/moon/src/app/(dashboard)/mr/[id]/page.tsx index 757d96015..3f0343b25 100644 --- a/moon/src/app/(dashboard)/mr/[id]/page.tsx +++ b/moon/src/app/(dashboard)/mr/[id]/page.tsx @@ -1,5 +1,5 @@ 'use client' -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { Card, Button, List, Tabs, TabsProps, Space, Timeline, Flex } from 'antd/lib'; import { CommentOutlined, MergeOutlined, CloseCircleOutlined, PullRequestOutlined } from '@ant-design/icons'; import { formatDistance, fromUnixTime } from 'date-fns'; @@ -44,13 +44,13 @@ export default function MRDetailPage({ params }: { params: Params }) { setLogin(res.ok); }; - const fetchDetail = async () => { + const fetchDetail = useCallback(async () => { const detail = await fetch(`/api/mr/${id}/detail`); const detail_json = await detail.json(); setMrDetail(detail_json.data.data); - }; + }, [id]); - const fetchFileList = async () => { + const fetchFileList = useCallback(async () => { set_to_loading(2) try { const res = await fetch(`/api/mr/${id}/files`); @@ -59,13 +59,13 @@ export default function MRDetailPage({ params }: { params: Params }) { } finally { cancel_loading(2) } - }; + }, [id]); useEffect(() => { fetchDetail() fetchFileList(); checkLogin(); - }, [id]); + }, [id, fetchDetail, fetchFileList]); const set_to_loading = (index: number) => { setLoadings((prevLoadings) => { diff --git a/moon/src/app/(dashboard)/mr/page.tsx b/moon/src/app/(dashboard)/mr/page.tsx index e3e682e3d..bd786a6b3 100644 --- a/moon/src/app/(dashboard)/mr/page.tsx +++ b/moon/src/app/(dashboard)/mr/page.tsx @@ -1,5 +1,5 @@ 'use client' -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import React from 'react'; import { List, PaginationProps, Tag, Tabs, TabsProps } from 'antd/lib'; import { format, formatDistance, fromUnixTime } from 'date-fns' @@ -23,7 +23,7 @@ export default function MergeRequestPage() { const [pageSize, setPageSize] = useState(10); const [status, setStatus] = useState("open") - const fetchData = async (page: number, per_page: number) => { + const fetchData = useCallback(async (page: number, per_page: number) => { try { const res = await fetch(`/api/mr/list`, { method: 'POST', @@ -47,12 +47,11 @@ export default function MergeRequestPage() { } catch (error) { console.error('Error fetching data:', error); } - }; + }, [status]); useEffect(() => { - fetchData(1, pageSize); - }, [pageSize, status]); + }, [pageSize, status, fetchData]); const getStatusTag = (status: string) => { switch (status) { diff --git a/moon/src/components/rich-editor/ExampleTheme.ts b/moon/src/components/rich-editor/ExampleTheme.ts index bbd871b65..7b5f09c72 100644 --- a/moon/src/components/rich-editor/ExampleTheme.ts +++ b/moon/src/components/rich-editor/ExampleTheme.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * */ -export default { +const theme = { code: 'editor-code', heading: { h1: 'editor-heading-h1', @@ -40,3 +40,5 @@ export default { underlineStrikethrough: 'editor-text-underlineStrikethrough', }, }; + +export default theme; \ No newline at end of file