diff --git a/apps/docs/app/api/search/route.ts b/apps/docs/app/api/search/route.ts index be205cd553..b777ae890f 100644 --- a/apps/docs/app/api/search/route.ts +++ b/apps/docs/app/api/search/route.ts @@ -1,16 +1,126 @@ -import { createFromSource } from 'fumadocs-core/search/server' -import { source } from '@/lib/source' - -export const revalidate = 3600 // Revalidate every hour - -export const { GET } = createFromSource(source, { - localeMap: { - en: { language: 'english' }, - es: { language: 'spanish' }, - fr: { language: 'french' }, - de: { language: 'german' }, - // ja and zh are not supported by the stemmer library, so we'll skip language config for them - ja: {}, - zh: {}, - }, -}) +import { sql } from 'drizzle-orm' +import { type NextRequest, NextResponse } from 'next/server' +import { db, docsEmbeddings } from '@/lib/db' +import { generateSearchEmbedding } from '@/lib/embeddings' + +export const runtime = 'nodejs' +export const revalidate = 0 + +/** + * Hybrid search API endpoint + * - English: Vector embeddings + keyword search + * - Other languages: Keyword search only + */ +export async function GET(request: NextRequest) { + try { + const searchParams = request.nextUrl.searchParams + const query = searchParams.get('query') || searchParams.get('q') || '' + const locale = searchParams.get('locale') || 'en' + const limit = Number.parseInt(searchParams.get('limit') || '10', 10) + + if (!query || query.trim().length === 0) { + return NextResponse.json([]) + } + + const candidateLimit = limit * 3 + const similarityThreshold = 0.6 + + const localeMap: Record = { + en: 'english', + es: 'spanish', + fr: 'french', + de: 'german', + ja: 'simple', // PostgreSQL doesn't have Japanese support, use simple + zh: 'simple', // PostgreSQL doesn't have Chinese support, use simple + } + const tsConfig = localeMap[locale] || 'simple' + + const useVectorSearch = locale === 'en' + let vectorResults: Array<{ + chunkId: string + chunkText: string + sourceDocument: string + sourceLink: string + headerText: string + headerLevel: number + similarity: number + searchType: string + }> = [] + + if (useVectorSearch) { + const queryEmbedding = await generateSearchEmbedding(query) + vectorResults = await db + .select({ + chunkId: docsEmbeddings.chunkId, + chunkText: docsEmbeddings.chunkText, + sourceDocument: docsEmbeddings.sourceDocument, + sourceLink: docsEmbeddings.sourceLink, + headerText: docsEmbeddings.headerText, + headerLevel: docsEmbeddings.headerLevel, + similarity: sql`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector)`, + searchType: sql`'vector'`, + }) + .from(docsEmbeddings) + .where( + sql`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector) >= ${similarityThreshold}` + ) + .orderBy(sql`${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector`) + .limit(candidateLimit) + } + + const keywordResults = await db + .select({ + chunkId: docsEmbeddings.chunkId, + chunkText: docsEmbeddings.chunkText, + sourceDocument: docsEmbeddings.sourceDocument, + sourceLink: docsEmbeddings.sourceLink, + headerText: docsEmbeddings.headerText, + headerLevel: docsEmbeddings.headerLevel, + similarity: sql`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query}))`, + searchType: sql`'keyword'`, + }) + .from(docsEmbeddings) + .where(sql`${docsEmbeddings.chunkTextTsv} @@ plainto_tsquery(${tsConfig}, ${query})`) + .orderBy( + sql`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query})) DESC` + ) + .limit(candidateLimit) + + const seenIds = new Set() + const mergedResults = [] + + for (let i = 0; i < Math.max(vectorResults.length, keywordResults.length); i++) { + if (i < vectorResults.length && !seenIds.has(vectorResults[i].chunkId)) { + mergedResults.push(vectorResults[i]) + seenIds.add(vectorResults[i].chunkId) + } + if (i < keywordResults.length && !seenIds.has(keywordResults[i].chunkId)) { + mergedResults.push(keywordResults[i]) + seenIds.add(keywordResults[i].chunkId) + } + } + + const filteredResults = mergedResults.slice(0, limit) + const searchResults = filteredResults.map((result) => { + const title = result.headerText || result.sourceDocument.replace('.mdx', '') + const pathParts = result.sourceDocument + .replace('.mdx', '') + .split('/') + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + + return { + id: result.chunkId, + type: 'page' as const, + url: result.sourceLink, + content: title, + breadcrumbs: pathParts, + } + }) + + return NextResponse.json(searchResults) + } catch (error) { + console.error('Semantic search error:', error) + + return NextResponse.json([]) + } +} diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 5f5e0c2bb4..1a6805e5e0 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -462,6 +462,19 @@ export function SlackIcon(props: SVGProps) { ) } +export function SlackMonoIcon(props: SVGProps) { + return ( + + + + + + + + + ) +} + export function GithubIcon(props: SVGProps) { return ( diff --git a/apps/docs/content/docs/de/execution/costs.mdx b/apps/docs/content/docs/de/execution/costs.mdx index 251e487c82..1f5da14764 100644 --- a/apps/docs/content/docs/de/execution/costs.mdx +++ b/apps/docs/content/docs/de/execution/costs.mdx @@ -105,28 +105,32 @@ Die Modellaufschlüsselung zeigt: Die angezeigten Preise entsprechen den Tarifen vom 10. September 2025. Überprüfen Sie die Dokumentation der Anbieter für aktuelle Preise. +## Bring Your Own Key (BYOK) + +Sie können Ihre eigenen API-Schlüssel für gehostete Modelle (OpenAI, Anthropic, Google, Mistral) unter **Einstellungen → BYOK** verwenden, um Basispreise zu zahlen. Schlüssel werden verschlüsselt und gelten arbeitsbereichsweit. + ## Strategien zur Kostenoptimierung -- **Modellauswahl**: Wählen Sie Modelle basierend auf der Komplexität der Aufgabe. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Denken möglicherweise o1 oder Claude Opus erfordert. -- **Prompt-Engineering**: Gut strukturierte, präzise Prompts reduzieren den Token-Verbrauch ohne Qualitätseinbußen. +- **Modellauswahl**: Wählen Sie Modelle basierend auf der Aufgabenkomplexität. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Reasoning o1 oder Claude Opus erfordern könnte. +- **Prompt Engineering**: Gut strukturierte, prägnante Prompts reduzieren den Token-Verbrauch ohne Qualitätsverlust. - **Lokale Modelle**: Verwenden Sie Ollama oder VLLM für unkritische Aufgaben, um API-Kosten vollständig zu eliminieren. -- **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte KI-Modellaufrufe zu vermeiden. -- **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen KI-Anfrage anstatt einzelne Aufrufe zu tätigen. +- **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte AI-Modellaufrufe zu vermeiden. +- **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen AI-Anfrage, anstatt einzelne Aufrufe zu tätigen. ## Nutzungsüberwachung Überwachen Sie Ihre Nutzung und Abrechnung unter Einstellungen → Abonnement: -- **Aktuelle Nutzung**: Echtzeit-Nutzung und -Kosten für den aktuellen Zeitraum -- **Nutzungslimits**: Plangrenzen mit visuellen Fortschrittsanzeigen +- **Aktuelle Nutzung**: Echtzeit-Nutzung und Kosten für den aktuellen Zeitraum +- **Nutzungslimits**: Plan-Limits mit visuellen Fortschrittsindikatoren - **Abrechnungsdetails**: Prognostizierte Gebühren und Mindestverpflichtungen -- **Planverwaltung**: Upgrade-Optionen und Abrechnungsverlauf +- **Plan-Verwaltung**: Upgrade-Optionen und Abrechnungsverlauf -### Programmatische Nutzungsverfolgung +### Programmatisches Nutzungs-Tracking Sie können Ihre aktuelle Nutzung und Limits programmatisch über die API abfragen: -**Endpunkt:** +**Endpoint:** ```text GET /api/users/me/usage-limits @@ -172,69 +176,69 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt ``` **Rate-Limit-Felder:** -- `requestsPerMinute`: Dauerhafte Rate-Begrenzung (Tokens werden mit dieser Rate aufgefüllt) -- `maxBurst`: Maximale Tokens, die Sie ansammeln können (Burst-Kapazität) -- `remaining`: Aktuell verfügbare Tokens (können bis zu `maxBurst` sein) +- `requestsPerMinute`: Dauerhaftes Rate-Limit (Tokens werden mit dieser Rate aufgefüllt) +- `maxBurst`: Maximale Tokens, die Sie akkumulieren können (Burst-Kapazität) +- `remaining`: Aktuell verfügbare Tokens (kann bis zu `maxBurst` betragen) **Antwortfelder:** -- `currentPeriodCost` spiegelt die Nutzung in der aktuellen Abrechnungsperiode wider -- `limit` wird von individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet -- `plan` ist der aktive Plan mit der höchsten Priorität, der mit Ihrem Benutzer verknüpft ist +- `currentPeriodCost` spiegelt die Nutzung im aktuellen Abrechnungszeitraum wider +- `limit` wird aus individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet +- `plan` ist der Plan mit der höchsten Priorität, der Ihrem Benutzer zugeordnet ist ## Plan-Limits -Verschiedene Abonnementpläne haben unterschiedliche Nutzungslimits: +Verschiedene Abonnement-Pläne haben unterschiedliche Nutzungslimits: | Plan | Monatliches Nutzungslimit | Ratenlimits (pro Minute) | |------|-------------------|-------------------------| -| **Free** | 20 $ | 5 synchron, 10 asynchron | -| **Pro** | 100 $ | 10 synchron, 50 asynchron | -| **Team** | 500 $ (gepoolt) | 50 synchron, 100 asynchron | +| **Free** | 20 $ | 5 sync, 10 async | +| **Pro** | 100 $ | 10 sync, 50 async | +| **Team** | 500 $ (gemeinsam) | 50 sync, 100 async | | **Enterprise** | Individuell | Individuell | ## Abrechnungsmodell -Sim verwendet ein **Basisabonnement + Mehrverbrauch**-Abrechnungsmodell: +Sim verwendet ein **Basis-Abonnement + Mehrverbrauch**-Abrechnungsmodell: -### Wie es funktioniert +### So funktioniert es -**Pro-Plan ($20/Monat):** -- Monatliches Abonnement beinhaltet $20 Nutzung -- Nutzung unter $20 → Keine zusätzlichen Kosten -- Nutzung über $20 → Zahlen Sie den Mehrverbrauch am Monatsende -- Beispiel: $35 Nutzung = $20 (Abonnement) + $15 (Mehrverbrauch) +**Pro-Plan (20 $/Monat):** +- Monatsabonnement beinhaltet 20 $ Nutzung +- Nutzung unter 20 $ → Keine zusätzlichen Gebühren +- Nutzung über 20 $ → Mehrverbrauch am Monatsende zahlen +- Beispiel: 35 $ Nutzung = 20 $ (Abonnement) + 15 $ (Mehrverbrauch) -**Team-Plan ($40/Benutzer/Monat):** -- Gepoolte Nutzung für alle Teammitglieder -- Mehrverbrauch wird aus der Gesamtnutzung des Teams berechnet +**Team-Plan (40 $/Platz/Monat):** +- Gemeinsame Nutzung über alle Teammitglieder +- Mehrverbrauch wird aus der gesamten Team-Nutzung berechnet - Organisationsinhaber erhält eine Rechnung **Enterprise-Pläne:** -- Fester monatlicher Preis, kein Mehrverbrauch +- Fester Monatspreis, kein Mehrverbrauch - Individuelle Nutzungslimits gemäß Vereinbarung ### Schwellenwert-Abrechnung -Wenn der nicht abgerechnete Mehrverbrauch $50 erreicht, berechnet Sim automatisch den gesamten nicht abgerechneten Betrag. +Wenn der nicht abgerechnete Mehrverbrauch 50 $ erreicht, rechnet Sim automatisch den gesamten nicht abgerechneten Betrag ab. **Beispiel:** -- Tag 10: $70 Mehrverbrauch → Sofortige Abrechnung von $70 -- Tag 15: Zusätzliche $35 Nutzung ($105 insgesamt) → Bereits abgerechnet, keine Aktion -- Tag 20: Weitere $50 Nutzung ($155 insgesamt, $85 nicht abgerechnet) → Sofortige Abrechnung von $85 +- Tag 10: 70 $ Mehrverbrauch → 70 $ sofort abrechnen +- Tag 15: Zusätzliche 35 $ Nutzung (105 $ gesamt) → Bereits abgerechnet, keine Aktion +- Tag 20: Weitere 50 $ Nutzung (155 $ gesamt, 85 $ nicht abgerechnet) → 85 $ sofort abrechnen -Dies verteilt große Überziehungsgebühren über den Monat, anstatt eine große Rechnung am Ende des Abrechnungszeitraums zu erhalten. +Dies verteilt große Mehrverbrauchsgebühren über den Monat, anstatt einer großen Rechnung am Periodenende. ## Best Practices für Kostenmanagement 1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden -2. **Budgets festlegen**: Nutzen Sie Planlimits als Leitplanken für Ihre Ausgaben +2. **Budgets festlegen**: Nutzen Sie Plan-Limits als Leitplanken für Ihre Ausgaben 3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl 4. **Passende Modelle verwenden**: Passen Sie die Modellkomplexität an die Aufgabenanforderungen an -5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie wenn möglich mehrere Anfragen, um den Overhead zu reduzieren +5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie mehrere Anfragen, wenn möglich, um Overhead zu reduzieren ## Nächste Schritte - Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription) - Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen -- Erkunden Sie die [Externe API](/execution/api) für programmatische Kostenüberwachung +- Entdecken Sie die [externe API](/execution/api) für programmatische Kostenüberwachung - Sehen Sie sich [Workflow-Optimierungstechniken](/blocks) an, um Kosten zu reduzieren \ No newline at end of file diff --git a/apps/docs/content/docs/de/tools/firecrawl.mdx b/apps/docs/content/docs/de/tools/firecrawl.mdx index e80f5cbe8d..d7ee2e6a65 100644 --- a/apps/docs/content/docs/de/tools/firecrawl.mdx +++ b/apps/docs/content/docs/de/tools/firecrawl.mdx @@ -146,6 +146,32 @@ Extrahieren Sie strukturierte Daten aus vollständigen Webseiten mithilfe von na | `success` | boolean | Ob der Extraktionsvorgang erfolgreich war | | `data` | object | Extrahierte strukturierte Daten gemäß dem Schema oder der Eingabeaufforderung | +### `firecrawl_agent` + +Autonomer Web-Datenextraktions-Agent. Sucht und sammelt Informationen basierend auf natürlichsprachlichen Anweisungen, ohne dass spezifische URLs erforderlich sind. + +#### Eingabe + +| Parameter | Typ | Erforderlich | Beschreibung | +| --------- | ---- | -------- | ----------- | +| `prompt` | string | Ja | Natürlichsprachliche Beschreibung der zu extrahierenden Daten \(max. 10.000 Zeichen\) | +| `urls` | json | Nein | Optionales Array von URLs, auf die sich der Agent konzentrieren soll | +| `schema` | json | Nein | JSON-Schema, das die Struktur der zu extrahierenden Daten definiert | +| `maxCredits` | number | Nein | Maximale Credits, die für diese Agent-Aufgabe verwendet werden sollen | +| `strictConstrainToURLs` | boolean | Nein | Wenn true, besucht der Agent nur URLs, die im urls-Array angegeben sind | +| `apiKey` | string | Ja | Firecrawl API-Schlüssel | + +#### Ausgabe + +| Parameter | Typ | Beschreibung | +| --------- | ---- | ----------- | +| `success` | boolean | Ob die Agent-Operation erfolgreich war | +| `status` | string | Aktueller Status des Agent-Jobs \(processing, completed, failed\) | +| `data` | object | Vom Agent extrahierte Daten | +| `creditsUsed` | number | Anzahl der von dieser Agent-Aufgabe verbrauchten Credits | +| `expiresAt` | string | Zeitstempel, wann die Ergebnisse ablaufen \(24 Stunden\) | +| `sources` | object | Array der vom Agent verwendeten Quell-URLs | + ## Hinweise - Kategorie: `tools` diff --git a/apps/docs/content/docs/de/tools/intercom.mdx b/apps/docs/content/docs/de/tools/intercom.mdx index 4cf52d8559..92c774344a 100644 --- a/apps/docs/content/docs/de/tools/intercom.mdx +++ b/apps/docs/content/docs/de/tools/intercom.mdx @@ -55,8 +55,7 @@ Erstellen Sie einen neuen Kontakt in Intercom mit E-Mail, external_id oder Rolle | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Erstellte Kontaktdaten | +| `contact` | object | Erstelltes Kontaktobjekt | ### `intercom_get_contact` @@ -72,8 +71,7 @@ Einen einzelnen Kontakt anhand der ID von Intercom abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Kontaktdaten | +| `contact` | object | Kontaktobjekt | ### `intercom_update_contact` @@ -101,8 +99,7 @@ Einen bestehenden Kontakt in Intercom aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Aktualisierte Kontaktdaten | +| `contact` | object | Aktualisiertes Kontaktobjekt | ### `intercom_list_contacts` @@ -119,8 +116,7 @@ Alle Kontakte von Intercom mit Paginierungsunterstützung auflisten | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Liste der Kontakte | +| `contacts` | array | Array von Kontaktobjekten | ### `intercom_search_contacts` @@ -140,8 +136,7 @@ Suche nach Kontakten in Intercom mit einer Abfrage | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Suchergebnisse | +| `contacts` | array | Array von übereinstimmenden Kontaktobjekten | ### `intercom_delete_contact` @@ -157,8 +152,9 @@ Einen Kontakt aus Intercom nach ID löschen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Löschergebnis | +| `id` | string | ID des gelöschten Kontakts | +| `deleted` | boolean | Ob der Kontakt gelöscht wurde | +| `metadata` | object | Metadaten der Operation | ### `intercom_create_company` @@ -182,8 +178,7 @@ Ein Unternehmen in Intercom erstellen oder aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Erstellte oder aktualisierte Unternehmensdaten | +| `company` | object | Erstelltes oder aktualisiertes Unternehmensobjekt | ### `intercom_get_company` @@ -199,8 +194,7 @@ Ein einzelnes Unternehmen anhand der ID von Intercom abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Unternehmensdaten | +| `company` | object | Unternehmensobjekt | ### `intercom_list_companies` @@ -218,8 +212,7 @@ Listet alle Unternehmen von Intercom mit Paginierungsunterstützung auf. Hinweis | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Liste der Unternehmen | +| `companies` | array | Array von Unternehmensobjekten | ### `intercom_get_conversation` @@ -237,8 +230,7 @@ Eine einzelne Konversation anhand der ID von Intercom abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Konversationsdaten | +| `conversation` | object | Konversationsobjekt | ### `intercom_list_conversations` @@ -257,8 +249,7 @@ Alle Konversationen von Intercom mit Paginierungsunterstützung auflisten | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Liste der Konversationen | +| `conversations` | array | Array von Konversationsobjekten | ### `intercom_reply_conversation` @@ -279,8 +270,7 @@ Als Administrator auf eine Konversation in Intercom antworten | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Aktualisierte Konversation mit Antwort | +| `conversation` | object | Aktualisiertes Konversationsobjekt | ### `intercom_search_conversations` @@ -300,8 +290,7 @@ Nach Konversationen in Intercom mit einer Abfrage suchen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Status des Operationserfolgs | -| `output` | object | Suchergebnisse | +| `conversations` | array | Array von übereinstimmenden Konversationsobjekten | ### `intercom_create_ticket` @@ -321,10 +310,9 @@ Ein neues Ticket in Intercom erstellen #### Output -| Parameter | Type | Beschreibung | +| Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Erstellte Ticket-Daten | +| `ticket` | object | Erstelltes Ticket-Objekt | ### `intercom_get_ticket` @@ -338,10 +326,9 @@ Ein einzelnes Ticket anhand der ID von Intercom abrufen #### Output -| Parameter | Type | Beschreibung | +| Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Ticket-Daten | +| `ticket` | object | Ticket-Objekt | ### `intercom_create_message` @@ -363,10 +350,9 @@ Eine neue vom Administrator initiierte Nachricht in Intercom erstellen und sende #### Output -| Parameter | Type | Beschreibung | +| Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `success` | boolean | Erfolgsstatus der Operation | -| `output` | object | Erstellte Nachrichtendaten | +| `message` | object | Erstelltes Nachrichtenobjekt | ## Notizen diff --git a/apps/docs/content/docs/de/tools/pinecone.mdx b/apps/docs/content/docs/de/tools/pinecone.mdx index 2a53e3e103..34a2eec444 100644 --- a/apps/docs/content/docs/de/tools/pinecone.mdx +++ b/apps/docs/content/docs/de/tools/pinecone.mdx @@ -70,8 +70,7 @@ Text-Datensätze in einen Pinecone-Index einfügen oder aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `statusText` | string | Status des Einfügevorgangs | -| `upsertedCount` | number | Anzahl der erfolgreich eingefügten Datensätze | +| `statusText` | string | Status der Upsert-Operation | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/de/tools/supabase.mdx b/apps/docs/content/docs/de/tools/supabase.mdx index 90f04562d1..91ac39b87b 100644 --- a/apps/docs/content/docs/de/tools/supabase.mdx +++ b/apps/docs/content/docs/de/tools/supabase.mdx @@ -266,10 +266,11 @@ Eine Datei in einen Supabase-Speicher-Bucket hochladen | --------- | ---- | -------- | ----------- | | `projectId` | string | Ja | Ihre Supabase-Projekt-ID \(z.B. jdrkgepadsdopsntdlom\) | | `bucket` | string | Ja | Der Name des Speicher-Buckets | -| `path` | string | Ja | Der Pfad, unter dem die Datei gespeichert wird \(z.B. "ordner/datei.jpg"\) | +| `fileName` | string | Ja | Der Name der Datei \(z.B. "dokument.pdf", "bild.jpg"\) | +| `path` | string | Nein | Optionaler Ordnerpfad \(z.B. "ordner/unterordner/"\) | | `fileContent` | string | Ja | Der Dateiinhalt \(base64-kodiert für Binärdateien oder Klartext\) | | `contentType` | string | Nein | MIME-Typ der Datei \(z.B. "image/jpeg", "text/plain"\) | -| `upsert` | boolean | Nein | Wenn true, überschreibt vorhandene Datei \(Standard: false\) | +| `upsert` | boolean | Nein | Wenn true, wird die vorhandene Datei überschrieben \(Standard: false\) | | `apiKey` | string | Ja | Ihr Supabase Service Role Secret Key | #### Ausgabe diff --git a/apps/docs/content/docs/de/tools/typeform.mdx b/apps/docs/content/docs/de/tools/typeform.mdx index cc0b81877f..20109914b3 100644 --- a/apps/docs/content/docs/de/tools/typeform.mdx +++ b/apps/docs/content/docs/de/tools/typeform.mdx @@ -129,15 +129,18 @@ Vollständige Details und Struktur eines bestimmten Formulars abrufen | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `id` | string | Eindeutige Formular-ID | +| `id` | string | Eindeutige Formularkennung | | `title` | string | Formulartitel | | `type` | string | Formulartyp \(form, quiz, etc.\) | | `settings` | object | Formulareinstellungen einschließlich Sprache, Fortschrittsbalken, etc. | | `theme` | object | Theme-Referenz | | `workspace` | object | Workspace-Referenz | | `fields` | array | Array von Formularfeldern/Fragen | -| `welcome_screens` | array | Array von Begrüßungsbildschirmen | -| `thankyou_screens` | array | Array von Dankesbildschirmen | +| `welcome_screens` | array | Array von Willkommensbildschirmen \(leer, wenn keine konfiguriert\) | +| `thankyou_screens` | array | Array von Danke-Bildschirmen | +| `created_at` | string | Zeitstempel der Formularerstellung \(ISO-8601-Format\) | +| `last_updated_at` | string | Zeitstempel der letzten Formularaktualisierung \(ISO-8601-Format\) | +| `published_at` | string | Zeitstempel der Formularveröffentlichung \(ISO-8601-Format\) | | `_links` | object | Links zu verwandten Ressourcen einschließlich öffentlicher Formular-URL | ### `typeform_create_form` @@ -163,7 +166,12 @@ Ein neues Formular mit Feldern und Einstellungen erstellen | `id` | string | Eindeutige Kennung des erstellten Formulars | | `title` | string | Formulartitel | | `type` | string | Formulartyp | -| `fields` | array | Array der erstellten Formularfelder | +| `settings` | object | Formulareinstellungsobjekt | +| `theme` | object | Theme-Referenz | +| `workspace` | object | Workspace-Referenz | +| `fields` | array | Array von erstellten Formularfeldern \(leer, wenn keine hinzugefügt\) | +| `welcome_screens` | array | Array von Willkommensbildschirmen \(leer, wenn keine konfiguriert\) | +| `thankyou_screens` | array | Array von Danke-Bildschirmen | | `_links` | object | Links zu verwandten Ressourcen einschließlich öffentlicher Formular-URL | ### `typeform_update_form` @@ -182,16 +190,7 @@ Ein bestehendes Formular mit JSON Patch-Operationen aktualisieren | Parameter | Typ | Beschreibung | | --------- | ---- | ----------- | -| `id` | string | Eindeutige Kennung des aktualisierten Formulars | -| `title` | string | Formulartitel | -| `type` | string | Formulartyp | -| `settings` | object | Formulareinstellungen | -| `theme` | object | Theme-Referenz | -| `workspace` | object | Workspace-Referenz | -| `fields` | array | Array von Formularfeldern | -| `welcome_screens` | array | Array von Begrüßungsbildschirmen | -| `thankyou_screens` | array | Array von Dankesbildschirmen | -| `_links` | object | Links zu verwandten Ressourcen | +| `message` | string | Erfolgsbestätigungsnachricht | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/de/triggers/schedule.mdx b/apps/docs/content/docs/de/triggers/schedule.mdx index 4fdb02fc18..d85b1837b7 100644 --- a/apps/docs/content/docs/de/triggers/schedule.mdx +++ b/apps/docs/content/docs/de/triggers/schedule.mdx @@ -56,7 +56,7 @@ Sie müssen Ihren Workflow bereitstellen, damit der Zeitplan mit der Ausführung ## Automatische Deaktivierung -Zeitpläne werden nach **10 aufeinanderfolgenden Fehlschlägen** automatisch deaktiviert, um unkontrollierte Fehler zu verhindern. Bei Deaktivierung: +Zeitpläne werden nach **100 aufeinanderfolgenden Fehlern** automatisch deaktiviert, um unkontrollierte Fehler zu verhindern. Bei Deaktivierung: - Erscheint ein Warnhinweis auf dem Zeitplan-Block - Die Ausführung des Zeitplans wird gestoppt diff --git a/apps/docs/content/docs/en/execution/costs.mdx b/apps/docs/content/docs/en/execution/costs.mdx index 7f1f38adf8..960681b0eb 100644 --- a/apps/docs/content/docs/en/execution/costs.mdx +++ b/apps/docs/content/docs/en/execution/costs.mdx @@ -104,6 +104,10 @@ The model breakdown shows: Pricing shown reflects rates as of September 10, 2025. Check provider documentation for current pricing. +## Bring Your Own Key (BYOK) + +You can use your own API keys for hosted models (OpenAI, Anthropic, Google, Mistral) in **Settings → BYOK** to pay base prices. Keys are encrypted and apply workspace-wide. + ## Cost Optimization Strategies - **Model Selection**: Choose models based on task complexity. Simple tasks can use GPT-4.1-nano while complex reasoning might need o1 or Claude Opus. diff --git a/apps/docs/content/docs/en/tools/firecrawl.mdx b/apps/docs/content/docs/en/tools/firecrawl.mdx index 7c8dadc11b..b38686bc11 100644 --- a/apps/docs/content/docs/en/tools/firecrawl.mdx +++ b/apps/docs/content/docs/en/tools/firecrawl.mdx @@ -149,6 +149,32 @@ Extract structured data from entire webpages using natural language prompts and | `success` | boolean | Whether the extraction operation was successful | | `data` | object | Extracted structured data according to the schema or prompt | +### `firecrawl_agent` + +Autonomous web data extraction agent. Searches and gathers information based on natural language prompts without requiring specific URLs. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `prompt` | string | Yes | Natural language description of the data to extract \(max 10,000 characters\) | +| `urls` | json | No | Optional array of URLs to focus the agent on | +| `schema` | json | No | JSON Schema defining the structure of data to extract | +| `maxCredits` | number | No | Maximum credits to spend on this agent task | +| `strictConstrainToURLs` | boolean | No | If true, agent will only visit URLs provided in the urls array | +| `apiKey` | string | Yes | Firecrawl API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the agent operation was successful | +| `status` | string | Current status of the agent job \(processing, completed, failed\) | +| `data` | object | Extracted data from the agent | +| `creditsUsed` | number | Number of credits consumed by this agent task | +| `expiresAt` | string | Timestamp when the results expire \(24 hours\) | +| `sources` | object | Array of source URLs used by the agent | + ## Notes diff --git a/apps/docs/content/docs/en/tools/intercom.mdx b/apps/docs/content/docs/en/tools/intercom.mdx index 6fd78dd9f3..760e4f46e1 100644 --- a/apps/docs/content/docs/en/tools/intercom.mdx +++ b/apps/docs/content/docs/en/tools/intercom.mdx @@ -58,8 +58,7 @@ Create a new contact in Intercom with email, external_id, or role | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Created contact data | +| `contact` | object | Created contact object | ### `intercom_get_contact` @@ -75,8 +74,7 @@ Get a single contact by ID from Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Contact data | +| `contact` | object | Contact object | ### `intercom_update_contact` @@ -104,8 +102,7 @@ Update an existing contact in Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Updated contact data | +| `contact` | object | Updated contact object | ### `intercom_list_contacts` @@ -122,8 +119,7 @@ List all contacts from Intercom with pagination support | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | List of contacts | +| `contacts` | array | Array of contact objects | ### `intercom_search_contacts` @@ -143,8 +139,7 @@ Search for contacts in Intercom using a query | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Search results | +| `contacts` | array | Array of matching contact objects | ### `intercom_delete_contact` @@ -160,8 +155,9 @@ Delete a contact from Intercom by ID | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Deletion result | +| `id` | string | ID of deleted contact | +| `deleted` | boolean | Whether the contact was deleted | +| `metadata` | object | Operation metadata | ### `intercom_create_company` @@ -185,8 +181,7 @@ Create or update a company in Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Created or updated company data | +| `company` | object | Created or updated company object | ### `intercom_get_company` @@ -202,8 +197,7 @@ Retrieve a single company by ID from Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Company data | +| `company` | object | Company object | ### `intercom_list_companies` @@ -221,8 +215,7 @@ List all companies from Intercom with pagination support. Note: This endpoint ha | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | List of companies | +| `companies` | array | Array of company objects | ### `intercom_get_conversation` @@ -240,8 +233,7 @@ Retrieve a single conversation by ID from Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Conversation data | +| `conversation` | object | Conversation object | ### `intercom_list_conversations` @@ -260,8 +252,7 @@ List all conversations from Intercom with pagination support | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | List of conversations | +| `conversations` | array | Array of conversation objects | ### `intercom_reply_conversation` @@ -282,8 +273,7 @@ Reply to a conversation as an admin in Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Updated conversation with reply | +| `conversation` | object | Updated conversation object | ### `intercom_search_conversations` @@ -303,8 +293,7 @@ Search for conversations in Intercom using a query | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Search results | +| `conversations` | array | Array of matching conversation objects | ### `intercom_create_ticket` @@ -326,8 +315,7 @@ Create a new ticket in Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Created ticket data | +| `ticket` | object | Created ticket object | ### `intercom_get_ticket` @@ -343,8 +331,7 @@ Retrieve a single ticket by ID from Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Ticket data | +| `ticket` | object | Ticket object | ### `intercom_create_message` @@ -368,8 +355,7 @@ Create and send a new admin-initiated message in Intercom | Parameter | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Operation success status | -| `output` | object | Created message data | +| `message` | object | Created message object | diff --git a/apps/docs/content/docs/en/tools/pinecone.mdx b/apps/docs/content/docs/en/tools/pinecone.mdx index 6550c5a512..340f48da2f 100644 --- a/apps/docs/content/docs/en/tools/pinecone.mdx +++ b/apps/docs/content/docs/en/tools/pinecone.mdx @@ -74,7 +74,6 @@ Insert or update text records in a Pinecone index | Parameter | Type | Description | | --------- | ---- | ----------- | | `statusText` | string | Status of the upsert operation | -| `upsertedCount` | number | Number of records successfully upserted | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/en/tools/supabase.mdx b/apps/docs/content/docs/en/tools/supabase.mdx index 1e0b771a29..2c6ba254ff 100644 --- a/apps/docs/content/docs/en/tools/supabase.mdx +++ b/apps/docs/content/docs/en/tools/supabase.mdx @@ -269,7 +269,8 @@ Upload a file to a Supabase storage bucket | --------- | ---- | -------- | ----------- | | `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) | | `bucket` | string | Yes | The name of the storage bucket | -| `path` | string | Yes | The path where the file will be stored \(e.g., "folder/file.jpg"\) | +| `fileName` | string | Yes | The name of the file \(e.g., "document.pdf", "image.jpg"\) | +| `path` | string | No | Optional folder path \(e.g., "folder/subfolder/"\) | | `fileContent` | string | Yes | The file content \(base64 encoded for binary files, or plain text\) | | `contentType` | string | No | MIME type of the file \(e.g., "image/jpeg", "text/plain"\) | | `upsert` | boolean | No | If true, overwrites existing file \(default: false\) | diff --git a/apps/docs/content/docs/en/tools/typeform.mdx b/apps/docs/content/docs/en/tools/typeform.mdx index 829fc64613..bd1d055905 100644 --- a/apps/docs/content/docs/en/tools/typeform.mdx +++ b/apps/docs/content/docs/en/tools/typeform.mdx @@ -139,8 +139,11 @@ Retrieve complete details and structure of a specific form | `theme` | object | Theme reference | | `workspace` | object | Workspace reference | | `fields` | array | Array of form fields/questions | -| `welcome_screens` | array | Array of welcome screens | +| `welcome_screens` | array | Array of welcome screens \(empty if none configured\) | | `thankyou_screens` | array | Array of thank you screens | +| `created_at` | string | Form creation timestamp \(ISO 8601 format\) | +| `last_updated_at` | string | Form last update timestamp \(ISO 8601 format\) | +| `published_at` | string | Form publication timestamp \(ISO 8601 format\) | | `_links` | object | Related resource links including public form URL | ### `typeform_create_form` @@ -166,7 +169,12 @@ Create a new form with fields and settings | `id` | string | Created form unique identifier | | `title` | string | Form title | | `type` | string | Form type | -| `fields` | array | Array of created form fields | +| `settings` | object | Form settings object | +| `theme` | object | Theme reference | +| `workspace` | object | Workspace reference | +| `fields` | array | Array of created form fields \(empty if none added\) | +| `welcome_screens` | array | Array of welcome screens \(empty if none configured\) | +| `thankyou_screens` | array | Array of thank you screens | | `_links` | object | Related resource links including public form URL | ### `typeform_update_form` @@ -185,16 +193,7 @@ Update an existing form using JSON Patch operations | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Updated form unique identifier | -| `title` | string | Form title | -| `type` | string | Form type | -| `settings` | object | Form settings | -| `theme` | object | Theme reference | -| `workspace` | object | Workspace reference | -| `fields` | array | Array of form fields | -| `welcome_screens` | array | Array of welcome screens | -| `thankyou_screens` | array | Array of thank you screens | -| `_links` | object | Related resource links | +| `message` | string | Success confirmation message | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/en/triggers/schedule.mdx b/apps/docs/content/docs/en/triggers/schedule.mdx index bb7bfbaa80..ec2f65e91e 100644 --- a/apps/docs/content/docs/en/triggers/schedule.mdx +++ b/apps/docs/content/docs/en/triggers/schedule.mdx @@ -56,7 +56,7 @@ You must deploy your workflow for the schedule to start running. Configure the s ## Automatic Disabling -Schedules automatically disable after **10 consecutive failures** to prevent runaway errors. When disabled: +Schedules automatically disable after **100 consecutive failures** to prevent runaway errors. When disabled: - A warning badge appears on the schedule block - The schedule stops executing diff --git a/apps/docs/content/docs/es/execution/costs.mdx b/apps/docs/content/docs/es/execution/costs.mdx index 1a88efa4b3..52bd7fc577 100644 --- a/apps/docs/content/docs/es/execution/costs.mdx +++ b/apps/docs/content/docs/es/execution/costs.mdx @@ -105,26 +105,30 @@ El desglose del modelo muestra: Los precios mostrados reflejan las tarifas a partir del 10 de septiembre de 2025. Consulta la documentación del proveedor para conocer los precios actuales. +## Trae tu propia clave (BYOK) + +Puedes usar tus propias claves API para modelos alojados (OpenAI, Anthropic, Google, Mistral) en **Configuración → BYOK** para pagar precios base. Las claves están encriptadas y se aplican a todo el espacio de trabajo. + ## Estrategias de optimización de costos -- **Selección de modelos**: Elige modelos según la complejidad de la tarea. Las tareas simples pueden usar GPT-4.1-nano mientras que el razonamiento complejo podría necesitar o1 o Claude Opus. -- **Ingeniería de prompts**: Los prompts bien estructurados y concisos reducen el uso de tokens sin sacrificar la calidad. -- **Modelos locales**: Usa Ollama o VLLM para tareas no críticas para eliminar por completo los costos de API. -- **Almacenamiento en caché y reutilización**: Guarda resultados frecuentemente utilizados en variables o archivos para evitar llamadas repetidas al modelo de IA. -- **Procesamiento por lotes**: Procesa múltiples elementos en una sola solicitud de IA en lugar de hacer llamadas individuales. +- **Selección de modelo**: elige modelos según la complejidad de la tarea. Las tareas simples pueden usar GPT-4.1-nano mientras que el razonamiento complejo podría necesitar o1 o Claude Opus. +- **Ingeniería de prompts**: los prompts bien estructurados y concisos reducen el uso de tokens sin sacrificar calidad. +- **Modelos locales**: usa Ollama o VLLM para tareas no críticas para eliminar completamente los costos de API. +- **Almacenamiento en caché y reutilización**: guarda resultados usados frecuentemente en variables o archivos para evitar llamadas repetidas al modelo de IA. +- **Procesamiento por lotes**: procesa múltiples elementos en una sola solicitud de IA en lugar de hacer llamadas individuales. ## Monitoreo de uso Monitorea tu uso y facturación en Configuración → Suscripción: -- **Uso actual**: Uso y costos en tiempo real para el período actual -- **Límites de uso**: Límites del plan con indicadores visuales de progreso -- **Detalles de facturación**: Cargos proyectados y compromisos mínimos -- **Gestión del plan**: Opciones de actualización e historial de facturación +- **Uso actual**: uso y costos en tiempo real para el período actual +- **Límites de uso**: límites del plan con indicadores visuales de progreso +- **Detalles de facturación**: cargos proyectados y compromisos mínimos +- **Gestión de plan**: opciones de actualización e historial de facturación -### Seguimiento programático de uso +### Seguimiento de uso programático -Puedes consultar tu uso actual y límites de forma programática utilizando la API: +Puedes consultar tu uso y límites actuales de forma programática usando la API: **Endpoint:** @@ -135,13 +139,13 @@ GET /api/users/me/usage-limits **Autenticación:** - Incluye tu clave API en el encabezado `X-API-Key` -**Ejemplo de solicitud:** +**Solicitud de ejemplo:** ```bash curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" https://sim.ai/api/users/me/usage-limits ``` -**Ejemplo de respuesta:** +**Respuesta de ejemplo:** ```json { @@ -172,14 +176,14 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt ``` **Campos de límite de tasa:** -- `requestsPerMinute`: Límite de tasa sostenida (los tokens se recargan a esta velocidad) -- `maxBurst`: Máximo de tokens que puedes acumular (capacidad de ráfaga) -- `remaining`: Tokens disponibles actualmente (puede ser hasta `maxBurst`) +- `requestsPerMinute`: límite de tasa sostenida (los tokens se recargan a esta tasa) +- `maxBurst`: tokens máximos que puedes acumular (capacidad de ráfaga) +- `remaining`: tokens actuales disponibles (puede ser hasta `maxBurst`) **Campos de respuesta:** - `currentPeriodCost` refleja el uso en el período de facturación actual -- `limit` se deriva de límites individuales (Gratuito/Pro) o límites agrupados de la organización (Equipo/Empresa) -- `plan` es el plan activo de mayor prioridad asociado a tu usuario +- `limit` se deriva de límites individuales (Free/Pro) o límites de organización agrupados (Team/Enterprise) +- `plan` es el plan activo de mayor prioridad asociado con tu usuario ## Límites del plan @@ -187,10 +191,10 @@ Los diferentes planes de suscripción tienen diferentes límites de uso: | Plan | Límite de uso mensual | Límites de tasa (por minuto) | |------|-------------------|-------------------------| -| **Gratis** | $20 | 5 síncronas, 10 asíncronas | -| **Pro** | $100 | 10 síncronas, 50 asíncronas | -| **Equipo** | $500 (compartido) | 50 síncronas, 100 asíncronas | -| **Empresarial** | Personalizado | Personalizado | +| **Gratuito** | $20 | 5 sync, 10 async | +| **Pro** | $100 | 10 sync, 50 async | +| **Equipo** | $500 (compartido) | 50 sync, 100 async | +| **Empresa** | Personalizado | Personalizado | ## Modelo de facturación @@ -200,16 +204,16 @@ Sim utiliza un modelo de facturación de **suscripción base + excedente**: **Plan Pro ($20/mes):** - La suscripción mensual incluye $20 de uso -- Uso por debajo de $20 → Sin cargos adicionales -- Uso por encima de $20 → Pagas el excedente al final del mes +- Uso inferior a $20 → Sin cargos adicionales +- Uso superior a $20 → Paga el excedente al final del mes - Ejemplo: $35 de uso = $20 (suscripción) + $15 (excedente) -**Plan de Equipo ($40/usuario/mes):** -- Uso agrupado entre todos los miembros del equipo -- Excedente calculado del uso total del equipo +**Plan Equipo ($40/usuario/mes):** +- Uso compartido entre todos los miembros del equipo +- El excedente se calcula a partir del uso total del equipo - El propietario de la organización recibe una sola factura -**Planes Empresariales:** +**Planes Empresa:** - Precio mensual fijo, sin excedentes - Límites de uso personalizados según el acuerdo @@ -218,23 +222,23 @@ Sim utiliza un modelo de facturación de **suscripción base + excedente**: Cuando el excedente no facturado alcanza los $50, Sim factura automáticamente el monto total no facturado. **Ejemplo:** -- Día 10: $70 de excedente → Factura inmediata de $70 -- Día 15: $35 adicionales de uso ($105 en total) → Ya facturado, sin acción -- Día 20: Otros $50 de uso ($155 en total, $85 no facturados) → Factura inmediata de $85 +- Día 10: $70 de excedente → Factura $70 inmediatamente +- Día 15: $35 adicionales de uso ($105 total) → Ya facturado, sin acción +- Día 20: Otros $50 de uso ($155 total, $85 sin facturar) → Factura $85 inmediatamente -Esto distribuye los cargos por exceso a lo largo del mes en lugar de una gran factura al final del período. +Esto distribuye los cargos por excedentes grandes a lo largo del mes en lugar de una sola factura grande al final del período. -## Mejores prácticas para la gestión de costos +## Mejores prácticas de gestión de costos -1. **Monitorear regularmente**: Revisa tu panel de uso con frecuencia para evitar sorpresas -2. **Establecer presupuestos**: Utiliza los límites del plan como guías para tu gasto -3. **Optimizar flujos de trabajo**: Revisa las ejecuciones de alto costo y optimiza los prompts o la selección de modelos -4. **Usar modelos apropiados**: Ajusta la complejidad del modelo a los requisitos de la tarea -5. **Agrupar tareas similares**: Combina múltiples solicitudes cuando sea posible para reducir la sobrecarga +1. **Monitorea regularmente**: Revisa tu panel de uso con frecuencia para evitar sorpresas +2. **Establece presupuestos**: Usa los límites del plan como barreras de protección para tu gasto +3. **Optimiza flujos de trabajo**: Revisa las ejecuciones de alto costo y optimiza los prompts o la selección de modelos +4. **Usa modelos apropiados**: Ajusta la complejidad del modelo a los requisitos de la tarea +5. **Agrupa tareas similares**: Combina múltiples solicitudes cuando sea posible para reducir la sobrecarga ## Próximos pasos - Revisa tu uso actual en [Configuración → Suscripción](https://sim.ai/settings/subscription) -- Aprende sobre [Registro](/execution/logging) para seguir los detalles de ejecución -- Explora la [API externa](/execution/api) para el monitoreo programático de costos -- Consulta las [técnicas de optimización de flujo de trabajo](/blocks) para reducir costos \ No newline at end of file +- Aprende sobre [Registro](/execution/logging) para rastrear detalles de ejecución +- Explora la [API externa](/execution/api) para monitoreo programático de costos +- Consulta las [técnicas de optimización de flujos de trabajo](/blocks) para reducir costos \ No newline at end of file diff --git a/apps/docs/content/docs/es/tools/firecrawl.mdx b/apps/docs/content/docs/es/tools/firecrawl.mdx index 486d73d9ec..f557a1e78a 100644 --- a/apps/docs/content/docs/es/tools/firecrawl.mdx +++ b/apps/docs/content/docs/es/tools/firecrawl.mdx @@ -146,6 +146,32 @@ Extrae datos estructurados de páginas web completas utilizando instrucciones en | `success` | boolean | Si la operación de extracción fue exitosa | | `data` | object | Datos estructurados extraídos según el esquema o indicación | +### `firecrawl_agent` + +Agente autónomo de extracción de datos web. Busca y recopila información basándose en instrucciones en lenguaje natural sin requerir URLs específicas. + +#### Entrada + +| Parámetro | Tipo | Obligatorio | Descripción | +| --------- | ---- | -------- | ----------- | +| `prompt` | string | Sí | Descripción en lenguaje natural de los datos a extraer (máx. 10.000 caracteres) | +| `urls` | json | No | Array opcional de URLs en las que enfocar al agente | +| `schema` | json | No | Esquema JSON que define la estructura de los datos a extraer | +| `maxCredits` | number | No | Créditos máximos a gastar en esta tarea del agente | +| `strictConstrainToURLs` | boolean | No | Si es true, el agente solo visitará las URLs proporcionadas en el array urls | +| `apiKey` | string | Sí | Clave API de Firecrawl | + +#### Salida + +| Parámetro | Tipo | Descripción | +| --------- | ---- | ----------- | +| `success` | boolean | Si la operación del agente fue exitosa | +| `status` | string | Estado actual del trabajo del agente (processing, completed, failed) | +| `data` | object | Datos extraídos por el agente | +| `creditsUsed` | number | Número de créditos consumidos por esta tarea del agente | +| `expiresAt` | string | Marca de tiempo de cuándo expiran los resultados (24 horas) | +| `sources` | object | Array de URLs fuente utilizadas por el agente | + ## Notas - Categoría: `tools` diff --git a/apps/docs/content/docs/es/tools/intercom.mdx b/apps/docs/content/docs/es/tools/intercom.mdx index ae29cfb2a3..01ab3b26bd 100644 --- a/apps/docs/content/docs/es/tools/intercom.mdx +++ b/apps/docs/content/docs/es/tools/intercom.mdx @@ -55,8 +55,7 @@ Crear un nuevo contacto en Intercom con email, external_id o rol | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del contacto creado | +| `contact` | object | Objeto de contacto creado | ### `intercom_get_contact` @@ -72,8 +71,7 @@ Obtener un solo contacto por ID desde Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del contacto | +| `contact` | object | Objeto de contacto | ### `intercom_update_contact` @@ -101,8 +99,7 @@ Actualizar un contacto existente en Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del contacto actualizado | +| `contact` | object | Objeto de contacto actualizado | ### `intercom_list_contacts` @@ -119,8 +116,7 @@ Listar todos los contactos de Intercom con soporte de paginación | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Lista de contactos | +| `contacts` | array | Array de objetos de contacto | ### `intercom_search_contacts` @@ -140,8 +136,7 @@ Buscar contactos en Intercom usando una consulta | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Resultados de la búsqueda | +| `contacts` | array | Array de objetos de contacto coincidentes | ### `intercom_delete_contact` @@ -157,8 +152,9 @@ Eliminar un contacto de Intercom por ID | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Resultado de la eliminación | +| `id` | string | ID del contacto eliminado | +| `deleted` | boolean | Si el contacto fue eliminado | +| `metadata` | object | Metadatos de la operación | ### `intercom_create_company` @@ -182,8 +178,7 @@ Crear o actualizar una empresa en Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de la empresa creada o actualizada | +| `company` | object | Objeto de empresa creado o actualizado | ### `intercom_get_company` @@ -199,8 +194,7 @@ Recuperar una única empresa por ID desde Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de la empresa | +| `company` | object | Objeto de empresa | ### `intercom_list_companies` @@ -218,8 +212,7 @@ Lista todas las empresas de Intercom con soporte de paginación. Nota: Este endp | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Lista de empresas | +| `companies` | array | Array de objetos de empresa | ### `intercom_get_conversation` @@ -237,8 +230,7 @@ Recuperar una sola conversación por ID desde Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos de la conversación | +| `conversation` | object | Objeto de conversación | ### `intercom_list_conversations` @@ -257,8 +249,7 @@ Listar todas las conversaciones de Intercom con soporte de paginación | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Lista de conversaciones | +| `conversations` | array | Array de objetos de conversación | ### `intercom_reply_conversation` @@ -279,8 +270,7 @@ Responder a una conversación como administrador en Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Conversación actualizada con respuesta | +| `conversation` | object | Objeto de conversación actualizado | ### `intercom_search_conversations` @@ -300,8 +290,7 @@ Buscar conversaciones en Intercom usando una consulta | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Resultados de la búsqueda | +| `conversations` | array | Array de objetos de conversación coincidentes | ### `intercom_create_ticket` @@ -323,8 +312,7 @@ Crear un nuevo ticket en Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del ticket creado | +| `ticket` | object | Objeto de ticket creado | ### `intercom_get_ticket` @@ -340,8 +328,7 @@ Recuperar un solo ticket por ID desde Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del ticket | +| `ticket` | object | Objeto de ticket | ### `intercom_create_message` @@ -365,8 +352,7 @@ Crear y enviar un nuevo mensaje iniciado por el administrador en Intercom | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `success` | boolean | Estado de éxito de la operación | -| `output` | object | Datos del mensaje creado | +| `message` | object | Objeto de mensaje creado | ## Notas diff --git a/apps/docs/content/docs/es/tools/pinecone.mdx b/apps/docs/content/docs/es/tools/pinecone.mdx index be2a5bd0bc..713564004f 100644 --- a/apps/docs/content/docs/es/tools/pinecone.mdx +++ b/apps/docs/content/docs/es/tools/pinecone.mdx @@ -70,8 +70,7 @@ Insertar o actualizar registros de texto en un índice de Pinecone | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `statusText` | string | Estado de la operación de inserción | -| `upsertedCount` | number | Número de registros insertados correctamente | +| `statusText` | string | Estado de la operación de upsert | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/es/tools/supabase.mdx b/apps/docs/content/docs/es/tools/supabase.mdx index 1feae9393f..9e50d3d294 100644 --- a/apps/docs/content/docs/es/tools/supabase.mdx +++ b/apps/docs/content/docs/es/tools/supabase.mdx @@ -266,7 +266,8 @@ Subir un archivo a un bucket de almacenamiento de Supabase | --------- | ---- | -------- | ----------- | | `projectId` | string | Sí | ID de tu proyecto Supabase \(p. ej., jdrkgepadsdopsntdlom\) | | `bucket` | string | Sí | El nombre del bucket de almacenamiento | -| `path` | string | Sí | La ruta donde se almacenará el archivo \(p. ej., "carpeta/archivo.jpg"\) | +| `fileName` | string | Sí | El nombre del archivo \(p. ej., "documento.pdf", "imagen.jpg"\) | +| `path` | string | No | Ruta de carpeta opcional \(p. ej., "carpeta/subcarpeta/"\) | | `fileContent` | string | Sí | El contenido del archivo \(codificado en base64 para archivos binarios, o texto plano\) | | `contentType` | string | No | Tipo MIME del archivo \(p. ej., "image/jpeg", "text/plain"\) | | `upsert` | boolean | No | Si es verdadero, sobrescribe el archivo existente \(predeterminado: false\) | diff --git a/apps/docs/content/docs/es/tools/typeform.mdx b/apps/docs/content/docs/es/tools/typeform.mdx index 33a925e155..86a2ccc317 100644 --- a/apps/docs/content/docs/es/tools/typeform.mdx +++ b/apps/docs/content/docs/es/tools/typeform.mdx @@ -136,9 +136,12 @@ Recuperar detalles completos y estructura de un formulario específico | `theme` | object | Referencia del tema | | `workspace` | object | Referencia del espacio de trabajo | | `fields` | array | Array de campos/preguntas del formulario | -| `welcome_screens` | array | Array de pantallas de bienvenida | +| `welcome_screens` | array | Array de pantallas de bienvenida \(vacío si no hay ninguna configurada\) | | `thankyou_screens` | array | Array de pantallas de agradecimiento | -| `_links` | object | Enlaces a recursos relacionados incluyendo URL pública del formulario | +| `created_at` | string | Marca de tiempo de creación del formulario \(formato ISO 8601\) | +| `last_updated_at` | string | Marca de tiempo de última actualización del formulario \(formato ISO 8601\) | +| `published_at` | string | Marca de tiempo de publicación del formulario \(formato ISO 8601\) | +| `_links` | object | Enlaces de recursos relacionados incluyendo URL pública del formulario | ### `typeform_create_form` @@ -163,8 +166,13 @@ Crear un nuevo formulario con campos y configuraciones | `id` | string | Identificador único del formulario creado | | `title` | string | Título del formulario | | `type` | string | Tipo de formulario | -| `fields` | array | Array de campos del formulario creado | -| `_links` | object | Enlaces a recursos relacionados incluyendo URL pública del formulario | +| `settings` | object | Objeto de configuración del formulario | +| `theme` | object | Referencia del tema | +| `workspace` | object | Referencia del espacio de trabajo | +| `fields` | array | Array de campos del formulario creados \(vacío si no se agregó ninguno\) | +| `welcome_screens` | array | Array de pantallas de bienvenida \(vacío si no hay ninguna configurada\) | +| `thankyou_screens` | array | Array de pantallas de agradecimiento | +| `_links` | object | Enlaces de recursos relacionados incluyendo URL pública del formulario | ### `typeform_update_form` @@ -182,16 +190,7 @@ Actualizar un formulario existente usando operaciones JSON Patch | Parámetro | Tipo | Descripción | | --------- | ---- | ----------- | -| `id` | string | Identificador único del formulario actualizado | -| `title` | string | Título del formulario | -| `type` | string | Tipo de formulario | -| `settings` | object | Configuración del formulario | -| `theme` | object | Referencia del tema | -| `workspace` | object | Referencia del espacio de trabajo | -| `fields` | array | Array de campos del formulario | -| `welcome_screens` | array | Array de pantallas de bienvenida | -| `thankyou_screens` | array | Array de pantallas de agradecimiento | -| `_links` | object | Enlaces a recursos relacionados | +| `message` | string | Mensaje de confirmación de éxito | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/es/triggers/schedule.mdx b/apps/docs/content/docs/es/triggers/schedule.mdx index 636e87b19d..7ef0f750d5 100644 --- a/apps/docs/content/docs/es/triggers/schedule.mdx +++ b/apps/docs/content/docs/es/triggers/schedule.mdx @@ -56,7 +56,7 @@ Debes desplegar tu flujo de trabajo para que la programación comience a ejecuta ## Desactivación automática -Las programaciones se desactivan automáticamente después de **10 fallos consecutivos** para evitar errores descontrolados. Cuando se desactiva: +Las programaciones se desactivan automáticamente después de **100 fallos consecutivos** para evitar errores descontrolados. Cuando están desactivadas: - Aparece una insignia de advertencia en el bloque de programación - La programación deja de ejecutarse diff --git a/apps/docs/content/docs/fr/execution/costs.mdx b/apps/docs/content/docs/fr/execution/costs.mdx index 2aa79c32f3..5b34903448 100644 --- a/apps/docs/content/docs/fr/execution/costs.mdx +++ b/apps/docs/content/docs/fr/execution/costs.mdx @@ -105,26 +105,30 @@ La répartition des modèles montre : Les prix indiqués reflètent les tarifs en date du 10 septembre 2025. Consultez la documentation des fournisseurs pour les tarifs actuels. +## Apportez votre propre clé (BYOK) + +Vous pouvez utiliser vos propres clés API pour les modèles hébergés (OpenAI, Anthropic, Google, Mistral) dans **Paramètres → BYOK** pour payer les prix de base. Les clés sont chiffrées et s'appliquent à l'ensemble de l'espace de travail. + ## Stratégies d'optimisation des coûts -- **Sélection du modèle** : choisissez les modèles en fonction de la complexité de la tâche. Les tâches simples peuvent utiliser GPT-4.1-nano tandis que le raisonnement complexe pourrait nécessiter o1 ou Claude Opus. -- **Ingénierie de prompt** : des prompts bien structurés et concis réduisent l'utilisation de tokens sans sacrifier la qualité. +- **Sélection du modèle** : choisissez les modèles en fonction de la complexité de la tâche. Les tâches simples peuvent utiliser GPT-4.1-nano tandis que le raisonnement complexe peut nécessiter o1 ou Claude Opus. +- **Ingénierie des prompts** : des prompts bien structurés et concis réduisent l'utilisation de jetons sans sacrifier la qualité. - **Modèles locaux** : utilisez Ollama ou VLLM pour les tâches non critiques afin d'éliminer complètement les coûts d'API. -- **Mise en cache et réutilisation** : stockez les résultats fréquemment utilisés dans des variables ou des fichiers pour éviter des appels répétés aux modèles d'IA. -- **Traitement par lots** : traitez plusieurs éléments dans une seule requête d'IA plutôt que de faire des appels individuels. +- **Mise en cache et réutilisation** : stockez les résultats fréquemment utilisés dans des variables ou des fichiers pour éviter les appels répétés aux modèles d'IA. +- **Traitement par lots** : traitez plusieurs éléments dans une seule requête d'IA plutôt que d'effectuer des appels individuels. -## Suivi de l'utilisation +## Surveillance de l'utilisation Surveillez votre utilisation et votre facturation dans Paramètres → Abonnement : - **Utilisation actuelle** : utilisation et coûts en temps réel pour la période en cours -- **Limites d'utilisation** : limites du forfait avec indicateurs visuels de progression -- **Détails de facturation** : frais prévisionnels et engagements minimums +- **Limites d'utilisation** : limites du forfait avec indicateurs de progression visuels +- **Détails de facturation** : frais projetés et engagements minimums - **Gestion du forfait** : options de mise à niveau et historique de facturation -### Suivi d'utilisation programmatique +### Suivi programmatique de l'utilisation -Vous pouvez interroger votre utilisation actuelle et vos limites par programmation en utilisant l'API : +Vous pouvez interroger votre utilisation et vos limites actuelles de manière programmatique à l'aide de l'API : **Point de terminaison :** @@ -172,14 +176,14 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt ``` **Champs de limite de débit :** -- `requestsPerMinute` : limite de débit soutenu (les jetons se rechargent à ce rythme) +- `requestsPerMinute` : limite de débit soutenue (les jetons se rechargent à ce rythme) - `maxBurst` : nombre maximum de jetons que vous pouvez accumuler (capacité de rafale) - `remaining` : jetons actuellement disponibles (peut aller jusqu'à `maxBurst`) **Champs de réponse :** - `currentPeriodCost` reflète l'utilisation dans la période de facturation actuelle -- `limit` est dérivé des limites individuelles (Gratuit/Pro) ou des limites mutualisées de l'organisation (Équipe/Entreprise) -- `plan` est le plan actif de plus haute priorité associé à votre utilisateur +- `limit` est dérivé des limites individuelles (Free/Pro) ou des limites d'organisation mutualisées (Team/Enterprise) +- `plan` est le forfait actif de priorité la plus élevée associé à votre utilisateur ## Limites des forfaits @@ -196,21 +200,21 @@ Les différents forfaits d'abonnement ont des limites d'utilisation différentes Sim utilise un modèle de facturation **abonnement de base + dépassement** : -### Comment ça fonctionne +### Fonctionnement **Forfait Pro (20 $/mois) :** - L'abonnement mensuel inclut 20 $ d'utilisation -- Utilisation inférieure à 20 $ → Pas de frais supplémentaires +- Utilisation inférieure à 20 $ → Aucun frais supplémentaire - Utilisation supérieure à 20 $ → Paiement du dépassement en fin de mois - Exemple : 35 $ d'utilisation = 20 $ (abonnement) + 15 $ (dépassement) -**Forfait Équipe (40 $/siège/mois) :** -- Utilisation mutualisée pour tous les membres de l'équipe -- Dépassement calculé à partir de l'utilisation totale de l'équipe +**Forfait Équipe (40 $/utilisateur/mois) :** +- Utilisation mutualisée entre tous les membres de l'équipe +- Dépassement calculé sur l'utilisation totale de l'équipe - Le propriétaire de l'organisation reçoit une seule facture **Forfaits Entreprise :** -- Prix mensuel fixe, pas de dépassements +- Prix mensuel fixe, sans dépassement - Limites d'utilisation personnalisées selon l'accord ### Facturation par seuil @@ -220,21 +224,21 @@ Lorsque le dépassement non facturé atteint 50 $, Sim facture automatiquement l **Exemple :** - Jour 10 : 70 $ de dépassement → Facturation immédiate de 70 $ - Jour 15 : 35 $ d'utilisation supplémentaire (105 $ au total) → Déjà facturé, aucune action -- Jour 20 : 50 $ d'utilisation supplémentaire (155 $ au total, 85 $ non facturés) → Facturation immédiate de 85 $ +- Jour 20 : 50 $ d'utilisation supplémentaire (155 $ au total, 85 $ non facturé) → Facturation immédiate de 85 $ Cela répartit les frais de dépassement importants tout au long du mois au lieu d'une seule facture importante en fin de période. -## Meilleures pratiques de gestion des coûts +## Bonnes pratiques de gestion des coûts -1. **Surveillez régulièrement** : vérifiez fréquemment votre tableau de bord d'utilisation pour éviter les surprises -2. **Définissez des budgets** : utilisez les limites du plan comme garde-fous pour vos dépenses -3. **Optimisez les flux de travail** : examinez les exécutions à coût élevé et optimisez les prompts ou la sélection de modèles -4. **Utilisez des modèles appropriés** : adaptez la complexité du modèle aux exigences de la tâche -5. **Regroupez les tâches similaires** : combinez plusieurs requêtes lorsque c'est possible pour réduire les frais généraux +1. **Surveillez régulièrement** : Consultez fréquemment votre tableau de bord d'utilisation pour éviter les surprises +2. **Définissez des budgets** : Utilisez les limites des forfaits comme garde-fous pour vos dépenses +3. **Optimisez les flux de travail** : Examinez les exécutions coûteuses et optimisez les prompts ou la sélection de modèles +4. **Utilisez les modèles appropriés** : Adaptez la complexité du modèle aux exigences de la tâche +5. **Regroupez les tâches similaires** : Combinez plusieurs requêtes lorsque c'est possible pour réduire les frais généraux ## Prochaines étapes -- Examinez votre utilisation actuelle dans [Paramètres → Abonnement](https://sim.ai/settings/subscription) -- Apprenez-en plus sur la [Journalisation](/execution/logging) pour suivre les détails d'exécution +- Consultez votre utilisation actuelle dans [Paramètres → Abonnement](https://sim.ai/settings/subscription) +- Découvrez la [journalisation](/execution/logging) pour suivre les détails d'exécution - Explorez l'[API externe](/execution/api) pour la surveillance programmatique des coûts -- Consultez les [techniques d'optimisation de flux de travail](/blocks) pour réduire les coûts \ No newline at end of file +- Consultez les [techniques d'optimisation des workflows](/blocks) pour réduire les coûts \ No newline at end of file diff --git a/apps/docs/content/docs/fr/tools/firecrawl.mdx b/apps/docs/content/docs/fr/tools/firecrawl.mdx index af64244467..b2aa9a8985 100644 --- a/apps/docs/content/docs/fr/tools/firecrawl.mdx +++ b/apps/docs/content/docs/fr/tools/firecrawl.mdx @@ -146,6 +146,32 @@ Extrayez des données structurées de pages web entières à l'aide d'instructio | `success` | boolean | Indique si l'opération d'extraction a réussi | | `data` | object | Données structurées extraites selon le schéma ou l'invite | +### `firecrawl_agent` + +Agent autonome d'extraction de données web. Recherche et collecte des informations basées sur des instructions en langage naturel sans nécessiter d'URLs spécifiques. + +#### Entrée + +| Paramètre | Type | Obligatoire | Description | +| --------- | ---- | ----------- | ----------- | +| `prompt` | string | Oui | Description en langage naturel des données à extraire (max 10 000 caractères) | +| `urls` | json | Non | Tableau optionnel d'URLs sur lesquelles concentrer l'agent | +| `schema` | json | Non | Schéma JSON définissant la structure des données à extraire | +| `maxCredits` | number | Non | Nombre maximum de crédits à dépenser pour cette tâche d'agent | +| `strictConstrainToURLs` | boolean | Non | Si true, l'agent visitera uniquement les URLs fournies dans le tableau urls | +| `apiKey` | string | Oui | Clé API Firecrawl | + +#### Sortie + +| Paramètre | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Indique si l'opération de l'agent a réussi | +| `status` | string | Statut actuel de la tâche de l'agent (processing, completed, failed) | +| `data` | object | Données extraites par l'agent | +| `creditsUsed` | number | Nombre de crédits consommés par cette tâche d'agent | +| `expiresAt` | string | Horodatage d'expiration des résultats (24 heures) | +| `sources` | object | Tableau des URLs sources utilisées par l'agent | + ## Remarques - Catégorie : `tools` diff --git a/apps/docs/content/docs/fr/tools/intercom.mdx b/apps/docs/content/docs/fr/tools/intercom.mdx index c8ba7ba2af..9aaa5eda41 100644 --- a/apps/docs/content/docs/fr/tools/intercom.mdx +++ b/apps/docs/content/docs/fr/tools/intercom.mdx @@ -56,8 +56,7 @@ Créer un nouveau contact dans Intercom avec email, external_id ou role | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du contact créé | +| `contact` | object | Objet contact créé | ### `intercom_get_contact` @@ -73,8 +72,7 @@ Obtenir un seul contact par ID depuis Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du contact | +| `contact` | object | Objet contact | ### `intercom_update_contact` @@ -102,8 +100,7 @@ Mettre à jour un contact existant dans Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du contact mises à jour | +| `contact` | object | Objet contact mis à jour | ### `intercom_list_contacts` @@ -120,8 +117,7 @@ Lister tous les contacts d'Intercom avec prise en charge de la pagination | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Liste des contacts | +| `contacts` | array | Tableau d'objets contact | ### `intercom_search_contacts` @@ -141,8 +137,7 @@ Rechercher des contacts dans Intercom à l'aide d'une requête | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Résultats de la recherche | +| `contacts` | array | Tableau d'objets contact correspondants | ### `intercom_delete_contact` @@ -158,8 +153,9 @@ Supprimer un contact d'Intercom par ID | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Résultat de la suppression | +| `id` | string | ID du contact supprimé | +| `deleted` | boolean | Indique si le contact a été supprimé | +| `metadata` | object | Métadonnées de l'opération | ### `intercom_create_company` @@ -183,8 +179,7 @@ Créer ou mettre à jour une entreprise dans Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Données de l'entreprise créée ou mise à jour | +| `company` | object | Objet entreprise créé ou mis à jour | ### `intercom_get_company` @@ -200,8 +195,7 @@ Récupérer une seule entreprise par ID depuis Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | booléen | Statut de réussite de l'opération | -| `output` | objet | Données de l'entreprise | +| `company` | object | Objet entreprise | ### `intercom_list_companies` @@ -219,8 +213,7 @@ Liste toutes les entreprises d'Intercom avec prise en charge de la pagination. R | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Liste des entreprises | +| `companies` | array | Tableau d'objets entreprise | ### `intercom_get_conversation` @@ -238,8 +231,7 @@ Récupérer une seule conversation par ID depuis Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données de la conversation | +| `conversation` | object | Objet conversation | ### `intercom_list_conversations` @@ -258,8 +250,7 @@ Lister toutes les conversations depuis Intercom avec prise en charge de la pagin | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Liste des conversations | +| `conversations` | array | Tableau d'objets conversation | ### `intercom_reply_conversation` @@ -280,8 +271,7 @@ Répondre à une conversation en tant qu'administrateur dans Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Conversation mise à jour avec la réponse | +| `conversation` | object | Objet conversation mis à jour | ### `intercom_search_conversations` @@ -301,8 +291,7 @@ Rechercher des conversations dans Intercom à l'aide d'une requête | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Résultats de la recherche | +| `conversations` | array | Tableau d'objets conversation correspondants | ### `intercom_create_ticket` @@ -324,8 +313,7 @@ Créer un nouveau ticket dans Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du ticket créé | +| `ticket` | object | Objet ticket créé | ### `intercom_get_ticket` @@ -341,8 +329,7 @@ Récupérer un ticket unique par ID depuis Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du ticket | +| `ticket` | object | Objet ticket | ### `intercom_create_message` @@ -366,8 +353,7 @@ Créer et envoyer un nouveau message initié par l'administrateur dans Intercom | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `success` | boolean | Statut de réussite de l'opération | -| `output` | object | Données du message créé | +| `message` | object | Objet message créé | ## Notes diff --git a/apps/docs/content/docs/fr/tools/pinecone.mdx b/apps/docs/content/docs/fr/tools/pinecone.mdx index 19a3738503..de86e7af22 100644 --- a/apps/docs/content/docs/fr/tools/pinecone.mdx +++ b/apps/docs/content/docs/fr/tools/pinecone.mdx @@ -70,8 +70,7 @@ Insérer ou mettre à jour des enregistrements textuels dans un index Pinecone | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `statusText` | chaîne | Statut de l'opération d'insertion | -| `upsertedCount` | nombre | Nombre d'enregistrements insérés avec succès | +| `statusText` | string | Statut de l'opération d'insertion ou de mise à jour | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/fr/tools/supabase.mdx b/apps/docs/content/docs/fr/tools/supabase.mdx index a7a8def647..ad95a25c36 100644 --- a/apps/docs/content/docs/fr/tools/supabase.mdx +++ b/apps/docs/content/docs/fr/tools/supabase.mdx @@ -266,7 +266,8 @@ Téléverser un fichier vers un bucket de stockage Supabase | --------- | ---- | ----------- | ----------- | | `projectId` | string | Oui | L'ID de votre projet Supabase \(ex. : jdrkgepadsdopsntdlom\) | | `bucket` | string | Oui | Le nom du bucket de stockage | -| `path` | string | Oui | Le chemin où le fichier sera stocké \(ex. : "dossier/fichier.jpg"\) | +| `fileName` | string | Oui | Le nom du fichier \(ex. : "document.pdf", "image.jpg"\) | +| `path` | string | Non | Chemin de dossier optionnel \(ex. : "dossier/sousdossier/"\) | | `fileContent` | string | Oui | Le contenu du fichier \(encodé en base64 pour les fichiers binaires, ou texte brut\) | | `contentType` | string | Non | Type MIME du fichier \(ex. : "image/jpeg", "text/plain"\) | | `upsert` | boolean | Non | Si vrai, écrase le fichier existant \(par défaut : false\) | diff --git a/apps/docs/content/docs/fr/tools/typeform.mdx b/apps/docs/content/docs/fr/tools/typeform.mdx index 9a3373390a..d8764b26fb 100644 --- a/apps/docs/content/docs/fr/tools/typeform.mdx +++ b/apps/docs/content/docs/fr/tools/typeform.mdx @@ -132,13 +132,16 @@ Récupérer les détails complets et la structure d'un formulaire spécifique | `id` | chaîne | Identifiant unique du formulaire | | `title` | chaîne | Titre du formulaire | | `type` | chaîne | Type de formulaire \(form, quiz, etc.\) | -| `settings` | objet | Paramètres du formulaire incluant langue, barre de progression, etc. | +| `settings` | objet | Paramètres du formulaire incluant la langue, la barre de progression, etc. | | `theme` | objet | Référence du thème | | `workspace` | objet | Référence de l'espace de travail | | `fields` | tableau | Tableau des champs/questions du formulaire | -| `welcome_screens` | tableau | Tableau des écrans d'accueil | +| `welcome_screens` | tableau | Tableau des écrans d'accueil \(vide si aucun n'est configuré\) | | `thankyou_screens` | tableau | Tableau des écrans de remerciement | -| `_links` | objet | Liens vers les ressources associées, y compris l'URL publique du formulaire | +| `created_at` | chaîne | Horodatage de création du formulaire \(format ISO 8601\) | +| `last_updated_at` | chaîne | Horodatage de dernière mise à jour du formulaire \(format ISO 8601\) | +| `published_at` | chaîne | Horodatage de publication du formulaire \(format ISO 8601\) | +| `_links` | objet | Liens vers les ressources associées incluant l'URL publique du formulaire | ### `typeform_create_form` @@ -163,8 +166,13 @@ Créer un nouveau formulaire avec champs et paramètres | `id` | chaîne | Identifiant unique du formulaire créé | | `title` | chaîne | Titre du formulaire | | `type` | chaîne | Type de formulaire | -| `fields` | tableau | Tableau des champs du formulaire créé | -| `_links` | objet | Liens vers les ressources associées, y compris l'URL publique du formulaire | +| `settings` | objet | Objet de paramètres du formulaire | +| `theme` | objet | Référence du thème | +| `workspace` | objet | Référence de l'espace de travail | +| `fields` | tableau | Tableau des champs du formulaire créés \(vide si aucun n'a été ajouté\) | +| `welcome_screens` | tableau | Tableau des écrans d'accueil \(vide si aucun n'est configuré\) | +| `thankyou_screens` | tableau | Tableau des écrans de remerciement | +| `_links` | objet | Liens vers les ressources associées incluant l'URL publique du formulaire | ### `typeform_update_form` @@ -182,16 +190,7 @@ Mettre à jour un formulaire existant à l'aide d'opérations JSON Patch | Paramètre | Type | Description | | --------- | ---- | ----------- | -| `id` | chaîne | Identifiant unique du formulaire mis à jour | -| `title` | chaîne | Titre du formulaire | -| `type` | chaîne | Type de formulaire | -| `settings` | objet | Paramètres du formulaire | -| `theme` | objet | Référence du thème | -| `workspace` | objet | Référence de l'espace de travail | -| `fields` | tableau | Tableau des champs du formulaire | -| `welcome_screens` | tableau | Tableau des écrans d'accueil | -| `thankyou_screens` | tableau | Tableau des écrans de remerciement | -| `_links` | objet | Liens vers les ressources associées | +| `message` | chaîne | Message de confirmation de succès | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/fr/triggers/schedule.mdx b/apps/docs/content/docs/fr/triggers/schedule.mdx index df9e112687..746b92a88f 100644 --- a/apps/docs/content/docs/fr/triggers/schedule.mdx +++ b/apps/docs/content/docs/fr/triggers/schedule.mdx @@ -56,7 +56,7 @@ Vous devez déployer votre workflow pour que la planification commence à s'exé ## Désactivation automatique -Les planifications se désactivent automatiquement après **10 échecs consécutifs** pour éviter les erreurs incontrôlées. Lorsqu'elle est désactivée : +Les planifications se désactivent automatiquement après **100 échecs consécutifs** pour éviter les erreurs en cascade. Lorsqu'elles sont désactivées : - Un badge d'avertissement apparaît sur le bloc de planification - La planification cesse de s'exécuter diff --git a/apps/docs/content/docs/ja/execution/costs.mdx b/apps/docs/content/docs/ja/execution/costs.mdx index 1c587e3d1f..efbbedaaf4 100644 --- a/apps/docs/content/docs/ja/execution/costs.mdx +++ b/apps/docs/content/docs/ja/execution/costs.mdx @@ -105,43 +105,47 @@ AIブロックを使用するワークフローでは、ログで詳細なコス 表示価格は2025年9月10日時点のレートを反映しています。最新の価格については各プロバイダーのドキュメントをご確認ください。 +## Bring Your Own Key (BYOK) + +ホストされたモデル(OpenAI、Anthropic、Google、Mistral)に対して、**設定 → BYOK**で独自のAPIキーを使用し、基本価格で支払うことができます。キーは暗号化され、ワークスペース全体に適用されます。 + ## コスト最適化戦略 -- **モデル選択**: タスクの複雑さに基づいてモデルを選択してください。単純なタスクにはGPT-4.1-nanoを使用し、複雑な推論にはo1やClaude Opusが必要な場合があります。 -- **プロンプトエンジニアリング**: 構造化された簡潔なプロンプトは、品質を犠牲にすることなくトークン使用量を削減します。 -- **ローカルモデル**: 重要度の低いタスクにはOllamaやVLLMを使用して、API費用を完全に排除します。 -- **キャッシュと再利用**: 頻繁に使用される結果を変数やファイルに保存して、AIモデル呼び出しの繰り返しを避けます。 +- **モデルの選択**: タスクの複雑さに基づいてモデルを選択します。シンプルなタスクにはGPT-4.1-nanoを使用し、複雑な推論にはo1やClaude Opusが必要になる場合があります。 +- **プロンプトエンジニアリング**: 適切に構造化された簡潔なプロンプトは、品質を犠牲にすることなくトークン使用量を削減します。 +- **ローカルモデル**: 重要度の低いタスクにはOllamaやVLLMを使用して、APIコストを完全に排除します。 +- **キャッシュと再利用**: 頻繁に使用される結果を変数やファイルに保存して、AIモデルの繰り返し呼び出しを回避します。 - **バッチ処理**: 個別の呼び出しを行うのではなく、単一のAIリクエストで複数のアイテムを処理します。 -## 使用状況モニタリング +## 使用状況の監視 -設定 → サブスクリプションで使用状況と請求を監視できます: +設定 → サブスクリプションで使用状況と請求を監視します: - **現在の使用状況**: 現在の期間のリアルタイムの使用状況とコスト -- **使用制限**: 視覚的な進捗指標付きのプラン制限 -- **請求詳細**: 予測される料金と最低利用額 +- **使用制限**: 視覚的な進行状況インジケーター付きのプラン制限 +- **請求詳細**: 予測される料金と最低コミットメント - **プラン管理**: アップグレードオプションと請求履歴 ### プログラムによる使用状況の追跡 -APIを使用して、現在の使用状況と制限をプログラムで照会できます: +APIを使用して、現在の使用状況と制限をプログラムでクエリできます: -**エンドポイント:** +**エンドポイント:** ```text GET /api/users/me/usage-limits ``` -**認証:** -- APIキーを `X-API-Key` ヘッダーに含めてください +**認証:** +- `X-API-Key`ヘッダーにAPIキーを含めます -**リクエスト例:** +**リクエスト例:** ```bash curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" https://sim.ai/api/users/me/usage-limits ``` -**レスポンス例:** +**レスポンス例:** ```json { @@ -171,70 +175,70 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt } ``` -**レート制限フィールド:** -- `requestsPerMinute`:持続的なレート制限(トークンはこの速度で補充されます) -- `maxBurst`:蓄積できる最大トークン数(バースト容量) -- `remaining`:現在利用可能なトークン(最大で`maxBurst`まで) +**レート制限フィールド:** +- `requestsPerMinute`: 持続的なレート制限(トークンはこのレートで補充されます) +- `maxBurst`: 蓄積できる最大トークン数(バースト容量) +- `remaining`: 現在利用可能なトークン数(最大`maxBurst`まで) -**レスポンスフィールド:** +**レスポンスフィールド:** - `currentPeriodCost`は現在の請求期間の使用状況を反映します -- `limit`は個別の制限(無料/プロ)または組織のプール制限(チーム/エンタープライズ)から派生します -- `plan`はユーザーに関連付けられた最優先のアクティブなプランです +- `limit`は個別の制限(Free/Pro)またはプールされた組織の制限(Team/Enterprise)から導出されます +- `plan`はユーザーに関連付けられた最も優先度の高いアクティブなプランです -## プラン制限 +## プランの制限 -サブスクリプションプランによって使用制限が異なります: +サブスクリプションプランによって、使用量の制限が異なります。 -| プラン | 月間使用制限 | レート制限(毎分) | +| プラン | 月間使用量制限 | レート制限(1分あたり) | |------|-------------------|-------------------------| -| **Free** | $20 | 同期5、非同期10 | -| **Pro** | $100 | 同期10、非同期50 | -| **Team** | $500(プール) | 同期50、非同期100 | -| **Enterprise** | カスタム | カスタム | +| **無料** | $20 | 同期5、非同期10 | +| **プロ** | $100 | 同期10、非同期50 | +| **チーム** | $500(プール) | 同期50、非同期100 | +| **エンタープライズ** | カスタム | カスタム | ## 課金モデル -Simは**基本サブスクリプション+超過分**の課金モデルを使用しています: +Simは**基本サブスクリプション + 超過料金**の課金モデルを採用しています。 ### 仕組み **プロプラン(月額$20):** - 月額サブスクリプションには$20分の使用量が含まれます - 使用量が$20未満 → 追加料金なし -- 使用量が$20を超える → 月末に超過分を支払い -- 例:$35の使用量 = $20(サブスクリプション)+ $15(超過分) +- 使用量が$20超過 → 月末に超過分を支払い +- 例:使用量$35 = $20(サブスクリプション)+ $15(超過料金) -**チームプラン(席あたり月額$40):** -- チームメンバー全体でプールされた使用量 -- チーム全体の使用量から超過分を計算 -- 組織のオーナーが一括で請求を受ける +**チームプラン(1席あたり月額$40):** +- チームメンバー全員で使用量をプール +- チーム全体の使用量から超過料金を計算 +- 組織のオーナーが1つの請求書を受け取ります **エンタープライズプラン:** - 固定月額料金、超過料金なし -- 契約に基づくカスタム使用制限 +- 契約に基づくカスタム使用量制限 ### しきい値課金 -未請求の超過分が$50に達すると、Simは自動的に未請求の全額を請求します。 +未請求の超過料金が$50に達すると、Simは未請求金額の全額を自動的に請求します。 **例:** -- 10日目:$70の超過分 → 即時に$70を請求 -- 15日目:追加$35の使用(合計$105) → すでに請求済み、アクションなし -- 20日目:さらに$50の使用(合計$155、未請求$85) → 即時に$85を請求 +- 10日目:超過料金$70 → 即座に$70を請求 +- 15日目:追加使用量$35(合計$105) → すでに請求済み、アクションなし +- 20日目:さらに$50の使用量(合計$155、未請求$85) → 即座に$85を請求 -これにより、期間終了時に一度に大きな請求が発生するのではなく、月全体に大きな超過料金が分散されます。 +これにより、期間終了時の1回の大きな請求ではなく、大きな超過料金を月全体に分散させることができます。 ## コスト管理のベストプラクティス -1. **定期的な監視**: 予期せぬ事態を避けるため、使用状況ダッシュボードを頻繁に確認する -2. **予算の設定**: プランの制限を支出のガードレールとして使用する -3. **ワークフローの最適化**: コストの高い実行を見直し、プロンプトやモデル選択を最適化する -4. **適切なモデルの使用**: タスクの要件にモデルの複雑さを合わせる -5. **類似タスクのバッチ処理**: 可能な場合は複数のリクエストを組み合わせてオーバーヘッドを削減する +1. **定期的な監視**:予期しない事態を避けるため、使用状況ダッシュボードを頻繁に確認してください +2. **予算の設定**:プランの制限を支出のガードレールとして使用してください +3. **ワークフローの最適化**:コストの高い実行を確認し、プロンプトやモデルの選択を最適化してください +4. **適切なモデルの使用**:タスクの要件に合わせてモデルの複雑さを選択してください +5. **類似タスクのバッチ処理**:可能な限り複数のリクエストを組み合わせて、オーバーヘッドを削減してください ## 次のステップ - [設定 → サブスクリプション](https://sim.ai/settings/subscription)で現在の使用状況を確認する -- 実行詳細を追跡するための[ロギング](/execution/logging)について学ぶ +- 実行の詳細を追跡するための[ログ記録](/execution/logging)について学ぶ - プログラムによるコスト監視のための[外部API](/execution/api)を探索する -- コスト削減のための[ワークフロー最適化テクニック](/blocks)をチェックする \ No newline at end of file +- コストを削減するための[ワークフロー最適化テクニック](/blocks)を確認する \ No newline at end of file diff --git a/apps/docs/content/docs/ja/tools/firecrawl.mdx b/apps/docs/content/docs/ja/tools/firecrawl.mdx index f4cbbd6e65..67eff8f75b 100644 --- a/apps/docs/content/docs/ja/tools/firecrawl.mdx +++ b/apps/docs/content/docs/ja/tools/firecrawl.mdx @@ -146,7 +146,33 @@ Firecrawlを使用してウェブ上の情報を検索します | `success` | boolean | 抽出操作が成功したかどうか | | `data` | object | スキーマまたはプロンプトに従って抽出された構造化データ | -## 注意事項 +### `firecrawl_agent` -- カテゴリー: `tools` -- タイプ: `firecrawl` +自律型ウェブデータ抽出エージェント。特定のURLを必要とせず、自然言語プロンプトに基づいて情報を検索・収集します。 + +#### 入力 + +| パラメータ | 型 | 必須 | 説明 | +| --------- | ---- | -------- | ----------- | +| `prompt` | string | はい | 抽出するデータの自然言語による説明(最大10,000文字) | +| `urls` | json | いいえ | エージェントが焦点を当てるURLの配列(オプション) | +| `schema` | json | いいえ | 抽出するデータの構造を定義するJSONスキーマ | +| `maxCredits` | number | いいえ | このエージェントタスクに使用する最大クレジット数 | +| `strictConstrainToURLs` | boolean | いいえ | trueの場合、エージェントはurls配列で提供されたURLのみを訪問します | +| `apiKey` | string | はい | Firecrawl APIキー | + +#### 出力 + +| パラメータ | 型 | 説明 | +| --------- | ---- | ----------- | +| `success` | boolean | エージェント操作が成功したかどうか | +| `status` | string | エージェントジョブの現在のステータス(processing、completed、failed) | +| `data` | object | エージェントから抽出されたデータ | +| `creditsUsed` | number | このエージェントタスクで消費されたクレジット数 | +| `expiresAt` | string | 結果の有効期限のタイムスタンプ(24時間) | +| `sources` | object | エージェントが使用したソースURLの配列 | + +## 注記 + +- カテゴリ:`tools` +- タイプ:`firecrawl` diff --git a/apps/docs/content/docs/ja/tools/intercom.mdx b/apps/docs/content/docs/ja/tools/intercom.mdx index e66c82be21..7a68e0e0ad 100644 --- a/apps/docs/content/docs/ja/tools/intercom.mdx +++ b/apps/docs/content/docs/ja/tools/intercom.mdx @@ -55,8 +55,7 @@ Intercomをワークフローに統合します。連絡先の作成、取得、 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作の成功ステータス | -| `output` | object | 作成された連絡先データ | +| `contact` | object | 作成された連絡先オブジェクト | ### `intercom_get_contact` @@ -72,8 +71,7 @@ IDからIntercomの単一の連絡先を取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 連絡先データ | +| `contact` | object | 連絡先オブジェクト | ### `intercom_update_contact` @@ -101,8 +99,7 @@ Intercomの既存の連絡先を更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 更新された連絡先データ | +| `contact` | object | 更新された連絡先オブジェクト | ### `intercom_list_contacts` @@ -119,8 +116,7 @@ Intercomの既存の連絡先を更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 連絡先リスト | +| `contacts` | array | 連絡先オブジェクトの配列 | ### `intercom_search_contacts` @@ -140,8 +136,7 @@ Intercomの既存の連絡先を更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 検索結果 | +| `contacts` | array | 一致する連絡先オブジェクトの配列 | ### `intercom_delete_contact` @@ -157,8 +152,9 @@ IDでIntercomから連絡先を削除する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 削除結果 | +| `id` | string | 削除された連絡先のID | +| `deleted` | boolean | 連絡先が削除されたかどうか | +| `metadata` | object | 操作メタデータ | ### `intercom_create_company` @@ -182,8 +178,7 @@ Intercomで企業を作成または更新する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作の成功ステータス | -| `output` | object | 作成または更新された企業データ | +| `company` | object | 作成または更新された企業オブジェクト | ### `intercom_get_company` @@ -199,8 +194,7 @@ IDによってIntercomから単一の企業を取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作の成功ステータス | -| `output` | object | 企業データ | +| `company` | object | 企業オブジェクト | ### `intercom_list_companies` @@ -218,8 +212,7 @@ IDによってIntercomから単一の企業を取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 企業のリスト | +| `companies` | array | 企業オブジェクトの配列 | ### `intercom_get_conversation` @@ -237,8 +230,7 @@ IDによりIntercomから単一の会話を取得 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 会話データ | +| `conversation` | object | 会話オブジェクト | ### `intercom_list_conversations` @@ -257,8 +249,7 @@ IDによりIntercomから単一の会話を取得 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 会話のリスト | +| `conversations` | array | 会話オブジェクトの配列 | ### `intercom_reply_conversation` @@ -279,8 +270,7 @@ IDによりIntercomから単一の会話を取得 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 返信を含む更新された会話 | +| `conversation` | object | 更新された会話オブジェクト | ### `intercom_search_conversations` @@ -300,8 +290,7 @@ IDによりIntercomから単一の会話を取得 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 検索結果 | +| `conversations` | array | 一致する会話オブジェクトの配列 | ### `intercom_create_ticket` @@ -323,8 +312,7 @@ Intercomで新しいチケットを作成する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 作成されたチケットデータ | +| `ticket` | object | 作成されたチケットオブジェクト | ### `intercom_get_ticket` @@ -340,8 +328,7 @@ IDによりIntercomから単一のチケットを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | チケットデータ | +| `ticket` | object | チケットオブジェクト | ### `intercom_create_message` @@ -365,8 +352,7 @@ Intercomで管理者が開始した新しいメッセージを作成して送信 | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功ステータス | -| `output` | object | 作成されたメッセージデータ | +| `message` | object | 作成されたメッセージオブジェクト | ## メモ diff --git a/apps/docs/content/docs/ja/tools/pinecone.mdx b/apps/docs/content/docs/ja/tools/pinecone.mdx index 252307001e..be1964002c 100644 --- a/apps/docs/content/docs/ja/tools/pinecone.mdx +++ b/apps/docs/content/docs/ja/tools/pinecone.mdx @@ -71,7 +71,6 @@ Pineconeインデックスにテキストレコードを挿入または更新す | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | | `statusText` | string | アップサート操作のステータス | -| `upsertedCount` | number | 正常にアップサートされたレコードの数 | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/ja/tools/supabase.mdx b/apps/docs/content/docs/ja/tools/supabase.mdx index f0bb673225..4cf1f5c8b7 100644 --- a/apps/docs/content/docs/ja/tools/supabase.mdx +++ b/apps/docs/content/docs/ja/tools/supabase.mdx @@ -266,10 +266,11 @@ Supabaseストレージバケットにファイルをアップロードする | --------- | ---- | -------- | ----------- | | `projectId` | string | はい | あなたのSupabaseプロジェクトID(例:jdrkgepadsdopsntdlom) | | `bucket` | string | はい | ストレージバケットの名前 | -| `path` | string | はい | ファイルが保存されるパス(例:"folder/file.jpg") | +| `fileName` | string | はい | ファイルの名前(例:"document.pdf"、"image.jpg") | +| `path` | string | いいえ | オプションのフォルダパス(例:"folder/subfolder/") | | `fileContent` | string | はい | ファイルの内容(バイナリファイルの場合はbase64エンコード、またはプレーンテキスト) | | `contentType` | string | いいえ | ファイルのMIMEタイプ(例:"image/jpeg"、"text/plain") | -| `upsert` | boolean | いいえ | trueの場合、既存のファイルを上書きする(デフォルト:false) | +| `upsert` | boolean | いいえ | trueの場合、既存のファイルを上書き(デフォルト:false) | | `apiKey` | string | はい | あなたのSupabaseサービスロールシークレットキー | #### 出力 diff --git a/apps/docs/content/docs/ja/tools/typeform.mdx b/apps/docs/content/docs/ja/tools/typeform.mdx index 47087eb685..795a911a4e 100644 --- a/apps/docs/content/docs/ja/tools/typeform.mdx +++ b/apps/docs/content/docs/ja/tools/typeform.mdx @@ -129,15 +129,18 @@ Typeformアカウント内のすべてのフォームのリストを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `id` | string | フォームの一意の識別子 | +| `id` | string | フォームの一意識別子 | | `title` | string | フォームのタイトル | | `type` | string | フォームのタイプ(form、quizなど) | | `settings` | object | 言語、プログレスバーなどを含むフォーム設定 | | `theme` | object | テーマ参照 | | `workspace` | object | ワークスペース参照 | | `fields` | array | フォームフィールド/質問の配列 | -| `welcome_screens` | array | ウェルカム画面の配列 | +| `welcome_screens` | array | ウェルカム画面の配列(設定されていない場合は空) | | `thankyou_screens` | array | サンキュー画面の配列 | +| `created_at` | string | フォーム作成タイムスタンプ(ISO 8601形式) | +| `last_updated_at` | string | フォーム最終更新タイムスタンプ(ISO 8601形式) | +| `published_at` | string | フォーム公開タイムスタンプ(ISO 8601形式) | | `_links` | object | 公開フォームURLを含む関連リソースリンク | ### `typeform_create_form` @@ -160,10 +163,15 @@ Typeformアカウント内のすべてのフォームのリストを取得する | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `id` | string | 作成されたフォームの一意の識別子 | +| `id` | string | 作成されたフォームの一意識別子 | | `title` | string | フォームのタイトル | | `type` | string | フォームのタイプ | -| `fields` | array | 作成されたフォームフィールドの配列 | +| `settings` | object | フォーム設定オブジェクト | +| `theme` | object | テーマ参照 | +| `workspace` | object | ワークスペース参照 | +| `fields` | array | 作成されたフォームフィールドの配列(追加されていない場合は空) | +| `welcome_screens` | array | ウェルカム画面の配列(設定されていない場合は空) | +| `thankyou_screens` | array | サンキュー画面の配列 | | `_links` | object | 公開フォームURLを含む関連リソースリンク | ### `typeform_update_form` @@ -182,16 +190,7 @@ JSON Patchオペレーションを使用して既存のフォームを更新す | パラメータ | 型 | 説明 | | --------- | ---- | ----------- | -| `id` | string | 更新されたフォームの一意の識別子 | -| `title` | string | フォームのタイトル | -| `type` | string | フォームのタイプ | -| `settings` | object | フォーム設定 | -| `theme` | object | テーマ参照 | -| `workspace` | object | ワークスペース参照 | -| `fields` | array | フォームフィールドの配列 | -| `welcome_screens` | array | ウェルカム画面の配列 | -| `thankyou_screens` | array | サンクスページの配列 | -| `_links` | object | 関連リソースリンク | +| `message` | string | 成功確認メッセージ | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/ja/triggers/schedule.mdx b/apps/docs/content/docs/ja/triggers/schedule.mdx index f88d45d937..c7ffaf42c7 100644 --- a/apps/docs/content/docs/ja/triggers/schedule.mdx +++ b/apps/docs/content/docs/ja/triggers/schedule.mdx @@ -56,7 +56,7 @@ import { Image } from '@/components/ui/image' ## 自動無効化 -スケジュールは**10回連続で失敗**すると、エラーの連鎖を防ぐため自動的に無効化されます。無効化されると: +スケジュールは**100回連続で失敗**すると、エラーの連鎖を防ぐために自動的に無効化されます。無効化されると: - スケジュールブロックに警告バッジが表示されます - スケジュールの実行が停止します diff --git a/apps/docs/content/docs/zh/execution/costs.mdx b/apps/docs/content/docs/zh/execution/costs.mdx index 8787b5b0e6..27348044ec 100644 --- a/apps/docs/content/docs/zh/execution/costs.mdx +++ b/apps/docs/content/docs/zh/execution/costs.mdx @@ -105,43 +105,47 @@ totalCost = baseExecutionCharge + modelCost 显示的价格为截至 2025 年 9 月 10 日的费率。请查看提供商文档以获取最新价格。 +## 自带密钥(BYOK) + +你可以在 **设置 → BYOK** 中为托管模型(OpenAI、Anthropic、Google、Mistral)使用你自己的 API 密钥,以按基础价格计费。密钥会被加密,并在整个工作区范围内生效。 + ## 成本优化策略 -- **模型选择**:根据任务复杂性选择模型。简单任务可以使用 GPT-4.1-nano,而复杂推理可能需要 o1 或 Claude Opus。 -- **提示工程**:结构良好、简洁的提示可以减少令牌使用,同时保持质量。 -- **本地模型**:对于非关键任务,使用 Ollama 或 VLLM 完全消除 API 成本。 -- **缓存和重用**:将经常使用的结果存储在变量或文件中,以避免重复调用 AI 模型。 -- **批量处理**:在单次 AI 请求中处理多个项目,而不是逐一调用。 +- **模型选择**:根据任务复杂度选择合适的模型。简单任务可用 GPT-4.1-nano,复杂推理可选 o1 或 Claude Opus。 +- **提示工程**:结构清晰、简洁的提示能减少 token 使用量,同时保证质量。 +- **本地模型**:对于非关键任务,使用 Ollama 或 VLLM,可完全消除 API 成本。 +- **缓存与复用**:将常用结果存储在变量或文件中,避免重复调用 AI 模型。 +- **批量处理**:一次 AI 请求处理多个项目,减少单独调用次数。 ## 使用监控 -在 设置 → 订阅 中监控您的使用情况和账单: +你可以在 设置 → 订阅 中监控你的用量和账单: -- **当前使用情况**:当前周期的实时使用和成本 -- **使用限制**:计划限制及其可视化进度指示器 -- **账单详情**:预计费用和最低承诺 -- **计划管理**:升级选项和账单历史记录 +- **当前用量**:当前周期的实时用量和费用 +- **用量上限**:带有可视化进度指示的套餐限制 +- **账单明细**:预计费用和最低承诺金额 +- **套餐管理**:升级选项和账单历史 -### 程序化使用跟踪 +### 编程方式用量追踪 -您可以通过 API 程序化地查询当前的使用情况和限制: +你可以通过 API 以编程方式查询当前用量和限制: -**端点:** +**接口地址:** ```text GET /api/users/me/usage-limits ``` -**认证:** -- 在 `X-API-Key` 标头中包含您的 API 密钥 +**认证方式:** +- 在 `X-API-Key` header 中包含你的 API 密钥 -**示例请求:** +**请求示例:** ```bash curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" https://sim.ai/api/users/me/usage-limits ``` -**示例响应:** +**响应示例:** ```json { @@ -171,70 +175,70 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt } ``` -**速率限制字段:** -- `requestsPerMinute`:持续速率限制(令牌以此速率补充) -- `maxBurst`:您可以累积的最大令牌数(突发容量) -- `remaining`:当前可用令牌数(最多可达 `maxBurst`) +**限流字段:** +- `requestsPerMinute`:持续速率限制(token 按此速率补充) +- `maxBurst`:你可累计的最大 token 数(突发容量) +- `remaining`:当前可用 token 数(最多可达 `maxBurst`) **响应字段:** -- `currentPeriodCost` 反映当前计费周期的使用情况 -- `limit` 来源于个人限制(免费/专业)或组织池限制(团队/企业) -- `plan` 是与您的用户关联的最高优先级的活动计划 +- `currentPeriodCost` 反映当前账单周期的用量 +- `limit` 来源于个人限额(Free/Pro)或组织池化限额(Team/Enterprise) +- `plan` 是与你的用户关联的最高优先级的激活套餐 -## 计划限制 +## 套餐限制 -不同的订阅计划有不同的使用限制: +不同的订阅套餐有不同的使用限制: -| 方案 | 每月使用限额 | 速率限制(每分钟) | +| 套餐 | 每月使用额度 | 速率限制(每分钟) | |------|-------------------|-------------------------| | **Free** | $20 | 5 sync,10 async | | **Pro** | $100 | 10 sync,50 async | | **Team** | $500(共享) | 50 sync,100 async | -| **Enterprise** | 定制 | 定制 | +| **Enterprise** | 自定义 | 自定义 | ## 计费模式 -Sim 使用 **基础订阅 + 超额** 的计费模式: +Sim 采用**基础订阅 + 超额**计费模式: -### 工作原理 +### 计费方式说明 -**专业计划($20/月):** -- 每月订阅包含 $20 的使用额度 -- 使用低于 $20 → 无额外费用 -- 使用超过 $20 → 月底支付超额部分 +**Pro 套餐($20/月):** +- 月度订阅包含 $20 使用额度 +- 使用未超过 $20 → 无额外费用 +- 使用超过 $20 → 月底结算超额部分 - 示例:$35 使用 = $20(订阅)+ $15(超额) -**团队计划($40/每席位/月):** -- 团队成员之间共享使用额度 -- 超额费用根据团队总使用量计算 -- 组织所有者收到一张账单 +**Team 套餐($40/人/月):** +- 团队成员共享使用额度 +- 超额费用按团队总用量计算 +- 账单由组织所有者统一支付 -**企业计划:** +**Enterprise 套餐:** - 固定月费,无超额费用 -- 根据协议自定义使用限制 +- 使用额度可按协议定制 ### 阈值计费 -当未计费的超额费用达到 $50 时,Sim 会自动计费全额未计费金额。 +当未结算的超额费用达到 $50 时,Sim 会自动结算全部未结算金额。 **示例:** -- 第 10 天:$70 超额 → 立即计费 $70 -- 第 15 天:额外使用 $35(总计 $105)→ 已计费,无需操作 -- 第 20 天:再使用 $50(总计 $155,未计费 $85)→ 立即计费 $85 +- 第 10 天:超额 $70 → 立即结算 $70 +- 第 15 天:新增 $35 使用(累计 $105)→ 已结算,无需操作 +- 第 20 天:再用 $50(累计 $155,未结算 $85)→ 立即结算 $85 -这会将大量的超额费用分散到整个月,而不是在周期结束时收到一张大账单。 +这样可以将大额超额费用分摊到每月多次结算,避免期末一次性大额账单。 ## 成本管理最佳实践 -1. **定期监控**:经常检查您的使用仪表板,避免意外情况 -2. **设定预算**:使用计划限制作为支出控制的护栏 -3. **优化工作流程**:审查高成本的执行操作,优化提示或模型选择 -4. **使用合适的模型**:根据任务需求匹配模型复杂度 -5. **批量处理相似任务**:尽可能合并多个请求以减少开销 +1. **定期监控**:经常查看用量仪表盘,避免意外支出 +2. **设置预算**:用套餐额度作为支出警戒线 +3. **优化流程**:检查高成本执行,优化提示词或模型选择 +4. **选择合适模型**:根据任务需求匹配模型复杂度 +5. **批量处理相似任务**:尽量合并请求,减少额外开销 ## 下一步 -- 在[设置 → 订阅](https://sim.ai/settings/subscription)中查看您当前的使用情况 -- 了解[日志记录](/execution/logging)以跟踪执行详情 -- 探索[外部 API](/execution/api)以进行程序化成本监控 -- 查看[工作流优化技术](/blocks)以降低成本 \ No newline at end of file +- 在 [设置 → 订阅](https://sim.ai/settings/subscription) 中查看您当前的使用情况 +- 了解 [日志记录](/execution/logging),以跟踪执行详情 +- 探索 [外部 API](/execution/api),实现程序化成本监控 +- 查看 [工作流优化技巧](/blocks),以降低成本 \ No newline at end of file diff --git a/apps/docs/content/docs/zh/tools/firecrawl.mdx b/apps/docs/content/docs/zh/tools/firecrawl.mdx index 7d6c1f5b0e..8e6eb3dd51 100644 --- a/apps/docs/content/docs/zh/tools/firecrawl.mdx +++ b/apps/docs/content/docs/zh/tools/firecrawl.mdx @@ -146,7 +146,33 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | `success` | boolean | 提取操作是否成功 | | `data` | object | 根据模式或提示提取的结构化数据 | -## 注意 +### `firecrawl_agent` -- 类别:`tools` +自主网页数据提取代理。根据自然语言提示进行搜索和信息收集,无需指定具体 URL。 + +#### 输入 + +| 参数 | 类型 | 必需 | 描述 | +| --------- | ---- | -------- | ----------- | +| `prompt` | string | 是 | 要提取数据的自然语言描述(最多 10,000 个字符) | +| `urls` | json | 否 | 可选的 URL 数组,用于聚焦代理任务 | +| `schema` | json | 否 | 定义要提取数据结构的 JSON 架构 | +| `maxCredits` | number | 否 | 此代理任务可消耗的最大积分数 | +| `strictConstrainToURLs` | boolean | 否 | 若为 true,代理仅访问 urls 数组中提供的 URL | +| `apiKey` | string | 是 | Firecrawl API 密钥 | + +#### 输出 + +| 参数 | 类型 | 描述 | +| --------- | ---- | ----------- | +| `success` | boolean | 代理操作是否成功 | +| `status` | string | 代理任务的当前状态(processing、completed、failed) | +| `data` | object | 代理提取的数据 | +| `creditsUsed` | number | 此代理任务消耗的积分数 | +| `expiresAt` | string | 结果过期的时间戳(24 小时) | +| `sources` | object | 代理使用的来源 URL 数组 | + +## 说明 + +- 分类:`tools` - 类型:`firecrawl` diff --git a/apps/docs/content/docs/zh/tools/intercom.mdx b/apps/docs/content/docs/zh/tools/intercom.mdx index 4b4b2b8743..70b478ab85 100644 --- a/apps/docs/content/docs/zh/tools/intercom.mdx +++ b/apps/docs/content/docs/zh/tools/intercom.mdx @@ -55,8 +55,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 创建的联系人数据 | +| `contact` | object | 创建的联系人对象 | ### `intercom_get_contact` @@ -72,8 +71,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 联系人数据 | +| `contact` | object | 联系人对象 | ### `intercom_update_contact` @@ -101,8 +99,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 更新后的联系人数据 | +| `contact` | object | 更新后的联系人对象 | ### `intercom_list_contacts` @@ -119,8 +116,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 联系人列表 | +| `contacts` | array | 联系人对象数组 | ### `intercom_search_contacts` @@ -140,8 +136,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 搜索结果 | +| `contacts` | array | 匹配的联系人对象数组 | ### `intercom_delete_contact` @@ -157,8 +152,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 删除结果 | +| `id` | string | 已删除联系人的 ID | +| `deleted` | boolean | 联系人是否已被删除 | +| `metadata` | object | 操作元数据 | ### `intercom_create_company` @@ -182,8 +178,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 创建或更新的公司数据 | +| `company` | object | 新建或更新的公司对象 | ### `intercom_get_company` @@ -199,8 +194,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 公司数据 | +| `company` | object | 公司对象 | ### `intercom_list_companies` @@ -218,8 +212,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 公司列表 | +| `companies` | array | 公司对象数组 | ### `intercom_get_conversation` @@ -237,8 +230,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 会话数据 | +| `conversation` | object | 会话对象 | ### `intercom_list_conversations` @@ -257,8 +249,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | 布尔值 | 操作成功状态 | -| `output` | 对象 | 会话列表 | +| `conversations` | array | 会话对象数组 | ### `intercom_reply_conversation` @@ -279,8 +270,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 包含回复的更新对话 | +| `conversation` | object | 更新后的会话对象 | ### `intercom_search_conversations` @@ -300,8 +290,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 搜索结果 | +| `conversations` | array | 匹配的会话对象数组 | ### `intercom_create_ticket` @@ -323,8 +312,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 创建的工单数据 | +| `ticket` | object | 创建的工单对象 | ### `intercom_get_ticket` @@ -340,8 +328,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 工单数据 | +| `ticket` | object | 工单对象 | ### `intercom_create_message` @@ -365,8 +352,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `success` | boolean | 操作成功状态 | -| `output` | object | 创建的消息数据 | +| `message` | object | 创建的消息对象 | ## 注意事项 diff --git a/apps/docs/content/docs/zh/tools/pinecone.mdx b/apps/docs/content/docs/zh/tools/pinecone.mdx index 4f8cf87010..a326985b3a 100644 --- a/apps/docs/content/docs/zh/tools/pinecone.mdx +++ b/apps/docs/content/docs/zh/tools/pinecone.mdx @@ -68,10 +68,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" #### 输出 -| 参数 | 类型 | 描述 | +| 参数 | 类型 | 说明 | | --------- | ---- | ----------- | -| `statusText` | string | 插入操作的状态 | -| `upsertedCount` | number | 成功插入的记录数量 | +| `statusText` | string | upsert 操作的状态 | ### `pinecone_search_text` diff --git a/apps/docs/content/docs/zh/tools/supabase.mdx b/apps/docs/content/docs/zh/tools/supabase.mdx index c4cdcb9a11..71a132eeaf 100644 --- a/apps/docs/content/docs/zh/tools/supabase.mdx +++ b/apps/docs/content/docs/zh/tools/supabase.mdx @@ -266,10 +266,11 @@ Sim 的 Supabase 集成使您能够轻松地将代理工作流连接到您的 Su | --------- | ---- | -------- | ----------- | | `projectId` | string | 是 | 您的 Supabase 项目 ID \(例如:jdrkgepadsdopsntdlom\) | | `bucket` | string | 是 | 存储桶的名称 | -| `path` | string | 是 | 文件将存储的路径 \(例如:"folder/file.jpg"\) | -| `fileContent` | string | 是 | 文件内容 \(二进制文件为 base64 编码,或纯文本\) | -| `contentType` | string | 否 | 文件的 MIME 类型 \(例如:"image/jpeg", "text/plain"\) | -| `upsert` | boolean | 否 | 如果为 true,则覆盖现有文件 \(默认值:false\) | +| `fileName` | string | 是 | 文件名 \(例如:"document.pdf","image.jpg"\) | +| `path` | string | 否 | 可选的文件夹路径 \(例如:"folder/subfolder/"\) | +| `fileContent` | string | 是 | 文件内容(对于二进制文件为 base64 编码,或为纯文本)| +| `contentType` | string | 否 | 文件的 MIME 类型 \(例如:"image/jpeg","text/plain"\) | +| `upsert` | boolean | 否 | 如果为 true,则覆盖已存在的文件(默认值:false)| | `apiKey` | string | 是 | 您的 Supabase 服务角色密钥 | #### 输出 diff --git a/apps/docs/content/docs/zh/tools/typeform.mdx b/apps/docs/content/docs/zh/tools/typeform.mdx index 1e79963c28..3c274b9ef4 100644 --- a/apps/docs/content/docs/zh/tools/typeform.mdx +++ b/apps/docs/content/docs/zh/tools/typeform.mdx @@ -131,14 +131,18 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | --------- | ---- | ----------- | | `id` | string | 表单唯一标识符 | | `title` | string | 表单标题 | -| `type` | string | 表单类型 \(form, quiz 等\) | +| `type` | string | 表单类型 \(form、quiz 等\) | | `settings` | object | 表单设置,包括语言、进度条等 | | `theme` | object | 主题引用 | | `workspace` | object | 工作区引用 | | `fields` | array | 表单字段/问题数组 | -| `welcome_screens` | array | 欢迎页面数组 | -| `thankyou_screens` | array | 感谢页面数组 | -| `_links` | object | 包括公共表单 URL 在内的相关资源链接 | +| `welcome_screens` | array | 欢迎页数组(如未配置则为空) | +| `thankyou_screens` | array | 感谢页数组 | +| `created_at` | string | 表单创建时间戳(ISO 8601 格式) | +| `last_updated_at` | string | 表单最后更新时间戳(ISO 8601 格式) | +| `published_at` | string | 表单发布时间戳(ISO 8601 格式) | +| `_links` | object | 相关资源链接,包括公开表单 URL | + ### `typeform_create_form` @@ -160,11 +164,17 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `id` | string | 创建的表单唯一标识符 | +| `id` | string | 已创建表单的唯一标识符 | | `title` | string | 表单标题 | | `type` | string | 表单类型 | -| `fields` | array | 创建的表单字段数组 | -| `_links` | object | 包括公共表单 URL 在内的相关资源链接 | +| `settings` | object | 表单设置对象 | +| `theme` | object | 主题引用 | +| `workspace` | object | 工作区引用 | +| `fields` | array | 已创建表单字段数组(如未添加则为空) | +| `welcome_screens` | array | 欢迎页数组(如未配置则为空) | +| `thankyou_screens` | array | 感谢页数组 | +| `_links` | object | 相关资源链接,包括公开表单 URL | + ### `typeform_update_form` @@ -182,16 +192,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card" | 参数 | 类型 | 描述 | | --------- | ---- | ----------- | -| `id` | string | 更新的表单唯一标识符 | -| `title` | string | 表单标题 | -| `type` | string | 表单类型 | -| `settings` | object | 表单设置 | -| `theme` | object | 主题引用 | -| `workspace` | object | 工作区引用 | -| `fields` | array | 表单字段数组 | -| `welcome_screens` | array | 欢迎屏幕数组 | -| `thankyou_screens` | array | 感谢屏幕数组 | -| `_links` | object | 相关资源链接 | +| `message` | string | 成功确认消息 | ### `typeform_delete_form` diff --git a/apps/docs/content/docs/zh/triggers/schedule.mdx b/apps/docs/content/docs/zh/triggers/schedule.mdx index 84d7d2f39e..fca74cb4e7 100644 --- a/apps/docs/content/docs/zh/triggers/schedule.mdx +++ b/apps/docs/content/docs/zh/triggers/schedule.mdx @@ -56,7 +56,7 @@ import { Image } from '@/components/ui/image' ## 自动禁用 -计划在连续 **10 次失败** 后会自动禁用,以防止错误持续发生。禁用后: +为防止持续性错误,计划任务在**连续失败 100 次**后会自动禁用。禁用后: - 计划块上会显示警告徽章 - 计划将停止执行 diff --git a/apps/docs/i18n.lock b/apps/docs/i18n.lock index 06265e9b8b..0da0a77913 100644 --- a/apps/docs/i18n.lock +++ b/apps/docs/i18n.lock @@ -228,7 +228,7 @@ checksums: content/8: ab4fe131de634064f9a7744a11599434 content/9: 2f6c9564a33ad9f752df55840b0c8e16 content/10: fef34568e5bbd5a50e2a89412f85302c - content/11: b7ae0ecf6fbaa92b049c718720e4007e + content/11: a891bfb5cf490148001f05acde467f68 content/12: bcd95e6bef30b6f480fee33800928b13 content/13: 2ff1c8bf00c740f66bce8a4a7f768ca8 content/14: 16eb64906b9e981ea3c11525ff5a1c2e @@ -503,19 +503,19 @@ checksums: content/35: 371d0e46b4bd2c23f559b8bc112f6955 content/36: 4c6a3b159dfff0106b67269130253eba content/37: bcadfc362b69078beee0088e5936c98b - content/38: 21cc925781120afc2c4568f74ed8191a + content/38: e30b26e62abc96c1ff0694762584501d content/39: 5de052cae5ada1f845f7257ba431ebd1 content/40: 1a36fc873771b68a67d95a2130487aec content/41: 371d0e46b4bd2c23f559b8bc112f6955 content/42: b000bca7bd6658d4b5d21e6c7787d05e content/43: bcadfc362b69078beee0088e5936c98b - content/44: 448922b8585b0b4599e7023c80faa449 + content/44: 186da1feb6a6565956c7ea7707b388ad content/45: 776f62636d112cbd27d5064a40e29ec9 content/46: f512a5096a1d5a4e4a0afd762152b714 content/47: 371d0e46b4bd2c23f559b8bc112f6955 content/48: 06de592289fb5f4dff42f451ebf9658a content/49: bcadfc362b69078beee0088e5936c98b - content/50: d242a9680311743714a60bf1941ef9ac + content/50: b36b602337a0a9be8720b50ed3f949d5 content/51: a4cfd36d36633eee441423283d4d5fb3 content/52: 85ea23183709f33902aec778c7cb62b0 content/53: 371d0e46b4bd2c23f559b8bc112f6955 @@ -760,7 +760,7 @@ checksums: content/71: 64c89ec9ca2719c58cfed42033a52217 content/72: ec97af83ea30e033d7b1b4ada910c03e content/73: 371d0e46b4bd2c23f559b8bc112f6955 - content/74: b6f54fba68782b589ee4dfa0aebf7adb + content/74: a3dc735b07499600ffd588b1279eea42 content/75: bcadfc362b69078beee0088e5936c98b content/76: 64d66a993e96e5544d28bc75a2d0c6d6 content/77: 0295e0cd05bbf86d6d79400d787759f5 @@ -1279,7 +1279,7 @@ checksums: content/17: 371d0e46b4bd2c23f559b8bc112f6955 content/18: 11e0f62da7bc51d4c9a94d2c60dd06ce content/19: bcadfc362b69078beee0088e5936c98b - content/20: d1fa8dd2b26e182a3a02bc996ad7dd0b + content/20: d78f8e8d74ba810e10dfbebd4423764f content/21: b72dd04e96d85431c18c28de8a6b00d7 content/22: 147ca5082380639c3168a44122a67192 content/23: 371d0e46b4bd2c23f559b8bc112f6955 @@ -3483,8 +3483,14 @@ checksums: content/38: 3e7b1f581c8ef51fb3d9b6ecff47deb4 content/39: bcadfc362b69078beee0088e5936c98b content/40: 07994574571bcaeb3b86ce92c46d0527 - content/41: b3f310d5ef115bea5a8b75bf25d7ea9a - content/42: dc809f5be4a108f769310dd8290c0db4 + content/41: 5aba0f448543bbd7559573fed02724b2 + content/42: f0cdbc370d80551a27c44588ae689f9d + content/43: 371d0e46b4bd2c23f559b8bc112f6955 + content/44: 81b12b0196aa94b6f80686641125ea3a + content/45: bcadfc362b69078beee0088e5936c98b + content/46: 326cbcf1d379181f7f53c6a9ffb271f1 + content/47: b3f310d5ef115bea5a8b75bf25d7ea9a + content/48: dc809f5be4a108f769310dd8290c0db4 bda76150deadd23f7803a15b39c4db66: meta/title: 1255b55897f2be1443d3bb8c30cd9795 meta/description: 1e7574b6666c662c08e7e256a9fceb4d @@ -4581,39 +4587,41 @@ checksums: content/19: 83fc31418ff454a5e06b290e3708ef32 content/20: 4392b5939a6d5774fb080cad1ee1dbb8 content/21: 890b65b7326a9eeef3933a8b63f6ccdd - content/22: 892d6a80d8ac5a895a20408462f63cc5 - content/23: 930176b3786ebbe9eb1f76488f183140 - content/24: 22d9d167630c581e868d6d7a9fdddbcf - content/25: d250621762d63cd87b3359236c95bdac - content/26: 50be8ae73b8ce27de7ddd21964ee29e8 - content/27: cd622841b5bc748a7b2a0d9252e72bd5 - content/28: 38608a5d416eb33f373c6f9e6bf546b9 - content/29: 074c12c794283c3af53a3f038fbda2a6 - content/30: 5cdcf7e32294e087612b77914d850d26 - content/31: 7529829b2f064fedf956da639aaea8e1 - content/32: 7b5e2207a0d93fd434b92f2f290a8dd5 - content/33: f950b8f58af1973a3e00393d860bce02 - content/34: d5ff07fec9455183e1d93f7ddf1dab1b - content/35: 5d2d85e082d9fdd3859fb5c788d5f9a3 - content/36: 23a7de9c5adb6e07c28c23a9d4e03dc2 - content/37: 7bb928aba33a4013ad5f08487da5bbf9 - content/38: dbbf313837f13ddfa4a8843d71cb9cc4 - content/39: cf10560ae6defb8ee5da344fc6509f6e - content/40: 1dea5c6442c127ae290185db0cef067b - content/41: 332dab0588fb35dabb64b674ba6120eb - content/42: 714b3f99b0a8686bbb3434deb1f682b3 - content/43: ba18ac99184b17d7e49bd1abdc814437 - content/44: bed2b629274d55c38bd637e6a28dbc4a - content/45: 71487ae6f6fb1034d1787456de442e6d - content/46: 137d9874cf5ec8d09bd447f224cc7a7c - content/47: 6b5b4c3b2f98b8fc7dd908fef2605ce8 - content/48: 3af6812662546ce647a55939241fd88e - content/49: 6a4d7f0ccb8c28303251d1ef7b3dcca7 - content/50: 5dce779f77cc2b0abf12802a833df499 - content/51: aa47ff01b631252f024eaaae0c773e42 - content/52: 1266d1c7582bb617cdef56857be34f30 - content/53: c2cef2688104adaf6641092f43d4969a - content/54: 089fc64b4589b2eaa371de7e04c4aed9 + content/22: ada515cf6e2e0f9d3f57f720f79699d3 + content/23: 332e0d08f601da9fb56c6b7e7c8e9daf + content/24: 892d6a80d8ac5a895a20408462f63cc5 + content/25: 930176b3786ebbe9eb1f76488f183140 + content/26: 22d9d167630c581e868d6d7a9fdddbcf + content/27: d250621762d63cd87b3359236c95bdac + content/28: 50be8ae73b8ce27de7ddd21964ee29e8 + content/29: cd622841b5bc748a7b2a0d9252e72bd5 + content/30: 38608a5d416eb33f373c6f9e6bf546b9 + content/31: 074c12c794283c3af53a3f038fbda2a6 + content/32: 5cdcf7e32294e087612b77914d850d26 + content/33: 7529829b2f064fedf956da639aaea8e1 + content/34: 7b5e2207a0d93fd434b92f2f290a8dd5 + content/35: f950b8f58af1973a3e00393d860bce02 + content/36: d5ff07fec9455183e1d93f7ddf1dab1b + content/37: 5d2d85e082d9fdd3859fb5c788d5f9a3 + content/38: 23a7de9c5adb6e07c28c23a9d4e03dc2 + content/39: 7bb928aba33a4013ad5f08487da5bbf9 + content/40: dbbf313837f13ddfa4a8843d71cb9cc4 + content/41: cf10560ae6defb8ee5da344fc6509f6e + content/42: 1dea5c6442c127ae290185db0cef067b + content/43: 332dab0588fb35dabb64b674ba6120eb + content/44: 714b3f99b0a8686bbb3434deb1f682b3 + content/45: ba18ac99184b17d7e49bd1abdc814437 + content/46: bed2b629274d55c38bd637e6a28dbc4a + content/47: 71487ae6f6fb1034d1787456de442e6d + content/48: 137d9874cf5ec8d09bd447f224cc7a7c + content/49: 6b5b4c3b2f98b8fc7dd908fef2605ce8 + content/50: 3af6812662546ce647a55939241fd88e + content/51: 6a4d7f0ccb8c28303251d1ef7b3dcca7 + content/52: 5dce779f77cc2b0abf12802a833df499 + content/53: aa47ff01b631252f024eaaae0c773e42 + content/54: 1266d1c7582bb617cdef56857be34f30 + content/55: c2cef2688104adaf6641092f43d4969a + content/56: 089fc64b4589b2eaa371de7e04c4aed9 722959335ba76c9d0097860e2ad5a952: meta/title: 1f5b53b9904ec41d49c1e726e3d56b40 content/0: c2b41859d63a751682f0d9aec488e581 @@ -47164,97 +47172,97 @@ checksums: content/11: 371d0e46b4bd2c23f559b8bc112f6955 content/12: a71a30e9f91c10daf481ea8f542e91f6 content/13: bcadfc362b69078beee0088e5936c98b - content/14: 59c08999f9c404330ebd8f8a7d21e1a1 + content/14: d3278442dbea313782edd4793be28197 content/15: 49d191d312481589419c68a5506b0d71 content/16: dddb93e063541bfb5d72b6c506d3cb7f content/17: 371d0e46b4bd2c23f559b8bc112f6955 content/18: e93f2b44f05dd87c82fe9557cd677eeb content/19: bcadfc362b69078beee0088e5936c98b - content/20: b74416361f94e71f2a94139711a5dd21 + content/20: 5079238d0092205bb1ca4ec32b8f3d97 content/21: 2e70c0a22a98675a13b493b9761ff92f content/22: 107f6e51a1e896ee4d18f8ed4f82c50f content/23: 371d0e46b4bd2c23f559b8bc112f6955 content/24: e506fbf4b80deecb3b44b29b8dc3438b content/25: bcadfc362b69078beee0088e5936c98b - content/26: a9096a341b00ce4f4891daaca2586d1c + content/26: 4d1f3216d2694b7409792e34a6f181e0 content/27: 934a0124aa2118682b2b17fa258ff06a content/28: aa318cc874d5936ce1f3bf9710da2a44 content/29: 371d0e46b4bd2c23f559b8bc112f6955 content/30: 660ce6e5a45abe1940974f7d818a6ee7 content/31: bcadfc362b69078beee0088e5936c98b - content/32: 551c2f007a7035ba0d48374081b02eb1 + content/32: 5e9da15383417721362c8d33b0a12fb8 content/33: 1a1e332b525e86f7fd92f9da1ac0096c content/34: 00098e1591c0f80ef6287d934d391409 content/35: 371d0e46b4bd2c23f559b8bc112f6955 content/36: e52688ff2fa61ce71026f33930e1ec86 content/37: bcadfc362b69078beee0088e5936c98b - content/38: d84fb23e5dfc9d41a177acd7dfb28e72 + content/38: ac15076b8e6cac4bf3a106ea32de661d content/39: 17be090a79154f557bc96f940c687aea content/40: bb2f63774f45f14201d5c0c110458a90 content/41: 371d0e46b4bd2c23f559b8bc112f6955 content/42: 36afb2b0539e33ff83427a91fc5ba57b content/43: bcadfc362b69078beee0088e5936c98b - content/44: 45d8bfeced635336cacc9d4a8d08dbca + content/44: 1da7a9f86cda2b24d0e1ffd5ae167272 content/45: c76943404f9c8d34a85e6315359ed0c4 content/46: b5e111e430aa1c929fb07d5844bf65eb content/47: 371d0e46b4bd2c23f559b8bc112f6955 content/48: 6692edffddc28d3c64974ded23d1def2 content/49: bcadfc362b69078beee0088e5936c98b - content/50: dbc08cce26f9565e719891bbbf4632a9 + content/50: e7e86e6f7734e9af89b5724ac674ff2c content/51: d0ce65f5420745c45ab42b7edd135bf4 content/52: 4a3de8fb6c97898fcfa3800d149cd4e0 content/53: 371d0e46b4bd2c23f559b8bc112f6955 content/54: d16a985c206a21f4ffb1bbcdc0300c85 content/55: bcadfc362b69078beee0088e5936c98b - content/56: a7e001e39652db8eeb4d32968bda102b + content/56: a64e62cd3f79c43f9411af221e24aa9f content/57: 440f2732ad006bee8cccc975fdbf673a content/58: 7a7048c54763b0109643f37e583381ce content/59: 371d0e46b4bd2c23f559b8bc112f6955 content/60: 11ad0a529a7fcc5892ae811cde6894f6 content/61: bcadfc362b69078beee0088e5936c98b - content/62: c7055d8ce044e49929d4f005a28d7c0a + content/62: d3c54294a5180fda87c23e23d4ad17eb content/63: 2d7bad4340c1bc6a28e836e180e26c00 content/64: 576dbecf29644e7abf59d25ffda5728c content/65: 371d0e46b4bd2c23f559b8bc112f6955 content/66: 59015900ce6b64caff0784491ec59ff9 content/67: bcadfc362b69078beee0088e5936c98b - content/68: 2f225a893086726db6b6a994cc8a5e3c + content/68: 5e12d96ca701a7a8182558a4d070aed2 content/69: 63cbf703cf33e0fee06f12fb23184352 content/70: dae1fda5ec57e1b598a7e2596007a775 content/71: 371d0e46b4bd2c23f559b8bc112f6955 content/72: 757f42df5247f2e6684ab32888d30e11 content/73: bcadfc362b69078beee0088e5936c98b - content/74: 380f805a5118dd4957f4fcce41e01b86 + content/74: 46f9b95601bc643ba6175c2a0115df19 content/75: 935f1a713d05f32d3d826434a7e715ee content/76: e505d8f656fb6e3b65a98cb73d744598 content/77: 371d0e46b4bd2c23f559b8bc112f6955 content/78: 2e77859b0f2c89186fc6a2d51287ea47 content/79: bcadfc362b69078beee0088e5936c98b - content/80: 22bd99d5b844817b808b9d0d3baddac4 + content/80: b312d1e8bce1418da88cd9812096db20 content/81: e959b48af94a559e9c46cbd7653d2dd2 content/82: 5e3c04c5a9fabfceb7fcc00215f93bf9 content/83: 371d0e46b4bd2c23f559b8bc112f6955 content/84: a92b2a22061ee6fd453af32e0155f5aa content/85: bcadfc362b69078beee0088e5936c98b - content/86: d84fb23e5dfc9d41a177acd7dfb28e72 + content/86: a735b1d909700cdf6d07c1a94330a1c6 content/87: c886f11a0852010b90a1032b97118920 content/88: c60c832c08f9e1ff5f91565bf4ba549e content/89: 371d0e46b4bd2c23f559b8bc112f6955 content/90: 1545794f4e8e696db96c3b660de684ec content/91: bcadfc362b69078beee0088e5936c98b - content/92: 573530e346d195727862b03b380f40fc + content/92: 098eb544fe99ee061a081a1f2ef0e7c6 content/93: 3d31dedf076ec23547189a3eb5fe04c4 content/94: a261b9a2ef7724e4171487ef2435f259 content/95: 371d0e46b4bd2c23f559b8bc112f6955 content/96: bef786efecaaad82a34b861f37cde78f content/97: bcadfc362b69078beee0088e5936c98b - content/98: 1b166ea32dff5f8de92b256fe48200d7 + content/98: 317256505991a755bbb6d3870b778f4a content/99: e1a03f917ad8b0a1ebec9a601aa3eede content/100: 3aa857b8f85da07ee2d87e65c95b76d0 content/101: 371d0e46b4bd2c23f559b8bc112f6955 content/102: cc49a24c087d08717866a162cc47776c content/103: bcadfc362b69078beee0088e5936c98b - content/104: c6d621ee3cdc66de2c20b70a39aafe12 + content/104: 283b701d5bd6125f277a7f0ab3b4a7fe content/105: b3f310d5ef115bea5a8b75bf25d7ea9a content/106: 9d45ccf1c14d61412169be8f8510a960 9ed109808041fe9022eed66e1feedfdd: diff --git a/apps/docs/lib/db.ts b/apps/docs/lib/db.ts new file mode 100644 index 0000000000..9ecca9431f --- /dev/null +++ b/apps/docs/lib/db.ts @@ -0,0 +1,4 @@ +import { db } from '@sim/db' +import { docsEmbeddings } from '@sim/db/schema' + +export { db, docsEmbeddings } diff --git a/apps/docs/lib/embeddings.ts b/apps/docs/lib/embeddings.ts new file mode 100644 index 0000000000..c41a3f1989 --- /dev/null +++ b/apps/docs/lib/embeddings.ts @@ -0,0 +1,40 @@ +/** + * Generate embeddings for search queries using OpenAI API + */ +export async function generateSearchEmbedding(query: string): Promise { + const apiKey = process.env.OPENAI_API_KEY + + if (!apiKey) { + throw new Error('OPENAI_API_KEY environment variable is required') + } + + const response = await fetch('https://api.openai.com/v1/embeddings', { + method: 'POST', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + input: query, + model: 'text-embedding-3-small', + encoding_format: 'float', + }), + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`OpenAI API failed: ${response.status} ${response.statusText} - ${errorText}`) + } + + const data = await response.json() + + if (!data?.data || !Array.isArray(data.data) || data.data.length === 0) { + throw new Error('OpenAI API returned invalid response structure: missing or empty data array') + } + + if (!data.data[0]?.embedding || !Array.isArray(data.data[0].embedding)) { + throw new Error('OpenAI API returned invalid response structure: missing or invalid embedding') + } + + return data.data[0].embedding +} diff --git a/apps/docs/package.json b/apps/docs/package.json index a589e671ed..59b2610630 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -11,16 +11,19 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@sim/db": "workspace:*", "@tabler/icons-react": "^3.31.0", "@vercel/og": "^0.6.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "drizzle-orm": "^0.44.5", "fumadocs-core": "16.2.3", "fumadocs-mdx": "14.1.0", "fumadocs-ui": "16.2.3", "lucide-react": "^0.511.0", "next": "16.1.0-canary.21", "next-themes": "^0.4.6", + "postgres": "^3.4.5", "react": "19.2.1", "react-dom": "19.2.1", "tailwind-merge": "^3.0.2" diff --git a/apps/sim/app/(auth)/components/auth-background.tsx b/apps/sim/app/(auth)/components/auth-background.tsx index a06ec0cdbf..47d2d0b8b3 100644 --- a/apps/sim/app/(auth)/components/auth-background.tsx +++ b/apps/sim/app/(auth)/components/auth-background.tsx @@ -9,6 +9,7 @@ type AuthBackgroundProps = { export default function AuthBackground({ className, children }: AuthBackgroundProps) { return (
+
{children}
diff --git a/apps/sim/app/(auth)/login/login-form.tsx b/apps/sim/app/(auth)/login/login-form.tsx index 775bf95705..10b2313bfd 100644 --- a/apps/sim/app/(auth)/login/login-form.tsx +++ b/apps/sim/app/(auth)/login/login-form.tsx @@ -1,6 +1,7 @@ 'use client' import { useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { ArrowRight, ChevronRight, Eye, EyeOff } from 'lucide-react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' @@ -18,7 +19,6 @@ import { client } from '@/lib/auth/auth-client' import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env' import { cn } from '@/lib/core/utils/cn' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' diff --git a/apps/sim/app/(auth)/reset-password/page.tsx b/apps/sim/app/(auth)/reset-password/page.tsx index e1d2112645..29ef3e425c 100644 --- a/apps/sim/app/(auth)/reset-password/page.tsx +++ b/apps/sim/app/(auth)/reset-password/page.tsx @@ -1,9 +1,9 @@ 'use client' import { Suspense, useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import { createLogger } from '@/lib/logs/console/logger' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' import { SetNewPasswordForm } from '@/app/(auth)/reset-password/reset-password-form' diff --git a/apps/sim/app/(auth)/signup/signup-form.tsx b/apps/sim/app/(auth)/signup/signup-form.tsx index 654676f0e6..0c08283b37 100644 --- a/apps/sim/app/(auth)/signup/signup-form.tsx +++ b/apps/sim/app/(auth)/signup/signup-form.tsx @@ -1,6 +1,7 @@ 'use client' import { Suspense, useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { ArrowRight, ChevronRight, Eye, EyeOff } from 'lucide-react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' @@ -10,7 +11,6 @@ import { Label } from '@/components/ui/label' import { client, useSession } from '@/lib/auth/auth-client' import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' diff --git a/apps/sim/app/(auth)/sso/sso-form.tsx b/apps/sim/app/(auth)/sso/sso-form.tsx index ae699134e6..4d01ebd0b1 100644 --- a/apps/sim/app/(auth)/sso/sso-form.tsx +++ b/apps/sim/app/(auth)/sso/sso-form.tsx @@ -1,6 +1,7 @@ 'use client' import { useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' import { Button } from '@/components/ui/button' @@ -9,7 +10,6 @@ import { Label } from '@/components/ui/label' import { client } from '@/lib/auth/auth-client' import { env, isFalsy } from '@/lib/core/config/env' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' diff --git a/apps/sim/app/(auth)/verify/use-verification.ts b/apps/sim/app/(auth)/verify/use-verification.ts index fd30e960f3..f59c0446cd 100644 --- a/apps/sim/app/(auth)/verify/use-verification.ts +++ b/apps/sim/app/(auth)/verify/use-verification.ts @@ -1,9 +1,9 @@ 'use client' import { useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { useRouter, useSearchParams } from 'next/navigation' import { client, useSession } from '@/lib/auth/auth-client' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('useVerification') diff --git a/apps/sim/app/(landing)/actions/github.ts b/apps/sim/app/(landing)/actions/github.ts index 42f586a956..c5785f0e9c 100644 --- a/apps/sim/app/(landing)/actions/github.ts +++ b/apps/sim/app/(landing)/actions/github.ts @@ -1,4 +1,4 @@ -import { createLogger } from '@/lib/logs/console/logger' +import { createLogger } from '@sim/logger' const DEFAULT_STARS = '19.4k' diff --git a/apps/sim/app/(landing)/careers/page.tsx b/apps/sim/app/(landing)/careers/page.tsx index 2f12b3d9c4..37c5f7a0e1 100644 --- a/apps/sim/app/(landing)/careers/page.tsx +++ b/apps/sim/app/(landing)/careers/page.tsx @@ -1,7 +1,8 @@ 'use client' import { useRef, useState } from 'react' -import { Loader2, X } from 'lucide-react' +import { createLogger } from '@sim/logger' +import { X } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -15,7 +16,6 @@ import { import { Textarea } from '@/components/ui/textarea' import { isHosted } from '@/lib/core/config/feature-flags' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { soehne } from '@/app/_styles/fonts/soehne/soehne' import Footer from '@/app/(landing)/components/footer/footer' @@ -499,16 +499,11 @@ export default function CareersPage() { className='min-w-[200px] rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] text-white shadow-[inset_0_2px_4px_0_#9B77FF] transition-all duration-300 hover:opacity-90 disabled:opacity-50' size='lg' > - {isSubmitting ? ( - <> - - Submitting... - - ) : submitStatus === 'success' ? ( - 'Submitted' - ) : ( - 'Submit Application' - )} + {isSubmitting + ? 'Submitting...' + : submitStatus === 'success' + ? 'Submitted' + : 'Submit Application'}
diff --git a/apps/sim/app/(landing)/components/background/background-svg.tsx b/apps/sim/app/(landing)/components/background/background-svg.tsx index 7d3129c529..98bea428c5 100644 --- a/apps/sim/app/(landing)/components/background/background-svg.tsx +++ b/apps/sim/app/(landing)/components/background/background-svg.tsx @@ -5,39 +5,39 @@ export default function BackgroundSVG() { focusable='false' className='-translate-x-1/2 pointer-events-none absolute top-0 left-1/2 z-10 hidden h-full min-h-full w-[1308px] sm:block' width='1308' - height='4942' - viewBox='0 18 1308 4066' + height='4970' + viewBox='0 18 1308 4094' fill='none' xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMidYMin slice' > - {/* Pricing section (original height ~380 units) */} + {/* Pricing section (extended by ~28 units) */} - - + + - {/* Integrations section (original height ~412 units) */} - - - - - + {/* Integrations section (shifted down by 28 units) */} + + + + + - {/* Testimonials section (original short height ~149 units) */} - - - - - + {/* Testimonials section (shifted down by 28 units) */} + + + + + - {/* Footer section line */} - - - - - + {/* Footer section line (shifted down by 28 units) */} + + + + + +
{children}
diff --git a/apps/sim/app/(landing)/components/landing-pricing/landing-pricing.tsx b/apps/sim/app/(landing)/components/landing-pricing/landing-pricing.tsx index 6df35cdc12..2cc62e9860 100644 --- a/apps/sim/app/(landing)/components/landing-pricing/landing-pricing.tsx +++ b/apps/sim/app/(landing)/components/landing-pricing/landing-pricing.tsx @@ -1,6 +1,8 @@ 'use client' +import type { ComponentType, SVGProps } from 'react' import { useState } from 'react' +import { createLogger } from '@sim/logger' import type { LucideIcon } from 'lucide-react' import { ArrowRight, @@ -13,7 +15,6 @@ import { } from 'lucide-react' import { useRouter } from 'next/navigation' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { inter } from '@/app/_styles/fonts/inter/inter' import { ENTERPRISE_PLAN_FEATURES, @@ -24,7 +25,7 @@ import { const logger = createLogger('LandingPricing') interface PricingFeature { - icon: LucideIcon + icon: LucideIcon | ComponentType> text: string } diff --git a/apps/sim/app/(landing)/components/nav/nav.tsx b/apps/sim/app/(landing)/components/nav/nav.tsx index b4ce6ddbe9..d8ae4b9065 100644 --- a/apps/sim/app/(landing)/components/nav/nav.tsx +++ b/apps/sim/app/(landing)/components/nav/nav.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { ArrowRight, ChevronRight } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' @@ -8,7 +9,6 @@ import { useRouter } from 'next/navigation' import { GithubIcon } from '@/components/icons' import { useBrandConfig } from '@/lib/branding/branding' import { isHosted } from '@/lib/core/config/feature-flags' -import { createLogger } from '@/lib/logs/console/logger' import { soehne } from '@/app/_styles/fonts/soehne/soehne' import { getFormattedGitHubStars } from '@/app/(landing)/actions/github' @@ -20,7 +20,7 @@ interface NavProps { } export default function Nav({ hideAuthButtons = false, variant = 'landing' }: NavProps = {}) { - const [githubStars, setGithubStars] = useState('24k') + const [githubStars, setGithubStars] = useState('24.4k') const [isHovered, setIsHovered] = useState(false) const [isLoginHovered, setIsLoginHovered] = useState(false) const router = useRouter() diff --git a/apps/sim/app/_shell/hydration-error-handler.tsx b/apps/sim/app/_shell/hydration-error-handler.tsx index 8eae512e41..56050875f0 100644 --- a/apps/sim/app/_shell/hydration-error-handler.tsx +++ b/apps/sim/app/_shell/hydration-error-handler.tsx @@ -1,7 +1,7 @@ 'use client' import { useEffect } from 'react' -import { createLogger } from '@/lib/logs/console/logger' +import { createLogger } from '@sim/logger' const logger = createLogger('RootLayout') diff --git a/apps/sim/app/_shell/providers/theme-provider.tsx b/apps/sim/app/_shell/providers/theme-provider.tsx index a6bc5444e0..cbb31e4423 100644 --- a/apps/sim/app/_shell/providers/theme-provider.tsx +++ b/apps/sim/app/_shell/providers/theme-provider.tsx @@ -7,7 +7,7 @@ import { ThemeProvider as NextThemesProvider } from 'next-themes' export function ThemeProvider({ children, ...props }: ThemeProviderProps) { const pathname = usePathname() - // Force light mode on public/marketing pages, dark mode everywhere else + // Force light mode on public/marketing pages, allow user preference elsewhere const isLightModePage = pathname === '/' || pathname.startsWith('/login') || @@ -27,10 +27,10 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) { {children} diff --git a/apps/sim/app/_shell/zoom-prevention.tsx b/apps/sim/app/_shell/zoom-prevention.tsx deleted file mode 100644 index 66f0ceb74b..0000000000 --- a/apps/sim/app/_shell/zoom-prevention.tsx +++ /dev/null @@ -1,33 +0,0 @@ -'use client' - -import { useEffect } from 'react' - -export function ZoomPrevention() { - useEffect(() => { - const preventZoom = (e: KeyboardEvent | WheelEvent) => { - // Prevent zoom on ctrl/cmd + wheel - if (e instanceof WheelEvent && (e.ctrlKey || e.metaKey)) { - e.preventDefault() - } - - // Prevent zoom on ctrl/cmd + plus/minus/zero - if (e instanceof KeyboardEvent && (e.ctrlKey || e.metaKey)) { - if (e.key === '=' || e.key === '-' || e.key === '0') { - e.preventDefault() - } - } - } - - // Add event listeners - document.addEventListener('wheel', preventZoom, { passive: false }) - document.addEventListener('keydown', preventZoom) - - // Cleanup - return () => { - document.removeEventListener('wheel', preventZoom) - document.removeEventListener('keydown', preventZoom) - } - }, []) - - return null -} diff --git a/apps/sim/app/_styles/globals.css b/apps/sim/app/_styles/globals.css index f67e27e375..013e6b1d45 100644 --- a/apps/sim/app/_styles/globals.css +++ b/apps/sim/app/_styles/globals.css @@ -8,7 +8,7 @@ */ :root { --sidebar-width: 232px; - --panel-width: 244px; + --panel-width: 260px; --toolbar-triggers-height: 300px; --editor-connections-height: 200px; --terminal-height: 196px; @@ -26,41 +26,6 @@ height: var(--terminal-height); } -/** - * Workflow component z-index fixes and background colors - */ -.workflow-container .react-flow__edges { - z-index: 0 !important; -} - -.workflow-container .react-flow__node { - z-index: 21 !important; -} - -.workflow-container .react-flow__handle { - z-index: 30 !important; -} - -.workflow-container .react-flow__edge [data-testid="workflow-edge"] { - z-index: 0 !important; -} - -.workflow-container .react-flow__edge-labels { - z-index: 60 !important; -} - -.workflow-container, -.workflow-container .react-flow__pane, -.workflow-container .react-flow__renderer { - background-color: var(--bg) !important; -} - -.dark .workflow-container, -.dark .workflow-container .react-flow__pane, -.dark .workflow-container .react-flow__renderer { - background-color: var(--bg) !important; -} - /** * Landing loop animation styles (keyframes defined in tailwind.config.ts) */ @@ -75,101 +40,87 @@ } /** - * Dark color tokens - single source of truth for all colors (dark-only) + * Color tokens - single source of truth for all colors + * Light mode: Warm theme + * Dark mode: Dark neutral theme */ @layer base { :root, .light { - /* Neutrals (surfaces) - shadcn stone palette */ - --bg: #ffffff; /* pure white for landing/auth pages */ - --surface-1: #fafaf9; /* stone-50 */ - --surface-2: #ffffff; /* white */ - --surface-3: #f5f5f4; /* stone-100 */ - --surface-4: #f5f5f4; /* stone-100 */ - --surface-5: #eeedec; /* stone-150 */ - --surface-6: #f5f5f4; /* stone-100 */ - --surface-9: #f5f5f4; /* stone-100 */ - --surface-11: #e7e5e4; /* stone-200 */ - --surface-12: #d6d3d1; /* stone-300 */ - --surface-13: #a8a29e; /* stone-400 */ - --surface-14: #78716c; /* stone-500 */ - --surface-15: #57534e; /* stone-600 */ - --surface-elevated: #ffffff; /* white */ - --bg-strong: #e7e5e4; /* stone-200 */ - - /* Text - shadcn stone palette for proper contrast */ - --text-primary: #1c1917; /* stone-900 */ - --text-secondary: #292524; /* stone-800 */ - --text-tertiary: #57534e; /* stone-600 */ - --text-muted: #78716c; /* stone-500 */ - --text-subtle: #a8a29e; /* stone-400 */ - --text-inverse: #fafaf9; /* stone-50 */ - --text-error: #dc2626; - - /* Borders / dividers - shadcn stone palette */ - --border: #d6d3d1; /* stone-300 */ - --border-strong: #d6d3d1; /* stone-300 */ - --divider: #e7e5e4; /* stone-200 */ - --border-muted: #e7e5e4; /* stone-200 */ - --border-success: #d6d3d1; /* stone-300 */ + --bg: #f9faf8; /* main canvas - near white */ + --surface-1: #f9faf8; /* sidebar, panels - light warm gray */ + --surface-2: #fdfdfb; /* blocks, cards, modals - soft warm white */ + --surface-3: #f4f5f1; /* popovers, headers - more contrast */ + --surface-4: #f2f3ef; /* buttons base */ + --border: #d7dcda; /* primary border */ + --surface-5: #f0f1ed; /* inputs, form elements - subtle */ + --border-1: #d7dcda; /* stronger border - sage gray */ + --surface-6: #eceee9; /* popovers, elevated surfaces */ + --surface-7: #e8e9e4; + + --workflow-edge: #d7dcda; /* workflow handles/edges - matches border-1 */ + + /* Text - warm neutrals */ + --text-primary: #2d2d2d; + --text-secondary: #404040; + --text-tertiary: #5c5c5c; + --text-muted: #737373; + --text-subtle: #8c8c8c; + --text-inverse: #f0fff6; + --text-error: #ef4444; + + /* Borders / dividers */ + --divider: #e8e9e4; + --border-muted: #dfe0db; + --border-success: #d7dcda; /* Brand & state */ --brand-400: #8e4cfb; - --brand-500: #6f3dfa; --brand-secondary: #33b4ff; --brand-tertiary: #22c55e; - --brand-tertiary-2: #33c481; + --brand-tertiary-2: #32bd7e; --warning: #ea580c; /* Utility */ --white: #ffffff; - /* Font weights - lighter for light mode (-20 from dark) */ + /* Font weights - lighter for light mode */ --font-weight-base: 430; --font-weight-medium: 450; --font-weight-semibold: 500; - /* RGB for opacity usage - stone palette */ - --surface-4-rgb: 245 245 244; /* stone-100 */ - --surface-5-rgb: 238 237 236; /* stone-150 */ - --surface-7-rgb: 245 245 244; /* stone-100 */ - --surface-9-rgb: 245 245 244; /* stone-100 */ - --divider-rgb: 231 229 228; /* stone-200 */ - --white-rgb: 255 255 255; - --black-rgb: 0 0 0; - - /* Extended palette - mapped to shadcn stone palette */ - --c-0D0D0D: #0c0a09; /* stone-950 */ - --c-1A1A1A: #1c1917; /* stone-900 */ - --c-1F1F1F: #1c1917; /* stone-900 */ - --c-2A2A2A: #292524; /* stone-800 */ - --c-383838: #44403c; /* stone-700 */ - --c-414141: #57534e; /* stone-600 */ + /* Extended palette */ + --c-0D0D0D: #0d0d0d; + --c-1A1A1A: #1a1a1a; + --c-1F1F1F: #1f1f1f; + --c-2A2A2A: #2a2a2a; + --c-383838: #383838; + --c-414141: #414141; --c-442929: #442929; --c-491515: #491515; - --c-575757: #78716c; /* stone-500 */ - --c-686868: #78716c; /* stone-500 */ - --c-707070: #78716c; /* stone-500 */ - --c-727272: #78716c; /* stone-500 */ - --c-737373: #78716c; /* stone-500 */ - --c-808080: #a8a29e; /* stone-400 */ - --c-858585: #a8a29e; /* stone-400 */ - --c-868686: #a8a29e; /* stone-400 */ - --c-8D8D8D: #a8a29e; /* stone-400 */ - --c-939393: #a8a29e; /* stone-400 */ - --c-A8A8A8: #a8a29e; /* stone-400 */ - --c-B8B8B8: #d6d3d1; /* stone-300 */ - --c-C0C0C0: #d6d3d1; /* stone-300 */ - --c-CDCDCD: #d6d3d1; /* stone-300 */ - --c-D0D0D0: #d6d3d1; /* stone-300 */ - --c-D2D2D2: #d6d3d1; /* stone-300 */ - --c-E0E0E0: #e7e5e4; /* stone-200 */ - --c-E5E5E5: #e7e5e4; /* stone-200 */ - --c-E8E8E8: #e7e5e4; /* stone-200 */ - --c-EEEEEE: #f5f5f4; /* stone-100 */ - --c-F0F0F0: #f5f5f4; /* stone-100 */ - --c-F4F4F4: #fafaf9; /* stone-50 */ - --c-F5F5F5: #fafaf9; /* stone-50 */ + --c-575757: #575757; + --c-686868: #686868; + --c-707070: #707070; + --c-727272: #727272; + --c-737373: #737373; + --c-808080: #808080; + --c-858585: #858585; + --c-868686: #868686; + --c-8D8D8D: #8d8d8d; + --c-939393: #939393; + --c-A8A8A8: #a8a8a8; + --c-B8B8B8: #b8b8b8; + --c-C0C0C0: #c0c0c0; + --c-CDCDCD: #cdcdcd; + --c-D0D0D0: #d0d0d0; + --c-D2D2D2: #d2d2d2; + --c-E0E0E0: #e0e0e0; + --c-E5E5E5: #e5e5e5; + --c-E8E8E8: #e8e8e8; + --c-EEEEEE: #eeeeee; + --c-F0F0F0: #f0f0f0; + --c-F4F4F4: #f4f4f4; + --c-F5F5F5: #f5f5f5; /* Blues and cyans */ --c-00B0B0: #00b0b0; @@ -203,30 +154,27 @@ /* Terminal status badges */ --terminal-status-error-bg: #feeeee; --terminal-status-error-border: #f87171; - --terminal-status-info-bg: #f5f5f4; /* stone-100 */ - --terminal-status-info-border: #a8a29e; /* stone-400 */ - --terminal-status-info-color: #57534e; /* stone-600 */ + --terminal-status-info-bg: #f5f5f4; + --terminal-status-info-border: #a8a29e; + --terminal-status-info-color: #57534e; --terminal-status-warning-bg: #fef9e7; --terminal-status-warning-border: #f5c842; --terminal-status-warning-color: #a16207; } .dark { - /* Neutrals (surfaces) */ + /* Surface */ --bg: #1b1b1b; --surface-1: #1e1e1e; --surface-2: #232323; --surface-3: #242424; - --surface-4: #252525; - --surface-5: #272727; - --surface-6: #282828; - --surface-9: #363636; - --surface-11: #3d3d3d; - --surface-12: #434343; - --surface-13: #454545; - --surface-14: #4a4a4a; - --surface-15: #5a5a5a; - --surface-elevated: #202020; - --bg-strong: #0c0c0c; + --surface-4: #292929; + --border: #2c2c2c; + --surface-5: #363636; + --border-1: #3d3d3d; + --surface-6: #454545; + --surface-7: #454545; + + --workflow-edge: #454545; /* workflow handles/edges - same as surface-6 in dark */ /* Text */ --text-primary: #e6e6e6; @@ -237,9 +185,7 @@ --text-inverse: #1b1b1b; --text-error: #ef4444; - /* Borders / dividers */ - --border: #2c2c2c; - --border-strong: #303030; + /* --border-strong: #303030; */ --divider: #393939; --border-muted: #424242; --border-success: #575757; @@ -248,7 +194,7 @@ --brand-400: #8e4cfb; --brand-secondary: #33b4ff; --brand-tertiary: #22c55e; - --brand-tertiary-2: #33c481; + --brand-tertiary-2: #32bd7e; --warning: #ff6600; /* Utility */ @@ -259,15 +205,6 @@ --font-weight-medium: 480; --font-weight-semibold: 550; - /* RGB for opacity usage */ - --surface-4-rgb: 37 37 37; - --surface-5-rgb: 39 39 39; - --surface-7-rgb: 44 44 44; - --surface-9-rgb: 54 54 54; - --divider-rgb: 57 57 57; - --white-rgb: 255 255 255; - --black-rgb: 0 0 0; - /* Extended palette (exhaustive from code usage via -[#...]) */ /* Neutral deep shades */ --c-0D0D0D: #0d0d0d; @@ -395,34 +332,34 @@ } ::-webkit-scrollbar-thumb { - background-color: var(--surface-12); + background-color: var(--surface-7); border-radius: var(--radius); } ::-webkit-scrollbar-thumb:hover { - background-color: var(--surface-13); + background-color: var(--surface-7); } /* Dark Mode Global Scrollbar */ .dark ::-webkit-scrollbar-track { - background: var(--surface-5); + background: var(--surface-4); } .dark ::-webkit-scrollbar-thumb { - background-color: var(--surface-12); + background-color: var(--surface-7); } .dark ::-webkit-scrollbar-thumb:hover { - background-color: var(--surface-13); + background-color: var(--surface-7); } * { scrollbar-width: thin; - scrollbar-color: var(--surface-12) var(--surface-1); + scrollbar-color: var(--surface-7) var(--surface-1); } .dark * { - scrollbar-color: var(--surface-12) var(--surface-5); + scrollbar-color: var(--surface-7) var(--surface-4); } .copilot-scrollable { @@ -438,8 +375,8 @@ } .panel-tab-active { - background-color: var(--white); - color: var(--text-inverse); + background-color: var(--surface-5); + color: var(--text-primary); border-color: var(--border-muted); } @@ -450,7 +387,7 @@ } .panel-tab-inactive:hover { - background-color: var(--surface-9); + background-color: var(--surface-5); color: var(--text-primary); } @@ -601,25 +538,25 @@ input[type="search"]::-ms-clear { } .auth-button-gradient { - background: linear-gradient(to bottom, var(--brand-500), var(--brand-400)) !important; + background: linear-gradient(to bottom, var(--brand-primary-hex), var(--brand-400)) !important; border-color: var(--brand-400) !important; box-shadow: inset 0 2px 4px 0 var(--brand-400) !important; } .auth-button-gradient:hover { - background: linear-gradient(to bottom, var(--brand-500), var(--brand-400)) !important; + background: linear-gradient(to bottom, var(--brand-primary-hex), var(--brand-400)) !important; opacity: 0.9; } .auth-button-custom { - background: var(--brand-500) !important; - border-color: var(--brand-500) !important; + background: var(--brand-primary-hex) !important; + border-color: var(--brand-primary-hex) !important; box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1) !important; } .auth-button-custom:hover { - background: var(--brand-500) !important; - border-color: var(--brand-500) !important; + background: var(--brand-primary-hover-hex) !important; + border-color: var(--brand-primary-hover-hex) !important; opacity: 1; } @@ -642,7 +579,7 @@ input[type="search"]::-ms-clear { } html[data-panel-active-tab="copilot"] .panel-container [data-tab-button="copilot"] { - background-color: var(--surface-11) !important; + background-color: var(--border-1) !important; color: var(--text-primary) !important; } html[data-panel-active-tab="copilot"] .panel-container [data-tab-button="toolbar"], @@ -652,7 +589,7 @@ input[type="search"]::-ms-clear { } html[data-panel-active-tab="toolbar"] .panel-container [data-tab-button="toolbar"] { - background-color: var(--surface-11) !important; + background-color: var(--border-1) !important; color: var(--text-primary) !important; } html[data-panel-active-tab="toolbar"] .panel-container [data-tab-button="copilot"], @@ -662,7 +599,7 @@ input[type="search"]::-ms-clear { } html[data-panel-active-tab="editor"] .panel-container [data-tab-button="editor"] { - background-color: var(--surface-11) !important; + background-color: var(--border-1) !important; color: var(--text-primary) !important; } html[data-panel-active-tab="editor"] .panel-container [data-tab-button="copilot"], diff --git a/apps/sim/app/api/__test-utils__/utils.ts b/apps/sim/app/api/__test-utils__/utils.ts index 8b85d79dc5..3ecefb443c 100644 --- a/apps/sim/app/api/__test-utils__/utils.ts +++ b/apps/sim/app/api/__test-utils__/utils.ts @@ -1,6 +1,9 @@ +import { createMockLogger as createSimTestingMockLogger } from '@sim/testing' import { NextRequest } from 'next/server' import { vi } from 'vitest' +export { createMockLogger } from '@sim/testing' + export interface MockUser { id: string email: string @@ -214,12 +217,11 @@ export const mockDb = { })), } -export const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), -} +/** + * Mock logger using @sim/testing createMockLogger. + * This provides a consistent mock logger across all API tests. + */ +export const mockLogger = createSimTestingMockLogger() export const mockUser = { id: 'user-123', @@ -729,10 +731,11 @@ export function mockKnowledgeSchemas() { } /** - * Mock console logger + * Mock console logger using the shared mockLogger instance. + * This ensures tests can assert on the same mockLogger instance exported from this module. */ export function mockConsoleLogger() { - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue(mockLogger), })) } diff --git a/apps/sim/app/api/auth/accounts/route.ts b/apps/sim/app/api/auth/accounts/route.ts index 5739586c38..a51d8585c2 100644 --- a/apps/sim/app/api/auth/accounts/route.ts +++ b/apps/sim/app/api/auth/accounts/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('AuthAccountsAPI') diff --git a/apps/sim/app/api/auth/forget-password/route.test.ts b/apps/sim/app/api/auth/forget-password/route.test.ts index 8dc57e18e3..36cbb3e0e8 100644 --- a/apps/sim/app/api/auth/forget-password/route.test.ts +++ b/apps/sim/app/api/auth/forget-password/route.test.ts @@ -162,7 +162,7 @@ describe('Forget Password API Route', () => { expect(response.status).toBe(500) expect(data.message).toBe(errorMessage) - const logger = await import('@/lib/logs/console/logger') + const logger = await import('@sim/logger') const mockLogger = logger.createLogger('ForgetPasswordTest') expect(mockLogger.error).toHaveBeenCalledWith('Error requesting password reset:', { error: expect.any(Error), @@ -192,7 +192,7 @@ describe('Forget Password API Route', () => { expect(response.status).toBe(500) expect(data.message).toBe('Failed to send password reset email. Please try again later.') - const logger = await import('@/lib/logs/console/logger') + const logger = await import('@sim/logger') const mockLogger = logger.createLogger('ForgetPasswordTest') expect(mockLogger.error).toHaveBeenCalled() }) diff --git a/apps/sim/app/api/auth/forget-password/route.ts b/apps/sim/app/api/auth/forget-password/route.ts index 6844c40c61..e8f05ecfcf 100644 --- a/apps/sim/app/api/auth/forget-password/route.ts +++ b/apps/sim/app/api/auth/forget-password/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { auth } from '@/lib/auth' import { isSameOrigin } from '@/lib/core/utils/validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth/connections/route.test.ts b/apps/sim/app/api/auth/oauth/connections/route.test.ts index f3aceda583..35bdcbc152 100644 --- a/apps/sim/app/api/auth/oauth/connections/route.test.ts +++ b/apps/sim/app/api/auth/oauth/connections/route.test.ts @@ -4,7 +4,7 @@ * @vitest-environment node */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createMockRequest } from '@/app/api/__test-utils__/utils' +import { createMockLogger, createMockRequest } from '@/app/api/__test-utils__/utils' describe('OAuth Connections API Route', () => { const mockGetSession = vi.fn() @@ -14,12 +14,7 @@ describe('OAuth Connections API Route', () => { where: vi.fn().mockReturnThis(), limit: vi.fn(), } - const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - } + const mockLogger = createMockLogger() const mockParseProvider = vi.fn() const mockEvaluateScopeCoverage = vi.fn() @@ -51,7 +46,7 @@ describe('OAuth Connections API Route', () => { jwtDecode: vi.fn(), })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue(mockLogger), })) diff --git a/apps/sim/app/api/auth/oauth/connections/route.ts b/apps/sim/app/api/auth/oauth/connections/route.ts index 783f3d2ce2..148f4b20f2 100644 --- a/apps/sim/app/api/auth/oauth/connections/route.ts +++ b/apps/sim/app/api/auth/oauth/connections/route.ts @@ -1,10 +1,10 @@ import { account, db, user } from '@sim/db' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { jwtDecode } from 'jwt-decode' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import type { OAuthProvider } from '@/lib/oauth' import { evaluateScopeCoverage, parseProvider } from '@/lib/oauth' diff --git a/apps/sim/app/api/auth/oauth/credentials/route.test.ts b/apps/sim/app/api/auth/oauth/credentials/route.test.ts index 1e0a2889a0..93aceaccc1 100644 --- a/apps/sim/app/api/auth/oauth/credentials/route.test.ts +++ b/apps/sim/app/api/auth/oauth/credentials/route.test.ts @@ -6,6 +6,7 @@ import { NextRequest } from 'next/server' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { createMockLogger } from '@/app/api/__test-utils__/utils' describe('OAuth Credentials API Route', () => { const mockGetSession = vi.fn() @@ -17,12 +18,7 @@ describe('OAuth Credentials API Route', () => { where: vi.fn().mockReturnThis(), limit: vi.fn(), } - const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - } + const mockLogger = createMockLogger() const mockUUID = 'mock-uuid-12345678-90ab-cdef-1234-567890abcdef' @@ -65,7 +61,7 @@ describe('OAuth Credentials API Route', () => { jwtDecode: vi.fn(), })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue(mockLogger), })) diff --git a/apps/sim/app/api/auth/oauth/credentials/route.ts b/apps/sim/app/api/auth/oauth/credentials/route.ts index 04f5e9c5ba..76a71b2df9 100644 --- a/apps/sim/app/api/auth/oauth/credentials/route.ts +++ b/apps/sim/app/api/auth/oauth/credentials/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { account, user, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { jwtDecode } from 'jwt-decode' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { evaluateScopeCoverage, type OAuthProvider, parseProvider } from '@/lib/oauth' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/auth/oauth/disconnect/route.test.ts b/apps/sim/app/api/auth/oauth/disconnect/route.test.ts index deeabb89dd..9cd956fee6 100644 --- a/apps/sim/app/api/auth/oauth/disconnect/route.test.ts +++ b/apps/sim/app/api/auth/oauth/disconnect/route.test.ts @@ -4,7 +4,7 @@ * @vitest-environment node */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createMockRequest } from '@/app/api/__test-utils__/utils' +import { createMockLogger, createMockRequest } from '@/app/api/__test-utils__/utils' describe('OAuth Disconnect API Route', () => { const mockGetSession = vi.fn() @@ -12,12 +12,7 @@ describe('OAuth Disconnect API Route', () => { delete: vi.fn().mockReturnThis(), where: vi.fn(), } - const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - } + const mockLogger = createMockLogger() const mockUUID = 'mock-uuid-12345678-90ab-cdef-1234-567890abcdef' @@ -47,7 +42,7 @@ describe('OAuth Disconnect API Route', () => { or: vi.fn((...conditions) => ({ conditions, type: 'or' })), })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue(mockLogger), })) }) diff --git a/apps/sim/app/api/auth/oauth/disconnect/route.ts b/apps/sim/app/api/auth/oauth/disconnect/route.ts index 39f3b8648b..5050e86172 100644 --- a/apps/sim/app/api/auth/oauth/disconnect/route.ts +++ b/apps/sim/app/api/auth/oauth/disconnect/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, like, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth/microsoft/file/route.ts b/apps/sim/app/api/auth/oauth/microsoft/file/route.ts index 4bb6de84ce..af9d5d47e8 100644 --- a/apps/sim/app/api/auth/oauth/microsoft/file/route.ts +++ b/apps/sim/app/api/auth/oauth/microsoft/file/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getCredential, refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth/microsoft/files/route.ts b/apps/sim/app/api/auth/oauth/microsoft/files/route.ts index a5fe878875..1a689b808d 100644 --- a/apps/sim/app/api/auth/oauth/microsoft/files/route.ts +++ b/apps/sim/app/api/auth/oauth/microsoft/files/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getCredential, refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth/token/route.test.ts b/apps/sim/app/api/auth/oauth/token/route.test.ts index ed4838710c..4d22039777 100644 --- a/apps/sim/app/api/auth/oauth/token/route.test.ts +++ b/apps/sim/app/api/auth/oauth/token/route.test.ts @@ -4,7 +4,7 @@ * @vitest-environment node */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { createMockRequest } from '@/app/api/__test-utils__/utils' +import { createMockLogger, createMockRequest } from '@/app/api/__test-utils__/utils' describe('OAuth Token API Routes', () => { const mockGetUserId = vi.fn() @@ -13,12 +13,7 @@ describe('OAuth Token API Routes', () => { const mockAuthorizeCredentialUse = vi.fn() const mockCheckHybridAuth = vi.fn() - const mockLogger = { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - } + const mockLogger = createMockLogger() const mockUUID = 'mock-uuid-12345678-90ab-cdef-1234-567890abcdef' const mockRequestId = mockUUID.slice(0, 8) @@ -36,7 +31,7 @@ describe('OAuth Token API Routes', () => { refreshTokenIfNeeded: mockRefreshTokenIfNeeded, })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue(mockLogger), })) diff --git a/apps/sim/app/api/auth/oauth/token/route.ts b/apps/sim/app/api/auth/oauth/token/route.ts index b89aff1aa9..603f5c6b0b 100644 --- a/apps/sim/app/api/auth/oauth/token/route.ts +++ b/apps/sim/app/api/auth/oauth/token/route.ts @@ -1,15 +1,17 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getCredential, refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' const logger = createLogger('OAuthTokenAPI') +const SALESFORCE_INSTANCE_URL_REGEX = /__sf_instance__:([^\s]+)/ + const tokenRequestSchema = z.object({ credentialId: z .string({ required_error: 'Credential ID is required' }) @@ -78,10 +80,20 @@ export async function POST(request: NextRequest) { try { // Refresh the token if needed const { accessToken } = await refreshTokenIfNeeded(requestId, credential, credentialId) + + let instanceUrl: string | undefined + if (credential.providerId === 'salesforce' && credential.scope) { + const instanceMatch = credential.scope.match(SALESFORCE_INSTANCE_URL_REGEX) + if (instanceMatch) { + instanceUrl = instanceMatch[1] + } + } + return NextResponse.json( { accessToken, idToken: credential.idToken || undefined, + ...(instanceUrl && { instanceUrl }), }, { status: 200 } ) @@ -147,10 +159,21 @@ export async function GET(request: NextRequest) { try { const { accessToken } = await refreshTokenIfNeeded(requestId, credential, credentialId) + + // For Salesforce, extract instanceUrl from the scope field + let instanceUrl: string | undefined + if (credential.providerId === 'salesforce' && credential.scope) { + const instanceMatch = credential.scope.match(SALESFORCE_INSTANCE_URL_REGEX) + if (instanceMatch) { + instanceUrl = instanceMatch[1] + } + } + return NextResponse.json( { accessToken, idToken: credential.idToken || undefined, + ...(instanceUrl && { instanceUrl }), }, { status: 200 } ) diff --git a/apps/sim/app/api/auth/oauth/utils.test.ts b/apps/sim/app/api/auth/oauth/utils.test.ts index f53402f5b5..e144221a80 100644 --- a/apps/sim/app/api/auth/oauth/utils.test.ts +++ b/apps/sim/app/api/auth/oauth/utils.test.ts @@ -3,9 +3,11 @@ * * @vitest-environment node */ + +import { createSession, loggerMock } from '@sim/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -const mockSession = { user: { id: 'test-user-id' } } +const mockSession = createSession({ userId: 'test-user-id' }) const mockGetSession = vi.fn() vi.mock('@/lib/auth', () => ({ @@ -29,14 +31,7 @@ vi.mock('@/lib/oauth/oauth', () => ({ OAUTH_PROVIDERS: {}, })) -vi.mock('@/lib/logs/console/logger', () => ({ - createLogger: vi.fn().mockReturnValue({ - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - }), -})) +vi.mock('@sim/logger', () => loggerMock) import { db } from '@sim/db' import { refreshOAuthToken } from '@/lib/oauth' @@ -47,14 +42,14 @@ import { refreshTokenIfNeeded, } from '@/app/api/auth/oauth/utils' -const mockDb = db as any +const mockDbTyped = db as any const mockRefreshOAuthToken = refreshOAuthToken as any describe('OAuth Utils', () => { beforeEach(() => { vi.clearAllMocks() mockGetSession.mockResolvedValue(mockSession) - mockDb.limit.mockReturnValue([]) + mockDbTyped.limit.mockReturnValue([]) }) afterEach(() => { @@ -69,14 +64,14 @@ describe('OAuth Utils', () => { }) it('should get user ID from workflow when workflowId is provided', async () => { - mockDb.limit.mockReturnValueOnce([{ userId: 'workflow-owner-id' }]) + mockDbTyped.limit.mockReturnValueOnce([{ userId: 'workflow-owner-id' }]) const userId = await getUserId('request-id', 'workflow-id') - expect(mockDb.select).toHaveBeenCalled() - expect(mockDb.from).toHaveBeenCalled() - expect(mockDb.where).toHaveBeenCalled() - expect(mockDb.limit).toHaveBeenCalledWith(1) + expect(mockDbTyped.select).toHaveBeenCalled() + expect(mockDbTyped.from).toHaveBeenCalled() + expect(mockDbTyped.where).toHaveBeenCalled() + expect(mockDbTyped.limit).toHaveBeenCalledWith(1) expect(userId).toBe('workflow-owner-id') }) @@ -89,7 +84,7 @@ describe('OAuth Utils', () => { }) it('should return undefined if workflow is not found', async () => { - mockDb.limit.mockReturnValueOnce([]) + mockDbTyped.limit.mockReturnValueOnce([]) const userId = await getUserId('request-id', 'nonexistent-workflow-id') @@ -100,20 +95,20 @@ describe('OAuth Utils', () => { describe('getCredential', () => { it('should return credential when found', async () => { const mockCredential = { id: 'credential-id', userId: 'test-user-id' } - mockDb.limit.mockReturnValueOnce([mockCredential]) + mockDbTyped.limit.mockReturnValueOnce([mockCredential]) const credential = await getCredential('request-id', 'credential-id', 'test-user-id') - expect(mockDb.select).toHaveBeenCalled() - expect(mockDb.from).toHaveBeenCalled() - expect(mockDb.where).toHaveBeenCalled() - expect(mockDb.limit).toHaveBeenCalledWith(1) + expect(mockDbTyped.select).toHaveBeenCalled() + expect(mockDbTyped.from).toHaveBeenCalled() + expect(mockDbTyped.where).toHaveBeenCalled() + expect(mockDbTyped.limit).toHaveBeenCalledWith(1) expect(credential).toEqual(mockCredential) }) it('should return undefined when credential is not found', async () => { - mockDb.limit.mockReturnValueOnce([]) + mockDbTyped.limit.mockReturnValueOnce([]) const credential = await getCredential('request-id', 'nonexistent-id', 'test-user-id') @@ -127,7 +122,7 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'valid-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour in the future + accessTokenExpiresAt: new Date(Date.now() + 3600 * 1000), providerId: 'google', } @@ -142,7 +137,7 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'expired-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), // 1 hour in the past + accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), providerId: 'google', } @@ -155,8 +150,8 @@ describe('OAuth Utils', () => { const result = await refreshTokenIfNeeded('request-id', mockCredential, 'credential-id') expect(mockRefreshOAuthToken).toHaveBeenCalledWith('google', 'refresh-token') - expect(mockDb.update).toHaveBeenCalled() - expect(mockDb.set).toHaveBeenCalled() + expect(mockDbTyped.update).toHaveBeenCalled() + expect(mockDbTyped.set).toHaveBeenCalled() expect(result).toEqual({ accessToken: 'new-token', refreshed: true }) }) @@ -165,7 +160,7 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'expired-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), // 1 hour in the past + accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), providerId: 'google', } @@ -181,7 +176,7 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'token', refreshToken: null, - accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), // 1 hour in the past + accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), providerId: 'google', } @@ -198,11 +193,11 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'valid-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour in the future + accessTokenExpiresAt: new Date(Date.now() + 3600 * 1000), providerId: 'google', userId: 'test-user-id', } - mockDb.limit.mockReturnValueOnce([mockCredential]) + mockDbTyped.limit.mockReturnValueOnce([mockCredential]) const token = await refreshAccessTokenIfNeeded('credential-id', 'test-user-id', 'request-id') @@ -215,11 +210,11 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'expired-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), // 1 hour in the past + accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), providerId: 'google', userId: 'test-user-id', } - mockDb.limit.mockReturnValueOnce([mockCredential]) + mockDbTyped.limit.mockReturnValueOnce([mockCredential]) mockRefreshOAuthToken.mockResolvedValueOnce({ accessToken: 'new-token', @@ -230,13 +225,13 @@ describe('OAuth Utils', () => { const token = await refreshAccessTokenIfNeeded('credential-id', 'test-user-id', 'request-id') expect(mockRefreshOAuthToken).toHaveBeenCalledWith('google', 'refresh-token') - expect(mockDb.update).toHaveBeenCalled() - expect(mockDb.set).toHaveBeenCalled() + expect(mockDbTyped.update).toHaveBeenCalled() + expect(mockDbTyped.set).toHaveBeenCalled() expect(token).toBe('new-token') }) it('should return null if credential not found', async () => { - mockDb.limit.mockReturnValueOnce([]) + mockDbTyped.limit.mockReturnValueOnce([]) const token = await refreshAccessTokenIfNeeded('nonexistent-id', 'test-user-id', 'request-id') @@ -248,11 +243,11 @@ describe('OAuth Utils', () => { id: 'credential-id', accessToken: 'expired-token', refreshToken: 'refresh-token', - accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), // 1 hour in the past + accessTokenExpiresAt: new Date(Date.now() - 3600 * 1000), providerId: 'google', userId: 'test-user-id', } - mockDb.limit.mockReturnValueOnce([mockCredential]) + mockDbTyped.limit.mockReturnValueOnce([mockCredential]) mockRefreshOAuthToken.mockResolvedValueOnce(null) diff --git a/apps/sim/app/api/auth/oauth/utils.ts b/apps/sim/app/api/auth/oauth/utils.ts index 85b63961d1..cb9176e989 100644 --- a/apps/sim/app/api/auth/oauth/utils.ts +++ b/apps/sim/app/api/auth/oauth/utils.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { account, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { refreshOAuthToken } from '@/lib/oauth' const logger = createLogger('OAuthUtilsAPI') diff --git a/apps/sim/app/api/auth/oauth/wealthbox/item/route.ts b/apps/sim/app/api/auth/oauth/wealthbox/item/route.ts index 4337033779..61fc0b591d 100644 --- a/apps/sim/app/api/auth/oauth/wealthbox/item/route.ts +++ b/apps/sim/app/api/auth/oauth/wealthbox/item/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateEnum, validatePathSegment } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth/wealthbox/items/route.ts b/apps/sim/app/api/auth/oauth/wealthbox/items/route.ts index 83ba588ba2..e276111762 100644 --- a/apps/sim/app/api/auth/oauth/wealthbox/items/route.ts +++ b/apps/sim/app/api/auth/oauth/wealthbox/items/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/oauth2/callback/shopify/route.ts b/apps/sim/app/api/auth/oauth2/callback/shopify/route.ts index f1e0e24e42..b58fe329c7 100644 --- a/apps/sim/app/api/auth/oauth2/callback/shopify/route.ts +++ b/apps/sim/app/api/auth/oauth2/callback/shopify/route.ts @@ -1,9 +1,9 @@ import crypto from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ShopifyCallback') diff --git a/apps/sim/app/api/auth/oauth2/shopify/store/route.ts b/apps/sim/app/api/auth/oauth2/shopify/store/route.ts index ddc70d7d13..cf7aef92a6 100644 --- a/apps/sim/app/api/auth/oauth2/shopify/store/route.ts +++ b/apps/sim/app/api/auth/oauth2/shopify/store/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { safeAccountInsert } from '@/app/api/auth/oauth/utils' const logger = createLogger('ShopifyStore') diff --git a/apps/sim/app/api/auth/reset-password/route.test.ts b/apps/sim/app/api/auth/reset-password/route.test.ts index 58b9666448..9c9f2df5f9 100644 --- a/apps/sim/app/api/auth/reset-password/route.test.ts +++ b/apps/sim/app/api/auth/reset-password/route.test.ts @@ -148,7 +148,7 @@ describe('Reset Password API Route', () => { expect(response.status).toBe(500) expect(data.message).toBe(errorMessage) - const logger = await import('@/lib/logs/console/logger') + const logger = await import('@sim/logger') const mockLogger = logger.createLogger('PasswordResetAPI') expect(mockLogger.error).toHaveBeenCalledWith('Error during password reset:', { error: expect.any(Error), @@ -181,7 +181,7 @@ describe('Reset Password API Route', () => { 'Failed to reset password. Please try again or request a new reset link.' ) - const logger = await import('@/lib/logs/console/logger') + const logger = await import('@sim/logger') const mockLogger = logger.createLogger('PasswordResetAPI') expect(mockLogger.error).toHaveBeenCalled() }) diff --git a/apps/sim/app/api/auth/reset-password/route.ts b/apps/sim/app/api/auth/reset-password/route.ts index 6d3fe9197f..0caa1494f2 100644 --- a/apps/sim/app/api/auth/reset-password/route.ts +++ b/apps/sim/app/api/auth/reset-password/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { auth } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/auth/shopify/authorize/route.ts b/apps/sim/app/api/auth/shopify/authorize/route.ts index aa06b2c7b4..daa6dfecf0 100644 --- a/apps/sim/app/api/auth/shopify/authorize/route.ts +++ b/apps/sim/app/api/auth/shopify/authorize/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ShopifyAuthorize') diff --git a/apps/sim/app/api/auth/sso/providers/route.ts b/apps/sim/app/api/auth/sso/providers/route.ts index 09e07de745..f35f25ee2a 100644 --- a/apps/sim/app/api/auth/sso/providers/route.ts +++ b/apps/sim/app/api/auth/sso/providers/route.ts @@ -1,8 +1,8 @@ import { db, ssoProvider } from '@sim/db' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('SSO-Providers') diff --git a/apps/sim/app/api/auth/sso/register/route.ts b/apps/sim/app/api/auth/sso/register/route.ts index 185273dbb8..2743842136 100644 --- a/apps/sim/app/api/auth/sso/register/route.ts +++ b/apps/sim/app/api/auth/sso/register/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { auth } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { REDACTED_MARKER } from '@/lib/core/security/redaction' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('SSO-Register') diff --git a/apps/sim/app/api/auth/trello/authorize/route.ts b/apps/sim/app/api/auth/trello/authorize/route.ts index d3c05f9137..d5e23abf03 100644 --- a/apps/sim/app/api/auth/trello/authorize/route.ts +++ b/apps/sim/app/api/auth/trello/authorize/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('TrelloAuthorize') diff --git a/apps/sim/app/api/auth/trello/store/route.ts b/apps/sim/app/api/auth/trello/store/route.ts index 081e03d6a5..fff52b0a84 100644 --- a/apps/sim/app/api/auth/trello/store/route.ts +++ b/apps/sim/app/api/auth/trello/store/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { safeAccountInsert } from '@/app/api/auth/oauth/utils' import { db } from '@/../../packages/db' import { account } from '@/../../packages/db/schema' diff --git a/apps/sim/app/api/billing/credits/route.ts b/apps/sim/app/api/billing/credits/route.ts index 31d9089f5e..9a87e8c928 100644 --- a/apps/sim/app/api/billing/credits/route.ts +++ b/apps/sim/app/api/billing/credits/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { getCreditBalance } from '@/lib/billing/credits/balance' import { purchaseCredits } from '@/lib/billing/credits/purchase' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CreditsAPI') diff --git a/apps/sim/app/api/billing/portal/route.ts b/apps/sim/app/api/billing/portal/route.ts index c68b24e669..eb1a860550 100644 --- a/apps/sim/app/api/billing/portal/route.ts +++ b/apps/sim/app/api/billing/portal/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { subscription as subscriptionTable, user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { requireStripeClient } from '@/lib/billing/stripe-client' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('BillingPortal') diff --git a/apps/sim/app/api/billing/route.ts b/apps/sim/app/api/billing/route.ts index 33e1559afa..5ccf2dfd4f 100644 --- a/apps/sim/app/api/billing/route.ts +++ b/apps/sim/app/api/billing/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { member, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { getSimplifiedBillingSummary } from '@/lib/billing/core/billing' import { getOrganizationBillingData } from '@/lib/billing/core/organization' -import { createLogger } from '@/lib/logs/console/logger' /** * Gets the effective billing blocked status for a user. @@ -93,6 +93,7 @@ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const context = searchParams.get('context') || 'user' const contextId = searchParams.get('id') + const includeOrg = searchParams.get('includeOrg') === 'true' // Validate context parameter if (!['user', 'organization'].includes(context)) { @@ -115,14 +116,38 @@ export async function GET(request: NextRequest) { if (context === 'user') { // Get user billing (may include organization if they're part of one) billingData = await getSimplifiedBillingSummary(session.user.id, contextId || undefined) + // Attach effective billing blocked status (includes org owner check) const billingStatus = await getEffectiveBillingStatus(session.user.id) + billingData = { ...billingData, billingBlocked: billingStatus.billingBlocked, billingBlockedReason: billingStatus.billingBlockedReason, blockedByOrgOwner: billingStatus.blockedByOrgOwner, } + + // Optionally include organization membership and role + if (includeOrg) { + const userMembership = await db + .select({ + organizationId: member.organizationId, + role: member.role, + }) + .from(member) + .where(eq(member.userId, session.user.id)) + .limit(1) + + if (userMembership.length > 0) { + billingData = { + ...billingData, + organization: { + id: userMembership[0].organizationId, + role: userMembership[0].role as 'owner' | 'admin' | 'member', + }, + } + } + } } else { // Get user role in organization for permission checks first const memberRecord = await db diff --git a/apps/sim/app/api/billing/update-cost/route.ts b/apps/sim/app/api/billing/update-cost/route.ts index 4882f194d9..3e8e0a289d 100644 --- a/apps/sim/app/api/billing/update-cost/route.ts +++ b/apps/sim/app/api/billing/update-cost/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -8,7 +9,6 @@ import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing' import { checkInternalApiKey } from '@/lib/copilot/utils' import { isBillingEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('BillingUpdateCostAPI') diff --git a/apps/sim/app/api/careers/submit/route.ts b/apps/sim/app/api/careers/submit/route.ts index 5ed4b418ce..0d6e0646d7 100644 --- a/apps/sim/app/api/careers/submit/route.ts +++ b/apps/sim/app/api/careers/submit/route.ts @@ -1,10 +1,10 @@ import { render } from '@react-email/components' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import CareersConfirmationEmail from '@/components/emails/careers/careers-confirmation-email' import CareersSubmissionEmail from '@/components/emails/careers/careers-submission-email' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/chat/[identifier]/otp/route.test.ts b/apps/sim/app/api/chat/[identifier]/otp/route.test.ts new file mode 100644 index 0000000000..24526a80d6 --- /dev/null +++ b/apps/sim/app/api/chat/[identifier]/otp/route.test.ts @@ -0,0 +1,550 @@ +/** + * Tests for chat OTP API route + * + * @vitest-environment node + */ +import { NextRequest } from 'next/server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +describe('Chat OTP API Route', () => { + const mockEmail = 'test@example.com' + const mockChatId = 'chat-123' + const mockIdentifier = 'test-chat' + const mockOTP = '123456' + + const mockRedisSet = vi.fn() + const mockRedisGet = vi.fn() + const mockRedisDel = vi.fn() + const mockGetRedisClient = vi.fn() + + const mockDbSelect = vi.fn() + const mockDbInsert = vi.fn() + const mockDbDelete = vi.fn() + + const mockSendEmail = vi.fn() + const mockRenderOTPEmail = vi.fn() + const mockAddCorsHeaders = vi.fn() + const mockCreateSuccessResponse = vi.fn() + const mockCreateErrorResponse = vi.fn() + const mockSetChatAuthCookie = vi.fn() + const mockGenerateRequestId = vi.fn() + + let storageMethod: 'redis' | 'database' = 'redis' + + beforeEach(() => { + vi.resetModules() + vi.clearAllMocks() + + vi.spyOn(Math, 'random').mockReturnValue(0.123456) + vi.spyOn(Date, 'now').mockReturnValue(1640995200000) + + vi.stubGlobal('crypto', { + ...crypto, + randomUUID: vi.fn().mockReturnValue('test-uuid-1234'), + }) + + const mockRedisClient = { + set: mockRedisSet, + get: mockRedisGet, + del: mockRedisDel, + } + mockGetRedisClient.mockReturnValue(mockRedisClient) + mockRedisSet.mockResolvedValue('OK') + mockRedisGet.mockResolvedValue(null) + mockRedisDel.mockResolvedValue(1) + + vi.doMock('@/lib/core/config/redis', () => ({ + getRedisClient: mockGetRedisClient, + })) + + const createDbChain = (result: any) => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue(result), + }), + }), + }) + + mockDbSelect.mockImplementation(() => createDbChain([])) + mockDbInsert.mockImplementation(() => ({ + values: vi.fn().mockResolvedValue(undefined), + })) + mockDbDelete.mockImplementation(() => ({ + where: vi.fn().mockResolvedValue(undefined), + })) + + vi.doMock('@sim/db', () => ({ + db: { + select: mockDbSelect, + insert: mockDbInsert, + delete: mockDbDelete, + transaction: vi.fn(async (callback) => { + return callback({ + select: mockDbSelect, + insert: mockDbInsert, + delete: mockDbDelete, + }) + }), + }, + })) + + vi.doMock('@sim/db/schema', () => ({ + chat: { + id: 'id', + authType: 'authType', + allowedEmails: 'allowedEmails', + title: 'title', + }, + verification: { + id: 'id', + identifier: 'identifier', + value: 'value', + expiresAt: 'expiresAt', + createdAt: 'createdAt', + updatedAt: 'updatedAt', + }, + })) + + vi.doMock('drizzle-orm', () => ({ + eq: vi.fn((field, value) => ({ field, value, type: 'eq' })), + and: vi.fn((...conditions) => ({ conditions, type: 'and' })), + gt: vi.fn((field, value) => ({ field, value, type: 'gt' })), + lt: vi.fn((field, value) => ({ field, value, type: 'lt' })), + })) + + vi.doMock('@/lib/core/storage', () => ({ + getStorageMethod: vi.fn(() => storageMethod), + })) + + mockSendEmail.mockResolvedValue({ success: true }) + mockRenderOTPEmail.mockResolvedValue('OTP Email') + + vi.doMock('@/lib/messaging/email/mailer', () => ({ + sendEmail: mockSendEmail, + })) + + vi.doMock('@/components/emails/render-email', () => ({ + renderOTPEmail: mockRenderOTPEmail, + })) + + mockAddCorsHeaders.mockImplementation((response) => response) + mockCreateSuccessResponse.mockImplementation((data) => ({ + json: () => Promise.resolve(data), + status: 200, + })) + mockCreateErrorResponse.mockImplementation((message, status) => ({ + json: () => Promise.resolve({ error: message }), + status, + })) + + vi.doMock('@/app/api/chat/utils', () => ({ + addCorsHeaders: mockAddCorsHeaders, + setChatAuthCookie: mockSetChatAuthCookie, + })) + + vi.doMock('@/app/api/workflows/utils', () => ({ + createSuccessResponse: mockCreateSuccessResponse, + createErrorResponse: mockCreateErrorResponse, + })) + + vi.doMock('@sim/logger', () => ({ + createLogger: vi.fn().mockReturnValue({ + info: vi.fn(), + error: vi.fn(), + warn: vi.fn(), + debug: vi.fn(), + }), + })) + + vi.doMock('zod', () => ({ + z: { + object: vi.fn().mockReturnValue({ + parse: vi.fn().mockImplementation((data) => data), + }), + string: vi.fn().mockReturnValue({ + email: vi.fn().mockReturnThis(), + length: vi.fn().mockReturnThis(), + }), + }, + })) + + mockGenerateRequestId.mockReturnValue('req-123') + vi.doMock('@/lib/core/utils/request', () => ({ + generateRequestId: mockGenerateRequestId, + })) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + describe('POST - Store OTP (Redis path)', () => { + beforeEach(() => { + storageMethod = 'redis' + }) + + it('should store OTP in Redis when storage method is redis', async () => { + const { POST } = await import('./route') + + mockDbSelect.mockImplementationOnce(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'email', + allowedEmails: [mockEmail], + title: 'Test Chat', + }, + ]), + }), + }), + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'POST', + body: JSON.stringify({ email: mockEmail }), + }) + + await POST(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockRedisSet).toHaveBeenCalledWith( + `otp:${mockEmail}:${mockChatId}`, + expect.any(String), + 'EX', + 900 // 15 minutes + ) + + expect(mockDbInsert).not.toHaveBeenCalled() + }) + }) + + describe('POST - Store OTP (Database path)', () => { + beforeEach(() => { + storageMethod = 'database' + mockGetRedisClient.mockReturnValue(null) + }) + + it('should store OTP in database when storage method is database', async () => { + const { POST } = await import('./route') + + mockDbSelect.mockImplementationOnce(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'email', + allowedEmails: [mockEmail], + title: 'Test Chat', + }, + ]), + }), + }), + })) + + const mockInsertValues = vi.fn().mockResolvedValue(undefined) + mockDbInsert.mockImplementationOnce(() => ({ + values: mockInsertValues, + })) + + const mockDeleteWhere = vi.fn().mockResolvedValue(undefined) + mockDbDelete.mockImplementation(() => ({ + where: mockDeleteWhere, + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'POST', + body: JSON.stringify({ email: mockEmail }), + }) + + await POST(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockDbDelete).toHaveBeenCalled() + + expect(mockDbInsert).toHaveBeenCalled() + expect(mockInsertValues).toHaveBeenCalledWith({ + id: expect.any(String), + identifier: `chat-otp:${mockChatId}:${mockEmail}`, + value: expect.any(String), + expiresAt: expect.any(Date), + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + }) + + expect(mockRedisSet).not.toHaveBeenCalled() + }) + }) + + describe('PUT - Verify OTP (Redis path)', () => { + beforeEach(() => { + storageMethod = 'redis' + mockRedisGet.mockResolvedValue(mockOTP) + }) + + it('should retrieve OTP from Redis and verify successfully', async () => { + const { PUT } = await import('./route') + + mockDbSelect.mockImplementationOnce(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'email', + }, + ]), + }), + }), + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockRedisGet).toHaveBeenCalledWith(`otp:${mockEmail}:${mockChatId}`) + + expect(mockRedisDel).toHaveBeenCalledWith(`otp:${mockEmail}:${mockChatId}`) + + expect(mockDbSelect).toHaveBeenCalledTimes(1) + }) + }) + + describe('PUT - Verify OTP (Database path)', () => { + beforeEach(() => { + storageMethod = 'database' + mockGetRedisClient.mockReturnValue(null) + }) + + it('should retrieve OTP from database and verify successfully', async () => { + const { PUT } = await import('./route') + + let selectCallCount = 0 + + mockDbSelect.mockImplementation(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockImplementation(() => { + selectCallCount++ + if (selectCallCount === 1) { + return Promise.resolve([ + { + id: mockChatId, + authType: 'email', + }, + ]) + } + return Promise.resolve([ + { + value: mockOTP, + expiresAt: new Date(Date.now() + 10 * 60 * 1000), + }, + ]) + }), + }), + }), + })) + + const mockDeleteWhere = vi.fn().mockResolvedValue(undefined) + mockDbDelete.mockImplementation(() => ({ + where: mockDeleteWhere, + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockDbSelect).toHaveBeenCalledTimes(2) + + expect(mockDbDelete).toHaveBeenCalled() + + expect(mockRedisGet).not.toHaveBeenCalled() + }) + + it('should reject expired OTP from database', async () => { + const { PUT } = await import('./route') + + let selectCallCount = 0 + + mockDbSelect.mockImplementation(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockImplementation(() => { + selectCallCount++ + if (selectCallCount === 1) { + return Promise.resolve([ + { + id: mockChatId, + authType: 'email', + }, + ]) + } + return Promise.resolve([]) + }), + }), + }), + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockCreateErrorResponse).toHaveBeenCalledWith( + 'No verification code found, request a new one', + 400 + ) + }) + }) + + describe('DELETE OTP (Redis path)', () => { + beforeEach(() => { + storageMethod = 'redis' + }) + + it('should delete OTP from Redis after verification', async () => { + const { PUT } = await import('./route') + + mockRedisGet.mockResolvedValue(mockOTP) + + mockDbSelect.mockImplementationOnce(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'email', + }, + ]), + }), + }), + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockRedisDel).toHaveBeenCalledWith(`otp:${mockEmail}:${mockChatId}`) + expect(mockDbDelete).not.toHaveBeenCalled() + }) + }) + + describe('DELETE OTP (Database path)', () => { + beforeEach(() => { + storageMethod = 'database' + mockGetRedisClient.mockReturnValue(null) + }) + + it('should delete OTP from database after verification', async () => { + const { PUT } = await import('./route') + + let selectCallCount = 0 + mockDbSelect.mockImplementation(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockImplementation(() => { + selectCallCount++ + if (selectCallCount === 1) { + return Promise.resolve([{ id: mockChatId, authType: 'email' }]) + } + return Promise.resolve([ + { value: mockOTP, expiresAt: new Date(Date.now() + 10 * 60 * 1000) }, + ]) + }), + }), + }), + })) + + const mockDeleteWhere = vi.fn().mockResolvedValue(undefined) + mockDbDelete.mockImplementation(() => ({ + where: mockDeleteWhere, + })) + + const request = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUT(request, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockDbDelete).toHaveBeenCalled() + expect(mockRedisDel).not.toHaveBeenCalled() + }) + }) + + describe('Behavior consistency between Redis and Database', () => { + it('should have same behavior for missing OTP in both storage methods', async () => { + storageMethod = 'redis' + mockRedisGet.mockResolvedValue(null) + + const { PUT: PUTRedis } = await import('./route') + + mockDbSelect.mockImplementation(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([{ id: mockChatId, authType: 'email' }]), + }), + }), + })) + + const requestRedis = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'PUT', + body: JSON.stringify({ email: mockEmail, otp: mockOTP }), + }) + + await PUTRedis(requestRedis, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockCreateErrorResponse).toHaveBeenCalledWith( + 'No verification code found, request a new one', + 400 + ) + }) + + it('should have same OTP expiry time in both storage methods', async () => { + const OTP_EXPIRY = 15 * 60 + + storageMethod = 'redis' + const { POST: POSTRedis } = await import('./route') + + mockDbSelect.mockImplementation(() => ({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue([ + { + id: mockChatId, + authType: 'email', + allowedEmails: [mockEmail], + title: 'Test Chat', + }, + ]), + }), + }), + })) + + const requestRedis = new NextRequest('http://localhost:3000/api/chat/test/otp', { + method: 'POST', + body: JSON.stringify({ email: mockEmail }), + }) + + await POSTRedis(requestRedis, { params: Promise.resolve({ identifier: mockIdentifier }) }) + + expect(mockRedisSet).toHaveBeenCalledWith( + expect.any(String), + expect.any(String), + 'EX', + OTP_EXPIRY + ) + }) + }) +}) diff --git a/apps/sim/app/api/chat/[identifier]/otp/route.ts b/apps/sim/app/api/chat/[identifier]/otp/route.ts index dd400b5905..52948e2bfc 100644 --- a/apps/sim/app/api/chat/[identifier]/otp/route.ts +++ b/apps/sim/app/api/chat/[identifier]/otp/route.ts @@ -1,13 +1,14 @@ +import { randomUUID } from 'crypto' import { db } from '@sim/db' -import { chat } from '@sim/db/schema' -import { eq } from 'drizzle-orm' +import { chat, verification } from '@sim/db/schema' +import { createLogger } from '@sim/logger' +import { and, eq, gt } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { z } from 'zod' import { renderOTPEmail } from '@/components/emails/render-email' import { getRedisClient } from '@/lib/core/config/redis' import { getStorageMethod } from '@/lib/core/storage' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { addCorsHeaders, setChatAuthCookie } from '@/app/api/chat/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' @@ -22,24 +23,11 @@ const OTP_EXPIRY = 15 * 60 // 15 minutes const OTP_EXPIRY_MS = OTP_EXPIRY * 1000 /** - * In-memory OTP storage for single-instance deployments without Redis. - * Only used when REDIS_URL is not configured (determined once at startup). - * - * Warning: This does NOT work in multi-instance/serverless deployments. + * Stores OTP in Redis or database depending on storage method. + * Uses the verification table for database storage. */ -const inMemoryOTPStore = new Map() - -function cleanupExpiredOTPs() { - const now = Date.now() - for (const [key, value] of inMemoryOTPStore.entries()) { - if (value.expiresAt < now) { - inMemoryOTPStore.delete(key) - } - } -} - async function storeOTP(email: string, chatId: string, otp: string): Promise { - const key = `otp:${email}:${chatId}` + const identifier = `chat-otp:${chatId}:${email}` const storageMethod = getStorageMethod() if (storageMethod === 'redis') { @@ -47,18 +35,28 @@ async function storeOTP(email: string, chatId: string, otp: string): Promise { + await tx.delete(verification).where(eq(verification.identifier, identifier)) + await tx.insert(verification).values({ + id: randomUUID(), + identifier, + value: otp, + expiresAt, + createdAt: now, + updatedAt: now, + }) }) } } async function getOTP(email: string, chatId: string): Promise { - const key = `otp:${email}:${chatId}` + const identifier = `chat-otp:${chatId}:${email}` const storageMethod = getStorageMethod() if (storageMethod === 'redis') { @@ -66,22 +64,27 @@ async function getOTP(email: string, chatId: string): Promise { if (!redis) { throw new Error('Redis configured but client unavailable') } + const key = `otp:${email}:${chatId}` return redis.get(key) } - const entry = inMemoryOTPStore.get(key) - if (!entry) return null + const now = new Date() + const [record] = await db + .select({ + value: verification.value, + expiresAt: verification.expiresAt, + }) + .from(verification) + .where(and(eq(verification.identifier, identifier), gt(verification.expiresAt, now))) + .limit(1) - if (entry.expiresAt < Date.now()) { - inMemoryOTPStore.delete(key) - return null - } + if (!record) return null - return entry.otp + return record.value } async function deleteOTP(email: string, chatId: string): Promise { - const key = `otp:${email}:${chatId}` + const identifier = `chat-otp:${chatId}:${email}` const storageMethod = getStorageMethod() if (storageMethod === 'redis') { @@ -89,9 +92,10 @@ async function deleteOTP(email: string, chatId: string): Promise { if (!redis) { throw new Error('Redis configured but client unavailable') } + const key = `otp:${email}:${chatId}` await redis.del(key) } else { - inMemoryOTPStore.delete(key) + await db.delete(verification).where(eq(verification.identifier, identifier)) } } diff --git a/apps/sim/app/api/chat/[identifier]/route.test.ts b/apps/sim/app/api/chat/[identifier]/route.test.ts index f23f6cad4e..b63aa797f6 100644 --- a/apps/sim/app/api/chat/[identifier]/route.test.ts +++ b/apps/sim/app/api/chat/[identifier]/route.test.ts @@ -120,7 +120,7 @@ describe('Chat Identifier API Route', () => { validateAuthToken: vi.fn().mockReturnValue(true), })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue({ debug: vi.fn(), info: vi.fn(), diff --git a/apps/sim/app/api/chat/[identifier]/route.ts b/apps/sim/app/api/chat/[identifier]/route.ts index ad3cb48746..5754d38b24 100644 --- a/apps/sim/app/api/chat/[identifier]/route.ts +++ b/apps/sim/app/api/chat/[identifier]/route.ts @@ -1,12 +1,12 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { chat, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' import { preprocessExecution } from '@/lib/execution/preprocessing' -import { createLogger } from '@/lib/logs/console/logger' import { LoggingSession } from '@/lib/logs/execution/logging-session' import { ChatFiles } from '@/lib/uploads' import { diff --git a/apps/sim/app/api/chat/manage/[id]/route.test.ts b/apps/sim/app/api/chat/manage/[id]/route.test.ts index 6e1d445ba9..1be5f483b2 100644 --- a/apps/sim/app/api/chat/manage/[id]/route.test.ts +++ b/apps/sim/app/api/chat/manage/[id]/route.test.ts @@ -50,7 +50,7 @@ describe('Chat Edit API Route', () => { chat: { id: 'id', identifier: 'identifier', userId: 'userId' }, })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue({ info: vi.fn(), error: vi.fn(), diff --git a/apps/sim/app/api/chat/manage/[id]/route.ts b/apps/sim/app/api/chat/manage/[id]/route.ts index d7141aa2e0..236ae10015 100644 --- a/apps/sim/app/api/chat/manage/[id]/route.ts +++ b/apps/sim/app/api/chat/manage/[id]/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { z } from 'zod' @@ -7,7 +8,6 @@ import { getSession } from '@/lib/auth' import { isDev } from '@/lib/core/config/feature-flags' import { encryptSecret } from '@/lib/core/security/encryption' import { getEmailDomain } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { deployWorkflow } from '@/lib/workflows/persistence/utils' import { checkChatAccess } from '@/app/api/chat/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/chat/route.test.ts b/apps/sim/app/api/chat/route.test.ts index 7156c80c72..0eb6288834 100644 --- a/apps/sim/app/api/chat/route.test.ts +++ b/apps/sim/app/api/chat/route.test.ts @@ -42,7 +42,7 @@ describe('Chat API Route', () => { workflow: { id: 'id', userId: 'userId', isDeployed: 'isDeployed' }, })) - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue({ info: vi.fn(), error: vi.fn(), diff --git a/apps/sim/app/api/chat/route.ts b/apps/sim/app/api/chat/route.ts index 3a49f32cf0..dd736b6529 100644 --- a/apps/sim/app/api/chat/route.ts +++ b/apps/sim/app/api/chat/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { v4 as uuidv4 } from 'uuid' @@ -8,7 +9,6 @@ import { getSession } from '@/lib/auth' import { isDev } from '@/lib/core/config/feature-flags' import { encryptSecret } from '@/lib/core/security/encryption' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { deployWorkflow } from '@/lib/workflows/persistence/utils' import { checkWorkflowAccessForChatCreation } from '@/app/api/chat/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/chat/utils.test.ts b/apps/sim/app/api/chat/utils.test.ts index 188c03b110..70d92990b4 100644 --- a/apps/sim/app/api/chat/utils.test.ts +++ b/apps/sim/app/api/chat/utils.test.ts @@ -52,7 +52,7 @@ vi.mock('@/lib/core/config/feature-flags', () => ({ describe('Chat API Utils', () => { beforeEach(() => { - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue({ info: vi.fn(), error: vi.fn(), diff --git a/apps/sim/app/api/chat/utils.ts b/apps/sim/app/api/chat/utils.ts index 94cc1ec300..712886a2ff 100644 --- a/apps/sim/app/api/chat/utils.ts +++ b/apps/sim/app/api/chat/utils.ts @@ -1,11 +1,11 @@ import { createHash } from 'crypto' import { db } from '@sim/db' import { chat, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest, NextResponse } from 'next/server' import { isDev } from '@/lib/core/config/feature-flags' import { decryptSecret } from '@/lib/core/security/encryption' -import { createLogger } from '@/lib/logs/console/logger' import { hasAdminPermission } from '@/lib/workspaces/permissions/utils' const logger = createLogger('ChatAuthUtils') diff --git a/apps/sim/app/api/chat/validate/route.ts b/apps/sim/app/api/chat/validate/route.ts index 0cfd91f7f5..0aecbd66f0 100644 --- a/apps/sim/app/api/chat/validate/route.ts +++ b/apps/sim/app/api/chat/validate/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' const logger = createLogger('ChatValidateAPI') diff --git a/apps/sim/app/api/copilot/api-keys/route.test.ts b/apps/sim/app/api/copilot/api-keys/route.test.ts new file mode 100644 index 0000000000..2556a7e93a --- /dev/null +++ b/apps/sim/app/api/copilot/api-keys/route.test.ts @@ -0,0 +1,361 @@ +/** + * Tests for copilot api-keys API route + * + * @vitest-environment node + */ +import { NextRequest } from 'next/server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { mockAuth, mockCryptoUuid, setupCommonApiMocks } from '@/app/api/__test-utils__/utils' + +describe('Copilot API Keys API Route', () => { + const mockFetch = vi.fn() + + beforeEach(() => { + vi.resetModules() + setupCommonApiMocks() + mockCryptoUuid() + + global.fetch = mockFetch + + vi.doMock('@/lib/copilot/constants', () => ({ + SIM_AGENT_API_URL_DEFAULT: 'https://agent.sim.example.com', + })) + + vi.doMock('@/lib/core/config/env', () => ({ + env: { + SIM_AGENT_API_URL: null, + COPILOT_API_KEY: 'test-api-key', + }, + })) + }) + + afterEach(() => { + vi.clearAllMocks() + vi.restoreAllMocks() + }) + + describe('GET', () => { + it('should return 401 when user is not authenticated', async () => { + const authMocks = mockAuth() + authMocks.setUnauthenticated() + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should return list of API keys with masked values', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const mockApiKeys = [ + { + id: 'key-1', + apiKey: 'sk-sim-abcdefghijklmnopqrstuv', + name: 'Production Key', + createdAt: '2024-01-01T00:00:00.000Z', + lastUsed: '2024-01-15T00:00:00.000Z', + }, + { + id: 'key-2', + apiKey: 'sk-sim-zyxwvutsrqponmlkjihgfe', + name: null, + createdAt: '2024-01-02T00:00:00.000Z', + lastUsed: null, + }, + ] + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(mockApiKeys), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.keys).toHaveLength(2) + expect(responseData.keys[0].id).toBe('key-1') + expect(responseData.keys[0].displayKey).toBe('•••••qrstuv') + expect(responseData.keys[0].name).toBe('Production Key') + expect(responseData.keys[1].displayKey).toBe('•••••jihgfe') + expect(responseData.keys[1].name).toBeNull() + }) + + it('should return empty array when user has no API keys', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve([]), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.keys).toEqual([]) + }) + + it('should forward userId to Sim Agent', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve([]), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + await GET(request) + + expect(mockFetch).toHaveBeenCalledWith( + 'https://agent.sim.example.com/api/validate-key/get-api-keys', + expect.objectContaining({ + method: 'POST', + headers: expect.objectContaining({ + 'Content-Type': 'application/json', + 'x-api-key': 'test-api-key', + }), + body: JSON.stringify({ userId: 'user-123' }), + }) + ) + }) + + it('should return error when Sim Agent returns non-ok response', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 503, + json: () => Promise.resolve({ error: 'Service unavailable' }), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(503) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Failed to get keys' }) + }) + + it('should return 500 when Sim Agent returns invalid response', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ invalid: 'response' }), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Invalid response from Sim Agent' }) + }) + + it('should handle network errors gracefully', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Failed to get keys' }) + }) + + it('should handle API keys with empty apiKey string', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const mockApiKeys = [ + { + id: 'key-1', + apiKey: '', + name: 'Empty Key', + createdAt: '2024-01-01T00:00:00.000Z', + lastUsed: null, + }, + ] + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(mockApiKeys), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.keys[0].displayKey).toBe('•••••') + }) + + it('should handle JSON parsing errors from Sim Agent', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.reject(new Error('Invalid JSON')), + }) + + const { GET } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await GET(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Invalid response from Sim Agent' }) + }) + }) + + describe('DELETE', () => { + it('should return 401 when user is not authenticated', async () => { + const authMocks = mockAuth() + authMocks.setUnauthenticated() + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=key-123') + const response = await DELETE(request) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should return 400 when id parameter is missing', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys') + const response = await DELETE(request) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'id is required' }) + }) + + it('should successfully delete an API key', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ success: true }), + }) + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=key-123') + const response = await DELETE(request) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData).toEqual({ success: true }) + + expect(mockFetch).toHaveBeenCalledWith( + 'https://agent.sim.example.com/api/validate-key/delete', + expect.objectContaining({ + method: 'POST', + headers: expect.objectContaining({ + 'Content-Type': 'application/json', + 'x-api-key': 'test-api-key', + }), + body: JSON.stringify({ userId: 'user-123', apiKeyId: 'key-123' }), + }) + ) + }) + + it('should return error when Sim Agent returns non-ok response', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: false, + status: 404, + json: () => Promise.resolve({ error: 'Key not found' }), + }) + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=non-existent') + const response = await DELETE(request) + + expect(response.status).toBe(404) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Failed to delete key' }) + }) + + it('should return 500 when Sim Agent returns invalid response', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ success: false }), + }) + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=key-123') + const response = await DELETE(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Invalid response from Sim Agent' }) + }) + + it('should handle network errors gracefully', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=key-123') + const response = await DELETE(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Failed to delete key' }) + }) + + it('should handle JSON parsing errors from Sim Agent on delete', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.reject(new Error('Invalid JSON')), + }) + + const { DELETE } = await import('@/app/api/copilot/api-keys/route') + const request = new NextRequest('http://localhost:3000/api/copilot/api-keys?id=key-123') + const response = await DELETE(request) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Invalid response from Sim Agent' }) + }) + }) +}) diff --git a/apps/sim/app/api/copilot/api-keys/validate/route.ts b/apps/sim/app/api/copilot/api-keys/validate/route.ts index b0204aef8a..77521f3b3e 100644 --- a/apps/sim/app/api/copilot/api-keys/validate/route.ts +++ b/apps/sim/app/api/copilot/api-keys/validate/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkServerSideUsageLimits } from '@/lib/billing/calculations/usage-monitor' import { checkInternalApiKey } from '@/lib/copilot/utils' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotApiKeysValidate') diff --git a/apps/sim/app/api/copilot/auto-allowed-tools/route.ts b/apps/sim/app/api/copilot/auto-allowed-tools/route.ts index 13a2d2e9e7..ecf6aa7f76 100644 --- a/apps/sim/app/api/copilot/auto-allowed-tools/route.ts +++ b/apps/sim/app/api/copilot/auto-allowed-tools/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { settings } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotAutoAllowedToolsAPI') diff --git a/apps/sim/app/api/copilot/chat/delete/route.test.ts b/apps/sim/app/api/copilot/chat/delete/route.test.ts new file mode 100644 index 0000000000..af36cfb5e0 --- /dev/null +++ b/apps/sim/app/api/copilot/chat/delete/route.test.ts @@ -0,0 +1,189 @@ +/** + * Tests for copilot chat delete API route + * + * @vitest-environment node + */ +import { NextRequest } from 'next/server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMockRequest, + mockAuth, + mockCryptoUuid, + setupCommonApiMocks, +} from '@/app/api/__test-utils__/utils' + +describe('Copilot Chat Delete API Route', () => { + const mockDelete = vi.fn() + const mockWhere = vi.fn() + + beforeEach(() => { + vi.resetModules() + setupCommonApiMocks() + mockCryptoUuid() + + mockDelete.mockReturnValue({ where: mockWhere }) + mockWhere.mockResolvedValue([]) + + vi.doMock('@sim/db', () => ({ + db: { + delete: mockDelete, + }, + })) + + vi.doMock('@sim/db/schema', () => ({ + copilotChats: { + id: 'id', + userId: 'userId', + }, + })) + + vi.doMock('drizzle-orm', () => ({ + eq: vi.fn((field, value) => ({ field, value, type: 'eq' })), + })) + }) + + afterEach(() => { + vi.clearAllMocks() + vi.restoreAllMocks() + }) + + describe('DELETE', () => { + it('should return 401 when user is not authenticated', async () => { + const authMocks = mockAuth() + authMocks.setUnauthenticated() + + const req = createMockRequest('DELETE', { + chatId: 'chat-123', + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ success: false, error: 'Unauthorized' }) + }) + + it('should successfully delete a chat', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockWhere.mockResolvedValueOnce([{ id: 'chat-123' }]) + + const req = createMockRequest('DELETE', { + chatId: 'chat-123', + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData).toEqual({ success: true }) + + expect(mockDelete).toHaveBeenCalled() + expect(mockWhere).toHaveBeenCalled() + }) + + it('should return 500 for invalid request body - missing chatId', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const req = createMockRequest('DELETE', {}) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to delete chat') + }) + + it('should return 500 for invalid request body - chatId is not a string', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const req = createMockRequest('DELETE', { + chatId: 12345, + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to delete chat') + }) + + it('should handle database errors gracefully', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockWhere.mockRejectedValueOnce(new Error('Database connection failed')) + + const req = createMockRequest('DELETE', { + chatId: 'chat-123', + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData).toEqual({ success: false, error: 'Failed to delete chat' }) + }) + + it('should handle JSON parsing errors in request body', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const req = new NextRequest('http://localhost:3000/api/copilot/chat/delete', { + method: 'DELETE', + body: '{invalid-json', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to delete chat') + }) + + it('should delete chat even if it does not exist (idempotent)', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + mockWhere.mockResolvedValueOnce([]) + + const req = createMockRequest('DELETE', { + chatId: 'non-existent-chat', + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData).toEqual({ success: true }) + }) + + it('should delete chat with empty string chatId (validation should fail)', async () => { + const authMocks = mockAuth() + authMocks.setAuthenticated() + + const req = createMockRequest('DELETE', { + chatId: '', + }) + + const { DELETE } = await import('@/app/api/copilot/chat/delete/route') + const response = await DELETE(req) + + expect(response.status).toBe(200) + expect(mockDelete).toHaveBeenCalled() + }) + }) +}) diff --git a/apps/sim/app/api/copilot/chat/delete/route.ts b/apps/sim/app/api/copilot/chat/delete/route.ts index 203a2b5c6d..8e900217b4 100644 --- a/apps/sim/app/api/copilot/chat/delete/route.ts +++ b/apps/sim/app/api/copilot/chat/delete/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('DeleteChatAPI') diff --git a/apps/sim/app/api/copilot/chat/route.ts b/apps/sim/app/api/copilot/chat/route.ts index eb7331e0ea..b14feb495d 100644 --- a/apps/sim/app/api/copilot/chat/route.ts +++ b/apps/sim/app/api/copilot/chat/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -17,7 +18,6 @@ import { import { getCredentialsServerTool } from '@/lib/copilot/tools/server/user/get-credentials' import type { CopilotProviderConfig } from '@/lib/copilot/types' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { CopilotFiles } from '@/lib/uploads' import { createFileContent } from '@/lib/uploads/utils/file-utils' import { tools } from '@/tools/registry' @@ -1066,7 +1066,6 @@ export async function GET(req: NextRequest) { model: chat.model, messages: Array.isArray(chat.messages) ? chat.messages : [], messageCount: Array.isArray(chat.messages) ? chat.messages.length : 0, - previewYaml: null, // Not needed for chat list planArtifact: chat.planArtifact || null, config: chat.config || null, createdAt: chat.createdAt, diff --git a/apps/sim/app/api/copilot/chat/update-messages/route.ts b/apps/sim/app/api/copilot/chat/update-messages/route.ts index bc17ddad52..4e25d726d9 100644 --- a/apps/sim/app/api/copilot/chat/update-messages/route.ts +++ b/apps/sim/app/api/copilot/chat/update-messages/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -10,7 +11,6 @@ import { createRequestTracker, createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotChatUpdateAPI') diff --git a/apps/sim/app/api/copilot/chat/update-title/route.ts b/apps/sim/app/api/copilot/chat/update-title/route.ts index c4266b7579..7c1451c642 100644 --- a/apps/sim/app/api/copilot/chat/update-title/route.ts +++ b/apps/sim/app/api/copilot/chat/update-title/route.ts @@ -5,11 +5,11 @@ import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('UpdateChatTitleAPI') diff --git a/apps/sim/app/api/copilot/chats/route.test.ts b/apps/sim/app/api/copilot/chats/route.test.ts new file mode 100644 index 0000000000..8cc3bb04e5 --- /dev/null +++ b/apps/sim/app/api/copilot/chats/route.test.ts @@ -0,0 +1,277 @@ +/** + * Tests for copilot chats list API route + * + * @vitest-environment node + */ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { mockCryptoUuid, setupCommonApiMocks } from '@/app/api/__test-utils__/utils' + +describe('Copilot Chats List API Route', () => { + const mockSelect = vi.fn() + const mockFrom = vi.fn() + const mockWhere = vi.fn() + const mockOrderBy = vi.fn() + + beforeEach(() => { + vi.resetModules() + setupCommonApiMocks() + mockCryptoUuid() + + mockSelect.mockReturnValue({ from: mockFrom }) + mockFrom.mockReturnValue({ where: mockWhere }) + mockWhere.mockReturnValue({ orderBy: mockOrderBy }) + mockOrderBy.mockResolvedValue([]) + + vi.doMock('@sim/db', () => ({ + db: { + select: mockSelect, + }, + })) + + vi.doMock('@sim/db/schema', () => ({ + copilotChats: { + id: 'id', + title: 'title', + workflowId: 'workflowId', + userId: 'userId', + updatedAt: 'updatedAt', + }, + })) + + vi.doMock('drizzle-orm', () => ({ + and: vi.fn((...conditions) => ({ conditions, type: 'and' })), + eq: vi.fn((field, value) => ({ field, value, type: 'eq' })), + desc: vi.fn((field) => ({ field, type: 'desc' })), + })) + + vi.doMock('@/lib/copilot/request-helpers', () => ({ + authenticateCopilotRequestSessionOnly: vi.fn(), + createUnauthorizedResponse: vi + .fn() + .mockReturnValue(new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })), + createInternalServerErrorResponse: vi + .fn() + .mockImplementation( + (message) => new Response(JSON.stringify({ error: message }), { status: 500 }) + ), + })) + }) + + afterEach(() => { + vi.clearAllMocks() + vi.restoreAllMocks() + }) + + describe('GET', () => { + it('should return 401 when user is not authenticated', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: null, + isAuthenticated: false, + }) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should return empty chats array when user has no chats', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockOrderBy.mockResolvedValueOnce([]) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData).toEqual({ + success: true, + chats: [], + }) + }) + + it('should return list of chats for authenticated user', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const mockChats = [ + { + id: 'chat-1', + title: 'First Chat', + workflowId: 'workflow-1', + updatedAt: new Date('2024-01-02'), + }, + { + id: 'chat-2', + title: 'Second Chat', + workflowId: 'workflow-2', + updatedAt: new Date('2024-01-01'), + }, + ] + mockOrderBy.mockResolvedValueOnce(mockChats) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + expect(responseData.chats).toHaveLength(2) + expect(responseData.chats[0].id).toBe('chat-1') + expect(responseData.chats[0].title).toBe('First Chat') + expect(responseData.chats[1].id).toBe('chat-2') + }) + + it('should return chats ordered by updatedAt descending', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const mockChats = [ + { + id: 'newest-chat', + title: 'Newest', + workflowId: 'workflow-1', + updatedAt: new Date('2024-01-10'), + }, + { + id: 'older-chat', + title: 'Older', + workflowId: 'workflow-2', + updatedAt: new Date('2024-01-05'), + }, + { + id: 'oldest-chat', + title: 'Oldest', + workflowId: 'workflow-3', + updatedAt: new Date('2024-01-01'), + }, + ] + mockOrderBy.mockResolvedValueOnce(mockChats) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.chats[0].id).toBe('newest-chat') + expect(responseData.chats[2].id).toBe('oldest-chat') + }) + + it('should handle chats with null workflowId', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const mockChats = [ + { + id: 'chat-no-workflow', + title: 'Chat without workflow', + workflowId: null, + updatedAt: new Date('2024-01-01'), + }, + ] + mockOrderBy.mockResolvedValueOnce(mockChats) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.chats[0].workflowId).toBeNull() + }) + + it('should handle database errors gracefully', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockOrderBy.mockRejectedValueOnce(new Error('Database connection failed')) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to fetch user chats') + }) + + it('should only return chats belonging to authenticated user', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const mockChats = [ + { + id: 'my-chat', + title: 'My Chat', + workflowId: 'workflow-1', + updatedAt: new Date('2024-01-01'), + }, + ] + mockOrderBy.mockResolvedValueOnce(mockChats) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + await GET(request as any) + + expect(mockSelect).toHaveBeenCalled() + expect(mockWhere).toHaveBeenCalled() + }) + + it('should return 401 when userId is null despite isAuthenticated being true', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: null, + isAuthenticated: true, + }) + + const { GET } = await import('@/app/api/copilot/chats/route') + const request = new Request('http://localhost:3000/api/copilot/chats') + const response = await GET(request as any) + + expect(response.status).toBe(401) + }) + }) +}) diff --git a/apps/sim/app/api/copilot/chats/route.ts b/apps/sim/app/api/copilot/chats/route.ts index 03dcd6dccd..e7b82e2d63 100644 --- a/apps/sim/app/api/copilot/chats/route.ts +++ b/apps/sim/app/api/copilot/chats/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { copilotChats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { @@ -7,7 +8,6 @@ import { createInternalServerErrorResponse, createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotChatsListAPI') diff --git a/apps/sim/app/api/copilot/checkpoints/revert/route.ts b/apps/sim/app/api/copilot/checkpoints/revert/route.ts index f0b635f20e..7f65e0317e 100644 --- a/apps/sim/app/api/copilot/checkpoints/revert/route.ts +++ b/apps/sim/app/api/copilot/checkpoints/revert/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { workflowCheckpoints, workflow as workflowTable } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -11,7 +12,6 @@ import { createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { isUuidV4 } from '@/executor/constants' const logger = createLogger('CheckpointRevertAPI') diff --git a/apps/sim/app/api/copilot/checkpoints/route.ts b/apps/sim/app/api/copilot/checkpoints/route.ts index 5110ae12f9..b1517986a0 100644 --- a/apps/sim/app/api/copilot/checkpoints/route.ts +++ b/apps/sim/app/api/copilot/checkpoints/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { copilotChats, workflowCheckpoints } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -10,7 +11,6 @@ import { createRequestTracker, createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('WorkflowCheckpointsAPI') diff --git a/apps/sim/app/api/copilot/confirm/route.ts b/apps/sim/app/api/copilot/confirm/route.ts index fed0ad3dff..9fd5476c9e 100644 --- a/apps/sim/app/api/copilot/confirm/route.ts +++ b/apps/sim/app/api/copilot/confirm/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { @@ -9,7 +10,6 @@ import { type NotificationStatus, } from '@/lib/copilot/request-helpers' import { getRedisClient } from '@/lib/core/config/redis' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotConfirmAPI') diff --git a/apps/sim/app/api/copilot/context-usage/route.ts b/apps/sim/app/api/copilot/context-usage/route.ts index fba208bb44..ac8f834327 100644 --- a/apps/sim/app/api/copilot/context-usage/route.ts +++ b/apps/sim/app/api/copilot/context-usage/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -5,7 +6,6 @@ import { getCopilotModel } from '@/lib/copilot/config' import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants' import type { CopilotProviderConfig } from '@/lib/copilot/types' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ContextUsageAPI') diff --git a/apps/sim/app/api/copilot/execute-copilot-server-tool/route.ts b/apps/sim/app/api/copilot/execute-copilot-server-tool/route.ts index c4bdbf4d8c..5627ae8976 100644 --- a/apps/sim/app/api/copilot/execute-copilot-server-tool/route.ts +++ b/apps/sim/app/api/copilot/execute-copilot-server-tool/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { @@ -8,7 +9,6 @@ import { createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' import { routeExecution } from '@/lib/copilot/tools/server/router' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ExecuteCopilotServerToolAPI') diff --git a/apps/sim/app/api/copilot/execute-tool/route.ts b/apps/sim/app/api/copilot/execute-tool/route.ts index e5cb66095f..adb88071e3 100644 --- a/apps/sim/app/api/copilot/execute-tool/route.ts +++ b/apps/sim/app/api/copilot/execute-tool/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { account, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -12,7 +13,6 @@ import { } from '@/lib/copilot/request-helpers' import { generateRequestId } from '@/lib/core/utils/request' import { getEffectiveDecryptedEnv } from '@/lib/environment/utils' -import { createLogger } from '@/lib/logs/console/logger' import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils' import { REFERENCE } from '@/executor/constants' import { createEnvVarPattern } from '@/executor/utils/reference-validation' diff --git a/apps/sim/app/api/copilot/feedback/route.test.ts b/apps/sim/app/api/copilot/feedback/route.test.ts new file mode 100644 index 0000000000..547d5cd3b9 --- /dev/null +++ b/apps/sim/app/api/copilot/feedback/route.test.ts @@ -0,0 +1,516 @@ +/** + * Tests for copilot feedback API route + * + * @vitest-environment node + */ +import { NextRequest } from 'next/server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMockRequest, + mockCryptoUuid, + setupCommonApiMocks, +} from '@/app/api/__test-utils__/utils' + +describe('Copilot Feedback API Route', () => { + const mockInsert = vi.fn() + const mockValues = vi.fn() + const mockReturning = vi.fn() + const mockSelect = vi.fn() + const mockFrom = vi.fn() + + beforeEach(() => { + vi.resetModules() + setupCommonApiMocks() + mockCryptoUuid() + + mockInsert.mockReturnValue({ values: mockValues }) + mockValues.mockReturnValue({ returning: mockReturning }) + mockReturning.mockResolvedValue([]) + mockSelect.mockReturnValue({ from: mockFrom }) + mockFrom.mockResolvedValue([]) + + vi.doMock('@sim/db', () => ({ + db: { + insert: mockInsert, + select: mockSelect, + }, + })) + + vi.doMock('@sim/db/schema', () => ({ + copilotFeedback: { + feedbackId: 'feedbackId', + userId: 'userId', + chatId: 'chatId', + userQuery: 'userQuery', + agentResponse: 'agentResponse', + isPositive: 'isPositive', + feedback: 'feedback', + workflowYaml: 'workflowYaml', + createdAt: 'createdAt', + }, + })) + + vi.doMock('drizzle-orm', () => ({ + eq: vi.fn((field, value) => ({ field, value, type: 'eq' })), + })) + + vi.doMock('@/lib/copilot/request-helpers', () => ({ + authenticateCopilotRequestSessionOnly: vi.fn(), + createUnauthorizedResponse: vi + .fn() + .mockReturnValue(new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })), + createBadRequestResponse: vi + .fn() + .mockImplementation( + (message) => new Response(JSON.stringify({ error: message }), { status: 400 }) + ), + createInternalServerErrorResponse: vi + .fn() + .mockImplementation( + (message) => new Response(JSON.stringify({ error: message }), { status: 500 }) + ), + createRequestTracker: vi.fn().mockReturnValue({ + requestId: 'test-request-id', + getDuration: vi.fn().mockReturnValue(100), + }), + })) + }) + + afterEach(() => { + vi.clearAllMocks() + vi.restoreAllMocks() + }) + + describe('POST', () => { + it('should return 401 when user is not authenticated', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: null, + isAuthenticated: false, + }) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should successfully submit positive feedback', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const feedbackRecord = { + feedbackId: 'feedback-123', + userId: 'user-123', + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + isPositive: true, + feedback: null, + workflowYaml: null, + createdAt: new Date('2024-01-01'), + } + mockReturning.mockResolvedValueOnce([feedbackRecord]) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + expect(responseData.feedbackId).toBe('feedback-123') + expect(responseData.message).toBe('Feedback submitted successfully') + }) + + it('should successfully submit negative feedback with text', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const feedbackRecord = { + feedbackId: 'feedback-456', + userId: 'user-123', + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I deploy?', + agentResponse: 'Here is how to deploy...', + isPositive: false, + feedback: 'The response was not helpful', + workflowYaml: null, + createdAt: new Date('2024-01-01'), + } + mockReturning.mockResolvedValueOnce([feedbackRecord]) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I deploy?', + agentResponse: 'Here is how to deploy...', + isPositiveFeedback: false, + feedback: 'The response was not helpful', + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + expect(responseData.feedbackId).toBe('feedback-456') + }) + + it('should successfully submit feedback with workflow YAML', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const workflowYaml = ` +blocks: + - id: starter + type: starter + - id: agent + type: agent +edges: + - source: starter + target: agent +` + + const feedbackRecord = { + feedbackId: 'feedback-789', + userId: 'user-123', + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'Build a simple agent workflow', + agentResponse: 'I created a workflow for you.', + isPositive: true, + feedback: null, + workflowYaml: workflowYaml, + createdAt: new Date('2024-01-01'), + } + mockReturning.mockResolvedValueOnce([feedbackRecord]) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'Build a simple agent workflow', + agentResponse: 'I created a workflow for you.', + isPositiveFeedback: true, + workflowYaml: workflowYaml, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + + expect(mockValues).toHaveBeenCalledWith( + expect.objectContaining({ + workflowYaml: workflowYaml, + }) + ) + }) + + it('should return 400 for invalid chatId format', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + chatId: 'not-a-uuid', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toContain('Invalid request data') + }) + + it('should return 400 for empty userQuery', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: '', + agentResponse: 'You can create a workflow by...', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toContain('Invalid request data') + }) + + it('should return 400 for empty agentResponse', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: '', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toContain('Invalid request data') + }) + + it('should return 400 for missing isPositiveFeedback', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toContain('Invalid request data') + }) + + it('should handle database errors gracefully', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockReturning.mockRejectedValueOnce(new Error('Database connection failed')) + + const req = createMockRequest('POST', { + chatId: '550e8400-e29b-41d4-a716-446655440000', + userQuery: 'How do I create a workflow?', + agentResponse: 'You can create a workflow by...', + isPositiveFeedback: true, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to submit feedback') + }) + + it('should handle JSON parsing errors in request body', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = new NextRequest('http://localhost:3000/api/copilot/feedback', { + method: 'POST', + body: '{invalid-json', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const { POST } = await import('@/app/api/copilot/feedback/route') + const response = await POST(req) + + expect(response.status).toBe(500) + }) + }) + + describe('GET', () => { + it('should return 401 when user is not authenticated', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: null, + isAuthenticated: false, + }) + + const { GET } = await import('@/app/api/copilot/feedback/route') + const request = new Request('http://localhost:3000/api/copilot/feedback') + const response = await GET(request as any) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should return empty feedback array when no feedback exists', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFrom.mockResolvedValueOnce([]) + + const { GET } = await import('@/app/api/copilot/feedback/route') + const request = new Request('http://localhost:3000/api/copilot/feedback') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + expect(responseData.feedback).toEqual([]) + }) + + it('should return all feedback records', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const mockFeedback = [ + { + feedbackId: 'feedback-1', + userId: 'user-123', + chatId: 'chat-1', + userQuery: 'Query 1', + agentResponse: 'Response 1', + isPositive: true, + feedback: null, + workflowYaml: null, + createdAt: new Date('2024-01-01'), + }, + { + feedbackId: 'feedback-2', + userId: 'user-456', + chatId: 'chat-2', + userQuery: 'Query 2', + agentResponse: 'Response 2', + isPositive: false, + feedback: 'Not helpful', + workflowYaml: 'yaml: content', + createdAt: new Date('2024-01-02'), + }, + ] + mockFrom.mockResolvedValueOnce(mockFeedback) + + const { GET } = await import('@/app/api/copilot/feedback/route') + const request = new Request('http://localhost:3000/api/copilot/feedback') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.success).toBe(true) + expect(responseData.feedback).toHaveLength(2) + expect(responseData.feedback[0].feedbackId).toBe('feedback-1') + expect(responseData.feedback[1].feedbackId).toBe('feedback-2') + }) + + it('should handle database errors gracefully', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFrom.mockRejectedValueOnce(new Error('Database connection failed')) + + const { GET } = await import('@/app/api/copilot/feedback/route') + const request = new Request('http://localhost:3000/api/copilot/feedback') + const response = await GET(request as any) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to retrieve feedback') + }) + + it('should return metadata with response', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFrom.mockResolvedValueOnce([]) + + const { GET } = await import('@/app/api/copilot/feedback/route') + const request = new Request('http://localhost:3000/api/copilot/feedback') + const response = await GET(request as any) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData.metadata).toBeDefined() + expect(responseData.metadata.requestId).toBeDefined() + expect(responseData.metadata.duration).toBeDefined() + }) + }) +}) diff --git a/apps/sim/app/api/copilot/feedback/route.ts b/apps/sim/app/api/copilot/feedback/route.ts index 86a95a9fc0..3ff0956122 100644 --- a/apps/sim/app/api/copilot/feedback/route.ts +++ b/apps/sim/app/api/copilot/feedback/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { copilotFeedback } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { @@ -9,7 +10,6 @@ import { createRequestTracker, createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotFeedbackAPI') diff --git a/apps/sim/app/api/copilot/stats/route.test.ts b/apps/sim/app/api/copilot/stats/route.test.ts new file mode 100644 index 0000000000..e48dff0169 --- /dev/null +++ b/apps/sim/app/api/copilot/stats/route.test.ts @@ -0,0 +1,367 @@ +/** + * Tests for copilot stats API route + * + * @vitest-environment node + */ +import { NextRequest } from 'next/server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + createMockRequest, + mockCryptoUuid, + setupCommonApiMocks, +} from '@/app/api/__test-utils__/utils' + +describe('Copilot Stats API Route', () => { + const mockFetch = vi.fn() + + beforeEach(() => { + vi.resetModules() + setupCommonApiMocks() + mockCryptoUuid() + + global.fetch = mockFetch + + vi.doMock('@/lib/copilot/request-helpers', () => ({ + authenticateCopilotRequestSessionOnly: vi.fn(), + createUnauthorizedResponse: vi + .fn() + .mockReturnValue(new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })), + createBadRequestResponse: vi + .fn() + .mockImplementation( + (message) => new Response(JSON.stringify({ error: message }), { status: 400 }) + ), + createInternalServerErrorResponse: vi + .fn() + .mockImplementation( + (message) => new Response(JSON.stringify({ error: message }), { status: 500 }) + ), + createRequestTracker: vi.fn().mockReturnValue({ + requestId: 'test-request-id', + getDuration: vi.fn().mockReturnValue(100), + }), + })) + + vi.doMock('@/lib/copilot/constants', () => ({ + SIM_AGENT_API_URL_DEFAULT: 'https://agent.sim.example.com', + })) + + vi.doMock('@/lib/core/config/env', () => ({ + env: { + SIM_AGENT_API_URL: null, + COPILOT_API_KEY: 'test-api-key', + }, + })) + }) + + afterEach(() => { + vi.clearAllMocks() + vi.restoreAllMocks() + }) + + describe('POST', () => { + it('should return 401 when user is not authenticated', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: null, + isAuthenticated: false, + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(401) + const responseData = await response.json() + expect(responseData).toEqual({ error: 'Unauthorized' }) + }) + + it('should successfully forward stats to Sim Agent', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ success: true }), + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + diffAccepted: true, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(200) + const responseData = await response.json() + expect(responseData).toEqual({ success: true }) + + expect(mockFetch).toHaveBeenCalledWith( + 'https://agent.sim.example.com/api/stats', + expect.objectContaining({ + method: 'POST', + headers: expect.objectContaining({ + 'Content-Type': 'application/json', + 'x-api-key': 'test-api-key', + }), + body: JSON.stringify({ + messageId: 'message-123', + diffCreated: true, + diffAccepted: true, + }), + }) + ) + }) + + it('should return 400 for invalid request body - missing messageId', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toBe('Invalid request body for copilot stats') + }) + + it('should return 400 for invalid request body - missing diffCreated', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toBe('Invalid request body for copilot stats') + }) + + it('should return 400 for invalid request body - missing diffAccepted', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toBe('Invalid request body for copilot stats') + }) + + it('should return 400 when upstream Sim Agent returns error', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockResolvedValueOnce({ + ok: false, + json: () => Promise.resolve({ error: 'Invalid message ID' }), + }) + + const req = createMockRequest('POST', { + messageId: 'invalid-message', + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData).toEqual({ success: false, error: 'Invalid message ID' }) + }) + + it('should handle upstream error with message field', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockResolvedValueOnce({ + ok: false, + json: () => Promise.resolve({ message: 'Rate limit exceeded' }), + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData).toEqual({ success: false, error: 'Rate limit exceeded' }) + }) + + it('should handle upstream error with no JSON response', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockResolvedValueOnce({ + ok: false, + json: () => Promise.reject(new Error('Not JSON')), + }) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData).toEqual({ success: false, error: 'Upstream error' }) + }) + + it('should handle network errors gracefully', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockRejectedValueOnce(new Error('Network error')) + + const req = createMockRequest('POST', { + messageId: 'message-123', + diffCreated: true, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(500) + const responseData = await response.json() + expect(responseData.error).toBe('Failed to forward copilot stats') + }) + + it('should handle JSON parsing errors in request body', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + const req = new NextRequest('http://localhost:3000/api/copilot/stats', { + method: 'POST', + body: '{invalid-json', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(400) + const responseData = await response.json() + expect(responseData.error).toBe('Invalid request body for copilot stats') + }) + + it('should forward stats with diffCreated=false and diffAccepted=false', async () => { + const { authenticateCopilotRequestSessionOnly } = await import( + '@/lib/copilot/request-helpers' + ) + vi.mocked(authenticateCopilotRequestSessionOnly).mockResolvedValueOnce({ + userId: 'user-123', + isAuthenticated: true, + }) + + mockFetch.mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve({ success: true }), + }) + + const req = createMockRequest('POST', { + messageId: 'message-456', + diffCreated: false, + diffAccepted: false, + }) + + const { POST } = await import('@/app/api/copilot/stats/route') + const response = await POST(req) + + expect(response.status).toBe(200) + + expect(mockFetch).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ + body: JSON.stringify({ + messageId: 'message-456', + diffCreated: false, + diffAccepted: false, + }), + }) + ) + }) + }) +}) diff --git a/apps/sim/app/api/copilot/tools/mark-complete/route.ts b/apps/sim/app/api/copilot/tools/mark-complete/route.ts index 93bfef7d2d..1ada484e5b 100644 --- a/apps/sim/app/api/copilot/tools/mark-complete/route.ts +++ b/apps/sim/app/api/copilot/tools/mark-complete/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/copilot/constants' @@ -9,7 +10,6 @@ import { createUnauthorizedResponse, } from '@/lib/copilot/request-helpers' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotMarkToolCompleteAPI') diff --git a/apps/sim/app/api/copilot/training/examples/route.ts b/apps/sim/app/api/copilot/training/examples/route.ts index 7d735427df..1d23793cd7 100644 --- a/apps/sim/app/api/copilot/training/examples/route.ts +++ b/apps/sim/app/api/copilot/training/examples/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotTrainingExamplesAPI') diff --git a/apps/sim/app/api/copilot/training/route.ts b/apps/sim/app/api/copilot/training/route.ts index aed162af6a..4ff955eee0 100644 --- a/apps/sim/app/api/copilot/training/route.ts +++ b/apps/sim/app/api/copilot/training/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CopilotTrainingAPI') diff --git a/apps/sim/app/api/copilot/user-models/route.ts b/apps/sim/app/api/copilot/user-models/route.ts index 5708b3f602..d98d49baaa 100644 --- a/apps/sim/app/api/copilot/user-models/route.ts +++ b/apps/sim/app/api/copilot/user-models/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { db } from '@/../../packages/db' import { settings } from '@/../../packages/db/schema' diff --git a/apps/sim/app/api/creators/[id]/route.ts b/apps/sim/app/api/creators/[id]/route.ts index c55b915cbf..326504b969 100644 --- a/apps/sim/app/api/creators/[id]/route.ts +++ b/apps/sim/app/api/creators/[id]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { member, templateCreators } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CreatorProfileByIdAPI') diff --git a/apps/sim/app/api/creators/[id]/verify/route.ts b/apps/sim/app/api/creators/[id]/verify/route.ts index 2bb13115bd..45cd2dc0b0 100644 --- a/apps/sim/app/api/creators/[id]/verify/route.ts +++ b/apps/sim/app/api/creators/[id]/verify/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { templateCreators, user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('CreatorVerificationAPI') diff --git a/apps/sim/app/api/creators/route.ts b/apps/sim/app/api/creators/route.ts index 96548e83e3..1113de3d45 100644 --- a/apps/sim/app/api/creators/route.ts +++ b/apps/sim/app/api/creators/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { member, templateCreators } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import type { CreatorProfileDetails } from '@/app/_types/creator-profile' const logger = createLogger('CreatorProfilesAPI') diff --git a/apps/sim/app/api/cron/renew-subscriptions/route.ts b/apps/sim/app/api/cron/renew-subscriptions/route.ts index 501fdfdc43..b60afc84cc 100644 --- a/apps/sim/app/api/cron/renew-subscriptions/route.ts +++ b/apps/sim/app/api/cron/renew-subscriptions/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { webhook as webhookTable, workflow as workflowTable } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' const logger = createLogger('TeamsSubscriptionRenewal') diff --git a/apps/sim/app/api/environment/route.ts b/apps/sim/app/api/environment/route.ts index 6425305f3b..5e7fa4006e 100644 --- a/apps/sim/app/api/environment/route.ts +++ b/apps/sim/app/api/environment/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { environment } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import type { EnvironmentVariable } from '@/stores/settings/environment/types' const logger = createLogger('EnvironmentAPI') diff --git a/apps/sim/app/api/files/authorization.ts b/apps/sim/app/api/files/authorization.ts index 65b3381a19..3366e5830d 100644 --- a/apps/sim/app/api/files/authorization.ts +++ b/apps/sim/app/api/files/authorization.ts @@ -1,7 +1,7 @@ import { db } from '@sim/db' import { document, workspaceFile } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, like, or } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { getFileMetadata } from '@/lib/uploads' import type { StorageContext } from '@/lib/uploads/config' import { diff --git a/apps/sim/app/api/files/delete/route.ts b/apps/sim/app/api/files/delete/route.ts index 2122181a32..1a5f491388 100644 --- a/apps/sim/app/api/files/delete/route.ts +++ b/apps/sim/app/api/files/delete/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { createLogger } from '@/lib/logs/console/logger' import type { StorageContext } from '@/lib/uploads/config' import { deleteFile, hasCloudStorage } from '@/lib/uploads/core/storage-service' import { extractStorageKey, inferContextFromKey } from '@/lib/uploads/utils/file-utils' diff --git a/apps/sim/app/api/files/download/route.ts b/apps/sim/app/api/files/download/route.ts index 38de9b4021..bd718ed8f4 100644 --- a/apps/sim/app/api/files/download/route.ts +++ b/apps/sim/app/api/files/download/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { createLogger } from '@/lib/logs/console/logger' import type { StorageContext } from '@/lib/uploads/config' import { hasCloudStorage } from '@/lib/uploads/core/storage-service' import { verifyFileAccess } from '@/app/api/files/authorization' diff --git a/apps/sim/app/api/files/multipart/route.ts b/apps/sim/app/api/files/multipart/route.ts index ee8c36547a..02ba826fc9 100644 --- a/apps/sim/app/api/files/multipart/route.ts +++ b/apps/sim/app/api/files/multipart/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { getStorageConfig, getStorageProvider, diff --git a/apps/sim/app/api/files/parse/route.ts b/apps/sim/app/api/files/parse/route.ts index d02d077325..4e4d54f18b 100644 --- a/apps/sim/app/api/files/parse/route.ts +++ b/apps/sim/app/api/files/parse/route.ts @@ -2,12 +2,12 @@ import { Buffer } from 'buffer' import { createHash } from 'crypto' import fsPromises, { readFile } from 'fs/promises' import path from 'path' +import { createLogger } from '@sim/logger' import binaryExtensionsList from 'binary-extensions' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation' import { isSupportedFileType, parseFile } from '@/lib/file-parsers' -import { createLogger } from '@/lib/logs/console/logger' import { isUsingCloudStorage, type StorageContext, StorageService } from '@/lib/uploads' import { UPLOAD_DIR_SERVER } from '@/lib/uploads/core/setup.server' import { getFileMetadataByKey } from '@/lib/uploads/server/metadata' diff --git a/apps/sim/app/api/files/presigned/batch/route.ts b/apps/sim/app/api/files/presigned/batch/route.ts index 4f52f334dd..f2aa4aa320 100644 --- a/apps/sim/app/api/files/presigned/batch/route.ts +++ b/apps/sim/app/api/files/presigned/batch/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import type { StorageContext } from '@/lib/uploads/config' import { USE_BLOB_STORAGE } from '@/lib/uploads/config' import { diff --git a/apps/sim/app/api/files/presigned/route.ts b/apps/sim/app/api/files/presigned/route.ts index adbd439701..6068140660 100644 --- a/apps/sim/app/api/files/presigned/route.ts +++ b/apps/sim/app/api/files/presigned/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { CopilotFiles } from '@/lib/uploads' import type { StorageContext } from '@/lib/uploads/config' import { USE_BLOB_STORAGE } from '@/lib/uploads/config' diff --git a/apps/sim/app/api/files/serve/[...path]/route.ts b/apps/sim/app/api/files/serve/[...path]/route.ts index 0843a2e968..e339615f87 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.ts @@ -1,8 +1,8 @@ import { readFile } from 'fs/promises' +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { createLogger } from '@/lib/logs/console/logger' import { CopilotFiles, isUsingCloudStorage } from '@/lib/uploads' import type { StorageContext } from '@/lib/uploads/config' import { downloadFile } from '@/lib/uploads/core/storage-service' diff --git a/apps/sim/app/api/files/upload/route.ts b/apps/sim/app/api/files/upload/route.ts index c23f46ec84..eca3667926 100644 --- a/apps/sim/app/api/files/upload/route.ts +++ b/apps/sim/app/api/files/upload/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { sanitizeFileName } from '@/executor/constants' import '@/lib/uploads/core/setup.server' import { getSession } from '@/lib/auth' diff --git a/apps/sim/app/api/files/utils.ts b/apps/sim/app/api/files/utils.ts index 50286bdba2..953c9b8989 100644 --- a/apps/sim/app/api/files/utils.ts +++ b/apps/sim/app/api/files/utils.ts @@ -1,7 +1,7 @@ import { existsSync } from 'fs' import { join, resolve, sep } from 'path' +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { UPLOAD_DIR } from '@/lib/uploads/config' import { sanitizeFileKey } from '@/lib/uploads/utils/file-utils' diff --git a/apps/sim/app/api/folders/[id]/duplicate/route.ts b/apps/sim/app/api/folders/[id]/duplicate/route.ts index 914485a3b6..60b3e99961 100644 --- a/apps/sim/app/api/folders/[id]/duplicate/route.ts +++ b/apps/sim/app/api/folders/[id]/duplicate/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { workflow, workflowFolder } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/folders/[id]/route.ts b/apps/sim/app/api/folders/[id]/route.ts index 773427e230..ebd44f9816 100644 --- a/apps/sim/app/api/folders/[id]/route.ts +++ b/apps/sim/app/api/folders/[id]/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { workflow, workflowFolder } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('FoldersIDAPI') diff --git a/apps/sim/app/api/folders/route.ts b/apps/sim/app/api/folders/route.ts index 050d8524d7..e976f1a945 100644 --- a/apps/sim/app/api/folders/route.ts +++ b/apps/sim/app/api/folders/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { workflowFolder } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, asc, desc, eq, isNull } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('FoldersAPI') diff --git a/apps/sim/app/api/function/execute/route.test.ts b/apps/sim/app/api/function/execute/route.test.ts index d49cfbb6f2..12bf26a7ab 100644 --- a/apps/sim/app/api/function/execute/route.test.ts +++ b/apps/sim/app/api/function/execute/route.test.ts @@ -82,7 +82,7 @@ vi.mock('@/lib/execution/isolated-vm', () => ({ }), })) -vi.mock('@/lib/logs/console/logger', () => ({ +vi.mock('@sim/logger', () => ({ createLogger: vi.fn(() => ({ info: vi.fn(), error: vi.fn(), diff --git a/apps/sim/app/api/function/execute/route.ts b/apps/sim/app/api/function/execute/route.ts index ce42d5e67f..cb1da555af 100644 --- a/apps/sim/app/api/function/execute/route.ts +++ b/apps/sim/app/api/function/execute/route.ts @@ -1,10 +1,10 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { isE2bEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' import { executeInE2B } from '@/lib/execution/e2b' import { executeInIsolatedVM } from '@/lib/execution/isolated-vm' import { CodeLanguage, DEFAULT_CODE_LANGUAGE, isValidCodeLanguage } from '@/lib/execution/languages' -import { createLogger } from '@/lib/logs/console/logger' import { escapeRegExp, normalizeName, REFERENCE } from '@/executor/constants' import { createEnvVarPattern, diff --git a/apps/sim/app/api/guardrails/validate/route.ts b/apps/sim/app/api/guardrails/validate/route.ts index 93be5e8b47..5f47383390 100644 --- a/apps/sim/app/api/guardrails/validate/route.ts +++ b/apps/sim/app/api/guardrails/validate/route.ts @@ -1,10 +1,10 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' import { validateHallucination } from '@/lib/guardrails/validate_hallucination' import { validateJson } from '@/lib/guardrails/validate_json' import { validatePII } from '@/lib/guardrails/validate_pii' import { validateRegex } from '@/lib/guardrails/validate_regex' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('GuardrailsValidateAPI') diff --git a/apps/sim/app/api/help/route.ts b/apps/sim/app/api/help/route.ts index 27a9d03afa..ca3d040c2a 100644 --- a/apps/sim/app/api/help/route.ts +++ b/apps/sim/app/api/help/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { renderHelpConfirmationEmail } from '@/components/emails' @@ -5,7 +6,6 @@ import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { generateRequestId } from '@/lib/core/utils/request' import { getEmailDomain } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress } from '@/lib/messaging/email/utils' diff --git a/apps/sim/app/api/jobs/[jobId]/route.ts b/apps/sim/app/api/jobs/[jobId]/route.ts index 58c01d103a..74dc52407d 100644 --- a/apps/sim/app/api/jobs/[jobId]/route.ts +++ b/apps/sim/app/api/jobs/[jobId]/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { runs } from '@trigger.dev/sdk' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createErrorResponse } from '@/app/api/workflows/utils' const logger = createLogger('TaskStatusAPI') @@ -31,7 +31,7 @@ export async function GET( const payload = run.payload as any if (payload?.workflowId) { - const { verifyWorkflowAccess } = await import('@/socket-server/middleware/permissions') + const { verifyWorkflowAccess } = await import('@/socket/middleware/permissions') const accessCheck = await verifyWorkflowAccess(authenticatedUserId, payload.workflowId) if (!accessCheck.hasAccess) { logger.warn(`[${requestId}] User ${authenticatedUserId} denied access to task ${taskId}`, { diff --git a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/[chunkId]/route.ts b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/[chunkId]/route.ts index 1df8cde317..08c02d508b 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/[chunkId]/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/[chunkId]/route.ts @@ -1,9 +1,9 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { deleteChunk, updateChunk } from '@/lib/knowledge/chunks/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkChunkAccess } from '@/app/api/knowledge/utils' const logger = createLogger('ChunkByIdAPI') @@ -100,7 +100,12 @@ export async function PUT( try { const validatedData = UpdateChunkSchema.parse(body) - const updatedChunk = await updateChunk(chunkId, validatedData, requestId) + const updatedChunk = await updateChunk( + chunkId, + validatedData, + requestId, + accessCheck.knowledgeBase?.workspaceId + ) logger.info( `[${requestId}] Chunk updated: ${chunkId} in document ${documentId} in knowledge base ${knowledgeBaseId}` diff --git a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts index 284f069378..c5d8590097 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { batchChunkOperation, createChunk, queryChunks } from '@/lib/knowledge/chunks/service' -import { createLogger } from '@/lib/logs/console/logger' import { getUserId } from '@/app/api/auth/oauth/utils' import { checkDocumentAccess, checkDocumentWriteAccess } from '@/app/api/knowledge/utils' import { calculateCost } from '@/providers/utils' @@ -184,7 +184,8 @@ export async function POST( documentId, docTags, validatedData, - requestId + requestId, + accessCheck.knowledgeBase?.workspaceId ) let cost = null diff --git a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.ts b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.ts index 6e5495aa7c..9d3ad15219 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -8,7 +9,6 @@ import { retryDocumentProcessing, updateDocument, } from '@/lib/knowledge/documents/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkDocumentAccess, checkDocumentWriteAccess } from '@/app/api/knowledge/utils' const logger = createLogger('DocumentByIdAPI') diff --git a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/tag-definitions/route.ts b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/tag-definitions/route.ts index 5403857e48..e228255cd9 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/[documentId]/tag-definitions/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/[documentId]/tag-definitions/route.ts @@ -1,4 +1,5 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -10,7 +11,6 @@ import { getDocumentTagDefinitions, } from '@/lib/knowledge/tags/service' import type { BulkTagDefinitionsData } from '@/lib/knowledge/tags/types' -import { createLogger } from '@/lib/logs/console/logger' import { checkDocumentAccess, checkDocumentWriteAccess } from '@/app/api/knowledge/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/knowledge/[id]/documents/route.ts b/apps/sim/app/api/knowledge/[id]/documents/route.ts index 4c57d21bd1..7aba07d610 100644 --- a/apps/sim/app/api/knowledge/[id]/documents/route.ts +++ b/apps/sim/app/api/knowledge/[id]/documents/route.ts @@ -1,4 +1,5 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -11,7 +12,6 @@ import { processDocumentsWithQueue, } from '@/lib/knowledge/documents/service' import type { DocumentSortField, SortOrder } from '@/lib/knowledge/documents/types' -import { createLogger } from '@/lib/logs/console/logger' import { getUserId } from '@/app/api/auth/oauth/utils' import { checkKnowledgeBaseAccess, checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils' diff --git a/apps/sim/app/api/knowledge/[id]/next-available-slot/route.ts b/apps/sim/app/api/knowledge/[id]/next-available-slot/route.ts index fc17e86fec..b328b7d5b6 100644 --- a/apps/sim/app/api/knowledge/[id]/next-available-slot/route.ts +++ b/apps/sim/app/api/knowledge/[id]/next-available-slot/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { getNextAvailableSlot, getTagDefinitions } from '@/lib/knowledge/tags/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils' const logger = createLogger('NextAvailableSlotAPI') diff --git a/apps/sim/app/api/knowledge/[id]/route.ts b/apps/sim/app/api/knowledge/[id]/route.ts index 4096779f99..a26273b4a4 100644 --- a/apps/sim/app/api/knowledge/[id]/route.ts +++ b/apps/sim/app/api/knowledge/[id]/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -7,7 +8,6 @@ import { getKnowledgeBaseById, updateKnowledgeBase, } from '@/lib/knowledge/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkKnowledgeBaseAccess, checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils' const logger = createLogger('KnowledgeBaseByIdAPI') diff --git a/apps/sim/app/api/knowledge/[id]/tag-definitions/[tagId]/route.ts b/apps/sim/app/api/knowledge/[id]/tag-definitions/[tagId]/route.ts index a0f18b54e5..a141461ec0 100644 --- a/apps/sim/app/api/knowledge/[id]/tag-definitions/[tagId]/route.ts +++ b/apps/sim/app/api/knowledge/[id]/tag-definitions/[tagId]/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { deleteTagDefinition } from '@/lib/knowledge/tags/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/knowledge/[id]/tag-definitions/route.ts b/apps/sim/app/api/knowledge/[id]/tag-definitions/route.ts index 6e45c64d13..09f1fc7873 100644 --- a/apps/sim/app/api/knowledge/[id]/tag-definitions/route.ts +++ b/apps/sim/app/api/knowledge/[id]/tag-definitions/route.ts @@ -1,10 +1,10 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { SUPPORTED_FIELD_TYPES } from '@/lib/knowledge/constants' import { createTagDefinition, getTagDefinitions } from '@/lib/knowledge/tags/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/knowledge/[id]/tag-usage/route.ts b/apps/sim/app/api/knowledge/[id]/tag-usage/route.ts index 55ef74ef67..788ae89758 100644 --- a/apps/sim/app/api/knowledge/[id]/tag-usage/route.ts +++ b/apps/sim/app/api/knowledge/[id]/tag-usage/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { getTagUsage } from '@/lib/knowledge/tags/service' -import { createLogger } from '@/lib/logs/console/logger' import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/knowledge/route.ts b/apps/sim/app/api/knowledge/route.ts index fbcba90ec7..3910fca333 100644 --- a/apps/sim/app/api/knowledge/route.ts +++ b/apps/sim/app/api/knowledge/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { createKnowledgeBase, getKnowledgeBases } from '@/lib/knowledge/service' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('KnowledgeBaseAPI') diff --git a/apps/sim/app/api/knowledge/search/route.ts b/apps/sim/app/api/knowledge/search/route.ts index 4172ebc2d6..6e3f584029 100644 --- a/apps/sim/app/api/knowledge/search/route.ts +++ b/apps/sim/app/api/knowledge/search/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' @@ -5,7 +6,6 @@ import { ALL_TAG_SLOTS } from '@/lib/knowledge/constants' import { getDocumentTagDefinitions } from '@/lib/knowledge/tags/service' import { buildUndefinedTagsError, validateTagValue } from '@/lib/knowledge/tags/utils' import type { StructuredFilter } from '@/lib/knowledge/types' -import { createLogger } from '@/lib/logs/console/logger' import { estimateTokenCount } from '@/lib/tokenization/estimators' import { getUserId } from '@/app/api/auth/oauth/utils' import { @@ -183,11 +183,11 @@ export async function POST(request: NextRequest) { ) } - // Generate query embedding only if query is provided + const workspaceId = accessChecks.find((ac) => ac?.hasAccess)?.knowledgeBase?.workspaceId + const hasQuery = validatedData.query && validatedData.query.trim().length > 0 - // Start embedding generation early and await when needed const queryEmbeddingPromise = hasQuery - ? generateSearchEmbedding(validatedData.query!) + ? generateSearchEmbedding(validatedData.query!, undefined, workspaceId) : Promise.resolve(null) // Check if any requested knowledge bases were not accessible diff --git a/apps/sim/app/api/knowledge/search/utils.test.ts b/apps/sim/app/api/knowledge/search/utils.test.ts index 882d658534..53ceeaa0ae 100644 --- a/apps/sim/app/api/knowledge/search/utils.test.ts +++ b/apps/sim/app/api/knowledge/search/utils.test.ts @@ -7,7 +7,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('drizzle-orm') -vi.mock('@/lib/logs/console/logger', () => ({ +vi.mock('@sim/logger', () => ({ createLogger: vi.fn(() => ({ info: vi.fn(), debug: vi.fn(), diff --git a/apps/sim/app/api/knowledge/search/utils.ts b/apps/sim/app/api/knowledge/search/utils.ts index 74b47664d0..3eba10f911 100644 --- a/apps/sim/app/api/knowledge/search/utils.ts +++ b/apps/sim/app/api/knowledge/search/utils.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { document, embedding } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import type { StructuredFilter } from '@/lib/knowledge/types' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('KnowledgeSearchUtils') diff --git a/apps/sim/app/api/knowledge/utils.ts b/apps/sim/app/api/knowledge/utils.ts index b1f796a1a6..b829709172 100644 --- a/apps/sim/app/api/knowledge/utils.ts +++ b/apps/sim/app/api/knowledge/utils.ts @@ -99,7 +99,7 @@ export interface EmbeddingData { export interface KnowledgeBaseAccessResult { hasAccess: true - knowledgeBase: Pick + knowledgeBase: Pick } export interface KnowledgeBaseAccessDenied { @@ -113,7 +113,7 @@ export type KnowledgeBaseAccessCheck = KnowledgeBaseAccessResult | KnowledgeBase export interface DocumentAccessResult { hasAccess: true document: DocumentData - knowledgeBase: Pick + knowledgeBase: Pick } export interface DocumentAccessDenied { @@ -128,7 +128,7 @@ export interface ChunkAccessResult { hasAccess: true chunk: EmbeddingData document: DocumentData - knowledgeBase: Pick + knowledgeBase: Pick } export interface ChunkAccessDenied { diff --git a/apps/sim/app/api/logs/[id]/route.ts b/apps/sim/app/api/logs/[id]/route.ts index 466868c080..c97764784b 100644 --- a/apps/sim/app/api/logs/[id]/route.ts +++ b/apps/sim/app/api/logs/[id]/route.ts @@ -5,11 +5,11 @@ import { workflowDeploymentVersion, workflowExecutionLogs, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('LogDetailsByIdAPI') diff --git a/apps/sim/app/api/logs/cleanup/route.ts b/apps/sim/app/api/logs/cleanup/route.ts index 7f55cfd373..853fee2002 100644 --- a/apps/sim/app/api/logs/cleanup/route.ts +++ b/apps/sim/app/api/logs/cleanup/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { subscription, user, workflow, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray, lt, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { snapshotService } from '@/lib/logs/execution/snapshot/service' import { isUsingCloudStorage, StorageService } from '@/lib/uploads' diff --git a/apps/sim/app/api/logs/execution/[executionId]/route.ts b/apps/sim/app/api/logs/execution/[executionId]/route.ts index d785a76c8d..2c3cd164ef 100644 --- a/apps/sim/app/api/logs/execution/[executionId]/route.ts +++ b/apps/sim/app/api/logs/execution/[executionId]/route.ts @@ -5,11 +5,11 @@ import { workflowExecutionLogs, workflowExecutionSnapshots, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('LogsByExecutionIdAPI') diff --git a/apps/sim/app/api/logs/export/route.ts b/apps/sim/app/api/logs/export/route.ts index 5b98331132..e43e62b458 100644 --- a/apps/sim/app/api/logs/export/route.ts +++ b/apps/sim/app/api/logs/export/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { buildFilterConditions, LogFilterParamsSchema } from '@/lib/logs/filters' const logger = createLogger('LogsExportAPI') diff --git a/apps/sim/app/api/logs/route.ts b/apps/sim/app/api/logs/route.ts index 6f1811fd64..cfc14ac39b 100644 --- a/apps/sim/app/api/logs/route.ts +++ b/apps/sim/app/api/logs/route.ts @@ -6,12 +6,12 @@ import { workflowDeploymentVersion, workflowExecutionLogs, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq, isNotNull, isNull, or, type SQL, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { buildFilterConditions, LogFilterParamsSchema } from '@/lib/logs/filters' const logger = createLogger('LogsAPI') diff --git a/apps/sim/app/api/logs/triggers/route.ts b/apps/sim/app/api/logs/triggers/route.ts index 1d241cd5b3..dfbcd1001c 100644 --- a/apps/sim/app/api/logs/triggers/route.ts +++ b/apps/sim/app/api/logs/triggers/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { permissions, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, isNotNull, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('TriggersAPI') diff --git a/apps/sim/app/api/mcp/servers/[id]/refresh/route.ts b/apps/sim/app/api/mcp/servers/[id]/refresh/route.ts index ba58b0ba7a..2e3474e68d 100644 --- a/apps/sim/app/api/mcp/servers/[id]/refresh/route.ts +++ b/apps/sim/app/api/mcp/servers/[id]/refresh/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { mcpServers } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import type { McpServerStatusConfig } from '@/lib/mcp/types' diff --git a/apps/sim/app/api/mcp/servers/[id]/route.ts b/apps/sim/app/api/mcp/servers/[id]/route.ts index 40c35fdb73..fc986ccc9f 100644 --- a/apps/sim/app/api/mcp/servers/[id]/route.ts +++ b/apps/sim/app/api/mcp/servers/[id]/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { mcpServers } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import { validateMcpServerUrl } from '@/lib/mcp/url-validator' diff --git a/apps/sim/app/api/mcp/servers/route.ts b/apps/sim/app/api/mcp/servers/route.ts index 8dc3db4dc9..d8ca7c93ff 100644 --- a/apps/sim/app/api/mcp/servers/route.ts +++ b/apps/sim/app/api/mcp/servers/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { mcpServers } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, isNull } from 'drizzle-orm' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import type { McpTransport } from '@/lib/mcp/types' diff --git a/apps/sim/app/api/mcp/servers/test-connection/route.ts b/apps/sim/app/api/mcp/servers/test-connection/route.ts index 1c4add215e..cc52ec88e4 100644 --- a/apps/sim/app/api/mcp/servers/test-connection/route.ts +++ b/apps/sim/app/api/mcp/servers/test-connection/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { getEffectiveDecryptedEnv } from '@/lib/environment/utils' -import { createLogger } from '@/lib/logs/console/logger' import { McpClient } from '@/lib/mcp/client' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import type { McpServerConfig, McpTransport } from '@/lib/mcp/types' diff --git a/apps/sim/app/api/mcp/tools/discover/route.ts b/apps/sim/app/api/mcp/tools/discover/route.ts index 8ae3dfb59a..de88cbb28b 100644 --- a/apps/sim/app/api/mcp/tools/discover/route.ts +++ b/apps/sim/app/api/mcp/tools/discover/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import type { McpToolDiscoveryResponse } from '@/lib/mcp/types' diff --git a/apps/sim/app/api/mcp/tools/execute/route.ts b/apps/sim/app/api/mcp/tools/execute/route.ts index d58d0bea24..1bcdf6488e 100644 --- a/apps/sim/app/api/mcp/tools/execute/route.ts +++ b/apps/sim/app/api/mcp/tools/execute/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware' import { mcpService } from '@/lib/mcp/service' import type { McpTool, McpToolCall, McpToolResult } from '@/lib/mcp/types' diff --git a/apps/sim/app/api/mcp/tools/stored/route.ts b/apps/sim/app/api/mcp/tools/stored/route.ts index b3906954aa..09519aa677 100644 --- a/apps/sim/app/api/mcp/tools/stored/route.ts +++ b/apps/sim/app/api/mcp/tools/stored/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { workflow, workflowBlocks } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { withMcpAuth } from '@/lib/mcp/middleware' import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils' diff --git a/apps/sim/app/api/memory/[id]/route.ts b/apps/sim/app/api/memory/[id]/route.ts index 516a907fc7..617979ef16 100644 --- a/apps/sim/app/api/memory/[id]/route.ts +++ b/apps/sim/app/api/memory/[id]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { memory, permissions, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('MemoryByIdAPI') diff --git a/apps/sim/app/api/memory/route.ts b/apps/sim/app/api/memory/route.ts index e6afab0f9a..fe159b9664 100644 --- a/apps/sim/app/api/memory/route.ts +++ b/apps/sim/app/api/memory/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { memory, permissions, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, isNull, like } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('MemoryAPI') diff --git a/apps/sim/app/api/notifications/poll/route.ts b/apps/sim/app/api/notifications/poll/route.ts index 00157a0bf2..cbc0246096 100644 --- a/apps/sim/app/api/notifications/poll/route.ts +++ b/apps/sim/app/api/notifications/poll/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { createLogger } from '@/lib/logs/console/logger' import { pollInactivityAlerts } from '@/lib/notifications/inactivity-polling' const logger = createLogger('InactivityAlertPoll') diff --git a/apps/sim/app/api/organizations/[id]/invitations/[invitationId]/route.ts b/apps/sim/app/api/organizations/[id]/invitations/[invitationId]/route.ts index d6e53dbbdc..bf9332caa0 100644 --- a/apps/sim/app/api/organizations/[id]/invitations/[invitationId]/route.ts +++ b/apps/sim/app/api/organizations/[id]/invitations/[invitationId]/route.ts @@ -11,12 +11,12 @@ import { type WorkspaceInvitationStatus, workspaceInvitation, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { requireStripeClient } from '@/lib/billing/stripe-client' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationInvitation') diff --git a/apps/sim/app/api/organizations/[id]/invitations/route.ts b/apps/sim/app/api/organizations/[id]/invitations/route.ts index 5a61a277a5..46cdabea9d 100644 --- a/apps/sim/app/api/organizations/[id]/invitations/route.ts +++ b/apps/sim/app/api/organizations/[id]/invitations/route.ts @@ -9,6 +9,7 @@ import { workspace, workspaceInvitation, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray, isNull, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { @@ -22,7 +23,6 @@ import { validateSeatAvailability, } from '@/lib/billing/validation/seat-management' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/organizations/[id]/members/[memberId]/route.ts b/apps/sim/app/api/organizations/[id]/members/[memberId]/route.ts index 577f2730cf..6793a5d13b 100644 --- a/apps/sim/app/api/organizations/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/organizations/[id]/members/[memberId]/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { member, user, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { getUserUsageData } from '@/lib/billing/core/usage' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationMemberAPI') diff --git a/apps/sim/app/api/organizations/[id]/members/route.ts b/apps/sim/app/api/organizations/[id]/members/route.ts index 9b54abf607..4ada7c2ba8 100644 --- a/apps/sim/app/api/organizations/[id]/members/route.ts +++ b/apps/sim/app/api/organizations/[id]/members/route.ts @@ -1,6 +1,7 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { invitation, member, organization, user, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getEmailSubject, renderInvitationEmail } from '@/components/emails/render-email' @@ -8,7 +9,6 @@ import { getSession } from '@/lib/auth' import { getUserUsageData } from '@/lib/billing/core/usage' import { validateSeatAvailability } from '@/lib/billing/validation/seat-management' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { quickValidateEmail } from '@/lib/messaging/email/validation' diff --git a/apps/sim/app/api/organizations/[id]/route.ts b/apps/sim/app/api/organizations/[id]/route.ts index 65e9743942..b528e60256 100644 --- a/apps/sim/app/api/organizations/[id]/route.ts +++ b/apps/sim/app/api/organizations/[id]/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { member, organization } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, ne } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -8,7 +9,6 @@ import { getOrganizationSeatAnalytics, getOrganizationSeatInfo, } from '@/lib/billing/validation/seat-management' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationAPI') diff --git a/apps/sim/app/api/organizations/[id]/seats/route.ts b/apps/sim/app/api/organizations/[id]/seats/route.ts index 9f877e3b36..eaadf5717a 100644 --- a/apps/sim/app/api/organizations/[id]/seats/route.ts +++ b/apps/sim/app/api/organizations/[id]/seats/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { member, organization, subscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -7,7 +8,6 @@ import { getSession } from '@/lib/auth' import { getPlanPricing } from '@/lib/billing/core/billing' import { requireStripeClient } from '@/lib/billing/stripe-client' import { isBillingEnabled } from '@/lib/core/config/feature-flags' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationSeatsAPI') diff --git a/apps/sim/app/api/organizations/[id]/workspaces/route.ts b/apps/sim/app/api/organizations/[id]/workspaces/route.ts index b4f3fb5079..6669c8a8b4 100644 --- a/apps/sim/app/api/organizations/[id]/workspaces/route.ts +++ b/apps/sim/app/api/organizations/[id]/workspaces/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { member, permissions, user, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationWorkspacesAPI') diff --git a/apps/sim/app/api/organizations/route.ts b/apps/sim/app/api/organizations/route.ts index 81ae107c3b..28cc31183c 100644 --- a/apps/sim/app/api/organizations/route.ts +++ b/apps/sim/app/api/organizations/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { member, organization } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, or } from 'drizzle-orm' import { NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { createOrganizationForTeamPlan } from '@/lib/billing/organization' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('OrganizationsAPI') diff --git a/apps/sim/app/api/providers/ollama/models/route.ts b/apps/sim/app/api/providers/ollama/models/route.ts index d135afc9e9..f396f21b0a 100644 --- a/apps/sim/app/api/providers/ollama/models/route.ts +++ b/apps/sim/app/api/providers/ollama/models/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import type { ModelsObject } from '@/providers/ollama/types' const logger = createLogger('OllamaModelsAPI') diff --git a/apps/sim/app/api/providers/openrouter/models/route.ts b/apps/sim/app/api/providers/openrouter/models/route.ts index 2703870aa8..cf3419e5ba 100644 --- a/apps/sim/app/api/providers/openrouter/models/route.ts +++ b/apps/sim/app/api/providers/openrouter/models/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { filterBlacklistedModels } from '@/providers/utils' const logger = createLogger('OpenRouterModelsAPI') diff --git a/apps/sim/app/api/providers/route.ts b/apps/sim/app/api/providers/route.ts index 04910ed1c8..a78a5f999d 100644 --- a/apps/sim/app/api/providers/route.ts +++ b/apps/sim/app/api/providers/route.ts @@ -1,13 +1,12 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils' import type { StreamingExecution } from '@/executor/types' import { executeProviderRequest } from '@/providers' -import { getApiKey } from '@/providers/utils' const logger = createLogger('ProvidersAPI') @@ -80,23 +79,20 @@ export async function POST(request: NextRequest) { verbosity, }) - let finalApiKey: string + let finalApiKey: string | undefined = apiKey try { if (provider === 'vertex' && vertexCredential) { finalApiKey = await resolveVertexCredential(requestId, vertexCredential) - } else { - finalApiKey = getApiKey(provider, model, apiKey) } } catch (error) { - logger.error(`[${requestId}] Failed to get API key:`, { + logger.error(`[${requestId}] Failed to resolve Vertex credential:`, { provider, model, error: error instanceof Error ? error.message : String(error), - hasProvidedApiKey: !!apiKey, hasVertexCredential: !!vertexCredential, }) return NextResponse.json( - { error: error instanceof Error ? error.message : 'API key error' }, + { error: error instanceof Error ? error.message : 'Credential error' }, { status: 400 } ) } @@ -108,7 +104,6 @@ export async function POST(request: NextRequest) { hasApiKey: !!finalApiKey, }) - // Execute provider request directly with the managed key const response = await executeProviderRequest(provider, { model, systemPrompt, diff --git a/apps/sim/app/api/providers/vllm/models/route.ts b/apps/sim/app/api/providers/vllm/models/route.ts index f9f76332ea..65bbccbbe1 100644 --- a/apps/sim/app/api/providers/vllm/models/route.ts +++ b/apps/sim/app/api/providers/vllm/models/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('VLLMModelsAPI') diff --git a/apps/sim/app/api/proxy/image/route.ts b/apps/sim/app/api/proxy/image/route.ts index 70d1fd81d3..1caf695fb9 100644 --- a/apps/sim/app/api/proxy/image/route.ts +++ b/apps/sim/app/api/proxy/image/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateImageUrl } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ImageProxyAPI') diff --git a/apps/sim/app/api/proxy/route.ts b/apps/sim/app/api/proxy/route.ts index cb223aebd7..24702aa48f 100644 --- a/apps/sim/app/api/proxy/route.ts +++ b/apps/sim/app/api/proxy/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { z } from 'zod' @@ -7,7 +8,6 @@ import { isDev } from '@/lib/core/config/feature-flags' import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { executeTool } from '@/tools' import { getTool, validateRequiredParametersAfterMerge } from '@/tools/utils' diff --git a/apps/sim/app/api/proxy/stt/route.ts b/apps/sim/app/api/proxy/stt/route.ts index d2a94cda74..a7b05f19a1 100644 --- a/apps/sim/app/api/proxy/stt/route.ts +++ b/apps/sim/app/api/proxy/stt/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { extractAudioFromVideo, isVideoFile } from '@/lib/audio/extractor' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { createLogger } from '@/lib/logs/console/logger' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import type { UserFile } from '@/executor/types' import type { TranscriptSegment } from '@/tools/stt/types' diff --git a/apps/sim/app/api/proxy/tts/route.ts b/apps/sim/app/api/proxy/tts/route.ts index f3db903461..1ae734f21b 100644 --- a/apps/sim/app/api/proxy/tts/route.ts +++ b/apps/sim/app/api/proxy/tts/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { StorageService } from '@/lib/uploads' const logger = createLogger('ProxyTTSAPI') diff --git a/apps/sim/app/api/proxy/tts/stream/route.ts b/apps/sim/app/api/proxy/tts/stream/route.ts index 316c0d0a0a..35b045fc94 100644 --- a/apps/sim/app/api/proxy/tts/stream/route.ts +++ b/apps/sim/app/api/proxy/tts/stream/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { env } from '@/lib/core/config/env' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { validateAuthToken } from '@/app/api/chat/utils' const logger = createLogger('ProxyTTSStreamAPI') diff --git a/apps/sim/app/api/proxy/tts/unified/route.ts b/apps/sim/app/api/proxy/tts/unified/route.ts index 827dfae61c..cf9464452b 100644 --- a/apps/sim/app/api/proxy/tts/unified/route.ts +++ b/apps/sim/app/api/proxy/tts/unified/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { StorageService } from '@/lib/uploads' import type { AzureTtsParams, diff --git a/apps/sim/app/api/proxy/video/route.ts b/apps/sim/app/api/proxy/video/route.ts index 9aa4091ef6..9074a290a1 100644 --- a/apps/sim/app/api/proxy/video/route.ts +++ b/apps/sim/app/api/proxy/video/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' -import { createLogger } from '@/lib/logs/console/logger' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import type { UserFile } from '@/executor/types' import type { VideoRequestBody } from '@/tools/video/types' diff --git a/apps/sim/app/api/resume/[workflowId]/[executionId]/[contextId]/route.ts b/apps/sim/app/api/resume/[workflowId]/[executionId]/[contextId]/route.ts index 8fcdfe59d8..9feef89bf3 100644 --- a/apps/sim/app/api/resume/[workflowId]/[executionId]/[contextId]/route.ts +++ b/apps/sim/app/api/resume/[workflowId]/[executionId]/[contextId]/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' import { preprocessExecution } from '@/lib/execution/preprocessing' -import { createLogger } from '@/lib/logs/console/logger' import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-manager' import { validateWorkflowAccess } from '@/app/api/workflows/middleware' diff --git a/apps/sim/app/api/resume/[workflowId]/[executionId]/route.ts b/apps/sim/app/api/resume/[workflowId]/[executionId]/route.ts index f27ad36c78..1e3cc4b53e 100644 --- a/apps/sim/app/api/resume/[workflowId]/[executionId]/route.ts +++ b/apps/sim/app/api/resume/[workflowId]/[executionId]/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-manager' import { validateWorkflowAccess } from '@/app/api/workflows/middleware' diff --git a/apps/sim/app/api/schedules/[id]/route.test.ts b/apps/sim/app/api/schedules/[id]/route.test.ts index a24fb07a78..0ab1195884 100644 --- a/apps/sim/app/api/schedules/[id]/route.test.ts +++ b/apps/sim/app/api/schedules/[id]/route.test.ts @@ -43,7 +43,7 @@ vi.mock('@/lib/core/utils/request', () => ({ generateRequestId: () => 'test-request-id', })) -vi.mock('@/lib/logs/console/logger', () => ({ +vi.mock('@sim/logger', () => ({ createLogger: () => ({ info: vi.fn(), warn: vi.fn(), diff --git a/apps/sim/app/api/schedules/[id]/route.ts b/apps/sim/app/api/schedules/[id]/route.ts index c3aa491e00..031358ba25 100644 --- a/apps/sim/app/api/schedules/[id]/route.ts +++ b/apps/sim/app/api/schedules/[id]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { workflow, workflowSchedule } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { validateCronExpression } from '@/lib/workflows/schedules/utils' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/schedules/execute/route.ts b/apps/sim/app/api/schedules/execute/route.ts index 5254028d61..cadad529f5 100644 --- a/apps/sim/app/api/schedules/execute/route.ts +++ b/apps/sim/app/api/schedules/execute/route.ts @@ -1,11 +1,11 @@ import { db, workflowSchedule } from '@sim/db' +import { createLogger } from '@sim/logger' import { tasks } from '@trigger.dev/sdk' import { and, eq, isNull, lt, lte, not, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { executeScheduleJob } from '@/background/schedule-execution' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/schedules/route.test.ts b/apps/sim/app/api/schedules/route.test.ts index ac1ece178d..986e731138 100644 --- a/apps/sim/app/api/schedules/route.test.ts +++ b/apps/sim/app/api/schedules/route.test.ts @@ -40,7 +40,7 @@ vi.mock('@/lib/core/utils/request', () => ({ generateRequestId: () => 'test-request-id', })) -vi.mock('@/lib/logs/console/logger', () => ({ +vi.mock('@sim/logger', () => ({ createLogger: () => ({ info: vi.fn(), warn: vi.fn(), @@ -144,7 +144,7 @@ describe('Schedule GET API', () => { it('indicates disabled schedule with failures', async () => { mockDbChain([ [{ userId: 'user-1', workspaceId: null }], - [{ id: 'sched-1', status: 'disabled', failedCount: 10 }], + [{ id: 'sched-1', status: 'disabled', failedCount: 100 }], ]) const res = await GET(createRequest('http://test/api/schedules?workflowId=wf-1')) diff --git a/apps/sim/app/api/schedules/route.ts b/apps/sim/app/api/schedules/route.ts index 07f8cbc952..3b6ba81864 100644 --- a/apps/sim/app/api/schedules/route.ts +++ b/apps/sim/app/api/schedules/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { workflow, workflowSchedule } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('ScheduledAPI') diff --git a/apps/sim/app/api/status/route.ts b/apps/sim/app/api/status/route.ts index ebc5e98a9a..8c7a28a174 100644 --- a/apps/sim/app/api/status/route.ts +++ b/apps/sim/app/api/status/route.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import type { IncidentIOWidgetResponse, StatusResponse, StatusType } from '@/app/api/status/types' const logger = createLogger('StatusAPI') diff --git a/apps/sim/app/api/telemetry/route.ts b/apps/sim/app/api/telemetry/route.ts index e7bc3bc893..1eae8acdc9 100644 --- a/apps/sim/app/api/telemetry/route.ts +++ b/apps/sim/app/api/telemetry/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { env } from '@/lib/core/config/env' import { isProd } from '@/lib/core/config/feature-flags' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('TelemetryAPI') diff --git a/apps/sim/app/api/templates/[id]/approve/route.ts b/apps/sim/app/api/templates/[id]/approve/route.ts index 9f212829bf..c15c1916ee 100644 --- a/apps/sim/app/api/templates/[id]/approve/route.ts +++ b/apps/sim/app/api/templates/[id]/approve/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { templates } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { verifySuperUser } from '@/lib/templates/permissions' const logger = createLogger('TemplateApprovalAPI') diff --git a/apps/sim/app/api/templates/[id]/og-image/route.ts b/apps/sim/app/api/templates/[id]/og-image/route.ts index f628096b21..f6b2dd94bf 100644 --- a/apps/sim/app/api/templates/[id]/og-image/route.ts +++ b/apps/sim/app/api/templates/[id]/og-image/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { templates } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { verifyTemplateOwnership } from '@/lib/templates/permissions' import { uploadFile } from '@/lib/uploads/core/storage-service' import { isValidPng } from '@/lib/uploads/utils/validation' diff --git a/apps/sim/app/api/templates/[id]/reject/route.ts b/apps/sim/app/api/templates/[id]/reject/route.ts index 425f907833..af5ed2e12b 100644 --- a/apps/sim/app/api/templates/[id]/reject/route.ts +++ b/apps/sim/app/api/templates/[id]/reject/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { templates } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { verifySuperUser } from '@/lib/templates/permissions' const logger = createLogger('TemplateRejectionAPI') diff --git a/apps/sim/app/api/templates/[id]/route.ts b/apps/sim/app/api/templates/[id]/route.ts index d19731f49e..6feef0f32f 100644 --- a/apps/sim/app/api/templates/[id]/route.ts +++ b/apps/sim/app/api/templates/[id]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { templateCreators, templates, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { extractRequiredCredentials, sanitizeCredentials, @@ -169,7 +169,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ if (creatorId !== undefined) updateData.creatorId = creatorId if (updateState && template.workflowId) { - const { verifyWorkflowAccess } = await import('@/socket-server/middleware/permissions') + const { verifyWorkflowAccess } = await import('@/socket/middleware/permissions') const { hasAccess: hasWorkflowAccess } = await verifyWorkflowAccess( session.user.id, template.workflowId diff --git a/apps/sim/app/api/templates/[id]/star/route.ts b/apps/sim/app/api/templates/[id]/star/route.ts index 26fd3a9b4a..d7e23c9d45 100644 --- a/apps/sim/app/api/templates/[id]/star/route.ts +++ b/apps/sim/app/api/templates/[id]/star/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { templateStars, templates } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('TemplateStarAPI') diff --git a/apps/sim/app/api/templates/[id]/use/route.ts b/apps/sim/app/api/templates/[id]/use/route.ts index d32572611b..3ffb9f5b27 100644 --- a/apps/sim/app/api/templates/[id]/use/route.ts +++ b/apps/sim/app/api/templates/[id]/use/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { templates, workflow, workflowDeploymentVersion } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { regenerateWorkflowStateIds } from '@/lib/workflows/persistence/utils' const logger = createLogger('TemplateUseAPI') diff --git a/apps/sim/app/api/templates/approved/sanitized/route.ts b/apps/sim/app/api/templates/approved/sanitized/route.ts index d8ddb6a97f..2b6fad9652 100644 --- a/apps/sim/app/api/templates/approved/sanitized/route.ts +++ b/apps/sim/app/api/templates/approved/sanitized/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { templates } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { checkInternalApiKey } from '@/lib/copilot/utils' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { sanitizeForCopilot } from '@/lib/workflows/sanitization/json-sanitizer' const logger = createLogger('TemplatesSanitizedAPI') diff --git a/apps/sim/app/api/templates/route.ts b/apps/sim/app/api/templates/route.ts index 2cbbc9469c..7177aa0050 100644 --- a/apps/sim/app/api/templates/route.ts +++ b/apps/sim/app/api/templates/route.ts @@ -7,13 +7,13 @@ import { workflow, workflowDeploymentVersion, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq, ilike, or, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { extractRequiredCredentials, sanitizeCredentials, diff --git a/apps/sim/app/api/tools/asana/add-comment/route.ts b/apps/sim/app/api/tools/asana/add-comment/route.ts index bd00e151c0..b6ef38d944 100644 --- a/apps/sim/app/api/tools/asana/add-comment/route.ts +++ b/apps/sim/app/api/tools/asana/add-comment/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/asana/create-task/route.ts b/apps/sim/app/api/tools/asana/create-task/route.ts index 69200e6d90..41cd673295 100644 --- a/apps/sim/app/api/tools/asana/create-task/route.ts +++ b/apps/sim/app/api/tools/asana/create-task/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/asana/get-projects/route.ts b/apps/sim/app/api/tools/asana/get-projects/route.ts index f26da3fd9a..c57fae722b 100644 --- a/apps/sim/app/api/tools/asana/get-projects/route.ts +++ b/apps/sim/app/api/tools/asana/get-projects/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/asana/get-task/route.ts b/apps/sim/app/api/tools/asana/get-task/route.ts index bcc459e4c5..d60902fec2 100644 --- a/apps/sim/app/api/tools/asana/get-task/route.ts +++ b/apps/sim/app/api/tools/asana/get-task/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/asana/search-tasks/route.ts b/apps/sim/app/api/tools/asana/search-tasks/route.ts index 397b9b07ce..d9b7e82886 100644 --- a/apps/sim/app/api/tools/asana/search-tasks/route.ts +++ b/apps/sim/app/api/tools/asana/search-tasks/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/asana/update-task/route.ts b/apps/sim/app/api/tools/asana/update-task/route.ts index e83cc5ef9b..3bc242a293 100644 --- a/apps/sim/app/api/tools/asana/update-task/route.ts +++ b/apps/sim/app/api/tools/asana/update-task/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/confluence/attachment/route.ts b/apps/sim/app/api/tools/confluence/attachment/route.ts index bfaa2e82a2..7b55dc719a 100644 --- a/apps/sim/app/api/tools/confluence/attachment/route.ts +++ b/apps/sim/app/api/tools/confluence/attachment/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceAttachmentAPI') diff --git a/apps/sim/app/api/tools/confluence/attachments/route.ts b/apps/sim/app/api/tools/confluence/attachments/route.ts index 869c3b988d..6154f3e08b 100644 --- a/apps/sim/app/api/tools/confluence/attachments/route.ts +++ b/apps/sim/app/api/tools/confluence/attachments/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceAttachmentsAPI') diff --git a/apps/sim/app/api/tools/confluence/comment/route.ts b/apps/sim/app/api/tools/confluence/comment/route.ts index 94fd963af4..c94ac85e98 100644 --- a/apps/sim/app/api/tools/confluence/comment/route.ts +++ b/apps/sim/app/api/tools/confluence/comment/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { z } from 'zod' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceCommentAPI') diff --git a/apps/sim/app/api/tools/confluence/comments/route.ts b/apps/sim/app/api/tools/confluence/comments/route.ts index b9717e73d3..eac22a2b2e 100644 --- a/apps/sim/app/api/tools/confluence/comments/route.ts +++ b/apps/sim/app/api/tools/confluence/comments/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceCommentsAPI') diff --git a/apps/sim/app/api/tools/confluence/create-page/route.ts b/apps/sim/app/api/tools/confluence/create-page/route.ts index c50acf93e8..218b4ff61f 100644 --- a/apps/sim/app/api/tools/confluence/create-page/route.ts +++ b/apps/sim/app/api/tools/confluence/create-page/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceCreatePageAPI') diff --git a/apps/sim/app/api/tools/confluence/labels/route.ts b/apps/sim/app/api/tools/confluence/labels/route.ts index 6ab71167a5..557c542d12 100644 --- a/apps/sim/app/api/tools/confluence/labels/route.ts +++ b/apps/sim/app/api/tools/confluence/labels/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceLabelsAPI') diff --git a/apps/sim/app/api/tools/confluence/page/route.ts b/apps/sim/app/api/tools/confluence/page/route.ts index d9fdceb0eb..685eefffd2 100644 --- a/apps/sim/app/api/tools/confluence/page/route.ts +++ b/apps/sim/app/api/tools/confluence/page/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { z } from 'zod' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluencePageAPI') diff --git a/apps/sim/app/api/tools/confluence/pages/route.ts b/apps/sim/app/api/tools/confluence/pages/route.ts index 67fed46f71..e83198ffee 100644 --- a/apps/sim/app/api/tools/confluence/pages/route.ts +++ b/apps/sim/app/api/tools/confluence/pages/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluencePagesAPI') diff --git a/apps/sim/app/api/tools/confluence/search/route.ts b/apps/sim/app/api/tools/confluence/search/route.ts index 1c522898c6..3782aace3e 100644 --- a/apps/sim/app/api/tools/confluence/search/route.ts +++ b/apps/sim/app/api/tools/confluence/search/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/confluence/space/route.ts b/apps/sim/app/api/tools/confluence/space/route.ts index 75bf8b324d..bda98ce6bc 100644 --- a/apps/sim/app/api/tools/confluence/space/route.ts +++ b/apps/sim/app/api/tools/confluence/space/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceSpaceAPI') diff --git a/apps/sim/app/api/tools/confluence/spaces/route.ts b/apps/sim/app/api/tools/confluence/spaces/route.ts index 028257e975..6d66aae097 100644 --- a/apps/sim/app/api/tools/confluence/spaces/route.ts +++ b/apps/sim/app/api/tools/confluence/spaces/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getConfluenceCloudId } from '@/tools/confluence/utils' const logger = createLogger('ConfluenceSpacesAPI') diff --git a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts index 21e9f75ef8..7487b6ee50 100644 --- a/apps/sim/app/api/tools/confluence/upload-attachment/route.ts +++ b/apps/sim/app/api/tools/confluence/upload-attachment/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { getConfluenceCloudId } from '@/tools/confluence/utils' diff --git a/apps/sim/app/api/tools/custom/route.test.ts b/apps/sim/app/api/tools/custom/route.test.ts index 5894171a24..88f61ca129 100644 --- a/apps/sim/app/api/tools/custom/route.test.ts +++ b/apps/sim/app/api/tools/custom/route.test.ts @@ -209,7 +209,7 @@ describe('Custom Tools API Routes', () => { })) // Mock logger - vi.doMock('@/lib/logs/console/logger', () => ({ + vi.doMock('@sim/logger', () => ({ createLogger: vi.fn().mockReturnValue({ info: vi.fn(), error: vi.fn(), diff --git a/apps/sim/app/api/tools/custom/route.ts b/apps/sim/app/api/tools/custom/route.ts index 0bb32c5148..e3c68302d0 100644 --- a/apps/sim/app/api/tools/custom/route.ts +++ b/apps/sim/app/api/tools/custom/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { customTools, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq, isNull, or } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { upsertCustomTools } from '@/lib/workflows/custom-tools/operations' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/tools/discord/channels/route.ts b/apps/sim/app/api/tools/discord/channels/route.ts index 25eed7c5c0..23b33dd762 100644 --- a/apps/sim/app/api/tools/discord/channels/route.ts +++ b/apps/sim/app/api/tools/discord/channels/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateNumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' interface DiscordChannel { id: string diff --git a/apps/sim/app/api/tools/discord/send-message/route.ts b/apps/sim/app/api/tools/discord/send-message/route.ts index ef6df171dc..cb113a460b 100644 --- a/apps/sim/app/api/tools/discord/send-message/route.ts +++ b/apps/sim/app/api/tools/discord/send-message/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateNumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/discord/servers/route.ts b/apps/sim/app/api/tools/discord/servers/route.ts index c7fa8c7561..c589ad4b20 100644 --- a/apps/sim/app/api/tools/discord/servers/route.ts +++ b/apps/sim/app/api/tools/discord/servers/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateNumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' interface DiscordServer { id: string diff --git a/apps/sim/app/api/tools/drive/file/route.ts b/apps/sim/app/api/tools/drive/file/route.ts index 62eb9686c7..931253b04b 100644 --- a/apps/sim/app/api/tools/drive/file/route.ts +++ b/apps/sim/app/api/tools/drive/file/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/drive/files/route.ts b/apps/sim/app/api/tools/drive/files/route.ts index fa4bb1596f..5584fe392f 100644 --- a/apps/sim/app/api/tools/drive/files/route.ts +++ b/apps/sim/app/api/tools/drive/files/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/add-label/route.ts b/apps/sim/app/api/tools/gmail/add-label/route.ts index a8f1391804..5654c10f5e 100644 --- a/apps/sim/app/api/tools/gmail/add-label/route.ts +++ b/apps/sim/app/api/tools/gmail/add-label/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/archive/route.ts b/apps/sim/app/api/tools/gmail/archive/route.ts index 2f62d211ed..604d5bbce5 100644 --- a/apps/sim/app/api/tools/gmail/archive/route.ts +++ b/apps/sim/app/api/tools/gmail/archive/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/delete/route.ts b/apps/sim/app/api/tools/gmail/delete/route.ts index ce3779c9d6..08730b1cfa 100644 --- a/apps/sim/app/api/tools/gmail/delete/route.ts +++ b/apps/sim/app/api/tools/gmail/delete/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/draft/route.ts b/apps/sim/app/api/tools/gmail/draft/route.ts index 90f849b0d0..e852d43786 100644 --- a/apps/sim/app/api/tools/gmail/draft/route.ts +++ b/apps/sim/app/api/tools/gmail/draft/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { diff --git a/apps/sim/app/api/tools/gmail/label/route.ts b/apps/sim/app/api/tools/gmail/label/route.ts index c7042034c3..7994c91fd0 100644 --- a/apps/sim/app/api/tools/gmail/label/route.ts +++ b/apps/sim/app/api/tools/gmail/label/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/labels/route.ts b/apps/sim/app/api/tools/gmail/labels/route.ts index 945db0afa4..36d9040ca4 100644 --- a/apps/sim/app/api/tools/gmail/labels/route.ts +++ b/apps/sim/app/api/tools/gmail/labels/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/mark-read/route.ts b/apps/sim/app/api/tools/gmail/mark-read/route.ts index 3525869567..8e0592ee8d 100644 --- a/apps/sim/app/api/tools/gmail/mark-read/route.ts +++ b/apps/sim/app/api/tools/gmail/mark-read/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/mark-unread/route.ts b/apps/sim/app/api/tools/gmail/mark-unread/route.ts index 17aca8e7fc..901023fcdb 100644 --- a/apps/sim/app/api/tools/gmail/mark-unread/route.ts +++ b/apps/sim/app/api/tools/gmail/mark-unread/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/move/route.ts b/apps/sim/app/api/tools/gmail/move/route.ts index 358768fe3e..37af235ff5 100644 --- a/apps/sim/app/api/tools/gmail/move/route.ts +++ b/apps/sim/app/api/tools/gmail/move/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/remove-label/route.ts b/apps/sim/app/api/tools/gmail/remove-label/route.ts index 74e179c910..a6bcd0e4c8 100644 --- a/apps/sim/app/api/tools/gmail/remove-label/route.ts +++ b/apps/sim/app/api/tools/gmail/remove-label/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/gmail/send/route.ts b/apps/sim/app/api/tools/gmail/send/route.ts index d9c3dc9ecc..f624eba41f 100644 --- a/apps/sim/app/api/tools/gmail/send/route.ts +++ b/apps/sim/app/api/tools/gmail/send/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { diff --git a/apps/sim/app/api/tools/gmail/unarchive/route.ts b/apps/sim/app/api/tools/gmail/unarchive/route.ts index 28bf5b879c..1479430c4a 100644 --- a/apps/sim/app/api/tools/gmail/unarchive/route.ts +++ b/apps/sim/app/api/tools/gmail/unarchive/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/google_calendar/calendars/route.ts b/apps/sim/app/api/tools/google_calendar/calendars/route.ts index 77b6291bfe..f934d6dd41 100644 --- a/apps/sim/app/api/tools/google_calendar/calendars/route.ts +++ b/apps/sim/app/api/tools/google_calendar/calendars/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' import { isUuidV4 } from '@/executor/constants' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/google_drive/upload/route.ts b/apps/sim/app/api/tools/google_drive/upload/route.ts index 13acfd3b25..fc9b26a8ea 100644 --- a/apps/sim/app/api/tools/google_drive/upload/route.ts +++ b/apps/sim/app/api/tools/google_drive/upload/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { diff --git a/apps/sim/app/api/tools/jira/issue/route.ts b/apps/sim/app/api/tools/jira/issue/route.ts index 5e2f8e6436..d77d98ffe3 100644 --- a/apps/sim/app/api/tools/jira/issue/route.ts +++ b/apps/sim/app/api/tools/jira/issue/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getJiraCloudId } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/jira/issues/route.ts b/apps/sim/app/api/tools/jira/issues/route.ts index 68368596cc..cc5f7a4cc9 100644 --- a/apps/sim/app/api/tools/jira/issues/route.ts +++ b/apps/sim/app/api/tools/jira/issues/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getJiraCloudId } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/jira/projects/route.ts b/apps/sim/app/api/tools/jira/projects/route.ts index 9551cde78d..e85e4cc947 100644 --- a/apps/sim/app/api/tools/jira/projects/route.ts +++ b/apps/sim/app/api/tools/jira/projects/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getJiraCloudId } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/jira/update/route.ts b/apps/sim/app/api/tools/jira/update/route.ts index 1a924faa23..b1e67f9531 100644 --- a/apps/sim/app/api/tools/jira/update/route.ts +++ b/apps/sim/app/api/tools/jira/update/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { z } from 'zod' import { validateJiraCloudId, validateJiraIssueKey } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getJiraCloudId } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/jira/write/route.ts b/apps/sim/app/api/tools/jira/write/route.ts index 0eb1148926..c80434ceb4 100644 --- a/apps/sim/app/api/tools/jira/write/route.ts +++ b/apps/sim/app/api/tools/jira/write/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { getJiraCloudId } from '@/tools/jira/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/linear/projects/route.ts b/apps/sim/app/api/tools/linear/projects/route.ts index 51863961d9..9e0ff73354 100644 --- a/apps/sim/app/api/tools/linear/projects/route.ts +++ b/apps/sim/app/api/tools/linear/projects/route.ts @@ -1,9 +1,9 @@ import type { Project } from '@linear/sdk' import { LinearClient } from '@linear/sdk' +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/linear/teams/route.ts b/apps/sim/app/api/tools/linear/teams/route.ts index cf1e36ce20..ee82c15425 100644 --- a/apps/sim/app/api/tools/linear/teams/route.ts +++ b/apps/sim/app/api/tools/linear/teams/route.ts @@ -1,9 +1,9 @@ import type { Team } from '@linear/sdk' import { LinearClient } from '@linear/sdk' +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/mail/send/route.ts b/apps/sim/app/api/tools/mail/send/route.ts index ede1dc9a64..d98b9b9bc0 100644 --- a/apps/sim/app/api/tools/mail/send/route.ts +++ b/apps/sim/app/api/tools/mail/send/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { Resend } from 'resend' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/microsoft-teams/channels/route.ts b/apps/sim/app/api/tools/microsoft-teams/channels/route.ts index 0d07ca4335..0dc1fa8a0a 100644 --- a/apps/sim/app/api/tools/microsoft-teams/channels/route.ts +++ b/apps/sim/app/api/tools/microsoft-teams/channels/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/microsoft-teams/chats/route.ts b/apps/sim/app/api/tools/microsoft-teams/chats/route.ts index 356f92475a..a0113647a6 100644 --- a/apps/sim/app/api/tools/microsoft-teams/chats/route.ts +++ b/apps/sim/app/api/tools/microsoft-teams/chats/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/microsoft-teams/teams/route.ts b/apps/sim/app/api/tools/microsoft-teams/teams/route.ts index 4dc4513535..a903815abe 100644 --- a/apps/sim/app/api/tools/microsoft-teams/teams/route.ts +++ b/apps/sim/app/api/tools/microsoft-teams/teams/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/microsoft_planner/tasks/route.ts b/apps/sim/app/api/tools/microsoft_planner/tasks/route.ts index 69b075399f..67566ad8a8 100644 --- a/apps/sim/app/api/tools/microsoft_planner/tasks/route.ts +++ b/apps/sim/app/api/tools/microsoft_planner/tasks/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' import type { PlannerTask } from '@/tools/microsoft_planner/types' diff --git a/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts b/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts index 44f91f1e8f..a604ca445d 100644 --- a/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/delete_chat_message/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts index a3f703b30e..3c21168a0e 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_channel/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { resolveMentionsForChannel, type TeamsMention } from '@/tools/microsoft_teams/utils' diff --git a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts index 635f48f917..0682429e7c 100644 --- a/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts +++ b/apps/sim/app/api/tools/microsoft_teams/write_chat/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { resolveMentionsForChat, type TeamsMention } from '@/tools/microsoft_teams/utils' diff --git a/apps/sim/app/api/tools/mistral/parse/route.ts b/apps/sim/app/api/tools/mistral/parse/route.ts index d3cd52c582..b31029d1bc 100644 --- a/apps/sim/app/api/tools/mistral/parse/route.ts +++ b/apps/sim/app/api/tools/mistral/parse/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { StorageService } from '@/lib/uploads' import { extractStorageKey, inferContextFromKey } from '@/lib/uploads/utils/file-utils' import { verifyFileAccess } from '@/app/api/files/authorization' diff --git a/apps/sim/app/api/tools/mongodb/delete/route.ts b/apps/sim/app/api/tools/mongodb/delete/route.ts index 56058881a9..b634677258 100644 --- a/apps/sim/app/api/tools/mongodb/delete/route.ts +++ b/apps/sim/app/api/tools/mongodb/delete/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBDeleteAPI') diff --git a/apps/sim/app/api/tools/mongodb/execute/route.ts b/apps/sim/app/api/tools/mongodb/execute/route.ts index bb1b2f0cda..afae959759 100644 --- a/apps/sim/app/api/tools/mongodb/execute/route.ts +++ b/apps/sim/app/api/tools/mongodb/execute/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMongoDBConnection, sanitizeCollectionName, validatePipeline } from '../utils' const logger = createLogger('MongoDBExecuteAPI') diff --git a/apps/sim/app/api/tools/mongodb/insert/route.ts b/apps/sim/app/api/tools/mongodb/insert/route.ts index b71a9efdd8..fd350ef3e5 100644 --- a/apps/sim/app/api/tools/mongodb/insert/route.ts +++ b/apps/sim/app/api/tools/mongodb/insert/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMongoDBConnection, sanitizeCollectionName } from '../utils' const logger = createLogger('MongoDBInsertAPI') diff --git a/apps/sim/app/api/tools/mongodb/query/route.ts b/apps/sim/app/api/tools/mongodb/query/route.ts index 1c451e5bc6..ae8276dea6 100644 --- a/apps/sim/app/api/tools/mongodb/query/route.ts +++ b/apps/sim/app/api/tools/mongodb/query/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBQueryAPI') diff --git a/apps/sim/app/api/tools/mongodb/update/route.ts b/apps/sim/app/api/tools/mongodb/update/route.ts index c4a420bf66..ac24d55396 100644 --- a/apps/sim/app/api/tools/mongodb/update/route.ts +++ b/apps/sim/app/api/tools/mongodb/update/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils' const logger = createLogger('MongoDBUpdateAPI') diff --git a/apps/sim/app/api/tools/mysql/delete/route.ts b/apps/sim/app/api/tools/mysql/delete/route.ts index 4387ab1277..4b33288036 100644 --- a/apps/sim/app/api/tools/mysql/delete/route.ts +++ b/apps/sim/app/api/tools/mysql/delete/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { buildDeleteQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils' const logger = createLogger('MySQLDeleteAPI') diff --git a/apps/sim/app/api/tools/mysql/execute/route.ts b/apps/sim/app/api/tools/mysql/execute/route.ts index eea3bd142b..8e4ac396af 100644 --- a/apps/sim/app/api/tools/mysql/execute/route.ts +++ b/apps/sim/app/api/tools/mysql/execute/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils' const logger = createLogger('MySQLExecuteAPI') diff --git a/apps/sim/app/api/tools/mysql/insert/route.ts b/apps/sim/app/api/tools/mysql/insert/route.ts index 04e30a4ad6..5e8fd4674b 100644 --- a/apps/sim/app/api/tools/mysql/insert/route.ts +++ b/apps/sim/app/api/tools/mysql/insert/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { buildInsertQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils' const logger = createLogger('MySQLInsertAPI') diff --git a/apps/sim/app/api/tools/mysql/query/route.ts b/apps/sim/app/api/tools/mysql/query/route.ts index 791b67dacb..ad8535ce29 100644 --- a/apps/sim/app/api/tools/mysql/query/route.ts +++ b/apps/sim/app/api/tools/mysql/query/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils' const logger = createLogger('MySQLQueryAPI') diff --git a/apps/sim/app/api/tools/mysql/update/route.ts b/apps/sim/app/api/tools/mysql/update/route.ts index f1b8e8c64a..c196bf9248 100644 --- a/apps/sim/app/api/tools/mysql/update/route.ts +++ b/apps/sim/app/api/tools/mysql/update/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { buildUpdateQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils' const logger = createLogger('MySQLUpdateAPI') diff --git a/apps/sim/app/api/tools/neo4j/create/route.ts b/apps/sim/app/api/tools/neo4j/create/route.ts index a8d8ed12a5..3fb66142a3 100644 --- a/apps/sim/app/api/tools/neo4j/create/route.ts +++ b/apps/sim/app/api/tools/neo4j/create/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { convertNeo4jTypesToJSON, createNeo4jDriver, diff --git a/apps/sim/app/api/tools/neo4j/delete/route.ts b/apps/sim/app/api/tools/neo4j/delete/route.ts index baa639b229..e010fe8b6a 100644 --- a/apps/sim/app/api/tools/neo4j/delete/route.ts +++ b/apps/sim/app/api/tools/neo4j/delete/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createNeo4jDriver, validateCypherQuery } from '@/app/api/tools/neo4j/utils' const logger = createLogger('Neo4jDeleteAPI') diff --git a/apps/sim/app/api/tools/neo4j/execute/route.ts b/apps/sim/app/api/tools/neo4j/execute/route.ts index 91eb8379b7..79d98975ff 100644 --- a/apps/sim/app/api/tools/neo4j/execute/route.ts +++ b/apps/sim/app/api/tools/neo4j/execute/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { convertNeo4jTypesToJSON, createNeo4jDriver, diff --git a/apps/sim/app/api/tools/neo4j/merge/route.ts b/apps/sim/app/api/tools/neo4j/merge/route.ts index 3e43762bb7..28f00a7e06 100644 --- a/apps/sim/app/api/tools/neo4j/merge/route.ts +++ b/apps/sim/app/api/tools/neo4j/merge/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { convertNeo4jTypesToJSON, createNeo4jDriver, diff --git a/apps/sim/app/api/tools/neo4j/query/route.ts b/apps/sim/app/api/tools/neo4j/query/route.ts index f5b8084959..84dd3cb511 100644 --- a/apps/sim/app/api/tools/neo4j/query/route.ts +++ b/apps/sim/app/api/tools/neo4j/query/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { convertNeo4jTypesToJSON, createNeo4jDriver, diff --git a/apps/sim/app/api/tools/neo4j/update/route.ts b/apps/sim/app/api/tools/neo4j/update/route.ts index 1f0d84015e..e5f2bfb76c 100644 --- a/apps/sim/app/api/tools/neo4j/update/route.ts +++ b/apps/sim/app/api/tools/neo4j/update/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { convertNeo4jTypesToJSON, createNeo4jDriver, diff --git a/apps/sim/app/api/tools/onedrive/files/route.ts b/apps/sim/app/api/tools/onedrive/files/route.ts index 0a551f5bd8..c894834576 100644 --- a/apps/sim/app/api/tools/onedrive/files/route.ts +++ b/apps/sim/app/api/tools/onedrive/files/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/onedrive/folder/route.ts b/apps/sim/app/api/tools/onedrive/folder/route.ts index 7f93d0e3fd..2cf68fa533 100644 --- a/apps/sim/app/api/tools/onedrive/folder/route.ts +++ b/apps/sim/app/api/tools/onedrive/folder/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/onedrive/folders/route.ts b/apps/sim/app/api/tools/onedrive/folders/route.ts index 61adffde4d..1eac6c2678 100644 --- a/apps/sim/app/api/tools/onedrive/folders/route.ts +++ b/apps/sim/app/api/tools/onedrive/folders/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/onedrive/upload/route.ts b/apps/sim/app/api/tools/onedrive/upload/route.ts index c5c4d29ae0..3e7fef64f4 100644 --- a/apps/sim/app/api/tools/onedrive/upload/route.ts +++ b/apps/sim/app/api/tools/onedrive/upload/route.ts @@ -1,10 +1,10 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import * as XLSX from 'xlsx' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getExtensionFromMimeType, processSingleFileToUserFile, diff --git a/apps/sim/app/api/tools/outlook/copy/route.ts b/apps/sim/app/api/tools/outlook/copy/route.ts index b4435931b5..0766b97322 100644 --- a/apps/sim/app/api/tools/outlook/copy/route.ts +++ b/apps/sim/app/api/tools/outlook/copy/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/delete/route.ts b/apps/sim/app/api/tools/outlook/delete/route.ts index 7e47dafb02..b5f8fafce5 100644 --- a/apps/sim/app/api/tools/outlook/delete/route.ts +++ b/apps/sim/app/api/tools/outlook/delete/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/draft/route.ts b/apps/sim/app/api/tools/outlook/draft/route.ts index 16ed64c02b..6dfdcec5c4 100644 --- a/apps/sim/app/api/tools/outlook/draft/route.ts +++ b/apps/sim/app/api/tools/outlook/draft/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/outlook/folders/route.ts b/apps/sim/app/api/tools/outlook/folders/route.ts index 91395e6846..7be86ebff0 100644 --- a/apps/sim/app/api/tools/outlook/folders/route.ts +++ b/apps/sim/app/api/tools/outlook/folders/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/mark-read/route.ts b/apps/sim/app/api/tools/outlook/mark-read/route.ts index 1873249d7a..b8b26515c6 100644 --- a/apps/sim/app/api/tools/outlook/mark-read/route.ts +++ b/apps/sim/app/api/tools/outlook/mark-read/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/mark-unread/route.ts b/apps/sim/app/api/tools/outlook/mark-unread/route.ts index 7b52941b52..f9fef10cc9 100644 --- a/apps/sim/app/api/tools/outlook/mark-unread/route.ts +++ b/apps/sim/app/api/tools/outlook/mark-unread/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/move/route.ts b/apps/sim/app/api/tools/outlook/move/route.ts index 5cdbc56f76..62f432db8f 100644 --- a/apps/sim/app/api/tools/outlook/move/route.ts +++ b/apps/sim/app/api/tools/outlook/move/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/outlook/send/route.ts b/apps/sim/app/api/tools/outlook/send/route.ts index 59293f535b..e3544171e3 100644 --- a/apps/sim/app/api/tools/outlook/send/route.ts +++ b/apps/sim/app/api/tools/outlook/send/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/postgresql/delete/route.ts b/apps/sim/app/api/tools/postgresql/delete/route.ts index ea6ce401b4..f18df3db1a 100644 --- a/apps/sim/app/api/tools/postgresql/delete/route.ts +++ b/apps/sim/app/api/tools/postgresql/delete/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createPostgresConnection, executeDelete } from '@/app/api/tools/postgresql/utils' const logger = createLogger('PostgreSQLDeleteAPI') diff --git a/apps/sim/app/api/tools/postgresql/execute/route.ts b/apps/sim/app/api/tools/postgresql/execute/route.ts index c66db63947..403823e367 100644 --- a/apps/sim/app/api/tools/postgresql/execute/route.ts +++ b/apps/sim/app/api/tools/postgresql/execute/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createPostgresConnection, executeQuery, diff --git a/apps/sim/app/api/tools/postgresql/insert/route.ts b/apps/sim/app/api/tools/postgresql/insert/route.ts index e3193e29f0..e01cc9fe27 100644 --- a/apps/sim/app/api/tools/postgresql/insert/route.ts +++ b/apps/sim/app/api/tools/postgresql/insert/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createPostgresConnection, executeInsert } from '@/app/api/tools/postgresql/utils' const logger = createLogger('PostgreSQLInsertAPI') diff --git a/apps/sim/app/api/tools/postgresql/query/route.ts b/apps/sim/app/api/tools/postgresql/query/route.ts index 135b044b65..a6ee4bad26 100644 --- a/apps/sim/app/api/tools/postgresql/query/route.ts +++ b/apps/sim/app/api/tools/postgresql/query/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createPostgresConnection, executeQuery } from '@/app/api/tools/postgresql/utils' const logger = createLogger('PostgreSQLQueryAPI') diff --git a/apps/sim/app/api/tools/postgresql/update/route.ts b/apps/sim/app/api/tools/postgresql/update/route.ts index 70933d74f3..862f6dffb4 100644 --- a/apps/sim/app/api/tools/postgresql/update/route.ts +++ b/apps/sim/app/api/tools/postgresql/update/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createPostgresConnection, executeUpdate } from '@/app/api/tools/postgresql/utils' const logger = createLogger('PostgreSQLUpdateAPI') diff --git a/apps/sim/app/api/tools/rds/delete/route.ts b/apps/sim/app/api/tools/rds/delete/route.ts index f26ab21c4a..e309796660 100644 --- a/apps/sim/app/api/tools/rds/delete/route.ts +++ b/apps/sim/app/api/tools/rds/delete/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createRdsClient, executeDelete } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSDeleteAPI') diff --git a/apps/sim/app/api/tools/rds/execute/route.ts b/apps/sim/app/api/tools/rds/execute/route.ts index 73463fc065..9510d40886 100644 --- a/apps/sim/app/api/tools/rds/execute/route.ts +++ b/apps/sim/app/api/tools/rds/execute/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createRdsClient, executeStatement } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSExecuteAPI') diff --git a/apps/sim/app/api/tools/rds/insert/route.ts b/apps/sim/app/api/tools/rds/insert/route.ts index a00f184cf8..6f766d423f 100644 --- a/apps/sim/app/api/tools/rds/insert/route.ts +++ b/apps/sim/app/api/tools/rds/insert/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createRdsClient, executeInsert } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSInsertAPI') diff --git a/apps/sim/app/api/tools/rds/query/route.ts b/apps/sim/app/api/tools/rds/query/route.ts index 5c9d022630..81d972d47a 100644 --- a/apps/sim/app/api/tools/rds/query/route.ts +++ b/apps/sim/app/api/tools/rds/query/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createRdsClient, executeStatement, validateQuery } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSQueryAPI') diff --git a/apps/sim/app/api/tools/rds/update/route.ts b/apps/sim/app/api/tools/rds/update/route.ts index 307a6e19f0..9648574e2c 100644 --- a/apps/sim/app/api/tools/rds/update/route.ts +++ b/apps/sim/app/api/tools/rds/update/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createRdsClient, executeUpdate } from '@/app/api/tools/rds/utils' const logger = createLogger('RDSUpdateAPI') diff --git a/apps/sim/app/api/tools/s3/copy-object/route.ts b/apps/sim/app/api/tools/s3/copy-object/route.ts index 4c96284be0..888aaf6308 100644 --- a/apps/sim/app/api/tools/s3/copy-object/route.ts +++ b/apps/sim/app/api/tools/s3/copy-object/route.ts @@ -1,9 +1,9 @@ import { CopyObjectCommand, type ObjectCannedACL, S3Client } from '@aws-sdk/client-s3' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/s3/delete-object/route.ts b/apps/sim/app/api/tools/s3/delete-object/route.ts index 1a3566a020..4319a45240 100644 --- a/apps/sim/app/api/tools/s3/delete-object/route.ts +++ b/apps/sim/app/api/tools/s3/delete-object/route.ts @@ -1,9 +1,9 @@ import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/s3/list-objects/route.ts b/apps/sim/app/api/tools/s3/list-objects/route.ts index 2d773fe30b..2b43592bde 100644 --- a/apps/sim/app/api/tools/s3/list-objects/route.ts +++ b/apps/sim/app/api/tools/s3/list-objects/route.ts @@ -1,9 +1,9 @@ import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/s3/put-object/route.ts b/apps/sim/app/api/tools/s3/put-object/route.ts index 3d84b9a23a..2f7aced28b 100644 --- a/apps/sim/app/api/tools/s3/put-object/route.ts +++ b/apps/sim/app/api/tools/s3/put-object/route.ts @@ -1,9 +1,9 @@ import { type ObjectCannedACL, PutObjectCommand, S3Client } from '@aws-sdk/client-s3' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/search/route.ts b/apps/sim/app/api/tools/search/route.ts index 2ae8af018a..8c0bca85a3 100644 --- a/apps/sim/app/api/tools/search/route.ts +++ b/apps/sim/app/api/tools/search/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { SEARCH_TOOL_COST } from '@/lib/billing/constants' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { executeTool } from '@/tools' const logger = createLogger('search') @@ -39,8 +39,10 @@ export async function POST(request: NextRequest) { const body = await request.json() const validated = SearchRequestSchema.parse(body) - if (!env.EXA_API_KEY) { - logger.error(`[${requestId}] EXA_API_KEY not configured`) + const exaApiKey = env.EXA_API_KEY + + if (!exaApiKey) { + logger.error(`[${requestId}] No Exa API key available`) return NextResponse.json( { success: false, error: 'Search service not configured' }, { status: 503 } @@ -57,7 +59,7 @@ export async function POST(request: NextRequest) { type: 'auto', useAutoprompt: true, highlights: true, - apiKey: env.EXA_API_KEY, + apiKey: exaApiKey, }) if (!result.success) { diff --git a/apps/sim/app/api/tools/sftp/delete/route.ts b/apps/sim/app/api/tools/sftp/delete/route.ts index b551af72bb..e1a5aec459 100644 --- a/apps/sim/app/api/tools/sftp/delete/route.ts +++ b/apps/sim/app/api/tools/sftp/delete/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { SFTPWrapper } from 'ssh2' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createSftpConnection, getFileType, diff --git a/apps/sim/app/api/tools/sftp/download/route.ts b/apps/sim/app/api/tools/sftp/download/route.ts index 3c5e343a07..cc954b90cf 100644 --- a/apps/sim/app/api/tools/sftp/download/route.ts +++ b/apps/sim/app/api/tools/sftp/download/route.ts @@ -1,9 +1,9 @@ import path from 'path' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createSftpConnection, getSftp, isPathSafe, sanitizePath } from '@/app/api/tools/sftp/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/sftp/list/route.ts b/apps/sim/app/api/tools/sftp/list/route.ts index 23c349e415..5d70f344b2 100644 --- a/apps/sim/app/api/tools/sftp/list/route.ts +++ b/apps/sim/app/api/tools/sftp/list/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createSftpConnection, getFileType, diff --git a/apps/sim/app/api/tools/sftp/mkdir/route.ts b/apps/sim/app/api/tools/sftp/mkdir/route.ts index ab74ae42ac..783c9a8d93 100644 --- a/apps/sim/app/api/tools/sftp/mkdir/route.ts +++ b/apps/sim/app/api/tools/sftp/mkdir/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { SFTPWrapper } from 'ssh2' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createSftpConnection, getSftp, diff --git a/apps/sim/app/api/tools/sftp/upload/route.ts b/apps/sim/app/api/tools/sftp/upload/route.ts index a0154f755f..b1f9f0622a 100644 --- a/apps/sim/app/api/tools/sftp/upload/route.ts +++ b/apps/sim/app/api/tools/sftp/upload/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { diff --git a/apps/sim/app/api/tools/sharepoint/site/route.ts b/apps/sim/app/api/tools/sharepoint/site/route.ts index ffa8d74b61..2ffecce942 100644 --- a/apps/sim/app/api/tools/sharepoint/site/route.ts +++ b/apps/sim/app/api/tools/sharepoint/site/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateMicrosoftGraphId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/sharepoint/sites/route.ts b/apps/sim/app/api/tools/sharepoint/sites/route.ts index 2f39cc0496..7e98bf6212 100644 --- a/apps/sim/app/api/tools/sharepoint/sites/route.ts +++ b/apps/sim/app/api/tools/sharepoint/sites/route.ts @@ -1,11 +1,11 @@ import { randomUUID } from 'crypto' import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateAlphanumericId } from '@/lib/core/security/input-validation' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' import type { SharepointSite } from '@/tools/sharepoint/types' diff --git a/apps/sim/app/api/tools/sharepoint/upload/route.ts b/apps/sim/app/api/tools/sharepoint/upload/route.ts index 00a4c7633e..a1a69e3c9d 100644 --- a/apps/sim/app/api/tools/sharepoint/upload/route.ts +++ b/apps/sim/app/api/tools/sharepoint/upload/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/slack/channels/route.ts b/apps/sim/app/api/tools/slack/channels/route.ts index d48d066130..b96badeba3 100644 --- a/apps/sim/app/api/tools/slack/channels/route.ts +++ b/apps/sim/app/api/tools/slack/channels/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/slack/read-messages/route.ts b/apps/sim/app/api/tools/slack/read-messages/route.ts index c0a87c3cf2..43cc77e05d 100644 --- a/apps/sim/app/api/tools/slack/read-messages/route.ts +++ b/apps/sim/app/api/tools/slack/read-messages/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { openDMChannel } from '../utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/slack/send-message/route.ts b/apps/sim/app/api/tools/slack/send-message/route.ts index 592721d0de..21d5983209 100644 --- a/apps/sim/app/api/tools/slack/send-message/route.ts +++ b/apps/sim/app/api/tools/slack/send-message/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { sendSlackMessage } from '../utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/slack/update-message/route.ts b/apps/sim/app/api/tools/slack/update-message/route.ts index d89f9b0a9f..a30d52a838 100644 --- a/apps/sim/app/api/tools/slack/update-message/route.ts +++ b/apps/sim/app/api/tools/slack/update-message/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/slack/users/route.ts b/apps/sim/app/api/tools/slack/users/route.ts index 666800f56e..7b11620585 100644 --- a/apps/sim/app/api/tools/slack/users/route.ts +++ b/apps/sim/app/api/tools/slack/users/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/slack/utils.ts b/apps/sim/app/api/tools/slack/utils.ts index b52d734203..4a18071bfc 100644 --- a/apps/sim/app/api/tools/slack/utils.ts +++ b/apps/sim/app/api/tools/slack/utils.ts @@ -1,4 +1,4 @@ -import type { Logger } from '@/lib/logs/console/logger' +import type { Logger } from '@sim/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/sms/send/route.ts b/apps/sim/app/api/tools/sms/send/route.ts index d16a1c57c1..6468dde307 100644 --- a/apps/sim/app/api/tools/sms/send/route.ts +++ b/apps/sim/app/api/tools/sms/send/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { env } from '@/lib/core/config/env' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { type SMSOptions, sendSMS } from '@/lib/messaging/sms/service' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/smtp/send/route.ts b/apps/sim/app/api/tools/smtp/send/route.ts index d20b27b328..75008909e3 100644 --- a/apps/sim/app/api/tools/smtp/send/route.ts +++ b/apps/sim/app/api/tools/smtp/send/route.ts @@ -1,9 +1,9 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import nodemailer from 'nodemailer' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/sqs/send/route.ts b/apps/sim/app/api/tools/sqs/send/route.ts index 402f5ca53c..c738adf9e7 100644 --- a/apps/sim/app/api/tools/sqs/send/route.ts +++ b/apps/sim/app/api/tools/sqs/send/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSqsClient, sendMessage } from '../utils' const logger = createLogger('SQSSendMessageAPI') diff --git a/apps/sim/app/api/tools/ssh/check-command-exists/route.ts b/apps/sim/app/api/tools/ssh/check-command-exists/route.ts index abed8abbfd..57fc1b087e 100644 --- a/apps/sim/app/api/tools/ssh/check-command-exists/route.ts +++ b/apps/sim/app/api/tools/ssh/check-command-exists/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHCheckCommandExistsAPI') diff --git a/apps/sim/app/api/tools/ssh/check-file-exists/route.ts b/apps/sim/app/api/tools/ssh/check-file-exists/route.ts index 0830488db1..445ab3bd39 100644 --- a/apps/sim/app/api/tools/ssh/check-file-exists/route.ts +++ b/apps/sim/app/api/tools/ssh/check-file-exists/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper, Stats } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, getFileType, diff --git a/apps/sim/app/api/tools/ssh/create-directory/route.ts b/apps/sim/app/api/tools/ssh/create-directory/route.ts index 06f7c412ff..43c0d27218 100644 --- a/apps/sim/app/api/tools/ssh/create-directory/route.ts +++ b/apps/sim/app/api/tools/ssh/create-directory/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, escapeShellArg, diff --git a/apps/sim/app/api/tools/ssh/delete-file/route.ts b/apps/sim/app/api/tools/ssh/delete-file/route.ts index a1cb694fa2..3961fe60c2 100644 --- a/apps/sim/app/api/tools/ssh/delete-file/route.ts +++ b/apps/sim/app/api/tools/ssh/delete-file/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, escapeShellArg, diff --git a/apps/sim/app/api/tools/ssh/download-file/route.ts b/apps/sim/app/api/tools/ssh/download-file/route.ts index 03f5b2cfab..3693f22edb 100644 --- a/apps/sim/app/api/tools/ssh/download-file/route.ts +++ b/apps/sim/app/api/tools/ssh/download-file/route.ts @@ -1,9 +1,9 @@ import { randomUUID } from 'crypto' import path from 'path' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHDownloadFileAPI') diff --git a/apps/sim/app/api/tools/ssh/execute-command/route.ts b/apps/sim/app/api/tools/ssh/execute-command/route.ts index c553b3554e..1d53d38535 100644 --- a/apps/sim/app/api/tools/ssh/execute-command/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-command/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, executeSSHCommand, sanitizeCommand } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHExecuteCommandAPI') diff --git a/apps/sim/app/api/tools/ssh/execute-script/route.ts b/apps/sim/app/api/tools/ssh/execute-script/route.ts index 0e7e44abfe..956318495f 100644 --- a/apps/sim/app/api/tools/ssh/execute-script/route.ts +++ b/apps/sim/app/api/tools/ssh/execute-script/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHExecuteScriptAPI') diff --git a/apps/sim/app/api/tools/ssh/get-system-info/route.ts b/apps/sim/app/api/tools/ssh/get-system-info/route.ts index 9f88415e55..9925013478 100644 --- a/apps/sim/app/api/tools/ssh/get-system-info/route.ts +++ b/apps/sim/app/api/tools/ssh/get-system-info/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, executeSSHCommand } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHGetSystemInfoAPI') diff --git a/apps/sim/app/api/tools/ssh/list-directory/route.ts b/apps/sim/app/api/tools/ssh/list-directory/route.ts index dbcfeb78eb..30f8f5d236 100644 --- a/apps/sim/app/api/tools/ssh/list-directory/route.ts +++ b/apps/sim/app/api/tools/ssh/list-directory/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, FileEntry, SFTPWrapper } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, getFileType, diff --git a/apps/sim/app/api/tools/ssh/move-rename/route.ts b/apps/sim/app/api/tools/ssh/move-rename/route.ts index d028399668..d1387026dd 100644 --- a/apps/sim/app/api/tools/ssh/move-rename/route.ts +++ b/apps/sim/app/api/tools/ssh/move-rename/route.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, escapeShellArg, diff --git a/apps/sim/app/api/tools/ssh/read-file-content/route.ts b/apps/sim/app/api/tools/ssh/read-file-content/route.ts index 3c8cb25dd4..c44390bfc0 100644 --- a/apps/sim/app/api/tools/ssh/read-file-content/route.ts +++ b/apps/sim/app/api/tools/ssh/read-file-content/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHReadFileContentAPI') diff --git a/apps/sim/app/api/tools/ssh/upload-file/route.ts b/apps/sim/app/api/tools/ssh/upload-file/route.ts index 7166856cb4..0f736a417d 100644 --- a/apps/sim/app/api/tools/ssh/upload-file/route.ts +++ b/apps/sim/app/api/tools/ssh/upload-file/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHUploadFileAPI') diff --git a/apps/sim/app/api/tools/ssh/utils.ts b/apps/sim/app/api/tools/ssh/utils.ts index b2d2a581c0..126849ba90 100644 --- a/apps/sim/app/api/tools/ssh/utils.ts +++ b/apps/sim/app/api/tools/ssh/utils.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { type Attributes, Client, type ConnectConfig } from 'ssh2' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('SSHUtils') diff --git a/apps/sim/app/api/tools/ssh/write-file-content/route.ts b/apps/sim/app/api/tools/ssh/write-file-content/route.ts index 5ba7274015..77c075abb4 100644 --- a/apps/sim/app/api/tools/ssh/write-file-content/route.ts +++ b/apps/sim/app/api/tools/ssh/write-file-content/route.ts @@ -1,8 +1,8 @@ import { randomUUID } from 'crypto' +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import type { Client, SFTPWrapper } from 'ssh2' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils' const logger = createLogger('SSHWriteFileContentAPI') diff --git a/apps/sim/app/api/tools/stagehand/agent/route.ts b/apps/sim/app/api/tools/stagehand/agent/route.ts index d1aeeb0950..ee5ffe6e25 100644 --- a/apps/sim/app/api/tools/stagehand/agent/route.ts +++ b/apps/sim/app/api/tools/stagehand/agent/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { env } from '@/lib/core/config/env' import { isSensitiveKey, REDACTED_MARKER } from '@/lib/core/security/redaction' -import { createLogger } from '@/lib/logs/console/logger' import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils' const logger = createLogger('StagehandAgentAPI') diff --git a/apps/sim/app/api/tools/stagehand/extract/route.ts b/apps/sim/app/api/tools/stagehand/extract/route.ts index 7da282815d..18f3e408b3 100644 --- a/apps/sim/app/api/tools/stagehand/extract/route.ts +++ b/apps/sim/app/api/tools/stagehand/extract/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { ensureZodObject, normalizeUrl } from '@/app/api/tools/stagehand/utils' const logger = createLogger('StagehandExtractAPI') diff --git a/apps/sim/app/api/tools/stagehand/utils.ts b/apps/sim/app/api/tools/stagehand/utils.ts index 4cf3e0c148..1e61f2971f 100644 --- a/apps/sim/app/api/tools/stagehand/utils.ts +++ b/apps/sim/app/api/tools/stagehand/utils.ts @@ -1,5 +1,5 @@ +import type { Logger } from '@sim/logger' import { z } from 'zod' -import type { Logger } from '@/lib/logs/console/logger' function jsonSchemaToZod(logger: Logger, jsonSchema: Record): z.ZodTypeAny { if (!jsonSchema) { diff --git a/apps/sim/app/api/tools/telegram/send-document/route.ts b/apps/sim/app/api/tools/telegram/send-document/route.ts index 968b110785..d0d656e0b9 100644 --- a/apps/sim/app/api/tools/telegram/send-document/route.ts +++ b/apps/sim/app/api/tools/telegram/send-document/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' import { convertMarkdownToHTML } from '@/tools/telegram/utils' diff --git a/apps/sim/app/api/tools/thinking/route.ts b/apps/sim/app/api/tools/thinking/route.ts index 97e41ff3eb..8b397db5ee 100644 --- a/apps/sim/app/api/tools/thinking/route.ts +++ b/apps/sim/app/api/tools/thinking/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import type { ThinkingToolParams, ThinkingToolResponse } from '@/tools/thinking/types' const logger = createLogger('ThinkingToolAPI') diff --git a/apps/sim/app/api/tools/vision/analyze/route.ts b/apps/sim/app/api/tools/vision/analyze/route.ts index ded0b5dc85..58c3515ad0 100644 --- a/apps/sim/app/api/tools/vision/analyze/route.ts +++ b/apps/sim/app/api/tools/vision/analyze/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils' import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server' diff --git a/apps/sim/app/api/tools/wealthbox/item/route.ts b/apps/sim/app/api/tools/wealthbox/item/route.ts index 12c423fcd5..b618470e6f 100644 --- a/apps/sim/app/api/tools/wealthbox/item/route.ts +++ b/apps/sim/app/api/tools/wealthbox/item/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateEnum, validatePathSegment } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/wealthbox/items/route.ts b/apps/sim/app/api/tools/wealthbox/items/route.ts index dd041f5d95..a07ff62c41 100644 --- a/apps/sim/app/api/tools/wealthbox/items/route.ts +++ b/apps/sim/app/api/tools/wealthbox/items/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { account } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateEnum, validatePathSegment } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' export const dynamic = 'force-dynamic' diff --git a/apps/sim/app/api/tools/webflow/collections/route.ts b/apps/sim/app/api/tools/webflow/collections/route.ts index 31ec540615..8562da8ac1 100644 --- a/apps/sim/app/api/tools/webflow/collections/route.ts +++ b/apps/sim/app/api/tools/webflow/collections/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' const logger = createLogger('WebflowCollectionsAPI') diff --git a/apps/sim/app/api/tools/webflow/items/route.ts b/apps/sim/app/api/tools/webflow/items/route.ts index 95acc644d7..b2c5512167 100644 --- a/apps/sim/app/api/tools/webflow/items/route.ts +++ b/apps/sim/app/api/tools/webflow/items/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' const logger = createLogger('WebflowItemsAPI') diff --git a/apps/sim/app/api/tools/webflow/sites/route.ts b/apps/sim/app/api/tools/webflow/sites/route.ts index f5fd93ee2a..47959f4c93 100644 --- a/apps/sim/app/api/tools/webflow/sites/route.ts +++ b/apps/sim/app/api/tools/webflow/sites/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' import { authorizeCredentialUse } from '@/lib/auth/credential-access' import { validateAlphanumericId } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils' const logger = createLogger('WebflowSitesAPI') diff --git a/apps/sim/app/api/tools/wordpress/upload/route.ts b/apps/sim/app/api/tools/wordpress/upload/route.ts index 56c0beaf3f..7f0434bc1f 100644 --- a/apps/sim/app/api/tools/wordpress/upload/route.ts +++ b/apps/sim/app/api/tools/wordpress/upload/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getFileExtension, getMimeTypeFromExtension, diff --git a/apps/sim/app/api/usage/route.ts b/apps/sim/app/api/usage/route.ts index 4ca818e787..f55f57c496 100644 --- a/apps/sim/app/api/usage/route.ts +++ b/apps/sim/app/api/usage/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' @@ -6,7 +7,6 @@ import { getOrganizationBillingData, isOrganizationOwnerOrAdmin, } from '@/lib/billing/core/organization' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('UnifiedUsageAPI') diff --git a/apps/sim/app/api/user/super-user/route.ts b/apps/sim/app/api/user/super-user/route.ts index cc39943434..28c8b9733e 100644 --- a/apps/sim/app/api/user/super-user/route.ts +++ b/apps/sim/app/api/user/super-user/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('SuperUserAPI') diff --git a/apps/sim/app/api/users/me/api-keys/[id]/route.ts b/apps/sim/app/api/users/me/api-keys/[id]/route.ts index fb5ea90e8d..56be3ce7bb 100644 --- a/apps/sim/app/api/users/me/api-keys/[id]/route.ts +++ b/apps/sim/app/api/users/me/api-keys/[id]/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { apiKey } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ApiKeyAPI') diff --git a/apps/sim/app/api/users/me/api-keys/route.ts b/apps/sim/app/api/users/me/api-keys/route.ts index ca4e78d577..252011ec95 100644 --- a/apps/sim/app/api/users/me/api-keys/route.ts +++ b/apps/sim/app/api/users/me/api-keys/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { apiKey } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { createApiKey, getApiKeyDisplayFormat } from '@/lib/api-key/auth' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('ApiKeysAPI') diff --git a/apps/sim/app/api/users/me/profile/route.ts b/apps/sim/app/api/users/me/profile/route.ts index 7f6ebe1489..1b627dbac1 100644 --- a/apps/sim/app/api/users/me/profile/route.ts +++ b/apps/sim/app/api/users/me/profile/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('UpdateUserProfileAPI') diff --git a/apps/sim/app/api/users/me/settings/route.ts b/apps/sim/app/api/users/me/settings/route.ts index 6fdf0986c5..6f6094558f 100644 --- a/apps/sim/app/api/users/me/settings/route.ts +++ b/apps/sim/app/api/users/me/settings/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { settings } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { nanoid } from 'nanoid' import { NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('UserSettingsAPI') diff --git a/apps/sim/app/api/users/me/settings/unsubscribe/route.ts b/apps/sim/app/api/users/me/settings/unsubscribe/route.ts index 8dc42c36e0..30c7799995 100644 --- a/apps/sim/app/api/users/me/settings/unsubscribe/route.ts +++ b/apps/sim/app/api/users/me/settings/unsubscribe/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import type { EmailType } from '@/lib/messaging/email/mailer' import { getEmailPreferences, diff --git a/apps/sim/app/api/users/me/subscription/[id]/transfer/route.ts b/apps/sim/app/api/users/me/subscription/[id]/transfer/route.ts index a20f600b7d..c00777ce34 100644 --- a/apps/sim/app/api/users/me/subscription/[id]/transfer/route.ts +++ b/apps/sim/app/api/users/me/subscription/[id]/transfer/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { member, organization, subscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('SubscriptionTransferAPI') diff --git a/apps/sim/app/api/users/me/usage-limits/route.ts b/apps/sim/app/api/users/me/usage-limits/route.ts index 9960a05294..26db257efc 100644 --- a/apps/sim/app/api/users/me/usage-limits/route.ts +++ b/apps/sim/app/api/users/me/usage-limits/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { checkHybridAuth } from '@/lib/auth/hybrid' import { checkServerSideUsageLimits } from '@/lib/billing' @@ -5,7 +6,6 @@ import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription' import { getEffectiveCurrentPeriodCost } from '@/lib/billing/core/usage' import { getUserStorageLimit, getUserStorageUsage } from '@/lib/billing/storage' import { RateLimiter } from '@/lib/core/rate-limiter' -import { createLogger } from '@/lib/logs/console/logger' import { createErrorResponse } from '@/app/api/workflows/utils' const logger = createLogger('UsageLimitsAPI') diff --git a/apps/sim/app/api/users/me/usage-logs/route.ts b/apps/sim/app/api/users/me/usage-logs/route.ts index b4751fbdcd..3c4f1229fe 100644 --- a/apps/sim/app/api/users/me/usage-logs/route.ts +++ b/apps/sim/app/api/users/me/usage-logs/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { checkHybridAuth } from '@/lib/auth/hybrid' import { getUserUsageLogs, type UsageLogSource } from '@/lib/billing/core/usage-log' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('UsageLogsAPI') diff --git a/apps/sim/app/api/v1/admin/auth.ts b/apps/sim/app/api/v1/admin/auth.ts index 642968b996..5e04bcc1d9 100644 --- a/apps/sim/app/api/v1/admin/auth.ts +++ b/apps/sim/app/api/v1/admin/auth.ts @@ -9,9 +9,9 @@ */ import { createHash, timingSafeEqual } from 'crypto' +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('AdminAuth') diff --git a/apps/sim/app/api/v1/admin/organizations/[id]/billing/route.ts b/apps/sim/app/api/v1/admin/organizations/[id]/billing/route.ts index 70d937604f..952b437144 100644 --- a/apps/sim/app/api/v1/admin/organizations/[id]/billing/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/[id]/billing/route.ts @@ -17,9 +17,9 @@ import { db } from '@sim/db' import { organization } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { getOrganizationBillingData } from '@/lib/billing/core/organization' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/organizations/[id]/members/[memberId]/route.ts b/apps/sim/app/api/v1/admin/organizations/[id]/members/[memberId]/route.ts index 58048cf685..2496c363c6 100644 --- a/apps/sim/app/api/v1/admin/organizations/[id]/members/[memberId]/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/[id]/members/[memberId]/route.ts @@ -27,9 +27,9 @@ import { db } from '@sim/db' import { member, organization, user, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { removeUserFromOrganization } from '@/lib/billing/organizations/membership' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/organizations/[id]/members/route.ts b/apps/sim/app/api/v1/admin/organizations/[id]/members/route.ts index a3c07e02ed..797831b887 100644 --- a/apps/sim/app/api/v1/admin/organizations/[id]/members/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/[id]/members/route.ts @@ -30,10 +30,10 @@ import { db } from '@sim/db' import { member, organization, user, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count, eq } from 'drizzle-orm' import { addUserToOrganization } from '@/lib/billing/organizations/membership' import { requireStripeClient } from '@/lib/billing/stripe-client' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/organizations/[id]/route.ts b/apps/sim/app/api/v1/admin/organizations/[id]/route.ts index ef5c9e9663..3d0373014e 100644 --- a/apps/sim/app/api/v1/admin/organizations/[id]/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/[id]/route.ts @@ -18,8 +18,8 @@ import { db } from '@sim/db' import { member, organization, subscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, count, eq } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/organizations/[id]/seats/route.ts b/apps/sim/app/api/v1/admin/organizations/[id]/seats/route.ts index 0cfe0c8d94..86e156a445 100644 --- a/apps/sim/app/api/v1/admin/organizations/[id]/seats/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/[id]/seats/route.ts @@ -6,8 +6,8 @@ * Response: AdminSingleResponse */ +import { createLogger } from '@sim/logger' import { getOrganizationSeatAnalytics } from '@/lib/billing/validation/seat-management' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, diff --git a/apps/sim/app/api/v1/admin/organizations/route.ts b/apps/sim/app/api/v1/admin/organizations/route.ts index a05fafc5f1..f19f822467 100644 --- a/apps/sim/app/api/v1/admin/organizations/route.ts +++ b/apps/sim/app/api/v1/admin/organizations/route.ts @@ -12,8 +12,8 @@ import { db } from '@sim/db' import { organization } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/subscriptions/[id]/route.ts b/apps/sim/app/api/v1/admin/subscriptions/[id]/route.ts index dac1dde893..50ba40f333 100644 --- a/apps/sim/app/api/v1/admin/subscriptions/[id]/route.ts +++ b/apps/sim/app/api/v1/admin/subscriptions/[id]/route.ts @@ -25,9 +25,9 @@ import { db } from '@sim/db' import { subscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { requireStripeClient } from '@/lib/billing/stripe-client' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/subscriptions/route.ts b/apps/sim/app/api/v1/admin/subscriptions/route.ts index be73517216..146d5c307b 100644 --- a/apps/sim/app/api/v1/admin/subscriptions/route.ts +++ b/apps/sim/app/api/v1/admin/subscriptions/route.ts @@ -14,8 +14,8 @@ import { db } from '@sim/db' import { subscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, count, eq, type SQL } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/users/[id]/billing/route.ts b/apps/sim/app/api/v1/admin/users/[id]/billing/route.ts index ef2535adc4..e5681df62a 100644 --- a/apps/sim/app/api/v1/admin/users/[id]/billing/route.ts +++ b/apps/sim/app/api/v1/admin/users/[id]/billing/route.ts @@ -20,10 +20,10 @@ import { db } from '@sim/db' import { member, organization, subscription, user, userStats } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, or } from 'drizzle-orm' import { nanoid } from 'nanoid' import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { badRequestResponse, diff --git a/apps/sim/app/api/v1/admin/users/[id]/route.ts b/apps/sim/app/api/v1/admin/users/[id]/route.ts index e1b52c7e9b..3700a427b1 100644 --- a/apps/sim/app/api/v1/admin/users/[id]/route.ts +++ b/apps/sim/app/api/v1/admin/users/[id]/route.ts @@ -8,8 +8,8 @@ import { db } from '@sim/db' import { user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, diff --git a/apps/sim/app/api/v1/admin/users/route.ts b/apps/sim/app/api/v1/admin/users/route.ts index 698d75808c..a8400bced6 100644 --- a/apps/sim/app/api/v1/admin/users/route.ts +++ b/apps/sim/app/api/v1/admin/users/route.ts @@ -12,8 +12,8 @@ import { db } from '@sim/db' import { user } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/workflows/[id]/export/route.ts b/apps/sim/app/api/v1/admin/workflows/[id]/export/route.ts index 7aa6ad503e..3570cc9f31 100644 --- a/apps/sim/app/api/v1/admin/workflows/[id]/export/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/[id]/export/route.ts @@ -8,8 +8,8 @@ import { db } from '@sim/db' import { workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { diff --git a/apps/sim/app/api/v1/admin/workflows/[id]/route.ts b/apps/sim/app/api/v1/admin/workflows/[id]/route.ts index 8aae98af42..ca596d6afd 100644 --- a/apps/sim/app/api/v1/admin/workflows/[id]/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/[id]/route.ts @@ -14,9 +14,9 @@ import { db } from '@sim/db' import { workflow, workflowBlocks, workflowEdges, workflowSchedule } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count, eq } from 'drizzle-orm' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, diff --git a/apps/sim/app/api/v1/admin/workflows/import/route.ts b/apps/sim/app/api/v1/admin/workflows/import/route.ts index 9dc00e5d0e..db83f52d07 100644 --- a/apps/sim/app/api/v1/admin/workflows/import/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/import/route.ts @@ -16,9 +16,9 @@ import { db } from '@sim/db' import { workflow, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { diff --git a/apps/sim/app/api/v1/admin/workflows/route.ts b/apps/sim/app/api/v1/admin/workflows/route.ts index 3c190330a2..5344a5db63 100644 --- a/apps/sim/app/api/v1/admin/workflows/route.ts +++ b/apps/sim/app/api/v1/admin/workflows/route.ts @@ -12,8 +12,8 @@ import { db } from '@sim/db' import { workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/export/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/export/route.ts index a943cfa7a3..f7e60502ad 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/export/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/export/route.ts @@ -13,9 +13,9 @@ import { db } from '@sim/db' import { workflow, workflowFolder, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { exportWorkspaceToZip } from '@/lib/workflows/operations/import-export' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/folders/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/folders/route.ts index a484643d1a..37cdc2b964 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/folders/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/folders/route.ts @@ -12,8 +12,8 @@ import { db } from '@sim/db' import { workflowFolder, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count, eq } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse, notFoundResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts index 11989448ec..fa569b7f24 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/import/route.ts @@ -25,9 +25,9 @@ import { db } from '@sim/db' import { workflow, workflowFolder, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { extractWorkflowName, extractWorkflowsFromZip, diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/route.ts index c9dd07a237..ee34556fc6 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/route.ts @@ -8,8 +8,8 @@ import { db } from '@sim/db' import { workflow, workflowFolder, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count, eq } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, diff --git a/apps/sim/app/api/v1/admin/workspaces/[id]/workflows/route.ts b/apps/sim/app/api/v1/admin/workspaces/[id]/workflows/route.ts index 867f5f2a7b..ea1ab87fc5 100644 --- a/apps/sim/app/api/v1/admin/workspaces/[id]/workflows/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/[id]/workflows/route.ts @@ -24,9 +24,9 @@ import { workflowSchedule, workspace, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count, eq, inArray } from 'drizzle-orm' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuthParams } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse, notFoundResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/admin/workspaces/route.ts b/apps/sim/app/api/v1/admin/workspaces/route.ts index 1f3fe3e197..0724770ced 100644 --- a/apps/sim/app/api/v1/admin/workspaces/route.ts +++ b/apps/sim/app/api/v1/admin/workspaces/route.ts @@ -12,8 +12,8 @@ import { db } from '@sim/db' import { workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { count } from 'drizzle-orm' -import { createLogger } from '@/lib/logs/console/logger' import { withAdminAuth } from '@/app/api/v1/admin/middleware' import { internalErrorResponse, listResponse } from '@/app/api/v1/admin/responses' import { diff --git a/apps/sim/app/api/v1/auth.ts b/apps/sim/app/api/v1/auth.ts index 30bf8d8e51..ce288dd676 100644 --- a/apps/sim/app/api/v1/auth.ts +++ b/apps/sim/app/api/v1/auth.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service' import { ANONYMOUS_USER_ID } from '@/lib/auth/constants' import { isAuthDisabled } from '@/lib/core/config/feature-flags' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('V1Auth') diff --git a/apps/sim/app/api/v1/logs/[id]/route.ts b/apps/sim/app/api/v1/logs/[id]/route.ts index aa53fb496c..b1d8f89ff3 100644 --- a/apps/sim/app/api/v1/logs/[id]/route.ts +++ b/apps/sim/app/api/v1/logs/[id]/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' diff --git a/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts b/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts index a68cd0f313..5c2967ef73 100644 --- a/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts +++ b/apps/sim/app/api/v1/logs/executions/[executionId]/route.ts @@ -5,9 +5,9 @@ import { workflowExecutionLogs, workflowExecutionSnapshots, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' diff --git a/apps/sim/app/api/v1/logs/route.ts b/apps/sim/app/api/v1/logs/route.ts index 8357175947..83a7b62192 100644 --- a/apps/sim/app/api/v1/logs/route.ts +++ b/apps/sim/app/api/v1/logs/route.ts @@ -1,9 +1,9 @@ import { db } from '@sim/db' import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' -import { createLogger } from '@/lib/logs/console/logger' import { buildLogFilters, getOrderBy } from '@/app/api/v1/logs/filters' import { createApiResponse, getUserLimits } from '@/app/api/v1/logs/meta' import { checkRateLimit, createRateLimitResponse } from '@/app/api/v1/middleware' diff --git a/apps/sim/app/api/v1/middleware.ts b/apps/sim/app/api/v1/middleware.ts index ae00e5eeac..4f0eac4ad9 100644 --- a/apps/sim/app/api/v1/middleware.ts +++ b/apps/sim/app/api/v1/middleware.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription' import { RateLimiter } from '@/lib/core/rate-limiter' -import { createLogger } from '@/lib/logs/console/logger' import { authenticateV1Request } from '@/app/api/v1/auth' const logger = createLogger('V1Middleware') diff --git a/apps/sim/app/api/wand/route.ts b/apps/sim/app/api/wand/route.ts index bb2a277768..3a14e74001 100644 --- a/apps/sim/app/api/wand/route.ts +++ b/apps/sim/app/api/wand/route.ts @@ -1,15 +1,16 @@ import { db } from '@sim/db' import { userStats, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import OpenAI, { AzureOpenAI } from 'openai' +import { getBYOKKey } from '@/lib/api-key/byok' import { getSession } from '@/lib/auth' import { logModelUsage } from '@/lib/billing/core/usage-log' import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing' import { env } from '@/lib/core/config/env' import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' import { getModelPricing } from '@/providers/utils' @@ -75,7 +76,8 @@ async function updateUserStatsForWand( completion_tokens?: number total_tokens?: number }, - requestId: string + requestId: string, + isBYOK = false ): Promise { if (!isBillingEnabled) { logger.debug(`[${requestId}] Billing is disabled, skipping wand usage cost update`) @@ -93,21 +95,24 @@ async function updateUserStatsForWand( const completionTokens = usage.completion_tokens || 0 const modelName = useWandAzure ? wandModelName : 'gpt-4o' - const pricing = getModelPricing(modelName) - - const costMultiplier = getCostMultiplier() - let modelCost = 0 + let costToStore = 0 + + if (!isBYOK) { + const pricing = getModelPricing(modelName) + const costMultiplier = getCostMultiplier() + let modelCost = 0 + + if (pricing) { + const inputCost = (promptTokens / 1000000) * pricing.input + const outputCost = (completionTokens / 1000000) * pricing.output + modelCost = inputCost + outputCost + } else { + modelCost = (promptTokens / 1000000) * 0.005 + (completionTokens / 1000000) * 0.015 + } - if (pricing) { - const inputCost = (promptTokens / 1000000) * pricing.input - const outputCost = (completionTokens / 1000000) * pricing.output - modelCost = inputCost + outputCost - } else { - modelCost = (promptTokens / 1000000) * 0.005 + (completionTokens / 1000000) * 0.015 + costToStore = modelCost * costMultiplier } - const costToStore = modelCost * costMultiplier - await db .update(userStats) .set({ @@ -122,6 +127,7 @@ async function updateUserStatsForWand( userId, tokensUsed: totalTokens, costAdded: costToStore, + isBYOK, }) await logModelUsage({ @@ -149,14 +155,6 @@ export async function POST(req: NextRequest) { return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 }) } - if (!client) { - logger.error(`[${requestId}] AI client not initialized. Missing API key.`) - return NextResponse.json( - { success: false, error: 'Wand generation service is not configured.' }, - { status: 503 } - ) - } - try { const body = (await req.json()) as RequestBody @@ -170,6 +168,7 @@ export async function POST(req: NextRequest) { ) } + let workspaceId: string | null = null if (workflowId) { const [workflowRecord] = await db .select({ workspaceId: workflow.workspaceId, userId: workflow.userId }) @@ -182,6 +181,8 @@ export async function POST(req: NextRequest) { return NextResponse.json({ success: false, error: 'Workflow not found' }, { status: 404 }) } + workspaceId = workflowRecord.workspaceId + if (workflowRecord.workspaceId) { const permission = await verifyWorkspaceMembership( session.user.id, @@ -199,6 +200,28 @@ export async function POST(req: NextRequest) { } } + let isBYOK = false + let activeClient = client + let byokApiKey: string | null = null + + if (workspaceId && !useWandAzure) { + const byokResult = await getBYOKKey(workspaceId, 'openai') + if (byokResult) { + isBYOK = true + byokApiKey = byokResult.apiKey + activeClient = new OpenAI({ apiKey: byokResult.apiKey }) + logger.info(`[${requestId}] Using BYOK OpenAI key for wand generation`) + } + } + + if (!activeClient) { + logger.error(`[${requestId}] AI client not initialized. Missing API key.`) + return NextResponse.json( + { success: false, error: 'Wand generation service is not configured.' }, + { status: 503 } + ) + } + const finalSystemPrompt = systemPrompt || 'You are a helpful AI assistant. Generate content exactly as requested by the user.' @@ -241,7 +264,7 @@ export async function POST(req: NextRequest) { if (useWandAzure) { headers['api-key'] = azureApiKey! } else { - headers.Authorization = `Bearer ${openaiApiKey}` + headers.Authorization = `Bearer ${byokApiKey || openaiApiKey}` } logger.debug(`[${requestId}] Making streaming request to: ${apiUrl}`) @@ -310,7 +333,7 @@ export async function POST(req: NextRequest) { logger.info(`[${requestId}] Received [DONE] signal`) if (finalUsage) { - await updateUserStatsForWand(session.user.id, finalUsage, requestId) + await updateUserStatsForWand(session.user.id, finalUsage, requestId, isBYOK) } controller.enqueue( @@ -395,7 +418,7 @@ export async function POST(req: NextRequest) { } } - const completion = await client.chat.completions.create({ + const completion = await activeClient.chat.completions.create({ model: useWandAzure ? wandModelName : 'gpt-4o', messages: messages, temperature: 0.3, @@ -417,7 +440,7 @@ export async function POST(req: NextRequest) { logger.info(`[${requestId}] Wand generation successful`) if (completion.usage) { - await updateUserStatsForWand(session.user.id, completion.usage, requestId) + await updateUserStatsForWand(session.user.id, completion.usage, requestId, isBYOK) } return NextResponse.json({ success: true, content: generatedContent }) diff --git a/apps/sim/app/api/webhooks/[id]/route.ts b/apps/sim/app/api/webhooks/[id]/route.ts index 286472f25e..6f7ffc3a3d 100644 --- a/apps/sim/app/api/webhooks/[id]/route.ts +++ b/apps/sim/app/api/webhooks/[id]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { webhook, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { validateInteger } from '@/lib/core/security/input-validation' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WebhookAPI') diff --git a/apps/sim/app/api/webhooks/[id]/test-url/route.ts b/apps/sim/app/api/webhooks/[id]/test-url/route.ts index 066c6b3cae..7b27b2280c 100644 --- a/apps/sim/app/api/webhooks/[id]/test-url/route.ts +++ b/apps/sim/app/api/webhooks/[id]/test-url/route.ts @@ -1,10 +1,10 @@ import { db, webhook, workflow } from '@sim/db' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { signTestWebhookToken } from '@/lib/webhooks/test-tokens' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts b/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts index 0b6be60936..a460803345 100644 --- a/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts +++ b/apps/sim/app/api/webhooks/cleanup/idempotency/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { cleanupExpiredIdempotencyKeys, getIdempotencyKeyStats } from '@/lib/core/idempotency' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('IdempotencyCleanupAPI') diff --git a/apps/sim/app/api/webhooks/poll/gmail/route.ts b/apps/sim/app/api/webhooks/poll/gmail/route.ts index 008561b603..7b8f6c250b 100644 --- a/apps/sim/app/api/webhooks/poll/gmail/route.ts +++ b/apps/sim/app/api/webhooks/poll/gmail/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { createLogger } from '@/lib/logs/console/logger' import { pollGmailWebhooks } from '@/lib/webhooks/gmail-polling-service' const logger = createLogger('GmailPollingAPI') diff --git a/apps/sim/app/api/webhooks/poll/outlook/route.ts b/apps/sim/app/api/webhooks/poll/outlook/route.ts index eccbfe7b34..c7266fa636 100644 --- a/apps/sim/app/api/webhooks/poll/outlook/route.ts +++ b/apps/sim/app/api/webhooks/poll/outlook/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { createLogger } from '@/lib/logs/console/logger' import { pollOutlookWebhooks } from '@/lib/webhooks/outlook-polling-service' const logger = createLogger('OutlookPollingAPI') diff --git a/apps/sim/app/api/webhooks/poll/rss/route.ts b/apps/sim/app/api/webhooks/poll/rss/route.ts index fabe2c4934..1f9201ee7d 100644 --- a/apps/sim/app/api/webhooks/poll/rss/route.ts +++ b/apps/sim/app/api/webhooks/poll/rss/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { verifyCronAuth } from '@/lib/auth/internal' import { acquireLock, releaseLock } from '@/lib/core/config/redis' -import { createLogger } from '@/lib/logs/console/logger' import { pollRssWebhooks } from '@/lib/webhooks/rss-polling-service' const logger = createLogger('RssPollingAPI') diff --git a/apps/sim/app/api/webhooks/route.ts b/apps/sim/app/api/webhooks/route.ts index 3210615b1d..4c2d2735f4 100644 --- a/apps/sim/app/api/webhooks/route.ts +++ b/apps/sim/app/api/webhooks/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { webhook, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import { getOAuthToken } from '@/app/api/auth/oauth/utils' diff --git a/apps/sim/app/api/webhooks/test/[id]/route.ts b/apps/sim/app/api/webhooks/test/[id]/route.ts index d66d69f407..46653c3bf7 100644 --- a/apps/sim/app/api/webhooks/test/[id]/route.ts +++ b/apps/sim/app/api/webhooks/test/[id]/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { checkWebhookPreprocessing, findWebhookAndWorkflow, diff --git a/apps/sim/app/api/webhooks/test/route.ts b/apps/sim/app/api/webhooks/test/route.ts index 021dc670bd..bf3aece243 100644 --- a/apps/sim/app/api/webhooks/test/route.ts +++ b/apps/sim/app/api/webhooks/test/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { webhook } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('WebhookTestAPI') diff --git a/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts b/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts index 3ac1c87ae5..e736db3987 100644 --- a/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts +++ b/apps/sim/app/api/webhooks/trigger/[path]/route.test.ts @@ -3,6 +3,8 @@ * * @vitest-environment node */ + +import { loggerMock } from '@sim/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { createMockRequest, @@ -176,6 +178,8 @@ vi.mock('drizzle-orm/postgres-js', () => ({ vi.mock('postgres', () => vi.fn().mockReturnValue({})) +vi.mock('@sim/logger', () => loggerMock) + process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test' import { POST } from '@/app/api/webhooks/trigger/[path]/route' @@ -257,9 +261,6 @@ describe('Webhook Trigger API Route', () => { expect(data.message).toBe('Webhook processed') }) - /** - * Test generic webhook with Bearer token authentication - */ it('should authenticate with Bearer token when no custom header is configured', async () => { globalMockData.webhooks.push({ id: 'generic-webhook-id', @@ -489,7 +490,7 @@ describe('Webhook Trigger API Route', () => { const headers = { 'Content-Type': 'application/json', - Authorization: 'Bearer exclusive-token', // Correct token but wrong header type + Authorization: 'Bearer exclusive-token', } const req = createMockRequest('POST', { event: 'exclusivity.test' }, headers) const params = Promise.resolve({ path: 'test-path' }) @@ -517,7 +518,7 @@ describe('Webhook Trigger API Route', () => { const headers = { 'Content-Type': 'application/json', - 'X-Wrong-Header': 'correct-token', // Correct token but wrong header name + 'X-Wrong-Header': 'correct-token', } const req = createMockRequest('POST', { event: 'wrong.header.name.test' }, headers) const params = Promise.resolve({ path: 'test-path' }) diff --git a/apps/sim/app/api/webhooks/trigger/[path]/route.ts b/apps/sim/app/api/webhooks/trigger/[path]/route.ts index b7ec7bafbe..549ce6a78d 100644 --- a/apps/sim/app/api/webhooks/trigger/[path]/route.ts +++ b/apps/sim/app/api/webhooks/trigger/[path]/route.ts @@ -1,6 +1,6 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { checkWebhookPreprocessing, findWebhookAndWorkflow, diff --git a/apps/sim/app/api/workflows/[id]/autolayout/route.ts b/apps/sim/app/api/workflows/[id]/autolayout/route.ts index a08c82fb72..06e2c33133 100644 --- a/apps/sim/app/api/workflows/[id]/autolayout/route.ts +++ b/apps/sim/app/api/workflows/[id]/autolayout/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { applyAutoLayout } from '@/lib/workflows/autolayout' import { DEFAULT_HORIZONTAL_SPACING, diff --git a/apps/sim/app/api/workflows/[id]/chat/status/route.ts b/apps/sim/app/api/workflows/[id]/chat/status/route.ts index 21b3758a7b..f7733e1407 100644 --- a/apps/sim/app/api/workflows/[id]/chat/status/route.ts +++ b/apps/sim/app/api/workflows/[id]/chat/status/route.ts @@ -1,8 +1,8 @@ import { db } from '@sim/db' import { chat } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' const logger = createLogger('ChatStatusAPI') diff --git a/apps/sim/app/api/workflows/[id]/deploy/route.ts b/apps/sim/app/api/workflows/[id]/deploy/route.ts index cb898ff5d3..c54124f47d 100644 --- a/apps/sim/app/api/workflows/[id]/deploy/route.ts +++ b/apps/sim/app/api/workflows/[id]/deploy/route.ts @@ -1,8 +1,8 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { deployWorkflow, loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { createSchedulesForDeploy, @@ -60,13 +60,20 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ const { loadWorkflowFromNormalizedTables } = await import('@/lib/workflows/persistence/utils') const normalizedData = await loadWorkflowFromNormalizedTables(id) if (normalizedData) { + const [workflowRecord] = await db + .select({ variables: workflow.variables }) + .from(workflow) + .where(eq(workflow.id, id)) + .limit(1) + const currentState = { blocks: normalizedData.blocks, edges: normalizedData.edges, loops: normalizedData.loops, parallels: normalizedData.parallels, + variables: workflowRecord?.variables || {}, } - const { hasWorkflowChanged } = await import('@/lib/workflows/utils') + const { hasWorkflowChanged } = await import('@/lib/workflows/comparison') needsRedeployment = hasWorkflowChanged(currentState as any, active.state as any) } } diff --git a/apps/sim/app/api/workflows/[id]/deployed/route.ts b/apps/sim/app/api/workflows/[id]/deployed/route.ts index 735b481e62..e939fc0f09 100644 --- a/apps/sim/app/api/workflows/[id]/deployed/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployed/route.ts @@ -1,9 +1,9 @@ import { db, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import type { NextRequest, NextResponse } from 'next/server' import { verifyInternalToken } from '@/lib/auth/internal' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { validateWorkflowPermissions } from '@/lib/workflows/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts index 4961ec65d0..1ef4761e68 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/activate/route.ts @@ -1,8 +1,8 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { validateWorkflowPermissions } from '@/lib/workflows/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts index a3153290ca..5b33e6c146 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/revert/route.ts @@ -1,9 +1,9 @@ import { db, workflow, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { env } from '@/lib/core/config/env' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' import { validateWorkflowPermissions } from '@/lib/workflows/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts b/apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts index 3206798d70..20642c90ee 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts @@ -1,9 +1,9 @@ import { db, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { validateWorkflowPermissions } from '@/lib/workflows/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/deployments/route.ts b/apps/sim/app/api/workflows/[id]/deployments/route.ts index a74c015ffb..80ee376aa4 100644 --- a/apps/sim/app/api/workflows/[id]/deployments/route.ts +++ b/apps/sim/app/api/workflows/[id]/deployments/route.ts @@ -1,8 +1,8 @@ import { db, user, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { desc, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { validateWorkflowPermissions } from '@/lib/workflows/utils' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/duplicate/route.ts b/apps/sim/app/api/workflows/[id]/duplicate/route.ts index 8e1bfe6497..41ce249d0c 100644 --- a/apps/sim/app/api/workflows/[id]/duplicate/route.ts +++ b/apps/sim/app/api/workflows/[id]/duplicate/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate' const logger = createLogger('WorkflowDuplicateAPI') diff --git a/apps/sim/app/api/workflows/[id]/execute/route.ts b/apps/sim/app/api/workflows/[id]/execute/route.ts index dd70158d38..5d1a7d7a02 100644 --- a/apps/sim/app/api/workflows/[id]/execute/route.ts +++ b/apps/sim/app/api/workflows/[id]/execute/route.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import { tasks } from '@trigger.dev/sdk' import { type NextRequest, NextResponse } from 'next/server' import { validate as uuidValidate, v4 as uuidv4 } from 'uuid' @@ -7,9 +8,9 @@ import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags' import { generateRequestId } from '@/lib/core/utils/request' import { SSE_HEADERS } from '@/lib/core/utils/sse' import { getBaseUrl } from '@/lib/core/utils/urls' +import { markExecutionCancelled } from '@/lib/execution/cancellation' import { processInputFileFields } from '@/lib/execution/files' import { preprocessExecution } from '@/lib/execution/preprocessing' -import { createLogger } from '@/lib/logs/console/logger' import { LoggingSession } from '@/lib/logs/execution/logging-session' import { ALL_TRIGGER_TYPES } from '@/lib/logs/types' import { executeWorkflowCore } from '@/lib/workflows/executor/execution-core' @@ -317,6 +318,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: loops: Record parallels: Record deploymentVersionId?: string + variables?: Record } | null = null let processedInput = input @@ -326,6 +328,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: : await loadDeployedWorkflowState(workflowId) if (workflowData) { + const deployedVariables = + !shouldUseDraftState && 'variables' in workflowData + ? (workflowData as any).variables + : undefined + cachedWorkflowData = { blocks: workflowData.blocks, edges: workflowData.edges, @@ -335,6 +342,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: !shouldUseDraftState && 'deploymentVersionId' in workflowData ? (workflowData.deploymentVersionId as string) : undefined, + variables: deployedVariables, } const serializedWorkflow = new Serializer().serializeWorkflow( @@ -404,11 +412,13 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: workflowStateOverride: effectiveWorkflowStateOverride, } + const executionVariables = cachedWorkflowData?.variables ?? workflow.variables ?? {} + const snapshot = new ExecutionSnapshot( metadata, workflow, processedInput, - workflow.variables || {}, + executionVariables, selectedOutputs ) @@ -470,6 +480,8 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: selectedOutputs, cachedWorkflowData?.blocks || {} ) + const streamVariables = cachedWorkflowData?.variables ?? (workflow as any).variables + const stream = await createStreamingResponse({ requestId, workflow: { @@ -477,7 +489,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: userId: actorUserId, workspaceId, isDeployed: workflow.isDeployed, - variables: (workflow as any).variables, + variables: streamVariables, }, input: processedInput, executingUserId: actorUserId, @@ -496,7 +508,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: } const encoder = new TextEncoder() - let executorInstance: any = null + const abortController = new AbortController() let isStreamClosed = false const stream = new ReadableStream({ @@ -674,11 +686,13 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: workflowStateOverride: effectiveWorkflowStateOverride, } + const sseExecutionVariables = cachedWorkflowData?.variables ?? workflow.variables ?? {} + const snapshot = new ExecutionSnapshot( metadata, workflow, processedInput, - workflow.variables || {}, + sseExecutionVariables, selectedOutputs ) @@ -688,11 +702,9 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: onBlockStart, onBlockComplete, onStream, - onExecutorCreated: (executor) => { - executorInstance = executor - }, }, loggingSession, + abortSignal: abortController.signal, }) if (result.status === 'paused') { @@ -769,11 +781,9 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id: }, cancel() { isStreamClosed = true - logger.info(`[${requestId}] Client aborted SSE stream, cancelling executor`) - - if (executorInstance && typeof executorInstance.cancel === 'function') { - executorInstance.cancel() - } + logger.info(`[${requestId}] Client aborted SSE stream, signalling cancellation`) + abortController.abort() + markExecutionCancelled(executionId).catch(() => {}) }, }) diff --git a/apps/sim/app/api/workflows/[id]/executions/[executionId]/cancel/route.ts b/apps/sim/app/api/workflows/[id]/executions/[executionId]/cancel/route.ts new file mode 100644 index 0000000000..2544bb342c --- /dev/null +++ b/apps/sim/app/api/workflows/[id]/executions/[executionId]/cancel/route.ts @@ -0,0 +1,47 @@ +import { createLogger } from '@sim/logger' +import { type NextRequest, NextResponse } from 'next/server' +import { checkHybridAuth } from '@/lib/auth/hybrid' +import { markExecutionCancelled } from '@/lib/execution/cancellation' + +const logger = createLogger('CancelExecutionAPI') + +export const runtime = 'nodejs' +export const dynamic = 'force-dynamic' + +export async function POST( + req: NextRequest, + { params }: { params: Promise<{ id: string; executionId: string }> } +) { + const { id: workflowId, executionId } = await params + + try { + const auth = await checkHybridAuth(req, { requireWorkflowId: false }) + if (!auth.success || !auth.userId) { + return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 }) + } + + logger.info('Cancel execution requested', { workflowId, executionId, userId: auth.userId }) + + const marked = await markExecutionCancelled(executionId) + + if (marked) { + logger.info('Execution marked as cancelled in Redis', { executionId }) + } else { + logger.info('Redis not available, cancellation will rely on connection close', { + executionId, + }) + } + + return NextResponse.json({ + success: true, + executionId, + redisAvailable: marked, + }) + } catch (error: any) { + logger.error('Failed to cancel execution', { workflowId, executionId, error: error.message }) + return NextResponse.json( + { error: error.message || 'Failed to cancel execution' }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/workflows/[id]/log/route.ts b/apps/sim/app/api/workflows/[id]/log/route.ts index dc41e04e62..744b4b545b 100644 --- a/apps/sim/app/api/workflows/[id]/log/route.ts +++ b/apps/sim/app/api/workflows/[id]/log/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { LoggingSession } from '@/lib/logs/execution/logging-session' import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans' import { validateWorkflowAccess } from '@/app/api/workflows/middleware' diff --git a/apps/sim/app/api/workflows/[id]/route.test.ts b/apps/sim/app/api/workflows/[id]/route.test.ts index abae661998..12ea444173 100644 --- a/apps/sim/app/api/workflows/[id]/route.test.ts +++ b/apps/sim/app/api/workflows/[id]/route.test.ts @@ -20,7 +20,7 @@ vi.mock('@/lib/auth', () => ({ getSession: () => mockGetSession(), })) -vi.mock('@/lib/logs/console/logger', () => ({ +vi.mock('@sim/logger', () => ({ createLogger: vi.fn(() => ({ debug: vi.fn(), info: vi.fn(), diff --git a/apps/sim/app/api/workflows/[id]/route.ts b/apps/sim/app/api/workflows/[id]/route.ts index c4bab613d3..92a19d41c7 100644 --- a/apps/sim/app/api/workflows/[id]/route.ts +++ b/apps/sim/app/api/workflows/[id]/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { templates, webhook, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' @@ -8,7 +9,6 @@ import { getSession } from '@/lib/auth' import { verifyInternalToken } from '@/lib/auth/internal' import { env } from '@/lib/core/config/env' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' import { getWorkflowAccessContext, getWorkflowById } from '@/lib/workflows/utils' diff --git a/apps/sim/app/api/workflows/[id]/state/route.ts b/apps/sim/app/api/workflows/[id]/state/route.ts index ba68cb6966..43957ad95a 100644 --- a/apps/sim/app/api/workflows/[id]/state/route.ts +++ b/apps/sim/app/api/workflows/[id]/state/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { webhook, workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { env } from '@/lib/core/config/env' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { extractAndPersistCustomTools } from '@/lib/workflows/persistence/custom-tools-persistence' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' import { sanitizeAgentToolsInBlocks } from '@/lib/workflows/sanitization/validation' diff --git a/apps/sim/app/api/workflows/[id]/status/route.ts b/apps/sim/app/api/workflows/[id]/status/route.ts index b25b21a6e8..b83dffed3a 100644 --- a/apps/sim/app/api/workflows/[id]/status/route.ts +++ b/apps/sim/app/api/workflows/[id]/status/route.ts @@ -1,10 +1,10 @@ -import { db, workflowDeploymentVersion } from '@sim/db' +import { db, workflow, workflowDeploymentVersion } from '@sim/db' +import { createLogger } from '@sim/logger' import { and, desc, eq } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' +import { hasWorkflowChanged } from '@/lib/workflows/comparison' import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' -import { hasWorkflowChanged } from '@/lib/workflows/utils' import { validateWorkflowAccess } from '@/app/api/workflows/middleware' import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils' @@ -22,17 +22,12 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ return createErrorResponse(validation.error.message, validation.error.status) } - // Check if the workflow has meaningful changes that would require redeployment let needsRedeployment = false if (validation.workflow.isDeployed) { - // Get current state from normalized tables (same logic as deployment API) - // Load current state from normalized tables using centralized helper const normalizedData = await loadWorkflowFromNormalizedTables(id) if (!normalizedData) { - // Workflow exists but has no blocks in normalized tables (empty workflow or not migrated) - // This is valid state - return success with no redeployment needed return createSuccessResponse({ isDeployed: validation.workflow.isDeployed, deployedAt: validation.workflow.deployedAt, @@ -41,11 +36,18 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ }) } + const [workflowRecord] = await db + .select({ variables: workflow.variables }) + .from(workflow) + .where(eq(workflow.id, id)) + .limit(1) + const currentState = { blocks: normalizedData.blocks, edges: normalizedData.edges, loops: normalizedData.loops, parallels: normalizedData.parallels, + variables: workflowRecord?.variables || {}, lastSaved: Date.now(), } @@ -69,6 +71,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ return createSuccessResponse({ isDeployed: validation.workflow.isDeployed, deployedAt: validation.workflow.deployedAt, + isPublished: validation.workflow.isPublished, needsRedeployment, }) } catch (error) { diff --git a/apps/sim/app/api/workflows/[id]/variables/route.ts b/apps/sim/app/api/workflows/[id]/variables/route.ts index 88f80ce05d..ec7d5d486f 100644 --- a/apps/sim/app/api/workflows/[id]/variables/route.ts +++ b/apps/sim/app/api/workflows/[id]/variables/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getWorkflowAccessContext } from '@/lib/workflows/utils' import type { Variable } from '@/stores/panel/variables/types' diff --git a/apps/sim/app/api/workflows/middleware.ts b/apps/sim/app/api/workflows/middleware.ts index 883e02125c..d3cbfa3b6d 100644 --- a/apps/sim/app/api/workflows/middleware.ts +++ b/apps/sim/app/api/workflows/middleware.ts @@ -1,3 +1,4 @@ +import { createLogger } from '@sim/logger' import type { NextRequest } from 'next/server' import { type ApiKeyAuthResult, @@ -5,7 +6,6 @@ import { updateApiKeyLastUsed, } from '@/lib/api-key/service' import { env } from '@/lib/core/config/env' -import { createLogger } from '@/lib/logs/console/logger' import { getWorkflowById } from '@/lib/workflows/utils' const logger = createLogger('WorkflowMiddleware') diff --git a/apps/sim/app/api/workflows/route.ts b/apps/sim/app/api/workflows/route.ts index 6b78495c55..4ff9d99acc 100644 --- a/apps/sim/app/api/workflows/route.ts +++ b/apps/sim/app/api/workflows/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { workflow, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workflows/utils.ts b/apps/sim/app/api/workflows/utils.ts index 348bedcb1e..a6646d3950 100644 --- a/apps/sim/app/api/workflows/utils.ts +++ b/apps/sim/app/api/workflows/utils.ts @@ -1,5 +1,5 @@ +import { createLogger } from '@sim/logger' import { NextResponse } from 'next/server' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkflowUtils') diff --git a/apps/sim/app/api/workflows/yaml/convert/route.ts b/apps/sim/app/api/workflows/yaml/convert/route.ts deleted file mode 100644 index 899585dfad..0000000000 --- a/apps/sim/app/api/workflows/yaml/convert/route.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { type NextRequest, NextResponse } from 'next/server' -import { simAgentClient } from '@/lib/copilot/client' -import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' -import { getAllBlocks } from '@/blocks/registry' -import type { BlockConfig } from '@/blocks/types' -import { resolveOutputType } from '@/blocks/utils' -import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' - -const logger = createLogger('WorkflowYamlAPI') - -export async function POST(request: NextRequest) { - const requestId = generateRequestId() - - try { - logger.info(`[${requestId}] Converting workflow JSON to YAML`) - - const body = await request.json() - const { workflowState, subBlockValues, includeMetadata = false } = body - - if (!workflowState) { - return NextResponse.json( - { success: false, error: 'workflowState is required' }, - { status: 400 } - ) - } - - // Ensure loop blocks have their data populated with defaults - if (workflowState.blocks) { - Object.entries(workflowState.blocks).forEach(([blockId, block]: [string, any]) => { - if (block.type === 'loop') { - // Ensure data field exists - if (!block.data) { - block.data = {} - } - - // Apply defaults if not set - if (!block.data.loopType) { - block.data.loopType = 'for' - } - if (!block.data.count && block.data.count !== 0) { - block.data.count = 5 - } - if (!block.data.collection) { - block.data.collection = '' - } - if (!block.data.maxConcurrency) { - block.data.maxConcurrency = 1 - } - - logger.debug(`[${requestId}] Applied defaults to loop block ${blockId}:`, { - loopType: block.data.loopType, - count: block.data.count, - }) - } - }) - } - - // Gather block registry and utilities for sim-agent - const blocks = getAllBlocks() - const blockRegistry = blocks.reduce( - (acc, block) => { - const blockType = block.type - acc[blockType] = { - ...block, - id: blockType, - subBlocks: block.subBlocks || [], - outputs: block.outputs || {}, - } as any - return acc - }, - {} as Record - ) - - // Call sim-agent directly - const result = await simAgentClient.makeRequest('/api/workflow/to-yaml', { - body: { - workflowState, - subBlockValues, - blockRegistry, - utilities: { - generateLoopBlocks: generateLoopBlocks.toString(), - generateParallelBlocks: generateParallelBlocks.toString(), - resolveOutputType: resolveOutputType.toString(), - }, - }, - }) - - if (!result.success || !result.data?.yaml) { - return NextResponse.json( - { - success: false, - error: result.error || 'Failed to generate YAML', - }, - { status: result.status || 500 } - ) - } - - logger.info(`[${requestId}] Successfully generated YAML`, { - yamlLength: result.data.yaml.length, - }) - - return NextResponse.json({ - success: true, - yaml: result.data.yaml, - }) - } catch (error) { - logger.error(`[${requestId}] YAML generation failed`, error) - return NextResponse.json( - { - success: false, - error: `Failed to generate YAML: ${error instanceof Error ? error.message : 'Unknown error'}`, - }, - { status: 500 } - ) - } -} diff --git a/apps/sim/app/api/workflows/yaml/export/route.ts b/apps/sim/app/api/workflows/yaml/export/route.ts deleted file mode 100644 index 4292e82d4d..0000000000 --- a/apps/sim/app/api/workflows/yaml/export/route.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { db } from '@sim/db' -import { workflow } from '@sim/db/schema' -import { eq } from 'drizzle-orm' -import { type NextRequest, NextResponse } from 'next/server' -import { getSession } from '@/lib/auth' -import { simAgentClient } from '@/lib/copilot/client' -import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' -import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils' -import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' -import { getAllBlocks } from '@/blocks/registry' -import type { BlockConfig } from '@/blocks/types' -import { resolveOutputType } from '@/blocks/utils' -import { generateLoopBlocks, generateParallelBlocks } from '@/stores/workflows/workflow/utils' - -const logger = createLogger('WorkflowYamlExportAPI') - -export async function GET(request: NextRequest) { - const requestId = generateRequestId() - const url = new URL(request.url) - const workflowId = url.searchParams.get('workflowId') - - try { - logger.info(`[${requestId}] Exporting workflow YAML from database: ${workflowId}`) - - if (!workflowId) { - return NextResponse.json({ success: false, error: 'workflowId is required' }, { status: 400 }) - } - - // Get the session for authentication - const session = await getSession() - if (!session?.user?.id) { - logger.warn(`[${requestId}] Unauthorized access attempt for workflow ${workflowId}`) - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - - const userId = session.user.id - - // Fetch the workflow from database - const workflowData = await db - .select() - .from(workflow) - .where(eq(workflow.id, workflowId)) - .then((rows) => rows[0]) - - if (!workflowData) { - logger.warn(`[${requestId}] Workflow ${workflowId} not found`) - return NextResponse.json({ error: 'Workflow not found' }, { status: 404 }) - } - - // Check if user has access to this workflow - let hasAccess = false - - // Case 1: User owns the workflow - if (workflowData.userId === userId) { - hasAccess = true - } - - // Case 2: Workflow belongs to a workspace the user has permissions for - if (!hasAccess && workflowData.workspaceId) { - const userPermission = await getUserEntityPermissions( - userId, - 'workspace', - workflowData.workspaceId - ) - if (userPermission !== null) { - hasAccess = true - } - } - - if (!hasAccess) { - logger.warn(`[${requestId}] User ${userId} denied access to workflow ${workflowId}`) - return NextResponse.json({ error: 'Access denied' }, { status: 403 }) - } - - // Try to load from normalized tables first - logger.debug(`[${requestId}] Attempting to load workflow ${workflowId} from normalized tables`) - const normalizedData = await loadWorkflowFromNormalizedTables(workflowId) - - let workflowState: any - const subBlockValues: Record> = {} - - if (normalizedData) { - logger.debug(`[${requestId}] Found normalized data for workflow ${workflowId}:`, { - blocksCount: Object.keys(normalizedData.blocks).length, - edgesCount: normalizedData.edges.length, - }) - - // Use normalized table data - construct state from normalized tables - workflowState = { - deploymentStatuses: {}, - blocks: normalizedData.blocks, - edges: normalizedData.edges, - loops: normalizedData.loops, - parallels: normalizedData.parallels, - lastSaved: Date.now(), - isDeployed: workflowData.isDeployed || false, - deployedAt: workflowData.deployedAt, - } - - // Extract subblock values from the normalized blocks - Object.entries(normalizedData.blocks).forEach(([blockId, block]: [string, any]) => { - subBlockValues[blockId] = {} - if (block.subBlocks) { - Object.entries(block.subBlocks).forEach(([subBlockId, subBlock]: [string, any]) => { - if (subBlock && typeof subBlock === 'object' && 'value' in subBlock) { - subBlockValues[blockId][subBlockId] = subBlock.value - } - }) - } - }) - - logger.info(`[${requestId}] Loaded workflow ${workflowId} from normalized tables`) - } else { - return NextResponse.json( - { success: false, error: 'Workflow has no normalized data' }, - { status: 400 } - ) - } - - // Ensure loop blocks have their data populated with defaults - if (workflowState.blocks) { - Object.entries(workflowState.blocks).forEach(([blockId, block]: [string, any]) => { - if (block.type === 'loop') { - // Ensure data field exists - if (!block.data) { - block.data = {} - } - - // Apply defaults if not set - if (!block.data.loopType) { - block.data.loopType = 'for' - } - if (!block.data.count && block.data.count !== 0) { - block.data.count = 5 - } - if (!block.data.collection) { - block.data.collection = '' - } - if (!block.data.maxConcurrency) { - block.data.maxConcurrency = 1 - } - - logger.debug(`[${requestId}] Applied defaults to loop block ${blockId}:`, { - loopType: block.data.loopType, - count: block.data.count, - }) - } - }) - } - - // Gather block registry and utilities for sim-agent - const blocks = getAllBlocks() - const blockRegistry = blocks.reduce( - (acc, block) => { - const blockType = block.type - acc[blockType] = { - ...block, - id: blockType, - subBlocks: block.subBlocks || [], - outputs: block.outputs || {}, - } as any - return acc - }, - {} as Record - ) - - // Call sim-agent directly - const result = await simAgentClient.makeRequest('/api/workflow/to-yaml', { - body: { - workflowState, - subBlockValues, - blockRegistry, - utilities: { - generateLoopBlocks: generateLoopBlocks.toString(), - generateParallelBlocks: generateParallelBlocks.toString(), - resolveOutputType: resolveOutputType.toString(), - }, - }, - }) - - if (!result.success || !result.data?.yaml) { - return NextResponse.json( - { - success: false, - error: result.error || 'Failed to generate YAML', - }, - { status: result.status || 500 } - ) - } - - logger.info(`[${requestId}] Successfully generated YAML from database`, { - yamlLength: result.data.yaml.length, - }) - - return NextResponse.json({ - success: true, - yaml: result.data.yaml, - }) - } catch (error) { - logger.error(`[${requestId}] YAML export failed`, error) - return NextResponse.json( - { - success: false, - error: `Failed to export YAML: ${error instanceof Error ? error.message : 'Unknown error'}`, - }, - { status: 500 } - ) - } -} diff --git a/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts b/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts index 34f9909295..f72a86f1d8 100644 --- a/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/api-keys/[keyId]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { apiKey } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, not } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkspaceApiKeyAPI') diff --git a/apps/sim/app/api/workspaces/[id]/api-keys/route.ts b/apps/sim/app/api/workspaces/[id]/api-keys/route.ts index f29df67dc9..0944b15fe2 100644 --- a/apps/sim/app/api/workspaces/[id]/api-keys/route.ts +++ b/apps/sim/app/api/workspaces/[id]/api-keys/route.ts @@ -1,5 +1,6 @@ import { db } from '@sim/db' import { apiKey, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray } from 'drizzle-orm' import { nanoid } from 'nanoid' import { type NextRequest, NextResponse } from 'next/server' @@ -7,7 +8,6 @@ import { z } from 'zod' import { createApiKey, getApiKeyDisplayFormat } from '@/lib/api-key/auth' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkspaceApiKeysAPI') diff --git a/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts b/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts new file mode 100644 index 0000000000..246cc6b245 --- /dev/null +++ b/apps/sim/app/api/workspaces/[id]/byok-keys/route.ts @@ -0,0 +1,256 @@ +import { db } from '@sim/db' +import { workspace, workspaceBYOKKeys } from '@sim/db/schema' +import { createLogger } from '@sim/logger' +import { and, eq } from 'drizzle-orm' +import { nanoid } from 'nanoid' +import { type NextRequest, NextResponse } from 'next/server' +import { z } from 'zod' +import { getSession } from '@/lib/auth' +import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' +import { generateRequestId } from '@/lib/core/utils/request' +import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' + +const logger = createLogger('WorkspaceBYOKKeysAPI') + +const VALID_PROVIDERS = ['openai', 'anthropic', 'google', 'mistral'] as const + +const UpsertKeySchema = z.object({ + providerId: z.enum(VALID_PROVIDERS), + apiKey: z.string().min(1, 'API key is required'), +}) + +const DeleteKeySchema = z.object({ + providerId: z.enum(VALID_PROVIDERS), +}) + +function maskApiKey(key: string): string { + if (key.length <= 8) { + return '•'.repeat(8) + } + if (key.length <= 12) { + return `${key.slice(0, 4)}...${key.slice(-4)}` + } + return `${key.slice(0, 6)}...${key.slice(-4)}` +} + +export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { + const requestId = generateRequestId() + const workspaceId = (await params).id + + try { + const session = await getSession() + if (!session?.user?.id) { + logger.warn(`[${requestId}] Unauthorized BYOK keys access attempt`) + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + const userId = session.user.id + + const ws = await db.select().from(workspace).where(eq(workspace.id, workspaceId)).limit(1) + if (!ws.length) { + return NextResponse.json({ error: 'Workspace not found' }, { status: 404 }) + } + + const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId) + if (!permission) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + const byokKeys = await db + .select({ + id: workspaceBYOKKeys.id, + providerId: workspaceBYOKKeys.providerId, + encryptedApiKey: workspaceBYOKKeys.encryptedApiKey, + createdBy: workspaceBYOKKeys.createdBy, + createdAt: workspaceBYOKKeys.createdAt, + updatedAt: workspaceBYOKKeys.updatedAt, + }) + .from(workspaceBYOKKeys) + .where(eq(workspaceBYOKKeys.workspaceId, workspaceId)) + .orderBy(workspaceBYOKKeys.providerId) + + const formattedKeys = await Promise.all( + byokKeys.map(async (key) => { + try { + const { decrypted } = await decryptSecret(key.encryptedApiKey) + return { + id: key.id, + providerId: key.providerId, + maskedKey: maskApiKey(decrypted), + createdBy: key.createdBy, + createdAt: key.createdAt, + updatedAt: key.updatedAt, + } + } catch (error) { + logger.error(`[${requestId}] Failed to decrypt BYOK key for provider ${key.providerId}`, { + error, + }) + return { + id: key.id, + providerId: key.providerId, + maskedKey: '••••••••', + createdBy: key.createdBy, + createdAt: key.createdAt, + updatedAt: key.updatedAt, + } + } + }) + ) + + return NextResponse.json({ keys: formattedKeys }) + } catch (error: unknown) { + logger.error(`[${requestId}] BYOK keys GET error`, error) + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to load BYOK keys' }, + { status: 500 } + ) + } +} + +export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { + const requestId = generateRequestId() + const workspaceId = (await params).id + + try { + const session = await getSession() + if (!session?.user?.id) { + logger.warn(`[${requestId}] Unauthorized BYOK key creation attempt`) + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + const userId = session.user.id + + const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId) + if (permission !== 'admin') { + return NextResponse.json( + { error: 'Only workspace admins can manage BYOK keys' }, + { status: 403 } + ) + } + + const body = await request.json() + const { providerId, apiKey } = UpsertKeySchema.parse(body) + + const { encrypted } = await encryptSecret(apiKey) + + const existingKey = await db + .select() + .from(workspaceBYOKKeys) + .where( + and( + eq(workspaceBYOKKeys.workspaceId, workspaceId), + eq(workspaceBYOKKeys.providerId, providerId) + ) + ) + .limit(1) + + if (existingKey.length > 0) { + await db + .update(workspaceBYOKKeys) + .set({ + encryptedApiKey: encrypted, + updatedAt: new Date(), + }) + .where(eq(workspaceBYOKKeys.id, existingKey[0].id)) + + logger.info(`[${requestId}] Updated BYOK key for ${providerId} in workspace ${workspaceId}`) + + return NextResponse.json({ + success: true, + key: { + id: existingKey[0].id, + providerId, + maskedKey: maskApiKey(apiKey), + updatedAt: new Date(), + }, + }) + } + + const [newKey] = await db + .insert(workspaceBYOKKeys) + .values({ + id: nanoid(), + workspaceId, + providerId, + encryptedApiKey: encrypted, + createdBy: userId, + createdAt: new Date(), + updatedAt: new Date(), + }) + .returning({ + id: workspaceBYOKKeys.id, + providerId: workspaceBYOKKeys.providerId, + createdAt: workspaceBYOKKeys.createdAt, + }) + + logger.info(`[${requestId}] Created BYOK key for ${providerId} in workspace ${workspaceId}`) + + return NextResponse.json({ + success: true, + key: { + ...newKey, + maskedKey: maskApiKey(apiKey), + }, + }) + } catch (error: unknown) { + logger.error(`[${requestId}] BYOK key POST error`, error) + if (error instanceof z.ZodError) { + return NextResponse.json({ error: error.errors[0].message }, { status: 400 }) + } + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to save BYOK key' }, + { status: 500 } + ) + } +} + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + const requestId = generateRequestId() + const workspaceId = (await params).id + + try { + const session = await getSession() + if (!session?.user?.id) { + logger.warn(`[${requestId}] Unauthorized BYOK key deletion attempt`) + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + const userId = session.user.id + + const permission = await getUserEntityPermissions(userId, 'workspace', workspaceId) + if (permission !== 'admin') { + return NextResponse.json( + { error: 'Only workspace admins can manage BYOK keys' }, + { status: 403 } + ) + } + + const body = await request.json() + const { providerId } = DeleteKeySchema.parse(body) + + const result = await db + .delete(workspaceBYOKKeys) + .where( + and( + eq(workspaceBYOKKeys.workspaceId, workspaceId), + eq(workspaceBYOKKeys.providerId, providerId) + ) + ) + + logger.info(`[${requestId}] Deleted BYOK key for ${providerId} from workspace ${workspaceId}`) + + return NextResponse.json({ success: true }) + } catch (error: unknown) { + logger.error(`[${requestId}] BYOK key DELETE error`, error) + if (error instanceof z.ZodError) { + return NextResponse.json({ error: error.errors[0].message }, { status: 400 }) + } + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Failed to delete BYOK key' }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/workspaces/[id]/duplicate/route.ts b/apps/sim/app/api/workspaces/[id]/duplicate/route.ts index 1354bec588..50f1d9c2ff 100644 --- a/apps/sim/app/api/workspaces/[id]/duplicate/route.ts +++ b/apps/sim/app/api/workspaces/[id]/duplicate/route.ts @@ -1,8 +1,8 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { duplicateWorkspace } from '@/lib/workspaces/duplicate' const logger = createLogger('WorkspaceDuplicateAPI') diff --git a/apps/sim/app/api/workspaces/[id]/environment/route.ts b/apps/sim/app/api/workspaces/[id]/environment/route.ts index 8328cf19e1..9c1ee4eb04 100644 --- a/apps/sim/app/api/workspaces/[id]/environment/route.ts +++ b/apps/sim/app/api/workspaces/[id]/environment/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { environment, workspace, workspaceEnvironment } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkspaceEnvironmentAPI') diff --git a/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts b/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts index f3719ab874..c35f283060 100644 --- a/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getWorkspaceFile } from '@/lib/uploads/contexts/workspace' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts b/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts index cf00bd1dd4..2c646d8e1d 100644 --- a/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/[fileId]/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { deleteWorkspaceFile } from '@/lib/uploads/contexts/workspace' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/workspaces/[id]/files/route.ts b/apps/sim/app/api/workspaces/[id]/files/route.ts index 7527081008..22a4233b0f 100644 --- a/apps/sim/app/api/workspaces/[id]/files/route.ts +++ b/apps/sim/app/api/workspaces/[id]/files/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { getSession } from '@/lib/auth' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { listWorkspaceFiles, uploadWorkspaceFile } from '@/lib/uploads/contexts/workspace' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import { verifyWorkspaceMembership } from '@/app/api/workflows/utils' diff --git a/apps/sim/app/api/workspaces/[id]/metrics/executions/route.ts b/apps/sim/app/api/workspaces/[id]/metrics/executions/route.ts index 3b424a25cd..4af974b0f3 100644 --- a/apps/sim/app/api/workspaces/[id]/metrics/executions/route.ts +++ b/apps/sim/app/api/workspaces/[id]/metrics/executions/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { pausedExecutions, permissions, workflow, workflowExecutionLogs } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, gte, inArray, isNotNull, isNull, lte, or, type SQL, sql } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('MetricsExecutionsAPI') diff --git a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts index 799d148a64..7e472af53b 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/route.ts @@ -1,11 +1,11 @@ import { db } from '@sim/db' import { workflow, workspaceNotificationSubscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' import { encryptSecret } from '@/lib/core/security/encryption' -import { createLogger } from '@/lib/logs/console/logger' import { ALL_TRIGGER_TYPES } from '@/lib/logs/types' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import { MAX_EMAIL_RECIPIENTS, MAX_WORKFLOW_IDS } from '../constants' diff --git a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts index 3cc3c3733d..3e95e22205 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/[notificationId]/test/route.ts @@ -1,12 +1,12 @@ import { createHmac } from 'crypto' import { db } from '@sim/db' import { account, workspaceNotificationSubscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { getSession } from '@/lib/auth' import { decryptSecret } from '@/lib/core/security/encryption' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/workspaces/[id]/notifications/route.ts b/apps/sim/app/api/workspaces/[id]/notifications/route.ts index b1aa69ae0a..2716a7ea57 100644 --- a/apps/sim/app/api/workspaces/[id]/notifications/route.ts +++ b/apps/sim/app/api/workspaces/[id]/notifications/route.ts @@ -1,12 +1,12 @@ import { db } from '@sim/db' import { workflow, workspaceNotificationSubscription } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { v4 as uuidv4 } from 'uuid' import { z } from 'zod' import { getSession } from '@/lib/auth' import { encryptSecret } from '@/lib/core/security/encryption' -import { createLogger } from '@/lib/logs/console/logger' import { ALL_TRIGGER_TYPES } from '@/lib/logs/types' import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils' import { MAX_EMAIL_RECIPIENTS, MAX_NOTIFICATIONS_PER_TYPE, MAX_WORKFLOW_IDS } from './constants' diff --git a/apps/sim/app/api/workspaces/[id]/permissions/route.ts b/apps/sim/app/api/workspaces/[id]/permissions/route.ts index 4c2e0dae3e..0025c90fc0 100644 --- a/apps/sim/app/api/workspaces/[id]/permissions/route.ts +++ b/apps/sim/app/api/workspaces/[id]/permissions/route.ts @@ -1,11 +1,11 @@ import crypto from 'crypto' import { db } from '@sim/db' import { permissions, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { getUsersWithPermissions, hasWorkspaceAdminAccess, diff --git a/apps/sim/app/api/workspaces/[id]/route.ts b/apps/sim/app/api/workspaces/[id]/route.ts index 7a77319b50..eed710c7c4 100644 --- a/apps/sim/app/api/workspaces/[id]/route.ts +++ b/apps/sim/app/api/workspaces/[id]/route.ts @@ -1,9 +1,9 @@ import { workflow } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('WorkspaceByIdAPI') diff --git a/apps/sim/app/api/workspaces/invitations/[invitationId]/route.test.ts b/apps/sim/app/api/workspaces/invitations/[invitationId]/route.test.ts index ecbb92a977..12833c9695 100644 --- a/apps/sim/app/api/workspaces/invitations/[invitationId]/route.test.ts +++ b/apps/sim/app/api/workspaces/invitations/[invitationId]/route.test.ts @@ -1,3 +1,4 @@ +import { createSession, createWorkspaceRecord, loggerMock } from '@sim/testing' import { NextRequest } from 'next/server' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -59,14 +60,7 @@ vi.mock('@/lib/workspaces/permissions/utils', () => ({ mockHasWorkspaceAdminAccess(userId, workspaceId), })) -vi.mock('@/lib/logs/console/logger', () => ({ - createLogger: vi.fn().mockReturnValue({ - debug: vi.fn(), - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }), -})) +vi.mock('@sim/logger', () => loggerMock) vi.mock('@/lib/core/utils/urls', () => ({ getBaseUrl: vi.fn().mockReturnValue('https://test.sim.ai'), @@ -127,9 +121,14 @@ const mockUser = { name: 'Test User', } -const mockWorkspace = { +const mockWorkspaceData = createWorkspaceRecord({ id: 'workspace-456', name: 'Test Workspace', +}) + +const mockWorkspace = { + id: mockWorkspaceData.id, + name: mockWorkspaceData.name, } const mockInvitation = { @@ -140,7 +139,7 @@ const mockInvitation = { status: 'pending', token: 'token-abc123', permissions: 'read', - expiresAt: new Date(Date.now() + 86400000), // 1 day from now + expiresAt: new Date(Date.now() + 86400000), createdAt: new Date(), updatedAt: new Date(), } @@ -154,7 +153,8 @@ describe('Workspace Invitation [invitationId] API Route', () => { describe('GET /api/workspaces/invitations/[invitationId]', () => { it('should return invitation details when called without token', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) dbSelectResults = [[mockInvitation], [mockWorkspace]] const request = new NextRequest('http://localhost/api/workspaces/invitations/invitation-789') @@ -202,15 +202,18 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should accept invitation when called with valid token', async () => { - mockGetSession.mockResolvedValue({ - user: { ...mockUser, email: 'invited@example.com' }, + const session = createSession({ + userId: mockUser.id, + email: 'invited@example.com', + name: mockUser.name, }) + mockGetSession.mockResolvedValue(session) dbSelectResults = [ - [mockInvitation], // invitation lookup - [mockWorkspace], // workspace lookup - [{ ...mockUser, email: 'invited@example.com' }], // user lookup - [], // existing permission check (empty = no existing) + [mockInvitation], + [mockWorkspace], + [{ ...mockUser, email: 'invited@example.com' }], + [], ] const request = new NextRequest( @@ -225,13 +228,16 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should redirect to error page when invitation expired', async () => { - mockGetSession.mockResolvedValue({ - user: { ...mockUser, email: 'invited@example.com' }, + const session = createSession({ + userId: mockUser.id, + email: 'invited@example.com', + name: mockUser.name, }) + mockGetSession.mockResolvedValue(session) const expiredInvitation = { ...mockInvitation, - expiresAt: new Date(Date.now() - 86400000), // 1 day ago + expiresAt: new Date(Date.now() - 86400000), } dbSelectResults = [[expiredInvitation], [mockWorkspace]] @@ -250,9 +256,12 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should redirect to error page when email mismatch', async () => { - mockGetSession.mockResolvedValue({ - user: { ...mockUser, email: 'wrong@example.com' }, + const session = createSession({ + userId: mockUser.id, + email: 'wrong@example.com', + name: mockUser.name, }) + mockGetSession.mockResolvedValue(session) dbSelectResults = [ [mockInvitation], @@ -274,8 +283,9 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should return 404 when invitation not found', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) - dbSelectResults = [[]] // Empty result + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) + dbSelectResults = [[]] const request = new NextRequest('http://localhost/api/workspaces/invitations/non-existent') const params = Promise.resolve({ invitationId: 'non-existent' }) @@ -306,7 +316,8 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should return 404 when invitation does not exist', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) dbSelectResults = [[]] const request = new NextRequest('http://localhost/api/workspaces/invitations/non-existent', { @@ -322,7 +333,8 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should return 403 when user lacks admin access', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) mockHasWorkspaceAdminAccess.mockResolvedValue(false) dbSelectResults = [[mockInvitation]] @@ -341,7 +353,8 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should return 400 when trying to delete non-pending invitation', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) mockHasWorkspaceAdminAccess.mockResolvedValue(true) const acceptedInvitation = { ...mockInvitation, status: 'accepted' } @@ -361,7 +374,8 @@ describe('Workspace Invitation [invitationId] API Route', () => { }) it('should successfully delete pending invitation when user has admin access', async () => { - mockGetSession.mockResolvedValue({ user: mockUser }) + const session = createSession({ userId: mockUser.id, email: mockUser.email }) + mockGetSession.mockResolvedValue(session) mockHasWorkspaceAdminAccess.mockResolvedValue(true) dbSelectResults = [[mockInvitation]] diff --git a/apps/sim/app/api/workspaces/invitations/[invitationId]/route.ts b/apps/sim/app/api/workspaces/invitations/[invitationId]/route.ts index 879624ac1b..0d427f1779 100644 --- a/apps/sim/app/api/workspaces/invitations/[invitationId]/route.ts +++ b/apps/sim/app/api/workspaces/invitations/[invitationId]/route.ts @@ -8,12 +8,12 @@ import { workspace, workspaceInvitation, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { WorkspaceInvitationEmail } from '@/components/emails/workspace-invitation' import { getSession } from '@/lib/auth' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress } from '@/lib/messaging/email/utils' import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils' diff --git a/apps/sim/app/api/workspaces/invitations/route.ts b/apps/sim/app/api/workspaces/invitations/route.ts index 62cfff3d97..6ad6285b35 100644 --- a/apps/sim/app/api/workspaces/invitations/route.ts +++ b/apps/sim/app/api/workspaces/invitations/route.ts @@ -9,12 +9,12 @@ import { workspace, workspaceInvitation, } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq, inArray } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { WorkspaceInvitationEmail } from '@/components/emails/workspace-invitation' import { getSession } from '@/lib/auth' import { getBaseUrl } from '@/lib/core/utils/urls' -import { createLogger } from '@/lib/logs/console/logger' import { sendEmail } from '@/lib/messaging/email/mailer' import { getFromEmailAddress } from '@/lib/messaging/email/utils' diff --git a/apps/sim/app/api/workspaces/members/[id]/route.ts b/apps/sim/app/api/workspaces/members/[id]/route.ts index b835d89336..ec990da241 100644 --- a/apps/sim/app/api/workspaces/members/[id]/route.ts +++ b/apps/sim/app/api/workspaces/members/[id]/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { permissions, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, eq } from 'drizzle-orm' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils' const logger = createLogger('WorkspaceMemberAPI') diff --git a/apps/sim/app/api/workspaces/route.ts b/apps/sim/app/api/workspaces/route.ts index b052d60495..6b8c36ba31 100644 --- a/apps/sim/app/api/workspaces/route.ts +++ b/apps/sim/app/api/workspaces/route.ts @@ -1,10 +1,10 @@ import { db } from '@sim/db' import { permissions, workflow, workspace } from '@sim/db/schema' +import { createLogger } from '@sim/logger' import { and, desc, eq, isNull } from 'drizzle-orm' import { NextResponse } from 'next/server' import { z } from 'zod' import { getSession } from '@/lib/auth' -import { createLogger } from '@/lib/logs/console/logger' import { buildDefaultWorkflowArtifacts } from '@/lib/workflows/defaults' import { saveWorkflowToNormalizedTables } from '@/lib/workflows/persistence/utils' diff --git a/apps/sim/app/api/yaml/autolayout/route.ts b/apps/sim/app/api/yaml/autolayout/route.ts index 3361813854..600212340f 100644 --- a/apps/sim/app/api/yaml/autolayout/route.ts +++ b/apps/sim/app/api/yaml/autolayout/route.ts @@ -1,7 +1,7 @@ +import { createLogger } from '@sim/logger' import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' import { generateRequestId } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { applyAutoLayout } from '@/lib/workflows/autolayout' import { DEFAULT_HORIZONTAL_SPACING, diff --git a/apps/sim/app/changelog/layout.tsx b/apps/sim/app/changelog/layout.tsx index 3ce5dbdfa6..f3237c0ca4 100644 --- a/apps/sim/app/changelog/layout.tsx +++ b/apps/sim/app/changelog/layout.tsx @@ -2,7 +2,8 @@ import Nav from '@/app/(landing)/components/nav/nav' export default function ChangelogLayout({ children }: { children: React.ReactNode }) { return ( -
+
+
diff --git a/apps/sim/app/chat/[identifier]/chat.tsx b/apps/sim/app/chat/[identifier]/chat.tsx index 96be0631d7..7c6c8f273c 100644 --- a/apps/sim/app/chat/[identifier]/chat.tsx +++ b/apps/sim/app/chat/[identifier]/chat.tsx @@ -1,9 +1,9 @@ 'use client' import { type RefObject, useCallback, useEffect, useRef, useState } from 'react' +import { createLogger } from '@sim/logger' import { v4 as uuidv4 } from 'uuid' import { noop } from '@/lib/core/utils/request' -import { createLogger } from '@/lib/logs/console/logger' import { getFormattedGitHubStars } from '@/app/(landing)/actions/github' import { ChatErrorState, @@ -117,7 +117,7 @@ export default function ChatClient({ identifier }: { identifier: string }) { const [error, setError] = useState(null) const messagesEndRef = useRef(null) const messagesContainerRef = useRef(null) - const [starCount, setStarCount] = useState('24k') + const [starCount, setStarCount] = useState('24.4k') const [conversationId, setConversationId] = useState('') const [showScrollButton, setShowScrollButton] = useState(false) @@ -553,7 +553,7 @@ export default function ChatClient({ identifier }: { identifier: string }) { // Standard text-based chat interface return ( -
+
{/* Header component */} diff --git a/apps/sim/app/chat/components/auth/email/email-auth.tsx b/apps/sim/app/chat/components/auth/email/email-auth.tsx index 63281b454e..fb2a5d8036 100644 --- a/apps/sim/app/chat/components/auth/email/email-auth.tsx +++ b/apps/sim/app/chat/components/auth/email/email-auth.tsx @@ -1,13 +1,12 @@ 'use client' import { type KeyboardEvent, useEffect, useState } from 'react' -import { Loader2 } from 'lucide-react' +import { createLogger } from '@sim/logger' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp' import { Label } from '@/components/ui/label' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' @@ -299,14 +298,7 @@ export default function EmailAuth({ className={`${buttonClass} flex w-full items-center justify-center gap-2 rounded-[10px] border font-medium text-[15px] text-white transition-all duration-200`} disabled={isSendingOtp} > - {isSendingOtp ? ( - <> - - Sending Code... - - ) : ( - 'Continue' - )} + {isSendingOtp ? 'Sending Code...' : 'Continue'} ) : ( diff --git a/apps/sim/app/chat/components/auth/password/password-auth.tsx b/apps/sim/app/chat/components/auth/password/password-auth.tsx index e132e9562b..f99847f73f 100644 --- a/apps/sim/app/chat/components/auth/password/password-auth.tsx +++ b/apps/sim/app/chat/components/auth/password/password-auth.tsx @@ -1,12 +1,12 @@ 'use client' import { type KeyboardEvent, useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { Eye, EyeOff } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' import Nav from '@/app/(landing)/components/nav/nav' diff --git a/apps/sim/app/chat/components/auth/sso/sso-auth.tsx b/apps/sim/app/chat/components/auth/sso/sso-auth.tsx index fca79215f7..8ceb4bf557 100644 --- a/apps/sim/app/chat/components/auth/sso/sso-auth.tsx +++ b/apps/sim/app/chat/components/auth/sso/sso-auth.tsx @@ -1,12 +1,12 @@ 'use client' import { type KeyboardEvent, useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { useRouter } from 'next/navigation' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { quickValidateEmail } from '@/lib/messaging/email/validation' import { inter } from '@/app/_styles/fonts/inter/inter' import { soehne } from '@/app/_styles/fonts/soehne/soehne' diff --git a/apps/sim/app/chat/components/input/input.tsx b/apps/sim/app/chat/components/input/input.tsx index 132a573ed2..ea41dbb954 100644 --- a/apps/sim/app/chat/components/input/input.tsx +++ b/apps/sim/app/chat/components/input/input.tsx @@ -9,7 +9,7 @@ import { VoiceInput } from '@/app/chat/components/input/voice-input' const logger = createLogger('ChatInput') -import { createLogger } from '@/lib/logs/console/logger' +import { createLogger } from '@sim/logger' const PLACEHOLDER_MOBILE = 'Enter a message' const PLACEHOLDER_DESKTOP = 'Enter a message or click the mic to speak' diff --git a/apps/sim/app/chat/components/message/components/file-download.tsx b/apps/sim/app/chat/components/message/components/file-download.tsx index 7be5237b16..be884b1a2a 100644 --- a/apps/sim/app/chat/components/message/components/file-download.tsx +++ b/apps/sim/app/chat/components/message/components/file-download.tsx @@ -1,10 +1,10 @@ 'use client' import { useState } from 'react' +import { createLogger } from '@sim/logger' import { ArrowDown, Download, Loader2, Music } from 'lucide-react' import { Button } from '@/components/emcn' import { DefaultFileIcon, getDocumentIcon } from '@/components/icons/document-icons' -import { createLogger } from '@/lib/logs/console/logger' import type { ChatFile } from '@/app/chat/components/message/message' const logger = createLogger('ChatFileDownload') diff --git a/apps/sim/app/chat/components/voice-interface/components/particles.tsx b/apps/sim/app/chat/components/voice-interface/components/particles.tsx index e383e47dd1..3b206e3369 100644 --- a/apps/sim/app/chat/components/voice-interface/components/particles.tsx +++ b/apps/sim/app/chat/components/voice-interface/components/particles.tsx @@ -1,8 +1,8 @@ 'use client' import { useCallback, useEffect, useRef } from 'react' +import { createLogger } from '@sim/logger' import * as THREE from 'three' -import { createLogger } from '@/lib/logs/console/logger' const logger = createLogger('Particles') diff --git a/apps/sim/app/chat/components/voice-interface/voice-interface.tsx b/apps/sim/app/chat/components/voice-interface/voice-interface.tsx index d4dc002ff2..94411a0e29 100644 --- a/apps/sim/app/chat/components/voice-interface/voice-interface.tsx +++ b/apps/sim/app/chat/components/voice-interface/voice-interface.tsx @@ -1,10 +1,10 @@ 'use client' import { type RefObject, useCallback, useEffect, useRef, useState } from 'react' +import { createLogger } from '@sim/logger' import { Mic, MicOff, Phone } from 'lucide-react' import { Button } from '@/components/ui/button' import { cn } from '@/lib/core/utils/cn' -import { createLogger } from '@/lib/logs/console/logger' import { ParticlesVisualization } from '@/app/chat/components/voice-interface/components/particles' const logger = createLogger('VoiceInterface') diff --git a/apps/sim/app/chat/hooks/use-audio-streaming.ts b/apps/sim/app/chat/hooks/use-audio-streaming.ts index b37e7ac83c..b7bda6208e 100644 --- a/apps/sim/app/chat/hooks/use-audio-streaming.ts +++ b/apps/sim/app/chat/hooks/use-audio-streaming.ts @@ -1,7 +1,7 @@ 'use client' import { type RefObject, useCallback, useRef, useState } from 'react' -import { createLogger } from '@/lib/logs/console/logger' +import { createLogger } from '@sim/logger' const logger = createLogger('UseAudioStreaming') diff --git a/apps/sim/app/chat/hooks/use-chat-streaming.ts b/apps/sim/app/chat/hooks/use-chat-streaming.ts index 40960684ad..ac474fa377 100644 --- a/apps/sim/app/chat/hooks/use-chat-streaming.ts +++ b/apps/sim/app/chat/hooks/use-chat-streaming.ts @@ -1,8 +1,8 @@ 'use client' import { useRef, useState } from 'react' +import { createLogger } from '@sim/logger' import { isUserFile } from '@/lib/core/utils/display-filters' -import { createLogger } from '@/lib/logs/console/logger' import type { ChatFile, ChatMessage } from '@/app/chat/components/message/message' import { CHAT_ERROR_MESSAGES } from '@/app/chat/constants' diff --git a/apps/sim/app/invite/[id]/invite.tsx b/apps/sim/app/invite/[id]/invite.tsx index 70d66cd057..25a7493cfa 100644 --- a/apps/sim/app/invite/[id]/invite.tsx +++ b/apps/sim/app/invite/[id]/invite.tsx @@ -1,9 +1,9 @@ 'use client' import { useEffect, useState } from 'react' +import { createLogger } from '@sim/logger' import { useParams, useRouter, useSearchParams } from 'next/navigation' import { client, useSession } from '@/lib/auth/auth-client' -import { createLogger } from '@/lib/logs/console/logger' import { InviteLayout, InviteStatusCard } from '@/app/invite/components' const logger = createLogger('InviteById') diff --git a/apps/sim/app/invite/components/layout.tsx b/apps/sim/app/invite/components/layout.tsx index 653a3276cd..b5481c8371 100644 --- a/apps/sim/app/invite/components/layout.tsx +++ b/apps/sim/app/invite/components/layout.tsx @@ -8,7 +8,8 @@ interface InviteLayoutProps { export default function InviteLayout({ children }: InviteLayoutProps) { return ( -
+
+