fix oauth plc
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ my-blog/templates/oauth-assets.html
|
|||||||
cloudflared-config.yml
|
cloudflared-config.yml
|
||||||
.config
|
.config
|
||||||
oauth-server-example
|
oauth-server-example
|
||||||
|
atproto
|
||||||
|
@ -16,8 +16,10 @@ auto_translate = false
|
|||||||
comment_moderation = false
|
comment_moderation = false
|
||||||
ask_ai = true
|
ask_ai = true
|
||||||
provider = "ollama"
|
provider = "ollama"
|
||||||
model = "gemma3:4b"
|
model = "qwen3"
|
||||||
host = "https://localhost:11434"
|
model_translation = "llama3.2:1b"
|
||||||
|
model_technical = "phi3:mini"
|
||||||
|
host = "http://localhost:11434"
|
||||||
system_prompt = "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
system_prompt = "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
||||||
handle = "ai.syui.ai"
|
handle = "ai.syui.ai"
|
||||||
#num_predict = 200
|
#num_predict = 200
|
||||||
|
@ -16,5 +16,5 @@ VITE_AI_ENABLED=true
|
|||||||
VITE_AI_ASK_AI=true
|
VITE_AI_ASK_AI=true
|
||||||
VITE_AI_PROVIDER=ollama
|
VITE_AI_PROVIDER=ollama
|
||||||
VITE_AI_MODEL=gemma3:4b
|
VITE_AI_MODEL=gemma3:4b
|
||||||
VITE_AI_HOST=https://localhost:11434
|
VITE_AI_HOST=http://localhost:11434
|
||||||
VITE_AI_SYSTEM_PROMPT="あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
VITE_AI_SYSTEM_PROMPT="あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
||||||
|
@ -15,7 +15,7 @@ VITE_ATPROTO_HANDLE_LIST=["syui.syui.ai","ai.syui.ai","ai.ai"]
|
|||||||
VITE_AI_ENABLED=true
|
VITE_AI_ENABLED=true
|
||||||
VITE_AI_ASK_AI=true
|
VITE_AI_ASK_AI=true
|
||||||
VITE_AI_PROVIDER=ollama
|
VITE_AI_PROVIDER=ollama
|
||||||
VITE_AI_MODEL=gemma3:4b
|
VITE_AI_MODEL=gemma3:1b
|
||||||
VITE_AI_HOST=https://ollama.syui.ai
|
VITE_AI_HOST=https://ollama.syui.ai
|
||||||
VITE_AI_SYSTEM_PROMPT="あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
VITE_AI_SYSTEM_PROMPT="あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。"
|
||||||
|
|
||||||
|
@ -224,13 +224,7 @@ function App() {
|
|||||||
// Ensure handle is not DID
|
// Ensure handle is not DID
|
||||||
const handle = oauthResult.handle !== oauthResult.did ? oauthResult.handle : oauthResult.handle;
|
const handle = oauthResult.handle !== oauthResult.did ? oauthResult.handle : oauthResult.handle;
|
||||||
|
|
||||||
// Check if handle is allowed
|
// Note: appConfig.allowedHandles is used for PDS detection, not access control
|
||||||
if (appConfig.allowedHandles.length > 0 && !appConfig.allowedHandles.includes(handle)) {
|
|
||||||
// Handle not in allowed list
|
|
||||||
setError(`Access denied: ${handle} is not authorized for this application.`);
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user profile including avatar
|
// Get user profile including avatar
|
||||||
const userProfile = await getUserProfile(oauthResult.did, handle);
|
const userProfile = await getUserProfile(oauthResult.did, handle);
|
||||||
@ -1213,8 +1207,9 @@ function App() {
|
|||||||
|
|
||||||
// Extract content based on format
|
// Extract content based on format
|
||||||
const contentText = isNewFormat ? value.text : (value.content || value.body || '');
|
const contentText = isNewFormat ? value.text : (value.content || value.body || '');
|
||||||
// For AI comments, always use the loaded AI profile instead of record.value.author
|
// Use the author from the record if available, otherwise fall back to AI profile
|
||||||
const authorInfo = aiProfile;
|
const authorInfo = value.author || aiProfile;
|
||||||
|
|
||||||
const postInfo = isNewFormat ? value.post : null;
|
const postInfo = isNewFormat ? value.post : null;
|
||||||
const contentType = value.type || 'unknown';
|
const contentType = value.type || 'unknown';
|
||||||
const createdAt = value.createdAt || value.generated_at || '';
|
const createdAt = value.createdAt || value.generated_at || '';
|
||||||
|
@ -191,6 +191,7 @@ Answer:`;
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
'Origin': 'https://syui.ai',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: aiConfig.model,
|
model: aiConfig.model,
|
||||||
|
@ -113,7 +113,7 @@ export function getAppConfig(): AppConfig {
|
|||||||
const aiEnabled = import.meta.env.VITE_AI_ENABLED === 'true';
|
const aiEnabled = import.meta.env.VITE_AI_ENABLED === 'true';
|
||||||
const aiAskAi = import.meta.env.VITE_AI_ASK_AI === 'true';
|
const aiAskAi = import.meta.env.VITE_AI_ASK_AI === 'true';
|
||||||
const aiProvider = import.meta.env.VITE_AI_PROVIDER || 'ollama';
|
const aiProvider = import.meta.env.VITE_AI_PROVIDER || 'ollama';
|
||||||
const aiModel = import.meta.env.VITE_AI_MODEL || 'gemma2:2b';
|
const aiModel = import.meta.env.VITE_AI_MODEL || 'gemma3:4b';
|
||||||
const aiHost = import.meta.env.VITE_AI_HOST || 'https://ollama.syui.ai';
|
const aiHost = import.meta.env.VITE_AI_HOST || 'https://ollama.syui.ai';
|
||||||
const aiSystemPrompt = import.meta.env.VITE_AI_SYSTEM_PROMPT || 'You are a helpful AI assistant trained on this blog\'s content.';
|
const aiSystemPrompt = import.meta.env.VITE_AI_SYSTEM_PROMPT || 'You are a helpful AI assistant trained on this blog\'s content.';
|
||||||
const atprotoPds = import.meta.env.VITE_ATPROTO_PDS || 'syu.is';
|
const atprotoPds = import.meta.env.VITE_ATPROTO_PDS || 'syu.is';
|
||||||
|
@ -12,6 +12,7 @@ interface AtprotoSession {
|
|||||||
|
|
||||||
class AtprotoOAuthService {
|
class AtprotoOAuthService {
|
||||||
private oauthClient: BrowserOAuthClient | null = null;
|
private oauthClient: BrowserOAuthClient | null = null;
|
||||||
|
private oauthClientSyuIs: BrowserOAuthClient | null = null;
|
||||||
private agent: Agent | null = null;
|
private agent: Agent | null = null;
|
||||||
private initializePromise: Promise<void> | null = null;
|
private initializePromise: Promise<void> | null = null;
|
||||||
|
|
||||||
@ -31,22 +32,27 @@ class AtprotoOAuthService {
|
|||||||
|
|
||||||
private async _doInitialize(): Promise<void> {
|
private async _doInitialize(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
// Generate client ID based on current origin
|
// Generate client ID based on current origin
|
||||||
const clientId = this.getClientId();
|
const clientId = this.getClientId();
|
||||||
|
|
||||||
|
// Initialize both OAuth clients
|
||||||
// Support multiple PDS hosts for OAuth
|
|
||||||
this.oauthClient = await BrowserOAuthClient.load({
|
this.oauthClient = await BrowserOAuthClient.load({
|
||||||
clientId: clientId,
|
clientId: clientId,
|
||||||
handleResolver: 'https://bsky.social', // Default resolver
|
handleResolver: 'https://bsky.social',
|
||||||
|
plcDirectoryUrl: 'https://plc.directory',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.oauthClientSyuIs = await BrowserOAuthClient.load({
|
||||||
|
clientId: clientId,
|
||||||
|
handleResolver: 'https://syu.is',
|
||||||
|
plcDirectoryUrl: 'https://plc.syu.is',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to restore existing session from either client
|
||||||
// Try to restore existing session
|
let result = await this.oauthClient.init();
|
||||||
const result = await this.oauthClient.init();
|
if (!result?.session) {
|
||||||
|
result = await this.oauthClientSyuIs.init();
|
||||||
|
}
|
||||||
if (result?.session) {
|
if (result?.session) {
|
||||||
|
|
||||||
// Create Agent instance with proper configuration
|
// Create Agent instance with proper configuration
|
||||||
@ -92,41 +98,13 @@ class AtprotoOAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async processSession(session: any): Promise<{ did: string; handle: string }> {
|
private async processSession(session: any): Promise<{ did: string; handle: string }> {
|
||||||
|
|
||||||
|
|
||||||
// Log full session structure
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Check if agent has properties we can access
|
|
||||||
if (session.agent) {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const did = session.sub || session.did;
|
const did = session.sub || session.did;
|
||||||
let handle = session.handle || 'unknown';
|
let handle = session.handle || 'unknown';
|
||||||
|
|
||||||
// Create Agent directly with session (per official docs)
|
// Create Agent directly with session (per official docs)
|
||||||
try {
|
try {
|
||||||
this.agent = new Agent(session);
|
this.agent = new Agent(session);
|
||||||
|
|
||||||
|
|
||||||
// Check if agent has session info after creation
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (this.agent.session) {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
||||||
// Fallback to dpopFetch method
|
// Fallback to dpopFetch method
|
||||||
this.agent = new Agent({
|
this.agent = new Agent({
|
||||||
service: session.server?.serviceEndpoint || 'https://bsky.social',
|
service: session.server?.serviceEndpoint || 'https://bsky.social',
|
||||||
@ -204,61 +182,15 @@ class AtprotoOAuthService {
|
|||||||
return `${origin}/client-metadata.json`;
|
return `${origin}/client-metadata.json`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async detectPDSFromHandle(handle: string): Promise<string> {
|
|
||||||
// Handle detection for OAuth PDS routing
|
|
||||||
|
|
||||||
// Check if handle ends with known PDS domains first
|
|
||||||
const pdsMapping = {
|
|
||||||
'syu.is': 'https://syu.is',
|
|
||||||
'bsky.social': 'https://bsky.social',
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const [domain, pdsUrl] of Object.entries(pdsMapping)) {
|
|
||||||
if (handle.endsWith(`.${domain}`)) {
|
|
||||||
// Using PDS for domain match
|
|
||||||
return pdsUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For handles that don't match domain patterns, resolve via API
|
|
||||||
try {
|
|
||||||
// Try to resolve handle to get the actual PDS
|
|
||||||
const endpoints = ['https://syu.is', 'https://bsky.social'];
|
|
||||||
|
|
||||||
for (const endpoint of endpoints) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${endpoint}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`);
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
if (data.did) {
|
|
||||||
console.log('[OAuth Debug] Resolved handle via', endpoint, '- using that PDS');
|
|
||||||
return endpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('[OAuth Debug] Handle resolution failed, using default');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to bsky.social
|
|
||||||
// Using default bsky.social
|
|
||||||
return 'https://bsky.social';
|
|
||||||
}
|
|
||||||
|
|
||||||
async initiateOAuthFlow(handle?: string): Promise<void> {
|
async initiateOAuthFlow(handle?: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
if (!this.oauthClient || !this.oauthClientSyuIs) {
|
||||||
|
|
||||||
if (!this.oauthClient) {
|
|
||||||
|
|
||||||
await this.initialize();
|
await this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.oauthClient) {
|
if (!this.oauthClient || !this.oauthClientSyuIs) {
|
||||||
throw new Error('Failed to initialize OAuth client');
|
throw new Error('Failed to initialize OAuth clients');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If handle is not provided, prompt user
|
// If handle is not provided, prompt user
|
||||||
@ -269,61 +201,27 @@ class AtprotoOAuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine which OAuth client to use
|
||||||
|
const allowedHandlesStr = import.meta.env.VITE_ATPROTO_HANDLE_LIST || '[]';
|
||||||
|
let allowedHandles: string[] = [];
|
||||||
|
try {
|
||||||
|
allowedHandles = JSON.parse(allowedHandlesStr);
|
||||||
|
} catch {
|
||||||
|
allowedHandles = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const usesSyuIs = handle.endsWith('.syu.is') || allowedHandles.includes(handle);
|
||||||
// Detect PDS based on handle
|
const oauthClient = usesSyuIs ? this.oauthClientSyuIs : this.oauthClient;
|
||||||
const pdsUrl = await this.detectPDSFromHandle(handle);
|
|
||||||
// Starting OAuth flow
|
|
||||||
|
|
||||||
|
|
||||||
// Always re-initialize OAuth client with detected PDS
|
|
||||||
// Re-initializing OAuth client
|
|
||||||
|
|
||||||
// Clear existing client to force fresh initialization
|
|
||||||
this.oauthClient = null;
|
|
||||||
this.initializePromise = null;
|
|
||||||
|
|
||||||
this.oauthClient = await BrowserOAuthClient.load({
|
|
||||||
clientId: this.getClientId(),
|
|
||||||
handleResolver: pdsUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
// OAuth client initialized
|
|
||||||
|
|
||||||
// Start OAuth authorization flow
|
// Start OAuth authorization flow
|
||||||
|
const authUrl = await oauthClient.authorize(handle, {
|
||||||
|
|
||||||
try {
|
|
||||||
// Starting OAuth authorization
|
|
||||||
|
|
||||||
// Try to authorize with DID instead of handle for syu.is PDS only
|
|
||||||
let authTarget = handle;
|
|
||||||
if (pdsUrl === 'https://syu.is') {
|
|
||||||
try {
|
|
||||||
const resolveResponse = await fetch(`${pdsUrl}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`);
|
|
||||||
if (resolveResponse.ok) {
|
|
||||||
const resolveData = await resolveResponse.json();
|
|
||||||
authTarget = resolveData.did;
|
|
||||||
// Using DID for syu.is OAuth workaround
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Could not resolve to DID, using handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const authUrl = await this.oauthClient.authorize(authTarget, {
|
|
||||||
scope: 'atproto transition:generic',
|
scope: 'atproto transition:generic',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Redirect to authorization server
|
// Redirect to authorization server
|
||||||
window.location.href = authUrl.toString();
|
window.location.href = authUrl.toString();
|
||||||
} catch (authorizeError) {
|
|
||||||
// Authorization failed
|
|
||||||
throw authorizeError;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
throw new Error(`OAuth認証の開始に失敗しました: ${error}`);
|
throw new Error(`OAuth認証の開始に失敗しました: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,22 +277,16 @@ class AtprotoOAuthService {
|
|||||||
|
|
||||||
async checkSession(): Promise<{ did: string; handle: string } | null> {
|
async checkSession(): Promise<{ did: string; handle: string } | null> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
if (!this.oauthClient) {
|
if (!this.oauthClient) {
|
||||||
|
|
||||||
await this.initialize();
|
await this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.oauthClient) {
|
if (!this.oauthClient) {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const result = await this.oauthClient.init();
|
const result = await this.oauthClient.init();
|
||||||
|
|
||||||
|
|
||||||
if (result?.session) {
|
if (result?.session) {
|
||||||
// Use the common session processing method
|
// Use the common session processing method
|
||||||
return this.processSession(result.session);
|
return this.processSession(result.session);
|
||||||
@ -458,28 +350,20 @@ class AtprotoOAuthService {
|
|||||||
|
|
||||||
async logout(): Promise<void> {
|
async logout(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
// Clear Agent
|
// Clear Agent
|
||||||
this.agent = null;
|
this.agent = null;
|
||||||
|
|
||||||
|
|
||||||
// Clear BrowserOAuthClient session
|
// Clear BrowserOAuthClient session
|
||||||
if (this.oauthClient) {
|
if (this.oauthClient) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// BrowserOAuthClient may have a revoke or signOut method
|
// BrowserOAuthClient may have a revoke or signOut method
|
||||||
if (typeof (this.oauthClient as any).signOut === 'function') {
|
if (typeof (this.oauthClient as any).signOut === 'function') {
|
||||||
await (this.oauthClient as any).signOut();
|
await (this.oauthClient as any).signOut();
|
||||||
|
|
||||||
} else if (typeof (this.oauthClient as any).revoke === 'function') {
|
} else if (typeof (this.oauthClient as any).revoke === 'function') {
|
||||||
await (this.oauthClient as any).revoke();
|
await (this.oauthClient as any).revoke();
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (oauthError) {
|
} catch (oauthError) {
|
||||||
|
// Ignore logout errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the OAuth client to force re-initialization
|
// Reset the OAuth client to force re-initialization
|
||||||
@ -491,18 +375,16 @@ class AtprotoOAuthService {
|
|||||||
localStorage.removeItem('atproto_session');
|
localStorage.removeItem('atproto_session');
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
|
|
||||||
// Clear all localStorage items that might be related to OAuth
|
// Clear all OAuth-related storage
|
||||||
const keysToRemove: string[] = [];
|
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i);
|
||||||
if (key && (key.includes('oauth') || key.includes('atproto') || key.includes('session'))) {
|
if (key && (key.includes('oauth') || key.includes('atproto') || key.includes('session'))) {
|
||||||
keysToRemove.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keysToRemove.forEach(key => {
|
|
||||||
|
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear internal session info
|
||||||
|
(this as any)._sessionInfo = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
cb=ai.syui.log
|
cb=ai.syui.log
|
||||||
cl=( $cb.user )
|
cl=( $cb.chat.lang $cb.chat.comment)
|
||||||
f=~/.config/syui/ai/log/config.json
|
f=~/.config/syui/ai/log/config.json
|
||||||
|
|
||||||
default_collection="ai.syui.log.chat.comment"
|
default_collection="ai.syui.log.chat"
|
||||||
default_pds="syu.is"
|
default_pds="syu.is"
|
||||||
default_did=`cat $f|jq -r .admin.did`
|
default_did=`cat $f|jq -r .admin.did`
|
||||||
default_token=`cat $f|jq -r .admin.access_jwt`
|
default_token=`cat $f|jq -r .admin.access_jwt`
|
||||||
|
@ -1426,21 +1426,8 @@ async fn generate_ai_content(content: &str, prompt_type: &str, ai_config: &AiCon
|
|||||||
.timeout(std::time::Duration::from_secs(120)) // 2 minute timeout
|
.timeout(std::time::Duration::from_secs(120)) // 2 minute timeout
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// Try localhost first (for same-server deployment)
|
// Use configured Ollama host
|
||||||
let localhost_url = "http://localhost:11434/api/generate";
|
let ollama_url = format!("{}/api/generate", ai_config.ollama_host);
|
||||||
match client.post(localhost_url).json(&request).send().await {
|
|
||||||
Ok(response) if response.status().is_success() => {
|
|
||||||
let ollama_response: OllamaResponse = response.json().await?;
|
|
||||||
println!("{}", "✅ Used localhost Ollama".green());
|
|
||||||
return Ok(ollama_response.response);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("{}", "⚠️ Localhost Ollama not available, trying remote...".yellow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to remote host
|
|
||||||
let remote_url = format!("{}/api/generate", ai_config.ollama_host);
|
|
||||||
|
|
||||||
// Check if this is a local/private network connection (no CORS needed)
|
// Check if this is a local/private network connection (no CORS needed)
|
||||||
// RFC 1918 private networks + localhost
|
// RFC 1918 private networks + localhost
|
||||||
@ -1461,13 +1448,13 @@ async fn generate_ai_content(content: &str, prompt_type: &str, ai_config: &AiCon
|
|||||||
} else { false }
|
} else { false }
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut request_builder = client.post(&remote_url).json(&request);
|
let mut request_builder = client.post(&ollama_url).json(&request);
|
||||||
|
|
||||||
if !is_local {
|
if !is_local {
|
||||||
println!("{}", format!("🔗 Making request to: {} with Origin: {}", remote_url, ai_config.blog_host).blue());
|
println!("{}", format!("🔗 Making request to: {} with Origin: {}", ollama_url, ai_config.blog_host).blue());
|
||||||
request_builder = request_builder.header("Origin", &ai_config.blog_host);
|
request_builder = request_builder.header("Origin", &ai_config.blog_host);
|
||||||
} else {
|
} else {
|
||||||
println!("{}", format!("🔗 Making request to local network: {}", remote_url).blue());
|
println!("{}", format!("🔗 Making request to local network: {}", ollama_url).blue());
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = request_builder.send().await?;
|
let response = request_builder.send().await?;
|
||||||
@ -1477,7 +1464,7 @@ async fn generate_ai_content(content: &str, prompt_type: &str, ai_config: &AiCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ollama_response: OllamaResponse = response.json().await?;
|
let ollama_response: OllamaResponse = response.json().await?;
|
||||||
println!("{}", "✅ Used remote Ollama".green());
|
println!("{}", "✅ Ollama request successful".green());
|
||||||
Ok(ollama_response.response)
|
Ok(ollama_response.response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user