feat: Refactor service management scripts to use a unified dev script

- Added package.json to manage development scripts.
- Updated restart-services.ps1 to call the new dev script for starting services.
- Refactored start-admin.ps1, start-backend.ps1, start-frontend.ps1, and start-mcp.ps1 to utilize the dev script for starting respective services.
- Enhanced stop-services.ps1 to improve process termination logic by matching command patterns.
This commit is contained in:
2026-03-29 21:36:13 +08:00
parent 84f82c2a7e
commit 92a85eef20
137 changed files with 14181 additions and 2691 deletions

View File

@@ -7,7 +7,10 @@ use serde::{Deserialize, Serialize};
use crate::{
controllers::{
admin::{admin_username, check_auth, is_admin_logged_in, set_admin_logged_in, validate_admin_credentials},
admin::{
admin_username, check_auth, is_admin_logged_in, set_admin_logged_in,
validate_admin_credentials,
},
site_settings::{self, SiteSettingsPayload},
},
models::_entities::{ai_chunks, comments, friend_links, posts, reviews},
@@ -120,11 +123,15 @@ pub struct AdminSiteSettingsResponse {
pub social_email: Option<String>,
pub location: Option<String>,
pub tech_stack: Vec<String>,
pub music_playlist: Vec<site_settings::MusicTrackPayload>,
pub ai_enabled: bool,
pub paragraph_comments_enabled: bool,
pub ai_provider: Option<String>,
pub ai_api_base: Option<String>,
pub ai_api_key: Option<String>,
pub ai_chat_model: Option<String>,
pub ai_providers: Vec<site_settings::AiProviderConfig>,
pub ai_active_provider_id: Option<String>,
pub ai_embedding_model: Option<String>,
pub ai_system_prompt: Option<String>,
pub ai_top_k: Option<i32>,
@@ -140,6 +147,29 @@ pub struct AdminAiReindexResponse {
pub last_indexed_at: Option<String>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AdminAiProviderTestRequest {
pub provider: site_settings::AiProviderConfig,
}
#[derive(Clone, Debug, Serialize)]
pub struct AdminAiProviderTestResponse {
pub provider: String,
pub endpoint: String,
pub chat_model: String,
pub reply_preview: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AdminPostMetadataRequest {
pub markdown: String,
}
#[derive(Clone, Debug, Deserialize)]
pub struct AdminPostPolishRequest {
pub markdown: String,
}
fn format_timestamp(
value: Option<sea_orm::prelude::DateTimeWithTimeZone>,
pattern: &str,
@@ -166,10 +196,27 @@ fn tech_stack_values(value: &Option<serde_json::Value>) -> Vec<String> {
.collect()
}
fn music_playlist_values(
value: &Option<serde_json::Value>,
) -> Vec<site_settings::MusicTrackPayload> {
value
.as_ref()
.and_then(serde_json::Value::as_array)
.cloned()
.unwrap_or_default()
.into_iter()
.filter_map(|item| serde_json::from_value::<site_settings::MusicTrackPayload>(item).ok())
.filter(|item| !item.title.trim().is_empty() && !item.url.trim().is_empty())
.collect()
}
fn build_settings_response(
item: crate::models::_entities::site_settings::Model,
ai_chunks_count: u64,
) -> AdminSiteSettingsResponse {
let ai_providers = site_settings::ai_provider_configs(&item);
let ai_active_provider_id = site_settings::active_ai_provider_id(&item);
AdminSiteSettingsResponse {
id: item.id,
site_name: item.site_name,
@@ -188,11 +235,15 @@ fn build_settings_response(
social_email: item.social_email,
location: item.location,
tech_stack: tech_stack_values(&item.tech_stack),
music_playlist: music_playlist_values(&item.music_playlist),
ai_enabled: item.ai_enabled.unwrap_or(false),
paragraph_comments_enabled: item.paragraph_comments_enabled.unwrap_or(true),
ai_provider: item.ai_provider,
ai_api_base: item.ai_api_base,
ai_api_key: item.ai_api_key,
ai_chat_model: item.ai_chat_model,
ai_providers,
ai_active_provider_id,
ai_embedding_model: item.ai_embedding_model,
ai_system_prompt: item.ai_system_prompt,
ai_top_k: item.ai_top_k,
@@ -375,8 +426,9 @@ pub async fn update_site_settings(
check_auth()?;
let current = site_settings::load_current(&ctx).await?;
let mut item = current.into_active_model();
let mut item = current;
params.apply(&mut item);
let item = item.into_active_model();
let updated = item.update(&ctx.db).await?;
let ai_chunks_count = ai_chunks::Entity::find().count(&ctx.db).await?;
@@ -390,10 +442,51 @@ pub async fn reindex_ai(State(ctx): State<AppContext>) -> Result<Response> {
format::json(AdminAiReindexResponse {
indexed_chunks: summary.indexed_chunks,
last_indexed_at: format_timestamp(summary.last_indexed_at.map(Into::into), "%Y-%m-%d %H:%M:%S UTC"),
last_indexed_at: format_timestamp(
summary.last_indexed_at.map(Into::into),
"%Y-%m-%d %H:%M:%S UTC",
),
})
}
#[debug_handler]
pub async fn test_ai_provider(Json(payload): Json<AdminAiProviderTestRequest>) -> Result<Response> {
check_auth()?;
let result = ai::test_provider_connectivity(
&payload.provider.provider,
payload.provider.api_base.as_deref().unwrap_or_default(),
payload.provider.api_key.as_deref().unwrap_or_default(),
payload.provider.chat_model.as_deref().unwrap_or_default(),
)
.await?;
format::json(AdminAiProviderTestResponse {
provider: result.provider,
endpoint: result.endpoint,
chat_model: result.chat_model,
reply_preview: result.reply_preview,
})
}
#[debug_handler]
pub async fn generate_post_metadata(
State(ctx): State<AppContext>,
Json(payload): Json<AdminPostMetadataRequest>,
) -> Result<Response> {
check_auth()?;
format::json(ai::generate_post_metadata(&ctx, &payload.markdown).await?)
}
#[debug_handler]
pub async fn polish_post_markdown(
State(ctx): State<AppContext>,
Json(payload): Json<AdminPostPolishRequest>,
) -> Result<Response> {
check_auth()?;
format::json(ai::polish_post_markdown(&ctx, &payload.markdown).await?)
}
pub fn routes() -> Routes {
Routes::new()
.prefix("/api/admin")
@@ -405,4 +498,7 @@ pub fn routes() -> Routes {
.add("/site-settings", patch(update_site_settings))
.add("/site-settings", put(update_site_settings))
.add("/ai/reindex", post(reindex_ai))
.add("/ai/test-provider", post(test_ai_provider))
.add("/ai/post-metadata", post(generate_post_metadata))
.add("/ai/polish-post", post(polish_post_markdown))
}