fix token refresh
This commit is contained in:
@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(cargo init:*)",
|
|
||||||
"Bash(cargo:*)",
|
|
||||||
"Bash(find:*)",
|
|
||||||
"Bash(mkdir:*)",
|
|
||||||
"Bash(../target/debug/ailog new:*)",
|
|
||||||
"Bash(../target/debug/ailog build)",
|
|
||||||
"Bash(/Users/syui/ai/log/target/debug/ailog build)",
|
|
||||||
"Bash(ls:*)",
|
|
||||||
"Bash(curl:*)",
|
|
||||||
"Bash(pkill:*)",
|
|
||||||
"WebFetch(domain:docs.anthropic.com)",
|
|
||||||
"WebFetch(domain:github.com)",
|
|
||||||
"Bash(rm:*)",
|
|
||||||
"Bash(mv:*)",
|
|
||||||
"Bash(cp:*)",
|
|
||||||
"Bash(timeout:*)",
|
|
||||||
"Bash(grep:*)",
|
|
||||||
"Bash(./target/debug/ailog:*)",
|
|
||||||
"Bash(cat:*)",
|
|
||||||
"Bash(npm install)",
|
|
||||||
"Bash(npm run build:*)",
|
|
||||||
"Bash(chmod:*)",
|
|
||||||
"Bash(./scripts/tunnel.sh:*)",
|
|
||||||
"Bash(PRODUCTION=true cargo run -- build)",
|
|
||||||
"Bash(cloudflared tunnel:*)",
|
|
||||||
"Bash(npm install:*)",
|
|
||||||
"Bash(./scripts/build-oauth-partial.zsh:*)",
|
|
||||||
"Bash(./scripts/quick-oauth-update.zsh:*)",
|
|
||||||
"Bash(../target/debug/ailog serve)",
|
|
||||||
"Bash(./scripts/test-oauth.sh:*)",
|
|
||||||
"Bash(./run.zsh:*)",
|
|
||||||
"Bash(npm run dev:*)",
|
|
||||||
"Bash(./target/release/ailog:*)",
|
|
||||||
"Bash(rg:*)",
|
|
||||||
"Bash(../target/release/ailog build)",
|
|
||||||
"Bash(zsh run.zsh:*)",
|
|
||||||
"Bash(hugo:*)",
|
|
||||||
"WebFetch(domain:docs.bsky.app)",
|
|
||||||
"WebFetch(domain:syui.ai)",
|
|
||||||
"Bash(rustup target list:*)",
|
|
||||||
"Bash(rustup target:*)",
|
|
||||||
"Bash(git add:*)",
|
|
||||||
"Bash(git commit:*)",
|
|
||||||
"Bash(git push:*)",
|
|
||||||
"Bash(git tag:*)",
|
|
||||||
"Bash(../bin/ailog:*)",
|
|
||||||
"Bash(../target/release/ailog oauth build:*)",
|
|
||||||
"Bash(ailog:*)",
|
|
||||||
"WebFetch(domain:plc.directory)",
|
|
||||||
"WebFetch(domain:atproto.com)",
|
|
||||||
"WebFetch(domain:syu.is)",
|
|
||||||
"Bash(sed:*)",
|
|
||||||
"Bash(./scpt/run.zsh:*)",
|
|
||||||
"Bash(RUST_LOG=debug cargo run -- stream status)",
|
|
||||||
"Bash(RUST_LOG=debug cargo run -- stream test-api)"
|
|
||||||
],
|
|
||||||
"deny": []
|
|
||||||
}
|
|
||||||
}
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,3 +21,5 @@ oauth_old
|
|||||||
oauth_example
|
oauth_example
|
||||||
my-blog/static/oauth/assets/comment-atproto*
|
my-blog/static/oauth/assets/comment-atproto*
|
||||||
*.lock
|
*.lock
|
||||||
|
my-blog/config.toml
|
||||||
|
.claude/settings.local.json
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ailog"
|
name = "ailog"
|
||||||
version = "0.2.4"
|
version = "0.2.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["syui"]
|
authors = ["syui"]
|
||||||
description = "A static blog generator with AI features"
|
description = "A static blog generator with AI features"
|
||||||
@ -82,4 +82,4 @@ codegen-units = 1
|
|||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
# Optimize dependencies in dev builds
|
# Optimize dependencies in dev builds
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
@ -16,10 +16,10 @@ auto_translate = false
|
|||||||
comment_moderation = false
|
comment_moderation = false
|
||||||
ask_ai = true
|
ask_ai = true
|
||||||
provider = "ollama"
|
provider = "ollama"
|
||||||
model = "qwen3"
|
model = "gemma3"
|
||||||
model_translation = "llama3.2:1b"
|
model_translation = "llama3.2:1b"
|
||||||
model_technical = "phi3:mini"
|
model_technical = "phi3:mini"
|
||||||
host = "http://localhost:11434"
|
host = "http://192.168.11.95:11434"
|
||||||
system_prompt = "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
system_prompt = "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
||||||
handle = "ai.syui.ai"
|
handle = "ai.syui.ai"
|
||||||
#num_predict = 200
|
#num_predict = 200
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ailog-oauth",
|
"name": "ailog-oauth",
|
||||||
"version": "0.2.4",
|
"version": "0.2.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -537,19 +537,30 @@ fn migrate_config_if_needed(config_path: &std::path::Path, config_json: &str) ->
|
|||||||
// Load config with automatic token refresh
|
// Load config with automatic token refresh
|
||||||
pub async fn load_config_with_refresh() -> Result<AuthConfig> {
|
pub async fn load_config_with_refresh() -> Result<AuthConfig> {
|
||||||
let mut config = load_config()?;
|
let mut config = load_config()?;
|
||||||
|
let old_access_jwt = config.admin.access_jwt.clone();
|
||||||
|
|
||||||
// Test if current access token is still valid
|
// Always try to refresh token to avoid any expiration issues
|
||||||
if let Err(_) = test_api_access_with_auth(&config).await {
|
println!("{}", "🔄 Refreshing access token...".yellow());
|
||||||
println!("{}", "🔄 Access token expired, refreshing...".yellow());
|
println!("📍 Current access JWT: {}...", &old_access_jwt[..30.min(old_access_jwt.len())]);
|
||||||
|
|
||||||
// Try to refresh the token
|
// Try to refresh the token
|
||||||
match refresh_access_token(&mut config).await {
|
match refresh_access_token(&mut config).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
if config.admin.access_jwt != old_access_jwt {
|
||||||
|
println!("{}", "✅ Token refreshed with new JWT".green());
|
||||||
|
println!("📍 New access JWT: {}...", &config.admin.access_jwt[..30.min(config.admin.access_jwt.len())]);
|
||||||
save_config(&config)?;
|
save_config(&config)?;
|
||||||
println!("{}", "✅ Token refreshed successfully".green());
|
println!("{}", "💾 Config saved to disk".green());
|
||||||
|
} else {
|
||||||
|
println!("{}", "ℹ️ Token refresh returned same JWT (still valid)".cyan());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
}
|
||||||
return Err(anyhow::anyhow!("Failed to refresh token: {}. Please run 'ailog auth init' again.", e));
|
Err(e) => {
|
||||||
|
// If refresh fails, test if current token is still valid
|
||||||
|
if let Ok(_) = test_api_access_with_auth(&config).await {
|
||||||
|
println!("{}", "ℹ️ Refresh failed but current token is still valid".cyan());
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!("Token expired and refresh failed: {}. Please run 'ailog auth init' again.", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -584,6 +595,9 @@ async fn refresh_access_token(config: &mut AuthConfig) -> Result<()> {
|
|||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let url = format!("{}/xrpc/com.atproto.server.refreshSession", config.admin.pds);
|
let url = format!("{}/xrpc/com.atproto.server.refreshSession", config.admin.pds);
|
||||||
|
|
||||||
|
println!("🔑 Refreshing token at: {}", url);
|
||||||
|
println!("🔑 Using refresh JWT: {}...", &config.admin.refresh_jwt[..20.min(config.admin.refresh_jwt.len())]);
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.header("Authorization", format!("Bearer {}", config.admin.refresh_jwt))
|
.header("Authorization", format!("Bearer {}", config.admin.refresh_jwt))
|
||||||
@ -601,10 +615,16 @@ async fn refresh_access_token(config: &mut AuthConfig) -> Result<()> {
|
|||||||
// Update tokens
|
// Update tokens
|
||||||
if let Some(access_jwt) = refresh_response["accessJwt"].as_str() {
|
if let Some(access_jwt) = refresh_response["accessJwt"].as_str() {
|
||||||
config.admin.access_jwt = access_jwt.to_string();
|
config.admin.access_jwt = access_jwt.to_string();
|
||||||
|
println!("✅ New access JWT: {}...", &access_jwt[..20.min(access_jwt.len())]);
|
||||||
|
} else {
|
||||||
|
println!("⚠️ No accessJwt in refresh response");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(refresh_jwt) = refresh_response["refreshJwt"].as_str() {
|
if let Some(refresh_jwt) = refresh_response["refreshJwt"].as_str() {
|
||||||
config.admin.refresh_jwt = refresh_jwt.to_string();
|
config.admin.refresh_jwt = refresh_jwt.to_string();
|
||||||
|
println!("✅ New refresh JWT: {}...", &refresh_jwt[..20.min(refresh_jwt.len())]);
|
||||||
|
} else {
|
||||||
|
println!("⚠️ No refreshJwt in refresh response");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -612,8 +632,43 @@ async fn refresh_access_token(config: &mut AuthConfig) -> Result<()> {
|
|||||||
|
|
||||||
fn save_config(config: &AuthConfig) -> Result<()> {
|
fn save_config(config: &AuthConfig) -> Result<()> {
|
||||||
let config_path = get_config_path()?;
|
let config_path = get_config_path()?;
|
||||||
|
println!("💾 Saving config to: {}", config_path.display());
|
||||||
|
|
||||||
|
// Read old config to compare
|
||||||
|
let old_config = if config_path.exists() {
|
||||||
|
fs::read_to_string(&config_path).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let config_json = serde_json::to_string_pretty(config)?;
|
let config_json = serde_json::to_string_pretty(config)?;
|
||||||
fs::write(&config_path, config_json)?;
|
fs::write(&config_path, &config_json)?;
|
||||||
|
|
||||||
|
// Verify the write was successful
|
||||||
|
let saved_content = fs::read_to_string(&config_path)?;
|
||||||
|
if saved_content == config_json {
|
||||||
|
println!("✅ Config successfully saved to {}", config_path.display());
|
||||||
|
|
||||||
|
// Compare tokens if old config exists
|
||||||
|
if let Some(old) = old_config {
|
||||||
|
if let (Ok(old_json), Ok(new_json)) = (
|
||||||
|
serde_json::from_str::<AuthConfig>(&old),
|
||||||
|
serde_json::from_str::<AuthConfig>(&config_json)
|
||||||
|
) {
|
||||||
|
if old_json.admin.access_jwt != new_json.admin.access_jwt {
|
||||||
|
println!("📝 Access JWT was updated in file");
|
||||||
|
println!(" Old: {}...", &old_json.admin.access_jwt[..30.min(old_json.admin.access_jwt.len())]);
|
||||||
|
println!(" New: {}...", &new_json.admin.access_jwt[..30.min(new_json.admin.access_jwt.len())]);
|
||||||
|
}
|
||||||
|
if old_json.admin.refresh_jwt != new_json.admin.refresh_jwt {
|
||||||
|
println!("📝 Refresh JWT was updated in file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("❌ Config save verification failed!");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,8 +446,13 @@ pub async fn init_user_list(project_dir: Option<PathBuf>, handles: Option<String
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(project_dir: Option<PathBuf>, daemon: bool, ai_generate: bool) -> Result<()> {
|
pub async fn start(project_dir: Option<PathBuf>, daemon: bool, ai_generate: bool) -> Result<()> {
|
||||||
|
println!("{}", "🚀 Starting ailog stream server...".cyan());
|
||||||
|
println!("{}", "📋 Step 1: Loading and refreshing authentication...".cyan());
|
||||||
|
|
||||||
let mut config = load_config_with_refresh().await?;
|
let mut config = load_config_with_refresh().await?;
|
||||||
|
|
||||||
|
println!("{}", "📋 Step 2: Configuration loaded successfully".green());
|
||||||
|
|
||||||
// Load collection config with priority: env vars > project config > defaults
|
// Load collection config with priority: env vars > project config > defaults
|
||||||
let (collection_comment, _collection_user) = load_collection_config(project_dir.as_deref())?;
|
let (collection_comment, _collection_user) = load_collection_config(project_dir.as_deref())?;
|
||||||
|
|
||||||
@ -2019,7 +2024,14 @@ async fn generate_and_store_comment(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Generate comment using limited post content for brevity
|
// Generate comment using limited post content for brevity
|
||||||
let limited_contents = if post.contents.len() > 300 {
|
let limited_contents = if post.contents.len() > 300 {
|
||||||
format!("{}...", &post.contents[..300])
|
// Use char_indices to safely truncate at character boundaries
|
||||||
|
let truncate_pos = post.contents
|
||||||
|
.char_indices()
|
||||||
|
.take(100) // Take first 100 characters instead of bytes
|
||||||
|
.last()
|
||||||
|
.map(|(idx, ch)| idx + ch.len_utf8())
|
||||||
|
.unwrap_or(post.contents.len());
|
||||||
|
format!("{}...", &post.contents[..truncate_pos])
|
||||||
} else {
|
} else {
|
||||||
post.contents.clone()
|
post.contents.clone()
|
||||||
};
|
};
|
||||||
@ -2060,7 +2072,18 @@ async fn store_atproto_record(
|
|||||||
record_data: &serde_json::Value,
|
record_data: &serde_json::Value,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Always load fresh config to ensure we have valid tokens
|
// Always load fresh config to ensure we have valid tokens
|
||||||
let config = load_config_with_refresh().await?;
|
println!("{} Checking token before putRecord...", "🔄".yellow());
|
||||||
|
let config = match load_config_with_refresh().await {
|
||||||
|
Ok(c) => {
|
||||||
|
println!("{} Token check/refresh completed for putRecord", "✅".green());
|
||||||
|
println!("🔑 Using access JWT: {}...", &c.admin.access_jwt[..30.min(c.admin.access_jwt.len())]);
|
||||||
|
c
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("{} Failed to refresh token: {}", "❌".red(), e);
|
||||||
|
return Err(anyhow::anyhow!("Token refresh failed: {}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let url = format!("{}/xrpc/com.atproto.repo.putRecord", config.admin.pds);
|
let url = format!("{}/xrpc/com.atproto.repo.putRecord", config.admin.pds);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user