Compare commits

..

1 Commits

Author SHA1 Message Date
Emir Karabeg
75b1ed304e improvement(landing, blog): SEO and GEO optimization 2026-04-05 12:35:00 -07:00
345 changed files with 1434 additions and 1982 deletions

View File

@@ -9,26 +9,5 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
## Styling
Never update global styles. Keep all styling local to components.
## ID Generation
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`:
- `generateId()` — UUID v4, use by default
- `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers
Both use `crypto.getRandomValues()` under the hood and work in all contexts including non-secure (HTTP) browsers.
```typescript
// ✗ Bad
import { nanoid } from 'nanoid'
import { v4 as uuidv4 } from 'uuid'
const id = crypto.randomUUID()
// ✓ Good
import { generateId, generateShortId } from '@/lib/core/utils/uuid'
const uuid = generateId()
const shortId = generateShortId()
const tiny = generateShortId(8)
```
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -16,26 +16,5 @@ Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
## Styling
Never update global styles. Keep all styling local to components.
## ID Generation
Never use `crypto.randomUUID()`, `nanoid`, or the `uuid` package directly. Use the utilities from `@/lib/core/utils/uuid`:
- `generateId()` — UUID v4, use by default
- `generateShortId(size?)` — short URL-safe ID (default 21 chars), for compact identifiers
Both use `crypto.getRandomValues()` under the hood and work in all contexts including non-secure (HTTP) browsers.
```typescript
// ✗ Bad
import { nanoid } from 'nanoid'
import { v4 as uuidv4 } from 'uuid'
const id = crypto.randomUUID()
// ✓ Good
import { generateId, generateShortId } from '@/lib/core/utils/uuid'
const uuid = generateId()
const shortId = generateShortId()
const tiny = generateShortId(8)
```
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -7,7 +7,6 @@ You are a professional software engineer. All code must follow best practices: a
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
- **Styling**: Never update global styles. Keep all styling local to components
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid`
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
## Architecture

View File

@@ -7,7 +7,6 @@ You are a professional software engineer. All code must follow best practices: a
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
- **Styling**: Never update global styles. Keep all styling local to components
- **ID Generation**: Never use `crypto.randomUUID()`, `nanoid`, or `uuid` package. Use `generateId()` (UUID v4) or `generateShortId()` (compact) from `@/lib/core/utils/uuid`
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
## Architecture

View File

@@ -1,26 +1,39 @@
'use client'
import { useState } from 'react'
import { ArrowLeft, ChevronLeft } from 'lucide-react'
import Link from 'next/link'
export function BackLink() {
const [isHovered, setIsHovered] = useState(false)
return (
<Link
href='/blog'
className='group flex items-center gap-1 text-[var(--landing-text-muted)] text-sm hover:text-[var(--landing-text)]'
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className='group/link inline-flex items-center gap-1.5 font-season text-[var(--landing-text-muted)] text-sm tracking-[0.02em] hover:text-[var(--landing-text)]'
>
<span className='group-hover:-translate-x-0.5 inline-flex transition-transform duration-200'>
{isHovered ? (
<ArrowLeft className='h-4 w-4' aria-hidden='true' />
) : (
<ChevronLeft className='h-4 w-4' aria-hidden='true' />
)}
</span>
<svg
className='h-3 w-3 shrink-0'
viewBox='0 0 10 10'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<line
x1='1'
y1='5'
x2='10'
y2='5'
stroke='currentColor'
strokeWidth='1.33'
strokeLinecap='square'
className='origin-right scale-x-0 transition-transform duration-200 ease-out [transform-box:fill-box] group-hover/link:scale-x-100'
/>
<path
d='M6.5 2L3.5 5L6.5 8'
stroke='currentColor'
strokeWidth='1.33'
strokeLinecap='square'
strokeLinejoin='miter'
fill='none'
className='group-hover/link:-translate-x-[30%] transition-transform duration-200 ease-out'
/>
</svg>
Back to Blog
</Link>
)

View File

@@ -2,58 +2,51 @@ import { Skeleton } from '@/components/emcn'
export default function BlogPostLoading() {
return (
<article className='w-full'>
{/* Header area */}
<div className='mx-auto max-w-[1450px] px-6 pt-8 sm:px-8 sm:pt-12 md:px-12 md:pt-16'>
{/* Back link */}
<article className='w-full bg-[var(--landing-bg)]'>
<div className='px-5 pt-[60px] lg:px-16 lg:pt-[100px]'>
<div className='mb-6'>
<Skeleton className='h-[16px] w-[60px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
{/* Image + title row */}
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
{/* Image */}
<div className='w-full flex-shrink-0 md:w-[450px]'>
<Skeleton className='aspect-[450/360] w-full rounded-lg bg-[var(--landing-bg-elevated)]' />
<Skeleton className='aspect-[450/360] w-full rounded-[5px] bg-[var(--landing-bg-elevated)]' />
</div>
{/* Title + author */}
<div className='flex flex-1 flex-col justify-between'>
<div>
<Skeleton className='h-[48px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-2 h-[48px] w-[80%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[44px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-2 h-[44px] w-[80%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-4 h-[18px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-2 h-[18px] w-[70%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-4 flex items-center justify-between'>
<div className='mt-6 flex items-center gap-6'>
<Skeleton className='h-[12px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='flex items-center gap-2'>
<Skeleton className='h-[24px] w-[24px] rounded-full bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[20px] w-[20px] rounded-full bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[12px] w-[80px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<Skeleton className='h-[32px] w-[32px] rounded-[6px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
{/* Divider */}
<Skeleton className='mt-8 h-[1px] w-full bg-[var(--landing-bg-elevated)] sm:mt-12' />
{/* Date + description */}
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
<Skeleton className='h-[16px] w-[120px] flex-shrink-0 rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<div className='flex-1 space-y-2'>
<Skeleton className='h-[20px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[20px] w-[70%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
<div className='mt-8 h-px w-full bg-[var(--landing-bg-elevated)]' />
<div className='mx-5 border-[var(--landing-bg-elevated)] border-x lg:mx-16'>
<div className='mx-auto max-w-[900px] px-6 py-16'>
<div className='space-y-4'>
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-6 h-[24px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[92%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
</div>
{/* Article body */}
<div className='mx-auto max-w-[900px] px-6 pb-20 sm:px-8 md:px-12'>
<div className='space-y-4'>
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[95%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[88%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='mt-6 h-[24px] w-[200px] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[92%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
<Skeleton className='h-[16px] w-[85%] rounded-[4px] bg-[var(--landing-bg-elevated)]' />
</div>
</div>
<div className='-mt-px h-px w-full bg-[var(--landing-bg-elevated)]' />
</article>
)
}

View File

@@ -4,7 +4,7 @@ import Link from 'next/link'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
import { FAQ } from '@/lib/blog/faq'
import { getAllPostMeta, getPostBySlug, getRelatedPosts } from '@/lib/blog/registry'
import { buildArticleJsonLd, buildBreadcrumbJsonLd, buildPostMetadata } from '@/lib/blog/seo'
import { buildPostGraphJsonLd, buildPostMetadata } from '@/lib/blog/seo'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { BackLink } from '@/app/(landing)/blog/[slug]/back-link'
import { ShareButton } from '@/app/(landing)/blog/[slug]/share-button'
@@ -30,27 +30,27 @@ export default async function Page({ params }: { params: Promise<{ slug: string
const { slug } = await params
const post = await getPostBySlug(slug)
const Article = post.Content
const jsonLd = buildArticleJsonLd(post)
const breadcrumbLd = buildBreadcrumbJsonLd(post)
const graphJsonLd = buildPostGraphJsonLd(post)
const related = await getRelatedPosts(slug, 3)
return (
<article className='w-full' itemScope itemType='https://schema.org/BlogPosting'>
<article
className='w-full bg-[var(--landing-bg)]'
itemScope
itemType='https://schema.org/TechArticle'
>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(graphJsonLd) }}
/>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbLd) }}
/>
<header className='mx-auto max-w-[1450px] px-6 pt-8 sm:px-8 sm:pt-12 md:px-12 md:pt-16'>
<header className='px-5 pt-[60px] lg:px-16 lg:pt-[100px]'>
<div className='mb-6'>
<BackLink />
</div>
<div className='flex flex-col gap-8 md:flex-row md:gap-12'>
<div className='w-full flex-shrink-0 md:w-[450px]'>
<div className='relative w-full overflow-hidden rounded-lg'>
<div className='relative w-full overflow-hidden rounded-[5px]'>
<Image
src={post.ogImage}
alt={post.title}
@@ -65,18 +65,35 @@ export default async function Page({ params }: { params: Promise<{ slug: string
</div>
</div>
<div className='flex flex-1 flex-col justify-between'>
<h1
className='text-balance font-[500] text-[36px] text-[var(--landing-text)] leading-tight tracking-tight sm:text-[48px] md:text-[56px] lg:text-[64px]'
itemProp='headline'
>
{post.title}
</h1>
<div className='mt-4 flex items-center justify-between'>
<div>
<h1
className='text-balance font-[430] font-season text-[28px] text-white leading-[110%] tracking-[-0.02em] sm:text-[36px] md:text-[44px] lg:text-[52px]'
itemProp='headline'
>
{post.title}
</h1>
<p className='mt-4 font-[430] font-season text-[var(--landing-text-body)] text-base leading-[150%] tracking-[0.02em] sm:text-lg'>
{post.description}
</p>
</div>
<div className='mt-6 flex items-center gap-6'>
<time
className='font-martian-mono text-[var(--landing-text-subtle)] text-xs uppercase tracking-[0.1em]'
dateTime={post.date}
itemProp='datePublished'
>
{new Date(post.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</time>
<meta itemProp='dateModified' content={post.updated ?? post.date} />
<div className='flex items-center gap-3'>
{(post.authors || [post.author]).map((a, idx) => (
<div key={idx} className='flex items-center gap-2'>
{a?.avatarUrl ? (
<Avatar className='size-6'>
<Avatar className='size-5'>
<AvatarImage src={a.avatarUrl} alt={a.name} />
<AvatarFallback>{a.name.slice(0, 2)}</AvatarFallback>
</Avatar>
@@ -85,7 +102,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
href={a?.url || '#'}
target='_blank'
rel='noopener noreferrer author'
className='text-[var(--landing-text-muted)] text-sm leading-[1.5] hover:text-[var(--landing-text)] sm:text-md'
className='font-martian-mono text-[var(--landing-text-muted)] text-xs uppercase tracking-[0.1em] hover:text-white'
itemProp='author'
itemScope
itemType='https://schema.org/Person'
@@ -95,78 +112,72 @@ export default async function Page({ params }: { params: Promise<{ slug: string
</div>
))}
</div>
<ShareButton url={`${getBaseUrl()}/blog/${slug}`} title={post.title} />
<div className='ml-auto'>
<ShareButton url={`${getBaseUrl()}/blog/${slug}`} title={post.title} />
</div>
</div>
</div>
</div>
<hr className='mt-8 border-[var(--landing-bg-elevated)] border-t sm:mt-12' />
<div className='flex flex-col gap-6 py-8 sm:flex-row sm:items-start sm:justify-between sm:gap-8 sm:py-10'>
<div className='flex flex-shrink-0 items-center gap-4'>
<time
className='block text-[var(--landing-text-muted)] text-sm leading-[1.5] sm:text-md'
dateTime={post.date}
itemProp='datePublished'
>
{new Date(post.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</time>
<meta itemProp='dateModified' content={post.updated ?? post.date} />
</div>
<div className='flex-1'>
<p className='m-0 block translate-y-[-4px] font-[400] text-[var(--landing-text-muted)] text-lg leading-[1.5] sm:text-[20px] md:text-[26px]'>
{post.description}
</p>
</div>
</div>
</header>
<div className='mx-auto max-w-[900px] px-6 pb-20 sm:px-8 md:px-12' itemProp='articleBody'>
<div className='prose prose-lg prose-invert max-w-none prose-blockquote:border-[var(--landing-border-strong)] prose-hr:border-[var(--landing-bg-elevated)] prose-a:text-[var(--landing-text)] prose-blockquote:text-[var(--landing-text-muted)] prose-code:text-[var(--landing-text)] prose-headings:text-[var(--landing-text)] prose-li:text-[var(--landing-text-muted)] prose-p:text-[var(--landing-text-muted)] prose-strong:text-[var(--landing-text)]'>
<Article />
{post.faq && post.faq.length > 0 ? <FAQ items={post.faq} /> : null}
</div>
</div>
{related.length > 0 && (
<div className='mx-auto max-w-[900px] px-6 pb-24 sm:px-8 md:px-12'>
<h2 className='mb-4 font-[500] text-[24px] text-[var(--landing-text)]'>Related posts</h2>
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3'>
{related.map((p) => (
<Link key={p.slug} href={`/blog/${p.slug}`} className='group'>
<div className='overflow-hidden rounded-lg border border-[var(--landing-bg-elevated)]'>
<Image
src={p.ogImage}
alt={p.title}
width={600}
height={315}
className='h-[160px] w-full object-cover'
sizes='(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw'
loading='lazy'
unoptimized
/>
<div className='p-3'>
<div className='mb-1 text-[var(--landing-text-muted)] text-xs'>
{new Date(p.date).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</div>
<div className='font-[500] text-[var(--landing-text)] text-sm leading-tight'>
{p.title}
</div>
</div>
</div>
</Link>
))}
<div className='mt-8 h-px w-full bg-[var(--landing-bg-elevated)]' />
<div className='mx-5 border-[var(--landing-bg-elevated)] border-x lg:mx-16'>
<div className='mx-auto max-w-[900px] px-6 py-16' itemProp='articleBody'>
<div className='prose prose-lg prose-invert max-w-none prose-blockquote:border-[var(--landing-border-strong)] prose-hr:border-[var(--landing-bg-elevated)] prose-headings:font-[430] prose-headings:font-season prose-a:text-white prose-blockquote:text-[var(--landing-text-muted)] prose-code:text-white prose-headings:text-white prose-li:text-[var(--landing-text-body)] prose-p:text-[var(--landing-text-body)] prose-strong:text-white prose-headings:tracking-[-0.02em]'>
<Article />
{post.faq && post.faq.length > 0 ? <FAQ items={post.faq} /> : null}
</div>
</div>
)}
{related.length > 0 && (
<>
<div className='h-px w-full bg-[var(--landing-bg-elevated)]' />
<nav aria-label='Related posts' className='flex'>
{related.map((p) => (
<Link
key={p.slug}
href={`/blog/${p.slug}`}
className='group flex flex-1 flex-col gap-4 border-[var(--landing-bg-elevated)] p-6 transition-colors hover:bg-[var(--landing-bg-elevated)] md:border-l md:first:border-l-0'
>
<div className='relative aspect-video w-full overflow-hidden rounded-[5px]'>
<Image
src={p.ogImage}
alt={p.title}
fill
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
className='object-cover'
loading='lazy'
unoptimized
/>
</div>
<div className='flex flex-col gap-2'>
<span className='font-martian-mono text-[var(--landing-text-subtle)] text-xs uppercase tracking-[0.1em]'>
{new Date(p.date).toLocaleDateString('en-US', {
month: 'short',
year: '2-digit',
})}
</span>
<h3 className='font-[430] font-season text-lg text-white leading-tight tracking-[-0.01em]'>
{p.title}
</h3>
<p className='line-clamp-2 text-[var(--landing-text-muted)] text-sm leading-[150%]'>
{p.description}
</p>
</div>
</Link>
))}
</nav>
</>
)}
</div>
<div className='-mt-px h-px w-full bg-[var(--landing-bg-elevated)]' />
<meta itemProp='publisher' content='Sim' />
<meta itemProp='inLanguage' content='en-US' />
<meta itemProp='keywords' content={post.tags.join(', ')} />
{post.wordCount && <meta itemProp='wordCount' content={String(post.wordCount)} />}
</article>
)
}

View File

@@ -13,7 +13,29 @@ export async function generateMetadata({
const { id } = await params
const posts = (await getAllPostMeta()).filter((p) => p.author.id === id)
const author = posts[0]?.author
return { title: author?.name ?? 'Author' }
const name = author?.name ?? 'Author'
return {
title: `${name} — Sim Blog`,
description: `Read articles by ${name} on the Sim blog.`,
alternates: { canonical: `https://sim.ai/blog/authors/${id}` },
openGraph: {
title: `${name} — Sim Blog`,
description: `Read articles by ${name} on the Sim blog.`,
url: `https://sim.ai/blog/authors/${id}`,
siteName: 'Sim',
type: 'profile',
...(author?.avatarUrl
? { images: [{ url: author.avatarUrl, width: 400, height: 400, alt: name }] }
: {}),
},
twitter: {
card: 'summary',
title: `${name} — Sim Blog`,
description: `Read articles by ${name} on the Sim blog.`,
site: '@simdotai',
...(author?.xHandle ? { creator: `@${author.xHandle}` } : {}),
},
}
}
export default async function AuthorPage({ params }: { params: Promise<{ id: string }> }) {
@@ -27,19 +49,41 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
</main>
)
}
const personJsonLd = {
const graphJsonLd = {
'@context': 'https://schema.org',
'@type': 'Person',
name: author.name,
url: `https://sim.ai/blog/authors/${author.id}`,
sameAs: author.url ? [author.url] : [],
image: author.avatarUrl,
'@graph': [
{
'@type': 'Person',
name: author.name,
url: `https://sim.ai/blog/authors/${author.id}`,
sameAs: author.url ? [author.url] : [],
image: author.avatarUrl,
worksFor: {
'@type': 'Organization',
name: 'Sim',
url: 'https://sim.ai',
},
},
{
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://sim.ai' },
{ '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://sim.ai/blog' },
{
'@type': 'ListItem',
position: 3,
name: author.name,
item: `https://sim.ai/blog/authors/${author.id}`,
},
],
},
],
}
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(personJsonLd) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(graphJsonLd) }}
/>
<div className='mb-6 flex items-center gap-3'>
{author.avatarUrl ? (

View File

@@ -9,8 +9,14 @@ export default async function StudioLayout({ children }: { children: React.React
'@type': 'Organization',
name: 'Sim',
url: 'https://sim.ai',
description:
'Sim is an open-source platform for building, testing, and deploying AI agent workflows.',
logo: 'https://sim.ai/logo/primary/small.png',
sameAs: ['https://x.com/simdotai'],
sameAs: [
'https://x.com/simdotai',
'https://github.com/simstudioai/sim',
'https://www.linkedin.com/company/simdotai',
],
}
const websiteJsonLd = {
@@ -18,11 +24,6 @@ export default async function StudioLayout({ children }: { children: React.React
'@type': 'WebSite',
name: 'Sim',
url: 'https://sim.ai',
potentialAction: {
'@type': 'SearchAction',
target: 'https://sim.ai/search?q={search_term_string}',
'query-input': 'required name=search_term_string',
},
}
return (

View File

@@ -1,11 +1,60 @@
import type { Metadata } from 'next'
import Image from 'next/image'
import Link from 'next/link'
import { Badge } from '@/components/emcn'
import { getAllPostMeta } from '@/lib/blog/registry'
import { buildCollectionPageJsonLd } from '@/lib/blog/seo'
export const metadata: Metadata = {
title: 'Blog',
description: 'Announcements, insights, and guides from the Sim team.',
export async function generateMetadata({
searchParams,
}: {
searchParams: Promise<{ page?: string; tag?: string }>
}): Promise<Metadata> {
const { page, tag } = await searchParams
const pageNum = Math.max(1, Number(page || 1))
const titleParts = ['Blog']
if (tag) titleParts.push(tag)
if (pageNum > 1) titleParts.push(`Page ${pageNum}`)
const title = titleParts.join(' — ')
const description = tag
? `Sim blog posts tagged "${tag}" — insights and guides for building AI agent workflows.`
: 'Announcements, insights, and guides for building AI agent workflows.'
const canonicalParams = new URLSearchParams()
if (tag) canonicalParams.set('tag', tag)
if (pageNum > 1) canonicalParams.set('page', String(pageNum))
const qs = canonicalParams.toString()
const canonical = `https://sim.ai/blog${qs ? `?${qs}` : ''}`
return {
title,
description,
alternates: { canonical },
openGraph: {
title: `${title} | Sim`,
description,
url: canonical,
siteName: 'Sim',
locale: 'en_US',
type: 'website',
images: [
{
url: 'https://sim.ai/logo/primary/medium.png',
width: 1200,
height: 630,
alt: 'Sim Blog',
},
],
},
twitter: {
card: 'summary_large_image',
title: `${title} | Sim`,
description,
site: '@simdotai',
},
}
}
export const revalidate = 3600
@@ -37,19 +86,13 @@ export default async function BlogIndex({
const featured = pageNum === 1 ? posts.slice(0, 3) : []
const remaining = pageNum === 1 ? posts.slice(3) : posts
const blogJsonLd = {
'@context': 'https://schema.org',
'@type': 'Blog',
name: 'Sim Blog',
url: 'https://sim.ai/blog',
description: 'Announcements, insights, and guides for building AI agent workflows.',
}
const collectionJsonLd = buildCollectionPageJsonLd()
return (
<section className='bg-[var(--landing-bg)]'>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(blogJsonLd) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(collectionJsonLd) }}
/>
{/* Section header */}
@@ -81,7 +124,7 @@ export default async function BlogIndex({
{/* Featured posts */}
{featured.length > 0 && (
<>
<div className='flex'>
<nav aria-label='Featured posts' className='flex'>
{featured.map((p, index) => (
<Link
key={p.slug}
@@ -89,11 +132,14 @@ export default async function BlogIndex({
className='group flex flex-1 flex-col gap-4 border-[var(--landing-bg-elevated)] p-6 transition-colors hover:bg-[var(--landing-bg-elevated)] md:border-l md:first:border-l-0'
>
<div className='relative aspect-video w-full overflow-hidden rounded-[5px]'>
<img
<Image
src={p.ogImage}
alt={p.title}
className='h-full w-full object-cover'
loading={index < 3 ? 'eager' : 'lazy'}
fill
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
className='object-cover'
priority={index < 3}
unoptimized
/>
</div>
<div className='flex flex-col gap-2'>
@@ -112,7 +158,7 @@ export default async function BlogIndex({
</div>
</Link>
))}
</div>
</nav>
<div className='h-px w-full bg-[var(--landing-bg-elevated)]' />
</>
@@ -151,12 +197,14 @@ export default async function BlogIndex({
</div>
{/* Image */}
<div className='hidden h-[80px] w-[140px] shrink-0 overflow-hidden rounded-[5px] sm:block'>
<img
<div className='relative hidden h-[80px] w-[140px] shrink-0 overflow-hidden rounded-[5px] sm:block'>
<Image
src={p.ogImage}
alt={p.title}
className='h-full w-full object-cover'
loading='lazy'
fill
sizes='140px'
className='object-cover'
unoptimized
/>
</div>
</Link>
@@ -166,11 +214,12 @@ export default async function BlogIndex({
{/* Pagination */}
{totalPages > 1 && (
<div className='px-6 py-8'>
<nav aria-label='Pagination' className='px-6 py-8'>
<div className='flex items-center justify-center gap-3'>
{pageNum > 1 && (
<Link
href={`/blog?page=${pageNum - 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
rel='prev'
className='rounded-[5px] border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
Previous
@@ -182,13 +231,14 @@ export default async function BlogIndex({
{pageNum < totalPages && (
<Link
href={`/blog?page=${pageNum + 1}${tag ? `&tag=${encodeURIComponent(tag)}` : ''}`}
rel='next'
className='rounded-[5px] border border-[var(--landing-border-strong)] px-3 py-1 text-[var(--landing-text)] text-sm transition-colors hover:bg-[var(--landing-bg-elevated)]'
>
Next
</Link>
)}
</div>
</div>
</nav>
)}
</div>

View File

@@ -7,13 +7,18 @@ export async function GET() {
const posts = await getAllPostMeta()
const items = posts.slice(0, 50)
const site = 'https://sim.ai'
const lastBuildDate =
items.length > 0 ? new Date(items[0].date).toUTCString() : new Date().toUTCString()
const xml = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Sim Blog</title>
<link>${site}</link>
<description>Announcements, insights, and guides for AI agent workflows.</description>
<language>en-us</language>
<lastBuildDate>${lastBuildDate}</lastBuildDate>
<atom:link href="${site}/blog/rss.xml" rel="self" type="application/rss+xml" />
${items
.map(
(p) => `
@@ -26,6 +31,7 @@ export async function GET() {
${(p.authors || [p.author])
.map((a) => `<author><![CDATA[${a.name}${a.url ? ` (${a.url})` : ''}]]></author>`)
.join('\n')}
${p.tags.map((t) => `<category><![CDATA[${t}]]></category>`).join('\n ')}
</item>`
)
.join('')}

View File

@@ -4,12 +4,42 @@ import { getAllTags } from '@/lib/blog/registry'
export const metadata: Metadata = {
title: 'Tags',
description: 'Browse Sim blog posts by topic — AI agents, workflows, integrations, and more.',
alternates: { canonical: 'https://sim.ai/blog/tags' },
openGraph: {
title: 'Blog Tags | Sim',
description: 'Browse Sim blog posts by topic — AI agents, workflows, integrations, and more.',
url: 'https://sim.ai/blog/tags',
siteName: 'Sim',
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary',
title: 'Blog Tags | Sim',
description: 'Browse Sim blog posts by topic — AI agents, workflows, integrations, and more.',
site: '@simdotai',
},
}
const breadcrumbJsonLd = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://sim.ai' },
{ '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://sim.ai/blog' },
{ '@type': 'ListItem', position: 3, name: 'Tags', item: 'https://sim.ai/blog/tags' },
],
}
export default async function TagsIndex() {
const tags = await getAllTags()
return (
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
/>
<h1 className='mb-6 font-[500] text-[32px] text-[var(--landing-text)] leading-tight'>
Browse by tag
</h1>

View File

@@ -7,7 +7,7 @@ import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'
const logger = createLogger('github-stars')
const INITIAL_STARS = '27k'
const INITIAL_STARS = '27.6k'
/**
* Client component that displays GitHub stars count.

View File

@@ -9,11 +9,11 @@ import { a2aAgent, workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { generateSkillsFromWorkflow } from '@/lib/a2a/agent-card'
import { A2A_DEFAULT_CAPABILITIES } from '@/lib/a2a/constants'
import { sanitizeAgentName } from '@/lib/a2a/utils'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { captureServerEvent } from '@/lib/posthog/server'
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/persistence/utils'
import { hasValidStartBlockInState } from '@/lib/workflows/triggers/trigger-utils'
@@ -173,7 +173,7 @@ export async function POST(request: NextRequest) {
skillTags
)
const agentId = generateId()
const agentId = uuidv4()
const agentName = name || sanitizeAgentName(wf.name)
const [agent] = await db

View File

@@ -4,6 +4,7 @@ import { a2aAgent, a2aPushNotificationConfig, a2aTask, workflow } from '@sim/db/
import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { A2A_DEFAULT_TIMEOUT, A2A_MAX_HISTORY_LENGTH } from '@/lib/a2a/constants'
import { notifyTaskStateChange } from '@/lib/a2a/push-notifications'
import {
@@ -17,7 +18,6 @@ import { acquireLock, getRedisClient, releaseLock } from '@/lib/core/config/redi
import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server'
import { SSE_HEADERS } from '@/lib/core/utils/sse'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { markExecutionCancelled } from '@/lib/execution/cancellation'
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
import { getWorkspaceBilledAccountUserId } from '@/lib/workspaces/utils'
@@ -400,11 +400,11 @@ async function handleMessageSend(
const message = params.message
const taskId = message.taskId || generateTaskId()
const contextId = message.contextId || generateId()
const contextId = message.contextId || uuidv4()
// Distributed lock to prevent concurrent task processing
const lockKey = `a2a:task:${taskId}:lock`
const lockValue = generateId()
const lockValue = uuidv4()
const acquired = await acquireLock(lockKey, lockValue, 60)
if (!acquired) {
@@ -628,12 +628,12 @@ async function handleMessageStream(
}
const message = params.message
const contextId = message.contextId || generateId()
const contextId = message.contextId || uuidv4()
const taskId = message.taskId || generateTaskId()
// Distributed lock to prevent concurrent task processing
const lockKey = `a2a:task:${taskId}:lock`
const lockValue = generateId()
const lockValue = uuidv4()
const acquired = await acquireLock(lockKey, lockValue, 300)
if (!acquired) {
@@ -1427,7 +1427,7 @@ async function handlePushNotificationSet(
.where(eq(a2aPushNotificationConfig.id, existingConfig.id))
} else {
await db.insert(a2aPushNotificationConfig).values({
id: generateId(),
id: uuidv4(),
taskId: params.id,
url: config.url,
token: config.token || null,

View File

@@ -1,7 +1,7 @@
import type { Artifact, Message, PushNotificationConfig, Task, TaskState } from '@a2a-js/sdk'
import { v4 as uuidv4 } from 'uuid'
import { generateInternalToken } from '@/lib/auth/internal'
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
/** A2A v0.3 JSON-RPC method names */
export const A2A_METHODS = {
@@ -85,7 +85,7 @@ export function isJSONRPCRequest(obj: unknown): obj is JSONRPCRequest {
}
export function generateTaskId(): string {
return generateId()
return uuidv4()
}
export function createTaskStatus(state: TaskState): { state: TaskState; timestamp: string } {

View File

@@ -2,6 +2,7 @@ import { db } from '@sim/db'
import { academyCertificate, user } 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 { getCourseById } from '@/lib/academy/content'
@@ -9,7 +10,6 @@ import type { CertificateMetadata } from '@/lib/academy/types'
import { getSession } from '@/lib/auth'
import type { TokenBucketConfig } from '@/lib/core/rate-limiter'
import { RateLimiter } from '@/lib/core/rate-limiter'
import { generateShortId } from '@/lib/core/utils/uuid'
const logger = createLogger('AcademyCertificatesAPI')
@@ -106,7 +106,7 @@ export async function POST(req: NextRequest) {
const [certificate] = await db
.insert(academyCertificate)
.values({
id: generateShortId(),
id: nanoid(),
userId: session.user.id,
courseId,
status: 'active',
@@ -211,5 +211,5 @@ export async function GET(req: NextRequest) {
/** Generates a human-readable certificate number, e.g. SIM-2026-A3K9XZ2P */
function generateCertificateNumber(): string {
const year = new Date().getFullYear()
return `SIM-${year}-${generateShortId(8).toUpperCase()}`
return `SIM-${year}-${nanoid(8).toUpperCase()}`
}

View File

@@ -3,7 +3,6 @@ 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 { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('ShopifyAuthorize')
@@ -162,7 +161,7 @@ export async function GET(request: NextRequest) {
const baseUrl = getBaseUrl()
const redirectUri = `${baseUrl}/api/auth/oauth2/callback/shopify`
const state = generateId()
const state = crypto.randomUUID()
const oauthUrl =
`https://${cleanShop}/admin/oauth/authorize?` +

View File

@@ -1,4 +1,4 @@
import { randomInt } from 'crypto'
import { randomInt, randomUUID } from 'crypto'
import { db } from '@sim/db'
import { chat, verification } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -10,7 +10,6 @@ import { getRedisClient } from '@/lib/core/config/redis'
import { addCorsHeaders, isEmailAllowed } from '@/lib/core/security/deployment'
import { getStorageMethod } from '@/lib/core/storage'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { sendEmail } from '@/lib/messaging/email/mailer'
import { setChatAuthCookie } from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
@@ -62,7 +61,7 @@ async function storeOTP(email: string, chatId: string, otp: string): Promise<voi
await db.transaction(async (tx) => {
await tx.delete(verification).where(eq(verification.identifier, identifier))
await tx.insert(verification).values({
id: generateId(),
id: randomUUID(),
identifier,
value,
expiresAt,

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { chat, workflow } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -6,7 +7,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { addCorsHeaders, validateAuthToken } from '@/lib/core/security/deployment'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { preprocessExecution } from '@/lib/execution/preprocessing'
import { LoggingSession } from '@/lib/logs/execution/logging-session'
import { ChatFiles } from '@/lib/uploads'
@@ -103,7 +103,7 @@ export async function POST(
)
}
const executionId = generateId()
const executionId = randomUUID()
const loggingSession = new LoggingSession(
deployment.workflowId,
executionId,
@@ -150,7 +150,7 @@ export async function POST(
return addCorsHeaders(createErrorResponse('No input provided', 400), request)
}
const executionId = generateId()
const executionId = randomUUID()
const loggingSession = new LoggingSession(deployment.workflowId, executionId, 'chat', requestId)

View File

@@ -27,7 +27,6 @@ import {
createRequestTracker,
createUnauthorizedResponse,
} from '@/lib/copilot/request-helpers'
import { generateId } from '@/lib/core/utils/uuid'
import { captureServerEvent } from '@/lib/posthog/server'
import {
authorizeWorkflowByWorkspacePermission,
@@ -206,7 +205,7 @@ export async function POST(req: NextRequest) {
}
)
const userMessageIdToUse = userMessageId || generateId()
const userMessageIdToUse = userMessageId || crypto.randomUUID()
const reqLogger = logger.withMetadata({
requestId: tracker.requestId,
messageId: userMessageIdToUse,
@@ -407,8 +406,8 @@ export async function POST(req: NextRequest) {
}
if (stream) {
const executionId = generateId()
const runId = generateId()
const executionId = crypto.randomUUID()
const runId = crypto.randomUUID()
const sseStream = createSSEStream({
requestPayload,
userId: authenticatedUserId,
@@ -438,7 +437,7 @@ export async function POST(req: NextRequest) {
if (!result.success) return
const assistantMessage: Record<string, unknown> = {
id: generateId(),
id: crypto.randomUUID(),
role: 'assistant' as const,
content: result.content,
timestamp: new Date().toISOString(),
@@ -516,8 +515,8 @@ export async function POST(req: NextRequest) {
return new Response(sseStream, { headers: SSE_RESPONSE_HEADERS })
}
const nsExecutionId = generateId()
const nsRunId = generateId()
const nsExecutionId = crypto.randomUUID()
const nsRunId = crypto.randomUUID()
if (actualChatId) {
await createRunSegment({
@@ -577,7 +576,7 @@ export async function POST(req: NextRequest) {
}
const assistantMessage = {
id: generateId(),
id: crypto.randomUUID(),
role: 'assistant',
content: responseData.content,
timestamp: new Date().toISOString(),

View File

@@ -3,10 +3,10 @@ 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 { generateId } from '@/lib/core/utils/uuid'
import type { CreatorProfileDetails } from '@/app/_types/creator-profile'
const logger = createLogger('CreatorProfilesAPI')
@@ -147,7 +147,7 @@ export async function POST(request: NextRequest) {
}
// Create the profile
const profileId = generateId()
const profileId = uuidv4()
const now = new Date()
const details: CreatorProfileDetails = {}

View File

@@ -9,7 +9,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { hasCredentialSetsAccess } from '@/lib/billing'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { sendEmail } from '@/lib/messaging/email/mailer'
const logger = createLogger('CredentialSetInvite')
@@ -106,12 +105,12 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
const body = await req.json()
const { email } = createInviteSchema.parse(body)
const token = generateId()
const token = crypto.randomUUID()
const expiresAt = new Date()
expiresAt.setDate(expiresAt.getDate() + 7)
const invitation = {
id: generateId(),
id: crypto.randomUUID(),
credentialSetId: id,
email: email || null,
token,

View File

@@ -6,7 +6,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { hasCredentialSetsAccess } from '@/lib/billing'
import { generateId } from '@/lib/core/utils/uuid'
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
const logger = createLogger('CredentialSetMembers')
@@ -168,7 +167,7 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
return NextResponse.json({ error: 'Member not found' }, { status: 404 })
}
const requestId = generateId().slice(0, 8)
const requestId = crypto.randomUUID().slice(0, 8)
// Use transaction to ensure member deletion + webhook sync are atomic
await db.transaction(async (tx) => {

View File

@@ -10,7 +10,6 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
const logger = createLogger('CredentialSetInviteToken')
@@ -126,11 +125,11 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ tok
}
const now = new Date()
const requestId = generateId().slice(0, 8)
const requestId = crypto.randomUUID().slice(0, 8)
await db.transaction(async (tx) => {
await tx.insert(credentialSetMember).values({
id: generateId(),
id: crypto.randomUUID(),
credentialSetId: invitation.credentialSetId,
userId: session.user.id,
status: 'active',

View File

@@ -5,7 +5,6 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { syncAllWebhooksForCredentialSet } from '@/lib/webhooks/utils.server'
const logger = createLogger('CredentialSetMemberships')
@@ -61,7 +60,7 @@ export async function DELETE(req: NextRequest) {
}
try {
const requestId = generateId().slice(0, 8)
const requestId = crypto.randomUUID().slice(0, 8)
// Use transaction to ensure revocation + webhook sync are atomic
await db.transaction(async (tx) => {

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { hasCredentialSetsAccess } from '@/lib/billing'
import { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('CredentialSets')
@@ -151,7 +150,7 @@ export async function POST(req: Request) {
const now = new Date()
const newCredentialSet = {
id: generateId(),
id: crypto.randomUUID(),
organizationId,
name,
description: description || null,

View File

@@ -5,7 +5,6 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
const logger = createLogger('CredentialMembersAPI')
@@ -134,7 +133,7 @@ export async function POST(request: NextRequest, context: RouteContext) {
}
await db.insert(credentialMember).values({
id: generateId(),
id: crypto.randomUUID(),
credentialId,
userId,
role,

View File

@@ -6,7 +6,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { encryptSecret } from '@/lib/core/security/encryption'
import { generateId } from '@/lib/core/utils/uuid'
import { getCredentialActorContext } from '@/lib/credentials/access'
import {
syncPersonalEnvCredentialsForUser,
@@ -274,7 +273,7 @@ export async function DELETE(
await db
.insert(workspaceEnvironment)
.values({
id: workspaceRow?.id || generateId(),
id: workspaceRow?.id || crypto.randomUUID(),
workspaceId: access.credential.workspaceId,
variables: current,
createdAt: workspaceRow?.createdAt || new Date(),

View File

@@ -5,7 +5,6 @@ import { and, eq, lt } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
const logger = createLogger('CredentialDraftAPI')
@@ -76,7 +75,7 @@ export async function POST(request: Request) {
await db
.insert(pendingCredentialDraft)
.values({
id: generateId(),
id: crypto.randomUUID(),
userId,
workspaceId,
providerId,

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { encryptSecret } from '@/lib/core/security/encryption'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { getWorkspaceMemberUserIds } from '@/lib/credentials/environment'
import { syncWorkspaceOAuthCredentialsForUser } from '@/lib/credentials/oauth'
import { getServiceConfigByProviderId } from '@/lib/oauth'
@@ -537,7 +536,7 @@ export async function POST(request: NextRequest) {
}
const now = new Date()
const credentialId = generateId()
const credentialId = crypto.randomUUID()
const [workspaceRow] = await db
.select({ ownerId: workspace.ownerId })
.from(workspace)
@@ -566,7 +565,7 @@ export async function POST(request: NextRequest) {
if (workspaceUserIds.length > 0) {
for (const memberUserId of workspaceUserIds) {
await tx.insert(credentialMember).values({
id: generateId(),
id: crypto.randomUUID(),
credentialId,
userId: memberUserId,
role:
@@ -583,7 +582,7 @@ export async function POST(request: NextRequest) {
}
} else {
await tx.insert(credentialMember).values({
id: generateId(),
id: crypto.randomUUID(),
credentialId,
userId: session.user.id,
role: 'admin',

View File

@@ -8,7 +8,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { decryptSecret, encryptSecret } from '@/lib/core/security/encryption'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { syncPersonalEnvCredentialsForUser } from '@/lib/credentials/environment'
import type { EnvironmentVariable } from '@/stores/settings/environment'
@@ -43,7 +42,7 @@ export async function POST(req: NextRequest) {
await db
.insert(environment)
.values({
id: generateId(),
id: crypto.randomUUID(),
userId: session.user.id,
variables: encryptedVariables,
updatedAt: new Date(),

View File

@@ -82,12 +82,8 @@ vi.mock('drizzle-orm', () => ({
sql: vi.fn((strings: unknown, ...values: unknown[]) => ({ type: 'sql', sql: strings, values })),
}))
vi.mock('@/lib/core/utils/uuid', () => ({
generateId: vi.fn(() => 'test-uuid'),
generateShortId: vi.fn(() => 'mock-short-id'),
isValidUuid: vi.fn((v: string) =>
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)
),
vi.mock('uuid', () => ({
v4: vi.fn().mockReturnValue('test-uuid'),
}))
vi.mock('@/lib/auth', () => ({

View File

@@ -91,12 +91,8 @@ vi.mock('drizzle-orm', () => ({
sql: vi.fn((strings: unknown, ...values: unknown[]) => ({ type: 'sql', sql: strings, values })),
}))
vi.mock('@/lib/core/utils/uuid', () => ({
generateId: vi.fn(() => 'test-uuid'),
generateShortId: vi.fn(() => 'mock-short-id'),
isValidUuid: vi.fn((v: string) =>
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)
),
vi.mock('uuid', () => ({
v4: vi.fn().mockReturnValue('test-uuid'),
}))
vi.mock('@/lib/auth', () => ({
@@ -462,10 +458,10 @@ describe('File Upload Security Tests', () => {
expect(response.status).toBe(200)
})
it('should reject unsupported file types', async () => {
it('should reject JavaScript files', async () => {
const formData = new FormData()
const content = 'binary data'
const file = new File([content], 'archive.exe', { type: 'application/octet-stream' })
const maliciousJs = 'alert("XSS")'
const file = new File([maliciousJs], 'malicious.js', { type: 'application/javascript' })
formData.append('file', file)
formData.append('context', 'workspace')
formData.append('workspaceId', 'test-workspace-id')
@@ -479,7 +475,7 @@ describe('File Upload Security Tests', () => {
expect(response.status).toBe(400)
const data = await response.json()
expect(data.message).toContain("File type 'exe' is not allowed")
expect(data.message).toContain("File type 'js' is not allowed")
})
it('should reject files without extensions', async () => {

View File

@@ -8,7 +8,6 @@ import { generateWorkspaceFileKey } from '@/lib/uploads/contexts/workspace/works
import { isImageFileType } from '@/lib/uploads/utils/file-utils'
import {
SUPPORTED_AUDIO_EXTENSIONS,
SUPPORTED_CODE_EXTENSIONS,
SUPPORTED_DOCUMENT_EXTENSIONS,
SUPPORTED_VIDEO_EXTENSIONS,
validateFileType,
@@ -24,7 +23,6 @@ const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'] as const
const ALLOWED_EXTENSIONS = new Set<string>([
...SUPPORTED_DOCUMENT_EXTENSIONS,
...SUPPORTED_CODE_EXTENSIONS,
...IMAGE_EXTENSIONS,
...SUPPORTED_AUDIO_EXTENSIONS,
...SUPPORTED_VIDEO_EXTENSIONS,

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { duplicateWorkflow } from '@/lib/workflows/persistence/duplicate'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
@@ -68,7 +67,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
const targetWorkspaceId = workspaceId || sourceFolder.workspaceId
const { newFolderId, folderMapping } = await db.transaction(async (tx) => {
const newFolderId = clientNewId || generateId()
const newFolderId = clientNewId || crypto.randomUUID()
const now = new Date()
const targetParentId = parentId ?? sourceFolder.parentId
@@ -228,7 +227,7 @@ async function duplicateFolderStructure(
)
for (const childFolder of childFolders) {
const newChildFolderId = generateId()
const newChildFolderId = crypto.randomUUID()
folderMapping.set(childFolder.id, newChildFolderId)
await tx.insert(workflowFolder).values({

View File

@@ -6,7 +6,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { captureServerEvent } from '@/lib/posthog/server'
import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
@@ -93,7 +92,7 @@ export async function POST(request: NextRequest) {
)
}
const id = clientId || generateId()
const id = clientId || crypto.randomUUID()
const newFolder = await db.transaction(async (tx) => {
let sortOrder: number

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { form, workflow, workflowBlocks } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -6,7 +7,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { addCorsHeaders, validateAuthToken } from '@/lib/core/security/deployment'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { preprocessExecution } from '@/lib/execution/preprocessing'
import { LoggingSession } from '@/lib/logs/execution/logging-session'
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
@@ -119,7 +119,7 @@ export async function POST(
)
}
const executionId = generateId()
const executionId = randomUUID()
const loggingSession = new LoggingSession(
deployment.workflowId,
executionId,
@@ -165,7 +165,7 @@ export async function POST(
return addCorsHeaders(createErrorResponse('No form data provided', 400), request)
}
const executionId = generateId()
const executionId = randomUUID()
const loggingSession = new LoggingSession(deployment.workflowId, executionId, 'form', requestId)
const preprocessResult = await preprocessExecution({

View File

@@ -3,13 +3,13 @@ import { form } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
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 { generateId } from '@/lib/core/utils/uuid'
import { deployWorkflow } from '@/lib/workflows/persistence/utils'
import {
checkWorkflowAccessForFormCreation,
@@ -158,7 +158,7 @@ export async function POST(request: NextRequest) {
encryptedPassword = encrypted
}
const id = generateId()
const id = uuidv4()
logger.info('Creating form deployment with values:', {
workflowId,

View File

@@ -9,7 +9,6 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { hasLiveSyncAccess } from '@/lib/billing/core/subscription'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { dispatchSync } from '@/lib/knowledge/connectors/sync-engine'
import { allocateTagSlots } from '@/lib/knowledge/constants'
import { createTagDefinition } from '@/lib/knowledge/tags/service'
@@ -212,7 +211,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
}
const now = new Date()
const connectorId = generateId()
const connectorId = crypto.randomUUID()
const nextSyncAt =
syncIntervalMinutes > 0 ? new Date(now.getTime() + syncIntervalMinutes * 60 * 1000) : null

View File

@@ -1,8 +1,8 @@
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 { generateId } from '@/lib/core/utils/uuid'
import { deleteChunk, updateChunk } from '@/lib/knowledge/chunks/service'
import { checkChunkAccess } from '@/app/api/knowledge/utils'
@@ -17,7 +17,7 @@ export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string; chunkId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId, chunkId } = await params
try {
@@ -65,7 +65,7 @@ export async function PUT(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string; chunkId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId, chunkId } = await params
try {
@@ -147,7 +147,7 @@ export async function DELETE(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string; chunkId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId, chunkId } = await params
try {

View File

@@ -1,8 +1,8 @@
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 { generateId } from '@/lib/core/utils/uuid'
import { SUPPORTED_FIELD_TYPES } from '@/lib/knowledge/constants'
import {
cleanupUnusedTagDefinitions,
@@ -34,7 +34,7 @@ export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId } = await params
try {
@@ -79,7 +79,7 @@ export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId } = await params
try {
@@ -160,7 +160,7 @@ export async function DELETE(
req: NextRequest,
{ params }: { params: Promise<{ id: string; documentId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, documentId } = await params
const { searchParams } = new URL(req.url)
const action = searchParams.get('action') // 'cleanup' or 'all'

View File

@@ -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 { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
bulkDocumentOperation,
bulkDocumentOperationByFilter,
@@ -66,7 +66,7 @@ const BulkUpdateDocumentsSchema = z
})
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {
@@ -164,7 +164,7 @@ export async function GET(req: NextRequest, { params }: { params: Promise<{ id:
}
export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {
@@ -398,7 +398,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { document } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -6,7 +7,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
createDocumentRecords,
deleteDocument,
@@ -35,7 +35,7 @@ const UpsertDocumentSchema = z.object({
})
export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {

View File

@@ -1,7 +1,7 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { getNextAvailableSlot, getTagDefinitions } from '@/lib/knowledge/tags/service'
import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils'
@@ -9,7 +9,7 @@ const logger = createLogger('NextAvailableSlotAPI')
// GET /api/knowledge/[id]/next-available-slot - Get the next available tag slot for a knowledge base and field type
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
const { searchParams } = new URL(req.url)
const fieldType = searchParams.get('fieldType')

View File

@@ -1,7 +1,7 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { deleteTagDefinition } from '@/lib/knowledge/tags/service'
import { checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'
@@ -14,7 +14,7 @@ export async function DELETE(
req: NextRequest,
{ params }: { params: Promise<{ id: string; tagId: string }> }
) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId, tagId } = await params
try {

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { AuthType, checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { SUPPORTED_FIELD_TYPES } from '@/lib/knowledge/constants'
import { createTagDefinition, getTagDefinitions } from '@/lib/knowledge/tags/service'
import { checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'
@@ -13,7 +13,7 @@ const logger = createLogger('KnowledgeBaseTagDefinitionsAPI')
// GET /api/knowledge/[id]/tag-definitions - Get all tag definitions for a knowledge base
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {
@@ -53,7 +53,7 @@ export async function GET(req: NextRequest, { params }: { params: Promise<{ id:
// POST /api/knowledge/[id]/tag-definitions - Create a new tag definition
export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {

View File

@@ -1,7 +1,7 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { getTagUsage } from '@/lib/knowledge/tags/service'
import { checkKnowledgeBaseAccess } from '@/app/api/knowledge/utils'
@@ -11,7 +11,7 @@ const logger = createLogger('TagUsageAPI')
// GET /api/knowledge/[id]/tag-usage - Get usage statistics for all tag definitions
export async function GET(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const { id: knowledgeBaseId } = await params
try {

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'node:crypto'
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import {
@@ -29,7 +30,6 @@ import { DIRECT_TOOL_DEFS, SUBAGENT_TOOL_DEFS } from '@/lib/copilot/tools/mcp/de
import { env } from '@/lib/core/config/env'
import { RateLimiter } from '@/lib/core/rate-limiter'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import {
authorizeWorkflowByWorkspacePermission,
resolveWorkflowIdForUser,
@@ -638,7 +638,7 @@ async function handleDirectToolCall(
)
const toolCall = {
id: generateId(),
id: randomUUID(),
name: toolDef.toolId,
status: 'pending' as const,
params: args as Record<string, any>,
@@ -715,7 +715,7 @@ async function handleBuildToolCall(
}
}
const chatId = generateId()
const chatId = randomUUID()
const requestPayload = {
message: requestText,
@@ -724,12 +724,12 @@ async function handleBuildToolCall(
model: DEFAULT_COPILOT_MODEL,
mode: 'agent',
commands: ['fast'],
messageId: generateId(),
messageId: randomUUID(),
chatId,
}
const executionId = generateId()
const runId = generateId()
const executionId = crypto.randomUUID()
const runId = crypto.randomUUID()
const messageId = requestPayload.messageId as string
await createRunSegment({

View File

@@ -4,7 +4,6 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { generateId } from '@/lib/core/utils/uuid'
import {
McpDnsResolutionError,
McpDomainNotAllowedError,
@@ -103,7 +102,7 @@ export const POST = withMcpAuth('write')(
throw e
}
const serverId = body.url ? generateMcpServerId(workspaceId, body.url) : generateId()
const serverId = body.url ? generateMcpServerId(workspaceId, body.url) : crypto.randomUUID()
const [existingServer] = await db
.select({ id: mcpServers.id, deletedAt: mcpServers.deletedAt })

View File

@@ -4,7 +4,6 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { generateId } from '@/lib/core/utils/uuid'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -194,7 +193,7 @@ export const POST = withMcpAuth<RouteParams>('write')(
? body.parameterSchema
: await generateParameterSchemaForWorkflow(body.workflowId)
const toolId = generateId()
const toolId = crypto.randomUUID()
const [tool] = await db
.insert(workflowMcpTool)
.values({

View File

@@ -4,7 +4,6 @@ import { createLogger } from '@sim/logger'
import { and, eq, inArray, isNull, sql } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { generateId } from '@/lib/core/utils/uuid'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
@@ -113,7 +112,7 @@ export const POST = withMcpAuth('write')(
)
}
const serverId = generateId()
const serverId = crypto.randomUUID()
const [server] = await db
.insert(workflowMcpServer)
@@ -169,7 +168,7 @@ export const POST = withMcpAuth('write')(
const parameterSchema = await generateParameterSchemaForWorkflow(workflowRecord.id)
const toolId = generateId()
const toolId = crypto.randomUUID()
await db.insert(workflowMcpTool).values({
id: toolId,
serverId,

View File

@@ -5,7 +5,6 @@ import { and, eq, isNull, like } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
const logger = createLogger('MemoryAPI')
@@ -164,7 +163,7 @@ export async function POST(request: NextRequest) {
const initialData = Array.isArray(data) ? data : [data]
const now = new Date()
const id = `mem_${generateId().replace(/-/g, '')}`
const id = `mem_${crypto.randomUUID().replace(/-/g, '')}`
const { sql } = await import('drizzle-orm')

View File

@@ -17,7 +17,6 @@ import { processContextsServer, resolveActiveResourceContext } from '@/lib/copil
import { createRequestTracker, createUnauthorizedResponse } from '@/lib/copilot/request-helpers'
import { taskPubSub } from '@/lib/copilot/task-events'
import { generateWorkspaceContext } from '@/lib/copilot/workspace-context'
import { generateId } from '@/lib/core/utils/uuid'
import {
assertActiveWorkspaceAccess,
getUserEntityPermissions,
@@ -110,7 +109,7 @@ export async function POST(req: NextRequest) {
userTimezone,
} = MothershipMessageSchema.parse(body)
const userMessageId = providedMessageId || generateId()
const userMessageId = providedMessageId || crypto.randomUUID()
userMessageIdForLogs = userMessageId
const reqLogger = logger.withMetadata({
requestId: tracker.requestId,
@@ -281,8 +280,8 @@ export async function POST(req: NextRequest) {
}
}
const executionId = generateId()
const runId = generateId()
const executionId = crypto.randomUUID()
const runId = crypto.randomUUID()
const stream = createSSEStream({
requestPayload,
userId: authenticatedUserId,
@@ -311,7 +310,7 @@ export async function POST(req: NextRequest) {
if (!result.success) return
const assistantMessage: Record<string, unknown> = {
id: generateId(),
id: crypto.randomUUID(),
role: 'assistant' as const,
content: result.content,
timestamp: new Date().toISOString(),

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { releasePendingChatStream } from '@/lib/copilot/chat-streaming'
import { taskPubSub } from '@/lib/copilot/task-events'
import { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('MothershipChatStopAPI')
@@ -72,7 +71,7 @@ export async function POST(req: NextRequest) {
if (hasContent || hasBlocks) {
const assistantMessage: Record<string, unknown> = {
id: generateId(),
id: crypto.randomUUID(),
role: 'assistant' as const,
content,
timestamp: new Date().toISOString(),

View File

@@ -6,7 +6,6 @@ import { createRunSegment } from '@/lib/copilot/async-runs/repository'
import { buildIntegrationToolSchemas } from '@/lib/copilot/chat-payload'
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
import { generateWorkspaceContext } from '@/lib/copilot/workspace-context'
import { generateId } from '@/lib/core/utils/uuid'
import {
assertActiveWorkspaceAccess,
getUserEntityPermissions,
@@ -51,8 +50,8 @@ export async function POST(req: NextRequest) {
await assertActiveWorkspaceAccess(workspaceId, userId)
const effectiveChatId = chatId || generateId()
messageId = generateId()
const effectiveChatId = chatId || crypto.randomUUID()
messageId = crypto.randomUUID()
const reqLogger = logger.withMetadata({ messageId })
const [workspaceContext, integrationTools, userPermission] = await Promise.all([
generateWorkspaceContext(workspaceId, userId),
@@ -73,8 +72,8 @@ export async function POST(req: NextRequest) {
...(userPermission ? { userPermission } : {}),
}
const executionId = generateId()
const runId = generateId()
const executionId = crypto.randomUUID()
const runId = crypto.randomUUID()
await createRunSegment({
id: runId,

View File

@@ -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 { generateShortId } from '@/lib/core/utils/uuid'
import { pollInactivityAlerts } from '@/lib/notifications/inactivity-polling'
const logger = createLogger('InactivityAlertPoll')
@@ -13,7 +13,7 @@ const LOCK_KEY = 'inactivity-alert-polling-lock'
const LOCK_TTL_SECONDS = 120
export async function GET(request: NextRequest) {
const requestId = generateShortId()
const requestId = nanoid()
logger.info(`Inactivity alert polling triggered (${requestId})`)
let lockAcquired = false

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import {
invitation,
@@ -26,7 +27,6 @@ import { isOrgPlan, sqlIsPro } from '@/lib/billing/plan-helpers'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { ENTITLED_SUBSCRIPTION_STATUSES } from '@/lib/billing/subscriptions/utils'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { syncWorkspaceEnvCredentials } from '@/lib/credentials/environment'
import { sendEmail } from '@/lib/messaging/email/mailer'
@@ -321,7 +321,7 @@ export async function PUT(
if (status === 'accepted') {
await tx.insert(member).values({
id: generateId(),
id: randomUUID(),
userId: session.user.id,
organizationId,
role: orgInvitation.role,
@@ -423,7 +423,7 @@ export async function PUT(
if (autoAddGroup) {
await tx.insert(permissionGroupMember).values({
id: generateId(),
id: randomUUID(),
permissionGroupId: autoAddGroup.id,
userId: session.user.id,
assignedBy: null,
@@ -497,7 +497,7 @@ export async function PUT(
}
} else {
await tx.insert(permissions).values({
id: generateId(),
id: randomUUID(),
entityType: 'workspace',
entityId: wsInvitation.workspaceId,
userId: session.user.id,

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import {
invitation,
@@ -23,7 +24,6 @@ import {
validateSeatAvailability,
} from '@/lib/billing/validation/seat-management'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { sendEmail } from '@/lib/messaging/email/mailer'
import { quickValidateEmail } from '@/lib/messaging/email/validation'
import { hasWorkspaceAdminAccess } from '@/lib/workspaces/permissions/utils'
@@ -293,7 +293,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
const invitationsToCreate = emailsToInvite.map((email: string) => ({
id: generateId(),
id: randomUUID(),
email,
inviterId: session.user.id,
organizationId,
@@ -310,8 +310,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
for (const email of emailsToInvite) {
const orgInviteForEmail = invitationsToCreate.find((inv) => inv.email === email)
for (const wsInvitation of validWorkspaceInvitations) {
const wsInvitationId = generateId()
const token = generateId()
const wsInvitationId = randomUUID()
const token = randomUUID()
await db.insert(workspaceInvitation).values({
id: wsInvitationId,

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { invitation, member, organization, user, userStats } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -9,7 +10,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 { generateId } from '@/lib/core/utils/uuid'
import { sendEmail } from '@/lib/messaging/email/mailer'
import { quickValidateEmail } from '@/lib/messaging/email/validation'
@@ -231,7 +231,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
}
// Create invitation
const invitationId = generateId()
const invitationId = randomUUID()
const expiresAt = new Date()
expiresAt.setDate(expiresAt.getDate() + 7) // 7 days expiry

View File

@@ -6,7 +6,6 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { hasAccessControlAccess } from '@/lib/billing'
import { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('PermissionGroupBulkMembers')
@@ -130,7 +129,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
const newMembers = usersToAdd.map((userId) => ({
id: generateId(),
id: crypto.randomUUID(),
permissionGroupId: id,
userId,
assignedBy: session.user.id,

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { hasAccessControlAccess } from '@/lib/billing'
import { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('PermissionGroupMembers')
@@ -138,7 +137,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
}
const memberData = {
id: generateId(),
id: crypto.randomUUID(),
permissionGroupId: id,
userId,
assignedBy: session.user.id,

View File

@@ -7,7 +7,6 @@ import { z } from 'zod'
import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { hasAccessControlAccess } from '@/lib/billing'
import { generateId } from '@/lib/core/utils/uuid'
import {
DEFAULT_PERMISSION_GROUP_CONFIG,
type PermissionGroupConfig,
@@ -182,7 +181,7 @@ export async function POST(req: Request) {
const now = new Date()
const newGroup = {
id: generateId(),
id: crypto.randomUUID(),
organizationId,
name,
description: description || null,

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { AuthType } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { preprocessExecution } from '@/lib/execution/preprocessing'
import { PauseResumeManager } from '@/lib/workflows/executor/human-in-the-loop-manager'
import { getWorkspaceBilledAccountUserId } from '@/lib/workspaces/utils'
@@ -60,7 +60,7 @@ export async function POST(
userId = billedAccountUserId
}
const resumeExecutionId = generateId()
const resumeExecutionId = randomUUID()
const requestId = generateRequestId()
logger.info(`[${requestId}] Preprocessing resume execution`, {

View File

@@ -132,12 +132,8 @@ vi.mock('@sim/db', () => ({
},
}))
vi.mock('@/lib/core/utils/uuid', () => ({
generateId: vi.fn(() => 'schedule-execution-1'),
generateShortId: vi.fn(() => 'mock-short-id'),
isValidUuid: vi.fn((v: string) =>
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)
),
vi.mock('uuid', () => ({
v4: vi.fn().mockReturnValue('schedule-execution-1'),
}))
import { GET } from './route'

View File

@@ -2,11 +2,11 @@ import { db, workflowDeploymentVersion, workflowSchedule } from '@sim/db'
import { createLogger } from '@sim/logger'
import { and, eq, isNull, lt, lte, ne, not, or, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { verifyCronAuth } from '@/lib/auth/internal'
import { getJobQueue, shouldExecuteInline } from '@/lib/core/async-jobs'
import { createBullMQJobData, isBullMQEnabled } from '@/lib/core/bullmq'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { enqueueWorkspaceDispatch } from '@/lib/core/workspace-dispatch'
import {
executeJobInline,
@@ -89,7 +89,7 @@ export async function GET(request: NextRequest) {
const schedulePromises = dueSchedules.map(async (schedule) => {
const queueTime = schedule.lastQueuedAt ?? queuedAt
const executionId = generateId()
const executionId = uuidv4()
const correlation = {
executionId,
requestId,

View File

@@ -5,7 +5,6 @@ import { and, eq, isNull, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { captureServerEvent } from '@/lib/posthog/server'
import { validateCronExpression } from '@/lib/workflows/schedules/utils'
import { authorizeWorkflowByWorkspacePermission } from '@/lib/workflows/utils'
@@ -250,7 +249,7 @@ export async function POST(req: NextRequest) {
}
const now = new Date()
const id = generateId()
const id = crypto.randomUUID()
await db.insert(workflowSchedule).values({
id,

View File

@@ -4,7 +4,6 @@ import { createLogger } from '@sim/logger'
import { and, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { generateId } from '@/lib/core/utils/uuid'
import { verifyEffectiveSuperUser } from '@/lib/templates/permissions'
import { parseWorkflowJson } from '@/lib/workflows/operations/import-export'
import {
@@ -119,7 +118,7 @@ export async function POST(request: NextRequest) {
}
// Create new workflow record
const newWorkflowId = generateId()
const newWorkflowId = crypto.randomUUID()
const now = new Date()
const dedupedName = await deduplicateWorkflowName(
`[Debug Import] ${sourceWorkflow.name}`,

View File

@@ -2,7 +2,6 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import {
batchInsertRows,
createTable,
@@ -226,7 +225,7 @@ export async function POST(request: NextRequest) {
let inserted = 0
for (let i = 0; i < coerced.length; i += MAX_BATCH_SIZE) {
const batch = coerced.slice(i, i + MAX_BATCH_SIZE)
const batchRequestId = generateId().slice(0, 8)
const batchRequestId = crypto.randomUUID().slice(0, 8)
const result = await batchInsertRows(
{ tableId: table.id, rows: batch, workspaceId, userId: authResult.userId },
table,

View File

@@ -3,9 +3,9 @@ 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 { generateId } from '@/lib/core/utils/uuid'
const logger = createLogger('TemplateStarAPI')
@@ -86,7 +86,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
await db.transaction(async (tx) => {
// Add the star record
await tx.insert(templateStars).values({
id: generateId(),
id: uuidv4(),
userId: session.user.id,
templateId: id,
starredAt: new Date(),

View File

@@ -3,10 +3,10 @@ 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 { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
import { generateId } from '@/lib/core/utils/uuid'
import { canAccessTemplate, verifyTemplateOwnership } from '@/lib/templates/permissions'
import {
type RegenerateStateInput,
@@ -93,7 +93,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
const templateData = template[0]
// Create a new workflow ID
const newWorkflowId = generateId()
const newWorkflowId = uuidv4()
const now = new Date()
// Extract variables from the template state and remap to the new workflow
@@ -104,7 +104,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
if (!templateVariables || typeof templateVariables !== 'object') return {}
const mapped: Record<string, any> = {}
for (const [, variable] of Object.entries(templateVariables)) {
const newVarId = generateId()
const newVarId = uuidv4()
mapped[newVarId] = { ...variable, id: newVarId, workflowId: newWorkflowId }
}
return mapped
@@ -178,7 +178,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
// Create a deployment version for the new workflow
if (templateData.state) {
const newDeploymentVersionId = generateId()
const newDeploymentVersionId = uuidv4()
await tx.insert(workflowDeploymentVersion).values({
id: newDeploymentVersionId,
workflowId: newWorkflowId,

View File

@@ -9,11 +9,11 @@ import {
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 { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
import { canAccessTemplate, verifyEffectiveSuperUser } from '@/lib/templates/permissions'
import {
extractRequiredCredentials,
@@ -267,7 +267,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: permissionError || 'Access denied' }, { status: 403 })
}
const templateId = generateId()
const templateId = uuidv4()
const now = new Date()
// Get the active deployment version for the workflow to copy its state

View File

@@ -6,7 +6,6 @@ import { createA2AClient, extractTextContent, isTerminalState } from '@/lib/a2a/
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { validateUrlWithDNS } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { generateId } from '@/lib/core/utils/uuid'
export const dynamic = 'force-dynamic'
@@ -143,7 +142,7 @@ export async function POST(request: NextRequest) {
const message: Message = {
kind: 'message',
messageId: generateId(),
messageId: crypto.randomUUID(),
role: 'user',
parts,
...(validatedData.taskId && { taskId: validatedData.taskId }),

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createRawDynamoDBClient, describeTable, listTables } from '@/app/api/tools/dynamodb/utils'
const logger = createLogger('DynamoDBIntrospectAPI')
@@ -15,7 +15,7 @@ const IntrospectSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils'
const logger = createLogger('MongoDBDeleteAPI')
@@ -38,7 +38,7 @@ const DeleteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, sanitizeCollectionName, validatePipeline } from '../utils'
const logger = createLogger('MongoDBExecuteAPI')
@@ -30,7 +30,7 @@ const ExecuteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, sanitizeCollectionName } from '../utils'
const logger = createLogger('MongoDBInsertAPI')
@@ -35,7 +35,7 @@ const InsertSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, executeIntrospect } from '../utils'
const logger = createLogger('MongoDBIntrospectAPI')
@@ -18,7 +18,7 @@ const IntrospectSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils'
const logger = createLogger('MongoDBQueryAPI')
@@ -47,7 +47,7 @@ const QuerySchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMongoDBConnection, sanitizeCollectionName, validateFilter } from '../utils'
const logger = createLogger('MongoDBUpdateAPI')
@@ -57,7 +57,7 @@ const UpdateSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let client = null
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { buildDeleteQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLDeleteAPI')
@@ -19,7 +19,7 @@ const DeleteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLExecuteAPI')
@@ -18,7 +18,7 @@ const ExecuteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { buildInsertQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLInsertAPI')
@@ -40,7 +40,7 @@ const InsertSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMySQLConnection, executeIntrospect } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLIntrospectAPI')
@@ -17,7 +17,7 @@ const IntrospectSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createMySQLConnection, executeQuery, validateQuery } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLQueryAPI')
@@ -18,7 +18,7 @@ const QuerySchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { buildUpdateQuery, createMySQLConnection, executeQuery } from '@/app/api/tools/mysql/utils'
const logger = createLogger('MySQLUpdateAPI')
@@ -38,7 +38,7 @@ const UpdateSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const auth = await checkInternalAuth(request)

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
convertNeo4jTypesToJSON,
createNeo4jDriver,
@@ -23,7 +23,7 @@ const CreateSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createNeo4jDriver, validateCypherQuery } from '@/app/api/tools/neo4j/utils'
const logger = createLogger('Neo4jDeleteAPI')
@@ -20,7 +20,7 @@ const DeleteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
convertNeo4jTypesToJSON,
createNeo4jDriver,
@@ -23,7 +23,7 @@ const ExecuteSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { createNeo4jDriver } from '@/app/api/tools/neo4j/utils'
import type { Neo4jNodeSchema, Neo4jRelationshipSchema } from '@/tools/neo4j/types'
@@ -18,7 +18,7 @@ const IntrospectSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
convertNeo4jTypesToJSON,
createNeo4jDriver,
@@ -23,7 +23,7 @@ const MergeSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
convertNeo4jTypesToJSON,
createNeo4jDriver,
@@ -23,7 +23,7 @@ const QuerySchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
convertNeo4jTypesToJSON,
createNeo4jDriver,
@@ -23,7 +23,7 @@ const UpdateSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
let driver = null
let session = null

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -5,7 +6,6 @@ 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 { generateId } from '@/lib/core/utils/uuid'
import { refreshAccessTokenIfNeeded, resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'
@@ -18,7 +18,7 @@ import type { MicrosoftGraphDriveItem } from '@/tools/onedrive/types'
* Get files (not folders) from Microsoft OneDrive
*/
export async function GET(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
logger.info(`[${requestId}] OneDrive files request received`)
try {

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -5,7 +6,6 @@ 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 { generateId } from '@/lib/core/utils/uuid'
import { refreshAccessTokenIfNeeded, resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'
@@ -13,7 +13,7 @@ export const dynamic = 'force-dynamic'
const logger = createLogger('OneDriveFolderAPI')
export async function GET(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const session = await getSession()

View File

@@ -1,3 +1,4 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
@@ -5,7 +6,6 @@ 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 { generateId } from '@/lib/core/utils/uuid'
import { refreshAccessTokenIfNeeded, resolveOAuthAccountId } from '@/app/api/auth/oauth/utils'
export const dynamic = 'force-dynamic'
@@ -18,7 +18,7 @@ import type { MicrosoftGraphDriveItem } from '@/tools/onedrive/types'
* Get folders from Microsoft OneDrive
*/
export async function GET(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
try {
const session = await getSession()

View File

@@ -1,9 +1,9 @@
import { randomUUID } from 'crypto'
import type { ItemCreateParams } from '@1password/sdk'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
connectRequest,
createOnePasswordClient,
@@ -28,7 +28,7 @@ const CreateItemSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {
@@ -55,7 +55,7 @@ export async function POST(request: NextRequest) {
const parsedFields = params.fields
? (JSON.parse(params.fields) as Array<Record<string, any>>).map((f) => ({
id: f.id || generateId().slice(0, 8),
id: f.id || randomUUID().slice(0, 8),
title: f.label || f.title || '',
fieldType: toSdkFieldType(f.type || 'STRING'),
value: f.value || '',

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import { connectRequest, createOnePasswordClient, resolveCredentials } from '../utils'
const logger = createLogger('OnePasswordDeleteItemAPI')
@@ -17,7 +17,7 @@ const DeleteItemSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
connectRequest,
createOnePasswordClient,
@@ -22,7 +22,7 @@ const GetItemSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {

View File

@@ -1,8 +1,8 @@
import { randomUUID } from 'crypto'
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateId } from '@/lib/core/utils/uuid'
import {
connectRequest,
createOnePasswordClient,
@@ -21,7 +21,7 @@ const GetVaultSchema = z.object({
})
export async function POST(request: NextRequest) {
const requestId = generateId().slice(0, 8)
const requestId = randomUUID().slice(0, 8)
const auth = await checkInternalAuth(request)
if (!auth.success || !auth.userId) {

Some files were not shown because too many files have changed in this diff Show More