mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 10:53:06 +08:00
refactor: use ungh for github api (#34108)
This commit is contained in:
@@ -51,8 +51,6 @@ NEXT_PUBLIC_ALLOW_EMBED=
|
|||||||
# Allow rendering unsafe URLs which have "data:" scheme.
|
# Allow rendering unsafe URLs which have "data:" scheme.
|
||||||
NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME=false
|
NEXT_PUBLIC_ALLOW_UNSAFE_DATA_SCHEME=false
|
||||||
|
|
||||||
# Github Access Token, used for invoking Github API
|
|
||||||
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
|
|
||||||
# The maximum number of top-k value for RAG.
|
# The maximum number of top-k value for RAG.
|
||||||
NEXT_PUBLIC_TOP_K_MAX_VALUE=10
|
NEXT_PUBLIC_TOP_K_MAX_VALUE=10
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,9 @@
|
|||||||
* upload handling, and task status polling. Verifies the complete plugin
|
* upload handling, and task status polling. Verifies the complete plugin
|
||||||
* installation pipeline from source discovery to completion.
|
* installation pipeline from source discovery to completion.
|
||||||
*/
|
*/
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
||||||
|
|
||||||
vi.mock('@/config', () => ({
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
GITHUB_ACCESS_TOKEN: '',
|
import { checkForUpdates, fetchReleases, handleUpload } from '@/app/components/plugins/install-plugin/hooks'
|
||||||
}))
|
|
||||||
|
|
||||||
const mockToastNotify = vi.fn()
|
const mockToastNotify = vi.fn()
|
||||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||||
@@ -30,10 +28,6 @@ vi.mock('@/service/plugins', () => ({
|
|||||||
checkTaskStatus: vi.fn(),
|
checkTaskStatus: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const { useGitHubReleases, useGitHubUpload } = await import(
|
|
||||||
'@/app/components/plugins/install-plugin/hooks',
|
|
||||||
)
|
|
||||||
|
|
||||||
describe('Plugin Installation Flow Integration', () => {
|
describe('Plugin Installation Flow Integration', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
@@ -44,22 +38,22 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
it('fetches releases, checks for updates, and uploads the new version', async () => {
|
it('fetches releases, checks for updates, and uploads the new version', async () => {
|
||||||
const mockReleases = [
|
const mockReleases = [
|
||||||
{
|
{
|
||||||
tag_name: 'v2.0.0',
|
tag: 'v2.0.0',
|
||||||
assets: [{ browser_download_url: 'https://github.com/test/v2.difypkg', name: 'plugin-v2.difypkg' }],
|
assets: [{ downloadUrl: 'https://github.com/test/v2.difypkg' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag_name: 'v1.5.0',
|
tag: 'v1.5.0',
|
||||||
assets: [{ browser_download_url: 'https://github.com/test/v1.5.difypkg', name: 'plugin-v1.5.difypkg' }],
|
assets: [{ downloadUrl: 'https://github.com/test/v1.5.difypkg' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag_name: 'v1.0.0',
|
tag: 'v1.0.0',
|
||||||
assets: [{ browser_download_url: 'https://github.com/test/v1.difypkg', name: 'plugin-v1.difypkg' }],
|
assets: [{ downloadUrl: 'https://github.com/test/v1.difypkg' }],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: () => Promise.resolve(mockReleases),
|
json: () => Promise.resolve({ releases: mockReleases }),
|
||||||
})
|
})
|
||||||
|
|
||||||
mockUploadGitHub.mockResolvedValue({
|
mockUploadGitHub.mockResolvedValue({
|
||||||
@@ -67,8 +61,6 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
unique_identifier: 'test-plugin:2.0.0',
|
unique_identifier: 'test-plugin:2.0.0',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { fetchReleases, checkForUpdates } = useGitHubReleases()
|
|
||||||
|
|
||||||
const releases = await fetchReleases('test-org', 'test-repo')
|
const releases = await fetchReleases('test-org', 'test-repo')
|
||||||
expect(releases).toHaveLength(3)
|
expect(releases).toHaveLength(3)
|
||||||
expect(releases[0].tag_name).toBe('v2.0.0')
|
expect(releases[0].tag_name).toBe('v2.0.0')
|
||||||
@@ -77,7 +69,6 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
expect(needUpdate).toBe(true)
|
expect(needUpdate).toBe(true)
|
||||||
expect(toastProps.message).toContain('v2.0.0')
|
expect(toastProps.message).toContain('v2.0.0')
|
||||||
|
|
||||||
const { handleUpload } = useGitHubUpload()
|
|
||||||
const onSuccess = vi.fn()
|
const onSuccess = vi.fn()
|
||||||
const result = await handleUpload(
|
const result = await handleUpload(
|
||||||
'https://github.com/test-org/test-repo',
|
'https://github.com/test-org/test-repo',
|
||||||
@@ -104,18 +95,16 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
it('handles no new version available', async () => {
|
it('handles no new version available', async () => {
|
||||||
const mockReleases = [
|
const mockReleases = [
|
||||||
{
|
{
|
||||||
tag_name: 'v1.0.0',
|
tag: 'v1.0.0',
|
||||||
assets: [{ browser_download_url: 'https://github.com/test/v1.difypkg', name: 'plugin-v1.difypkg' }],
|
assets: [{ downloadUrl: 'https://github.com/test/v1.difypkg' }],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: () => Promise.resolve(mockReleases),
|
json: () => Promise.resolve({ releases: mockReleases }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { fetchReleases, checkForUpdates } = useGitHubReleases()
|
|
||||||
|
|
||||||
const releases = await fetchReleases('test-org', 'test-repo')
|
const releases = await fetchReleases('test-org', 'test-repo')
|
||||||
const { needUpdate, toastProps } = checkForUpdates(releases, 'v1.0.0')
|
const { needUpdate, toastProps } = checkForUpdates(releases, 'v1.0.0')
|
||||||
|
|
||||||
@@ -127,11 +116,9 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
it('handles empty releases', async () => {
|
it('handles empty releases', async () => {
|
||||||
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
;(globalThis.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: () => Promise.resolve([]),
|
json: () => Promise.resolve({ releases: [] }),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { fetchReleases, checkForUpdates } = useGitHubReleases()
|
|
||||||
|
|
||||||
const releases = await fetchReleases('test-org', 'test-repo')
|
const releases = await fetchReleases('test-org', 'test-repo')
|
||||||
expect(releases).toHaveLength(0)
|
expect(releases).toHaveLength(0)
|
||||||
|
|
||||||
@@ -147,7 +134,6 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
status: 404,
|
status: 404,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { fetchReleases } = useGitHubReleases()
|
|
||||||
const releases = await fetchReleases('nonexistent-org', 'nonexistent-repo')
|
const releases = await fetchReleases('nonexistent-org', 'nonexistent-repo')
|
||||||
|
|
||||||
expect(releases).toEqual([])
|
expect(releases).toEqual([])
|
||||||
@@ -159,7 +145,6 @@ describe('Plugin Installation Flow Integration', () => {
|
|||||||
it('handles upload failure gracefully', async () => {
|
it('handles upload failure gracefully', async () => {
|
||||||
mockUploadGitHub.mockRejectedValue(new Error('Upload failed'))
|
mockUploadGitHub.mockRejectedValue(new Error('Upload failed'))
|
||||||
|
|
||||||
const { handleUpload } = useGitHubUpload()
|
|
||||||
const onSuccess = vi.fn()
|
const onSuccess = vi.fn()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
import { render, screen, waitFor } from '@testing-library/react'
|
import { render, screen, waitFor } from '@testing-library/react'
|
||||||
import nock from 'nock'
|
|
||||||
import * as React from 'react'
|
|
||||||
import GithubStar from '../index'
|
import GithubStar from '../index'
|
||||||
|
|
||||||
const GITHUB_HOST = 'https://api.github.com'
|
const GITHUB_STAR_URL = 'https://ungh.cc/repos/langgenius/dify'
|
||||||
const GITHUB_PATH = '/repos/langgenius/dify'
|
|
||||||
|
|
||||||
const renderWithQueryClient = () => {
|
const renderWithQueryClient = () => {
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
@@ -18,40 +15,66 @@ const renderWithQueryClient = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockGithubStar = (status: number, body: Record<string, unknown>, delayMs = 0) => {
|
const createJsonResponse = (body: Record<string, unknown>, status = 200) => {
|
||||||
return nock(GITHUB_HOST).get(GITHUB_PATH).delay(delayMs).reply(status, body)
|
return new Response(JSON.stringify(body), {
|
||||||
|
status,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const createDeferred = <T,>() => {
|
||||||
|
let resolve!: (value: T | PromiseLike<T>) => void
|
||||||
|
let reject!: (reason?: unknown) => void
|
||||||
|
const promise = new Promise<T>((res, rej) => {
|
||||||
|
resolve = res
|
||||||
|
reject = rej
|
||||||
|
})
|
||||||
|
|
||||||
|
return { promise, resolve, reject }
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('GithubStar', () => {
|
describe('GithubStar', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nock.cleanAll()
|
vi.restoreAllMocks()
|
||||||
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Shows fetched star count when request succeeds
|
// Covers the fetched star count shown after a successful request.
|
||||||
it('should render fetched star count', async () => {
|
it('should render fetched star count', async () => {
|
||||||
mockGithubStar(200, { stargazers_count: 123456 })
|
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
|
||||||
|
createJsonResponse({ repo: { stars: 123456 } }),
|
||||||
|
)
|
||||||
|
|
||||||
renderWithQueryClient()
|
renderWithQueryClient()
|
||||||
|
|
||||||
expect(await screen.findByText('123,456')).toBeInTheDocument()
|
expect(await screen.findByText('123,456')).toBeInTheDocument()
|
||||||
|
expect(fetchSpy).toHaveBeenCalledWith(GITHUB_STAR_URL)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Falls back to default star count when request fails
|
// Covers the fallback star count shown when the request fails.
|
||||||
it('should render default star count on error', async () => {
|
it('should render default star count on error', async () => {
|
||||||
mockGithubStar(500, {})
|
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
|
||||||
|
createJsonResponse({}, 500),
|
||||||
|
)
|
||||||
|
|
||||||
renderWithQueryClient()
|
renderWithQueryClient()
|
||||||
|
|
||||||
expect(await screen.findByText('110,918')).toBeInTheDocument()
|
expect(await screen.findByText('110,918')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Renders loader while fetching data
|
// Covers the loading indicator while the fetch promise is still pending.
|
||||||
it('should show loader while fetching', async () => {
|
it('should show loader while fetching', async () => {
|
||||||
mockGithubStar(200, { stargazers_count: 222222 }, 50)
|
const deferred = createDeferred<Response>()
|
||||||
|
vi.spyOn(globalThis, 'fetch').mockReturnValueOnce(deferred.promise)
|
||||||
|
|
||||||
const { container } = renderWithQueryClient()
|
const { container } = renderWithQueryClient()
|
||||||
|
|
||||||
expect(container.querySelector('.animate-spin')).toBeInTheDocument()
|
expect(container.querySelector('.animate-spin')).toBeInTheDocument()
|
||||||
await waitFor(() => expect(screen.getByText('222,222')).toBeInTheDocument())
|
|
||||||
|
deferred.resolve(createJsonResponse({ repo: { stars: 222222 } }))
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('222,222')).toBeInTheDocument()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import type { GithubRepo } from '@/models/common'
|
|
||||||
import { RiLoader2Line } from '@remixicon/react'
|
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { IS_DEV } from '@/config'
|
|
||||||
|
|
||||||
const defaultData = {
|
type GithubStarResponse = {
|
||||||
stargazers_count: 110918,
|
repo: {
|
||||||
|
stars: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultData: GithubStarResponse = {
|
||||||
|
repo: { stars: 110918 },
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStar = async () => {
|
const getStar = async () => {
|
||||||
const res = await fetch('https://api.github.com/repos/langgenius/dify')
|
const res = await fetch('https://ungh.cc/repos/langgenius/dify')
|
||||||
|
|
||||||
if (!res.ok)
|
if (!res.ok)
|
||||||
throw new Error('Failed to fetch github star')
|
throw new Error('Failed to fetch github star')
|
||||||
@@ -19,21 +22,20 @@ const getStar = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GithubStar: FC<{ className: string }> = (props) => {
|
const GithubStar: FC<{ className: string }> = (props) => {
|
||||||
const { isFetching, isError, data } = useQuery<GithubRepo>({
|
const { isFetching, isError, data } = useQuery<GithubStarResponse>({
|
||||||
queryKey: ['github-star'],
|
queryKey: ['github-star'],
|
||||||
queryFn: getStar,
|
queryFn: getStar,
|
||||||
enabled: !IS_DEV,
|
|
||||||
retry: false,
|
retry: false,
|
||||||
placeholderData: defaultData,
|
placeholderData: defaultData,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isFetching)
|
if (isFetching)
|
||||||
return <RiLoader2Line className="size-3 shrink-0 animate-spin text-text-tertiary" />
|
return <span className="i-ri-loader-2-line size-3 shrink-0 animate-spin text-text-tertiary" />
|
||||||
|
|
||||||
if (isError)
|
if (isError)
|
||||||
return <span {...props}>{defaultData.stargazers_count.toLocaleString()}</span>
|
return <span {...props}>{defaultData.repo.stars.toLocaleString()}</span>
|
||||||
|
|
||||||
return <span {...props}>{data?.stargazers_count.toLocaleString()}</span>
|
return <span {...props}>{data?.repo.stars.toLocaleString()}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GithubStar
|
export default GithubStar
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { renderHook } from '@testing-library/react'
|
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { useGitHubReleases, useGitHubUpload } from '../hooks'
|
import { checkForUpdates, fetchReleases, handleUpload } from '../hooks'
|
||||||
|
|
||||||
const mockNotify = vi.fn()
|
const mockNotify = vi.fn()
|
||||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||||
@@ -15,10 +14,6 @@ vi.mock('@/app/components/base/ui/toast', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/config', () => ({
|
|
||||||
GITHUB_ACCESS_TOKEN: '',
|
|
||||||
}))
|
|
||||||
|
|
||||||
const mockUploadGitHub = vi.fn()
|
const mockUploadGitHub = vi.fn()
|
||||||
vi.mock('@/service/plugins', () => ({
|
vi.mock('@/service/plugins', () => ({
|
||||||
uploadGitHub: (...args: unknown[]) => mockUploadGitHub(...args),
|
uploadGitHub: (...args: unknown[]) => mockUploadGitHub(...args),
|
||||||
@@ -37,17 +32,17 @@ describe('install-plugin/hooks', () => {
|
|||||||
it('fetches releases from GitHub API and formats them', async () => {
|
it('fetches releases from GitHub API and formats them', async () => {
|
||||||
mockFetch.mockResolvedValue({
|
mockFetch.mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
json: () => Promise.resolve([
|
json: () => Promise.resolve({
|
||||||
{
|
releases: [
|
||||||
tag_name: 'v1.0.0',
|
{
|
||||||
assets: [{ browser_download_url: 'https://example.com/v1.zip', name: 'plugin.zip' }],
|
tag: 'v1.0.0',
|
||||||
body: 'Release notes',
|
assets: [{ downloadUrl: 'https://example.com/plugin.zip' }],
|
||||||
},
|
},
|
||||||
]),
|
],
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useGitHubReleases())
|
const releases = await fetchReleases('owner', 'repo')
|
||||||
const releases = await result.current.fetchReleases('owner', 'repo')
|
|
||||||
|
|
||||||
expect(releases).toHaveLength(1)
|
expect(releases).toHaveLength(1)
|
||||||
expect(releases[0].tag_name).toBe('v1.0.0')
|
expect(releases[0].tag_name).toBe('v1.0.0')
|
||||||
@@ -60,8 +55,7 @@ describe('install-plugin/hooks', () => {
|
|||||||
ok: false,
|
ok: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { result } = renderHook(() => useGitHubReleases())
|
const releases = await fetchReleases('owner', 'repo')
|
||||||
const releases = await result.current.fetchReleases('owner', 'repo')
|
|
||||||
|
|
||||||
expect(releases).toEqual([])
|
expect(releases).toEqual([])
|
||||||
expect(mockNotify).toHaveBeenCalledWith('Failed to fetch repository releases')
|
expect(mockNotify).toHaveBeenCalledWith('Failed to fetch repository releases')
|
||||||
@@ -70,29 +64,26 @@ describe('install-plugin/hooks', () => {
|
|||||||
|
|
||||||
describe('checkForUpdates', () => {
|
describe('checkForUpdates', () => {
|
||||||
it('detects newer version available', () => {
|
it('detects newer version available', () => {
|
||||||
const { result } = renderHook(() => useGitHubReleases())
|
|
||||||
const releases = [
|
const releases = [
|
||||||
{ tag_name: 'v1.0.0', assets: [] },
|
{ tag_name: 'v1.0.0', assets: [] },
|
||||||
{ tag_name: 'v2.0.0', assets: [] },
|
{ tag_name: 'v2.0.0', assets: [] },
|
||||||
]
|
]
|
||||||
const { needUpdate, toastProps } = result.current.checkForUpdates(releases, 'v1.0.0')
|
const { needUpdate, toastProps } = checkForUpdates(releases, 'v1.0.0')
|
||||||
expect(needUpdate).toBe(true)
|
expect(needUpdate).toBe(true)
|
||||||
expect(toastProps.message).toContain('v2.0.0')
|
expect(toastProps.message).toContain('v2.0.0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns no update when current is latest', () => {
|
it('returns no update when current is latest', () => {
|
||||||
const { result } = renderHook(() => useGitHubReleases())
|
|
||||||
const releases = [
|
const releases = [
|
||||||
{ tag_name: 'v1.0.0', assets: [] },
|
{ tag_name: 'v1.0.0', assets: [] },
|
||||||
]
|
]
|
||||||
const { needUpdate, toastProps } = result.current.checkForUpdates(releases, 'v1.0.0')
|
const { needUpdate, toastProps } = checkForUpdates(releases, 'v1.0.0')
|
||||||
expect(needUpdate).toBe(false)
|
expect(needUpdate).toBe(false)
|
||||||
expect(toastProps.type).toBe('info')
|
expect(toastProps.type).toBe('info')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns error for empty releases', () => {
|
it('returns error for empty releases', () => {
|
||||||
const { result } = renderHook(() => useGitHubReleases())
|
const { needUpdate, toastProps } = checkForUpdates([], 'v1.0.0')
|
||||||
const { needUpdate, toastProps } = result.current.checkForUpdates([], 'v1.0.0')
|
|
||||||
expect(needUpdate).toBe(false)
|
expect(needUpdate).toBe(false)
|
||||||
expect(toastProps.type).toBe('error')
|
expect(toastProps.type).toBe('error')
|
||||||
expect(toastProps.message).toContain('empty')
|
expect(toastProps.message).toContain('empty')
|
||||||
@@ -109,8 +100,7 @@ describe('install-plugin/hooks', () => {
|
|||||||
})
|
})
|
||||||
const onSuccess = vi.fn()
|
const onSuccess = vi.fn()
|
||||||
|
|
||||||
const { result } = renderHook(() => useGitHubUpload())
|
const pkg = await handleUpload(
|
||||||
const pkg = await result.current.handleUpload(
|
|
||||||
'https://github.com/owner/repo',
|
'https://github.com/owner/repo',
|
||||||
'v1.0.0',
|
'v1.0.0',
|
||||||
'plugin.difypkg',
|
'plugin.difypkg',
|
||||||
@@ -132,9 +122,8 @@ describe('install-plugin/hooks', () => {
|
|||||||
it('shows toast on upload error', async () => {
|
it('shows toast on upload error', async () => {
|
||||||
mockUploadGitHub.mockRejectedValue(new Error('Upload failed'))
|
mockUploadGitHub.mockRejectedValue(new Error('Upload failed'))
|
||||||
|
|
||||||
const { result } = renderHook(() => useGitHubUpload())
|
|
||||||
await expect(
|
await expect(
|
||||||
result.current.handleUpload('url', 'v1', 'pkg'),
|
handleUpload('url', 'v1', 'pkg'),
|
||||||
).rejects.toThrow('Upload failed')
|
).rejects.toThrow('Upload failed')
|
||||||
expect(mockNotify).toHaveBeenCalledWith('Error uploading package')
|
expect(mockNotify).toHaveBeenCalledWith('Error uploading package')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,101 +1,87 @@
|
|||||||
import type { GitHubRepoReleaseResponse } from '../types'
|
import type { GitHubRepoReleaseResponse } from '../types'
|
||||||
import { toast } from '@/app/components/base/ui/toast'
|
import { toast } from '@/app/components/base/ui/toast'
|
||||||
import { GITHUB_ACCESS_TOKEN } from '@/config'
|
|
||||||
import { uploadGitHub } from '@/service/plugins'
|
import { uploadGitHub } from '@/service/plugins'
|
||||||
import { compareVersion, getLatestVersion } from '@/utils/semver'
|
import { compareVersion, getLatestVersion } from '@/utils/semver'
|
||||||
|
|
||||||
|
const normalizeAssetName = (downloadUrl: string) => {
|
||||||
|
const parts = downloadUrl.split('/')
|
||||||
|
return parts[parts.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
const formatReleases = (releases: any) => {
|
const formatReleases = (releases: any) => {
|
||||||
return releases.map((release: any) => ({
|
return releases.map((release: any) => ({
|
||||||
tag_name: release.tag_name,
|
tag_name: release.tag,
|
||||||
assets: release.assets.map((asset: any) => ({
|
assets: release.assets.map((asset: any) => ({
|
||||||
browser_download_url: asset.browser_download_url,
|
browser_download_url: asset.downloadUrl,
|
||||||
name: asset.name,
|
name: normalizeAssetName(asset.downloadUrl),
|
||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGitHubReleases = () => {
|
export const fetchReleases = async (owner: string, repo: string) => {
|
||||||
const fetchReleases = async (owner: string, repo: string) => {
|
try {
|
||||||
try {
|
// Fetch releases without authentication from client
|
||||||
if (!GITHUB_ACCESS_TOKEN) {
|
const res = await fetch(`https://ungh.cc/repos/${owner}/${repo}/releases`)
|
||||||
// Fetch releases without authentication from client
|
if (!res.ok)
|
||||||
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`)
|
throw new Error('Failed to fetch repository releases')
|
||||||
if (!res.ok)
|
const data = await res.json()
|
||||||
throw new Error('Failed to fetch repository releases')
|
return formatReleases(data.releases)
|
||||||
const data = await res.json()
|
|
||||||
return formatReleases(data)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Fetch releases with authentication from server
|
|
||||||
const res = await fetch(`/repos/${owner}/${repo}/releases`)
|
|
||||||
const bodyJson = await res.json()
|
|
||||||
if (bodyJson.status !== 200)
|
|
||||||
throw new Error(bodyJson.data.message)
|
|
||||||
return formatReleases(bodyJson.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
toast.error(error.message)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
toast.error('Failed to fetch repository releases')
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
toast.error(error.message)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toast.error('Failed to fetch repository releases')
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const checkForUpdates = (fetchedReleases: GitHubRepoReleaseResponse[], currentVersion: string) => {
|
export const checkForUpdates = (fetchedReleases: GitHubRepoReleaseResponse[], currentVersion: string) => {
|
||||||
let needUpdate = false
|
let needUpdate = false
|
||||||
const toastProps: { type?: 'success' | 'error' | 'info' | 'warning', message: string } = {
|
const toastProps: { type?: 'success' | 'error' | 'info' | 'warning', message: string } = {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
message: 'No new version available',
|
message: 'No new version available',
|
||||||
}
|
}
|
||||||
if (fetchedReleases.length === 0) {
|
if (fetchedReleases.length === 0) {
|
||||||
toastProps.type = 'error'
|
toastProps.type = 'error'
|
||||||
toastProps.message = 'Input releases is empty'
|
toastProps.message = 'Input releases is empty'
|
||||||
return { needUpdate, toastProps }
|
|
||||||
}
|
|
||||||
const versions = fetchedReleases.map(release => release.tag_name)
|
|
||||||
const latestVersion = getLatestVersion(versions)
|
|
||||||
try {
|
|
||||||
needUpdate = compareVersion(latestVersion, currentVersion) === 1
|
|
||||||
if (needUpdate)
|
|
||||||
toastProps.message = `New version available: ${latestVersion}`
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
needUpdate = false
|
|
||||||
toastProps.type = 'error'
|
|
||||||
toastProps.message = 'Fail to compare versions, please check the version format'
|
|
||||||
}
|
|
||||||
return { needUpdate, toastProps }
|
return { needUpdate, toastProps }
|
||||||
}
|
}
|
||||||
|
const versions = fetchedReleases.map(release => release.tag_name)
|
||||||
return { fetchReleases, checkForUpdates }
|
const latestVersion = getLatestVersion(versions)
|
||||||
}
|
try {
|
||||||
|
needUpdate = compareVersion(latestVersion, currentVersion) === 1
|
||||||
export const useGitHubUpload = () => {
|
if (needUpdate)
|
||||||
const handleUpload = async (
|
toastProps.message = `New version available: ${latestVersion}`
|
||||||
repoUrl: string,
|
}
|
||||||
selectedVersion: string,
|
catch {
|
||||||
selectedPackage: string,
|
needUpdate = false
|
||||||
onSuccess?: (GitHubPackage: { manifest: any, unique_identifier: string }) => void,
|
toastProps.type = 'error'
|
||||||
) => {
|
toastProps.message = 'Fail to compare versions, please check the version format'
|
||||||
try {
|
}
|
||||||
const response = await uploadGitHub(repoUrl, selectedVersion, selectedPackage)
|
return { needUpdate, toastProps }
|
||||||
const GitHubPackage = {
|
}
|
||||||
manifest: response.manifest,
|
|
||||||
unique_identifier: response.unique_identifier,
|
export const handleUpload = async (
|
||||||
}
|
repoUrl: string,
|
||||||
if (onSuccess)
|
selectedVersion: string,
|
||||||
onSuccess(GitHubPackage)
|
selectedPackage: string,
|
||||||
return GitHubPackage
|
onSuccess?: (GitHubPackage: { manifest: any, unique_identifier: string }) => void,
|
||||||
}
|
) => {
|
||||||
catch (error) {
|
try {
|
||||||
toast.error('Error uploading package')
|
const response = await uploadGitHub(repoUrl, selectedVersion, selectedPackage)
|
||||||
throw error
|
const GitHubPackage = {
|
||||||
}
|
manifest: response.manifest,
|
||||||
|
unique_identifier: response.unique_identifier,
|
||||||
|
}
|
||||||
|
if (onSuccess)
|
||||||
|
onSuccess(GitHubPackage)
|
||||||
|
return GitHubPackage
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
toast.error('Error uploading package')
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
return { handleUpload }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,10 +74,16 @@ vi.mock('@/app/components/plugins/install-plugin/base/use-get-icon', () => ({
|
|||||||
default: () => ({ getIconUrl: mockGetIconUrl }),
|
default: () => ({ getIconUrl: mockGetIconUrl }),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const mockFetchReleases = vi.fn()
|
const { mockFetchReleases } = vi.hoisted(() => ({
|
||||||
vi.mock('../../hooks', () => ({
|
mockFetchReleases: vi.fn(),
|
||||||
useGitHubReleases: () => ({ fetchReleases: mockFetchReleases }),
|
|
||||||
}))
|
}))
|
||||||
|
vi.mock('../../hooks', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import('../../hooks')>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
fetchReleases: mockFetchReleases,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const mockRefreshPluginList = vi.fn()
|
const mockRefreshPluginList = vi.fn()
|
||||||
vi.mock('../../hooks/use-refresh-plugin-list', () => ({
|
vi.mock('../../hooks/use-refresh-plugin-list', () => ({
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-ico
|
|||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
import { InstallStepFromGitHub } from '../../types'
|
import { InstallStepFromGitHub } from '../../types'
|
||||||
import Installed from '../base/installed'
|
import Installed from '../base/installed'
|
||||||
import { useGitHubReleases } from '../hooks'
|
import { fetchReleases } from '../hooks'
|
||||||
import useHideLogic from '../hooks/use-hide-logic'
|
import useHideLogic from '../hooks/use-hide-logic'
|
||||||
import useRefreshPluginList from '../hooks/use-refresh-plugin-list'
|
import useRefreshPluginList from '../hooks/use-refresh-plugin-list'
|
||||||
import { convertRepoToUrl, parseGitHubUrl } from '../utils'
|
import { convertRepoToUrl, parseGitHubUrl } from '../utils'
|
||||||
@@ -31,7 +31,6 @@ type InstallFromGitHubProps = {
|
|||||||
const InstallFromGitHub: React.FC<InstallFromGitHubProps> = ({ updatePayload, onClose, onSuccess }) => {
|
const InstallFromGitHub: React.FC<InstallFromGitHubProps> = ({ updatePayload, onClose, onSuccess }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { getIconUrl } = useGetIcon()
|
const { getIconUrl } = useGetIcon()
|
||||||
const { fetchReleases } = useGitHubReleases()
|
|
||||||
const { refreshPluginList } = useRefreshPluginList()
|
const { refreshPluginList } = useRefreshPluginList()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { PluginCategoryEnum } from '../../../../types'
|
import { PluginCategoryEnum } from '../../../../types'
|
||||||
import SelectPackage from '../selectPackage'
|
import SelectPackage from '../selectPackage'
|
||||||
|
|
||||||
// Mock the useGitHubUpload hook
|
// Mock upload helper from hooks module
|
||||||
const mockHandleUpload = vi.fn()
|
const { mockHandleUpload } = vi.hoisted(() => ({
|
||||||
vi.mock('../../../hooks', () => ({
|
mockHandleUpload: vi.fn(),
|
||||||
useGitHubUpload: () => ({ handleUpload: mockHandleUpload }),
|
|
||||||
}))
|
}))
|
||||||
|
vi.mock('../../../hooks', async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import('../../../hooks')>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
handleUpload: mockHandleUpload,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Factory functions
|
// Factory functions
|
||||||
const createMockManifest = (): PluginDeclaration => ({
|
const createMockManifest = (): PluginDeclaration => ({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as React from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { PortalSelect } from '@/app/components/base/select'
|
import { PortalSelect } from '@/app/components/base/select'
|
||||||
import { useGitHubUpload } from '../../hooks'
|
import { handleUpload } from '../../hooks'
|
||||||
|
|
||||||
const i18nPrefix = 'installFromGitHub'
|
const i18nPrefix = 'installFromGitHub'
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ const SelectPackage: React.FC<SelectPackageProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const isEdit = Boolean(updatePayload)
|
const isEdit = Boolean(updatePayload)
|
||||||
const [isUploading, setIsUploading] = React.useState(false)
|
const [isUploading, setIsUploading] = React.useState(false)
|
||||||
const { handleUpload } = useGitHubUpload()
|
|
||||||
|
|
||||||
const handleUploadPackage = async () => {
|
const handleUploadPackage = async () => {
|
||||||
if (isUploading)
|
if (isUploading)
|
||||||
|
|||||||
@@ -103,12 +103,14 @@ vi.mock('@/service/use-tools', () => ({
|
|||||||
useInvalidateAllToolProviders: () => mockInvalidateAllToolProviders,
|
useInvalidateAllToolProviders: () => mockInvalidateAllToolProviders,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../../install-plugin/hooks', () => ({
|
vi.mock('../../install-plugin/hooks', async (importOriginal) => {
|
||||||
useGitHubReleases: () => ({
|
const actual = await importOriginal<typeof import('../../install-plugin/hooks')>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
checkForUpdates: mockCheckForUpdates,
|
checkForUpdates: mockCheckForUpdates,
|
||||||
fetchReleases: mockFetchReleases,
|
fetchReleases: mockFetchReleases,
|
||||||
}),
|
}
|
||||||
}))
|
})
|
||||||
|
|
||||||
// Auto upgrade settings mock
|
// Auto upgrade settings mock
|
||||||
let mockAutoUpgradeInfo: {
|
let mockAutoUpgradeInfo: {
|
||||||
|
|||||||
@@ -72,12 +72,14 @@ vi.mock('@/service/use-tools', () => ({
|
|||||||
useInvalidateAllToolProviders: () => mockInvalidateAllToolProviders,
|
useInvalidateAllToolProviders: () => mockInvalidateAllToolProviders,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../../../../install-plugin/hooks', () => ({
|
vi.mock('../../../../install-plugin/hooks', async (importOriginal) => {
|
||||||
useGitHubReleases: () => ({
|
const actual = await importOriginal<typeof import('../../../../install-plugin/hooks')>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
checkForUpdates: mockCheckForUpdates,
|
checkForUpdates: mockCheckForUpdates,
|
||||||
fetchReleases: mockFetchReleases,
|
fetchReleases: mockFetchReleases,
|
||||||
}),
|
}
|
||||||
}))
|
})
|
||||||
|
|
||||||
const createPluginDetail = (overrides: Partial<PluginDetail> = {}): PluginDetail => ({
|
const createPluginDetail = (overrides: Partial<PluginDetail> = {}): PluginDetail => ({
|
||||||
id: 'test-id',
|
id: 'test-id',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useProviderContext } from '@/context/provider-context'
|
|||||||
import { uninstallPlugin } from '@/service/plugins'
|
import { uninstallPlugin } from '@/service/plugins'
|
||||||
import { useInvalidateCheckInstalled } from '@/service/use-plugins'
|
import { useInvalidateCheckInstalled } from '@/service/use-plugins'
|
||||||
import { useInvalidateAllToolProviders } from '@/service/use-tools'
|
import { useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||||
import { useGitHubReleases } from '../../../install-plugin/hooks'
|
import { checkForUpdates, fetchReleases } from '../../../install-plugin/hooks'
|
||||||
import { PluginCategoryEnum, PluginSource } from '../../../types'
|
import { PluginCategoryEnum, PluginSource } from '../../../types'
|
||||||
|
|
||||||
type UsePluginOperationsParams = {
|
type UsePluginOperationsParams = {
|
||||||
@@ -39,7 +39,6 @@ export const usePluginOperations = ({
|
|||||||
onUpdate,
|
onUpdate,
|
||||||
}: UsePluginOperationsParams): UsePluginOperationsReturn => {
|
}: UsePluginOperationsParams): UsePluginOperationsReturn => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
|
||||||
const { setShowUpdatePluginModal } = useModalContext()
|
const { setShowUpdatePluginModal } = useModalContext()
|
||||||
const { refreshModelProviders } = useProviderContext()
|
const { refreshModelProviders } = useProviderContext()
|
||||||
const invalidateCheckInstalled = useInvalidateCheckInstalled()
|
const invalidateCheckInstalled = useInvalidateCheckInstalled()
|
||||||
|
|||||||
@@ -46,13 +46,15 @@ vi.mock('@/service/plugins', () => ({
|
|||||||
uninstallPlugin: (id: string) => mockUninstallPlugin(id),
|
uninstallPlugin: (id: string) => mockUninstallPlugin(id),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock GitHub releases hook
|
// Mock GitHub release helpers
|
||||||
vi.mock('../../install-plugin/hooks', () => ({
|
vi.mock('../../install-plugin/hooks', async (importOriginal) => {
|
||||||
useGitHubReleases: () => ({
|
const actual = await importOriginal<typeof import('../../install-plugin/hooks')>()
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
fetchReleases: mockFetchReleases,
|
fetchReleases: mockFetchReleases,
|
||||||
checkForUpdates: mockCheckForUpdates,
|
checkForUpdates: mockCheckForUpdates,
|
||||||
}),
|
}
|
||||||
}))
|
})
|
||||||
|
|
||||||
// Mock modal context
|
// Mock modal context
|
||||||
vi.mock('@/context/modal-context', () => ({
|
vi.mock('@/context/modal-context', () => ({
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
|||||||
import ActionButton from '../../base/action-button'
|
import ActionButton from '../../base/action-button'
|
||||||
import Confirm from '../../base/confirm'
|
import Confirm from '../../base/confirm'
|
||||||
import Tooltip from '../../base/tooltip'
|
import Tooltip from '../../base/tooltip'
|
||||||
import { useGitHubReleases } from '../install-plugin/hooks'
|
import { checkForUpdates, fetchReleases } from '../install-plugin/hooks'
|
||||||
import PluginInfo from '../plugin-page/plugin-info'
|
import PluginInfo from '../plugin-page/plugin-info'
|
||||||
import { PluginSource } from '../types'
|
import { PluginSource } from '../types'
|
||||||
|
|
||||||
@@ -54,7 +54,6 @@ const Action: FC<Props> = ({
|
|||||||
setTrue: showDeleting,
|
setTrue: showDeleting,
|
||||||
setFalse: hideDeleting,
|
setFalse: hideDeleting,
|
||||||
}] = useBoolean(false)
|
}] = useBoolean(false)
|
||||||
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
|
||||||
const { setShowUpdatePluginModal } = useModalContext()
|
const { setShowUpdatePluginModal } = useModalContext()
|
||||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import type { NextRequest } from '@/next/server'
|
|
||||||
import { Octokit } from '@octokit/core'
|
|
||||||
import { RequestError } from '@octokit/request-error'
|
|
||||||
import { GITHUB_ACCESS_TOKEN } from '@/config'
|
|
||||||
import { NextResponse } from '@/next/server'
|
|
||||||
|
|
||||||
type Params = {
|
|
||||||
owner: string
|
|
||||||
repo: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const octokit = new Octokit({
|
|
||||||
auth: GITHUB_ACCESS_TOKEN,
|
|
||||||
})
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: NextRequest,
|
|
||||||
{ params }: { params: Promise<Params> },
|
|
||||||
) {
|
|
||||||
const { owner, repo } = (await params)
|
|
||||||
try {
|
|
||||||
const releasesRes = await octokit.request('GET /repos/{owner}/{repo}/releases', {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
headers: {
|
|
||||||
'X-GitHub-Api-Version': '2022-11-28',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return NextResponse.json(releasesRes)
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
if (error instanceof RequestError)
|
|
||||||
return NextResponse.json(error.response)
|
|
||||||
else
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -292,9 +292,6 @@ export const resetHITLInputReg = () => HITL_INPUT_REG.lastIndex = 0
|
|||||||
|
|
||||||
export const DISABLE_UPLOAD_IMAGE_AS_ICON = env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON
|
export const DISABLE_UPLOAD_IMAGE_AS_ICON = env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON
|
||||||
|
|
||||||
export const GITHUB_ACCESS_TOKEN
|
|
||||||
= env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN
|
|
||||||
|
|
||||||
export const SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS = '.difypkg,.difybndl'
|
export const SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS = '.difypkg,.difybndl'
|
||||||
export const FULL_DOC_PREVIEW_LENGTH = 50
|
export const FULL_DOC_PREVIEW_LENGTH = 50
|
||||||
|
|
||||||
|
|||||||
@@ -283,16 +283,6 @@ Reserve snapshots for static, deterministic fragments (icons, badges, layout chr
|
|||||||
|
|
||||||
**Note**: Dify is a desktop application. **No need for** responsive/mobile testing.
|
**Note**: Dify is a desktop application. **No need for** responsive/mobile testing.
|
||||||
|
|
||||||
### 12. Mock API
|
|
||||||
|
|
||||||
Use Nock to mock API calls. Example:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const mockGithubStar = (status: number, body: Record<string, unknown>, delayMs = 0) => {
|
|
||||||
return nock(GITHUB_HOST).get(GITHUB_PATH).delay(delayMs).reply(status, body)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
### Example Structure
|
### Example Structure
|
||||||
|
|||||||
@@ -66,10 +66,6 @@ const clientSchema = {
|
|||||||
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: coercedBoolean.default(true),
|
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: coercedBoolean.default(true),
|
||||||
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: coercedBoolean.default(true),
|
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: coercedBoolean.default(true),
|
||||||
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: coercedBoolean.default(false),
|
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: coercedBoolean.default(false),
|
||||||
/**
|
|
||||||
* Github Access Token, used for invoking Github API
|
|
||||||
*/
|
|
||||||
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN: z.string().optional(),
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of tokens for segmentation
|
* The maximum number of tokens for segmentation
|
||||||
*/
|
*/
|
||||||
@@ -171,7 +167,6 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL : getRuntimeEnvFromBody('enableWebsiteFirecrawl'),
|
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL : getRuntimeEnvFromBody('enableWebsiteFirecrawl'),
|
||||||
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER : getRuntimeEnvFromBody('enableWebsiteJinareader'),
|
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER : getRuntimeEnvFromBody('enableWebsiteJinareader'),
|
||||||
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL : getRuntimeEnvFromBody('enableWebsiteWatercrawl'),
|
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL : getRuntimeEnvFromBody('enableWebsiteWatercrawl'),
|
||||||
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN: isServer ? process.env.NEXT_PUBLIC_GITHUB_ACCESS_TOKEN : getRuntimeEnvFromBody('githubAccessToken'),
|
|
||||||
NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: isServer ? process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH : getRuntimeEnvFromBody('indexingMaxSegmentationTokensLength'),
|
NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: isServer ? process.env.NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH : getRuntimeEnvFromBody('indexingMaxSegmentationTokensLength'),
|
||||||
NEXT_PUBLIC_IS_MARKETPLACE: isServer ? process.env.NEXT_PUBLIC_IS_MARKETPLACE : getRuntimeEnvFromBody('isMarketplace'),
|
NEXT_PUBLIC_IS_MARKETPLACE: isServer ? process.env.NEXT_PUBLIC_IS_MARKETPLACE : getRuntimeEnvFromBody('isMarketplace'),
|
||||||
NEXT_PUBLIC_LOOP_NODE_MAX_COUNT: isServer ? process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT : getRuntimeEnvFromBody('loopNodeMaxCount'),
|
NEXT_PUBLIC_LOOP_NODE_MAX_COUNT: isServer ? process.env.NEXT_PUBLIC_LOOP_NODE_MAX_COUNT : getRuntimeEnvFromBody('loopNodeMaxCount'),
|
||||||
|
|||||||
@@ -1937,6 +1937,11 @@
|
|||||||
"count": 4
|
"count": 4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"app/components/base/date-and-time-picker/hooks.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
"app/components/base/date-and-time-picker/time-picker/header.tsx": {
|
"app/components/base/date-and-time-picker/time-picker/header.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -2184,6 +2189,9 @@
|
|||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
"ts/no-explicit-any": {
|
"ts/no-explicit-any": {
|
||||||
"count": 3
|
"count": 3
|
||||||
}
|
}
|
||||||
@@ -4766,6 +4774,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/components/header/account-setting/model-provider-page/hooks.ts": {
|
"app/components/header/account-setting/model-provider-page/hooks.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
"ts/no-explicit-any": {
|
"ts/no-explicit-any": {
|
||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
@@ -4819,6 +4830,11 @@
|
|||||||
"count": 6
|
"count": 6
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
"app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts": {
|
"app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts": {
|
||||||
"ts/no-explicit-any": {
|
"ts/no-explicit-any": {
|
||||||
"count": 2
|
"count": 2
|
||||||
@@ -5038,6 +5054,11 @@
|
|||||||
"count": 4
|
"count": 4
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"app/components/plugins/install-plugin/hooks/use-fold-anim-into.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"app/components/plugins/install-plugin/install-bundle/index.tsx": {
|
"app/components/plugins/install-plugin/install-bundle/index.tsx": {
|
||||||
"erasable-syntax-only/enums": {
|
"erasable-syntax-only/enums": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -5242,6 +5263,11 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"app/components/plugins/plugin-auth/hooks/use-get-api.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts": {
|
"app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -7229,6 +7255,11 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
"app/components/workflow/nodes/_base/components/workflow-panel/index.tsx": {
|
"app/components/workflow/nodes/_base/components/workflow-panel/index.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -7260,6 +7291,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts": {
|
"app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 2
|
||||||
|
},
|
||||||
"react/set-state-in-effect": {
|
"react/set-state-in-effect": {
|
||||||
"count": 1
|
"count": 1
|
||||||
},
|
},
|
||||||
@@ -9827,6 +9861,11 @@
|
|||||||
"count": 7
|
"count": 7
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"service/use-flow.ts": {
|
||||||
|
"react/no-unnecessary-use-prefix": {
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"service/use-pipeline.ts": {
|
"service/use-pipeline.ts": {
|
||||||
"@tanstack/query/exhaustive-deps": {
|
"@tanstack/query/exhaustive-deps": {
|
||||||
"count": 2
|
"count": 2
|
||||||
|
|||||||
@@ -66,12 +66,14 @@ export default antfu(
|
|||||||
...pluginReact.configs['recommended-typescript'].rules,
|
...pluginReact.configs['recommended-typescript'].rules,
|
||||||
'react/prefer-namespace-import': 'error',
|
'react/prefer-namespace-import': 'error',
|
||||||
'react/set-state-in-effect': 'error',
|
'react/set-state-in-effect': 'error',
|
||||||
|
'react/no-unnecessary-use-prefix': 'error',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files: [...GLOB_TESTS, GLOB_MARKDOWN_CODE, 'vitest.setup.ts', 'test/i18n-mock.ts'],
|
files: [...GLOB_TESTS, GLOB_MARKDOWN_CODE, 'vitest.setup.ts', 'test/i18n-mock.ts'],
|
||||||
rules: {
|
rules: {
|
||||||
'react/component-hook-factories': 'off',
|
'react/component-hook-factories': 'off',
|
||||||
|
'react/no-unnecessary-use-prefix': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
reactRefresh.configs.next(),
|
reactRefresh.configs.next(),
|
||||||
|
|||||||
@@ -220,10 +220,6 @@ export type DataSources = {
|
|||||||
sources: DataSourceItem[]
|
sources: DataSourceItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GithubRepo = {
|
|
||||||
stargazers_count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PluginProvider = {
|
export type PluginProvider = {
|
||||||
tool_name: string
|
tool_name: string
|
||||||
is_enabled: boolean
|
is_enabled: boolean
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
export { NextResponse } from 'next/server'
|
|
||||||
export type { NextRequest } from 'next/server'
|
|
||||||
@@ -74,8 +74,6 @@
|
|||||||
"@lexical/text": "0.42.0",
|
"@lexical/text": "0.42.0",
|
||||||
"@lexical/utils": "0.42.0",
|
"@lexical/utils": "0.42.0",
|
||||||
"@monaco-editor/react": "4.7.0",
|
"@monaco-editor/react": "4.7.0",
|
||||||
"@octokit/core": "7.0.6",
|
|
||||||
"@octokit/request-error": "7.1.0",
|
|
||||||
"@orpc/client": "1.13.9",
|
"@orpc/client": "1.13.9",
|
||||||
"@orpc/contract": "1.13.9",
|
"@orpc/contract": "1.13.9",
|
||||||
"@orpc/openapi-client": "1.13.9",
|
"@orpc/openapi-client": "1.13.9",
|
||||||
@@ -228,7 +226,6 @@
|
|||||||
"jsdom-testing-mocks": "1.16.0",
|
"jsdom-testing-mocks": "1.16.0",
|
||||||
"knip": "6.0.2",
|
"knip": "6.0.2",
|
||||||
"lint-staged": "16.4.0",
|
"lint-staged": "16.4.0",
|
||||||
"nock": "14.0.11",
|
|
||||||
"postcss": "8.5.8",
|
"postcss": "8.5.8",
|
||||||
"postcss-js": "5.1.0",
|
"postcss-js": "5.1.0",
|
||||||
"react-server-dom-webpack": "19.2.4",
|
"react-server-dom-webpack": "19.2.4",
|
||||||
|
|||||||
168
web/pnpm-lock.yaml
generated
168
web/pnpm-lock.yaml
generated
@@ -109,12 +109,6 @@ importers:
|
|||||||
'@monaco-editor/react':
|
'@monaco-editor/react':
|
||||||
specifier: 4.7.0
|
specifier: 4.7.0
|
||||||
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
'@octokit/core':
|
|
||||||
specifier: 7.0.6
|
|
||||||
version: 7.0.6
|
|
||||||
'@octokit/request-error':
|
|
||||||
specifier: 7.1.0
|
|
||||||
version: 7.1.0
|
|
||||||
'@orpc/client':
|
'@orpc/client':
|
||||||
specifier: 1.13.9
|
specifier: 1.13.9
|
||||||
version: 1.13.9
|
version: 1.13.9
|
||||||
@@ -566,9 +560,6 @@ importers:
|
|||||||
lint-staged:
|
lint-staged:
|
||||||
specifier: 16.4.0
|
specifier: 16.4.0
|
||||||
version: 16.4.0
|
version: 16.4.0
|
||||||
nock:
|
|
||||||
specifier: 14.0.11
|
|
||||||
version: 14.0.11
|
|
||||||
postcss:
|
postcss:
|
||||||
specifier: 8.5.8
|
specifier: 8.5.8
|
||||||
version: 8.5.8
|
version: 8.5.8
|
||||||
@@ -1685,10 +1676,6 @@ packages:
|
|||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
'@mswjs/interceptors@0.41.3':
|
|
||||||
resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@1.1.1':
|
'@napi-rs/wasm-runtime@1.1.1':
|
||||||
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
|
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
|
||||||
|
|
||||||
@@ -1791,45 +1778,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==}
|
resolution: {integrity: sha512-y3SvzjuY1ygnzWA4Krwx/WaJAsTMP11DN+e21A8Fa8PW1oDtVB5NSRW7LWurAiS2oKRkuCgcjTYMkBuBkcPCRg==}
|
||||||
engines: {node: '>=12.4.0'}
|
engines: {node: '>=12.4.0'}
|
||||||
|
|
||||||
'@octokit/auth-token@6.0.0':
|
|
||||||
resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/core@7.0.6':
|
|
||||||
resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/endpoint@11.0.3':
|
|
||||||
resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/graphql@9.0.3':
|
|
||||||
resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/openapi-types@27.0.0':
|
|
||||||
resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==}
|
|
||||||
|
|
||||||
'@octokit/request-error@7.1.0':
|
|
||||||
resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/request@10.0.8':
|
|
||||||
resolution: {integrity: sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==}
|
|
||||||
engines: {node: '>= 20'}
|
|
||||||
|
|
||||||
'@octokit/types@16.0.0':
|
|
||||||
resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==}
|
|
||||||
|
|
||||||
'@open-draft/deferred-promise@2.2.0':
|
|
||||||
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
|
|
||||||
|
|
||||||
'@open-draft/logger@0.3.0':
|
|
||||||
resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==}
|
|
||||||
|
|
||||||
'@open-draft/until@2.1.0':
|
|
||||||
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
|
|
||||||
|
|
||||||
'@orpc/client@1.13.9':
|
'@orpc/client@1.13.9':
|
||||||
resolution: {integrity: sha512-RmD2HDgmGgF6zgHHdybE4zH6QJoHjC+/C3n56yLf+fmWbiZtwnOUETgGCroY6S8aK2fpy6hJ3wZaJUjfWVuGHg==}
|
resolution: {integrity: sha512-RmD2HDgmGgF6zgHHdybE4zH6QJoHjC+/C3n56yLf+fmWbiZtwnOUETgGCroY6S8aK2fpy6hJ3wZaJUjfWVuGHg==}
|
||||||
|
|
||||||
@@ -4127,9 +4075,6 @@ packages:
|
|||||||
engines: {node: '>=6.0.0'}
|
engines: {node: '>=6.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
before-after-hook@4.0.0:
|
|
||||||
resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==}
|
|
||||||
|
|
||||||
bezier-easing@2.1.0:
|
bezier-easing@2.1.0:
|
||||||
resolution: {integrity: sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==}
|
resolution: {integrity: sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==}
|
||||||
|
|
||||||
@@ -5223,9 +5168,6 @@ packages:
|
|||||||
engines: {node: '>= 10.17.0'}
|
engines: {node: '>= 10.17.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
fast-content-type-parse@3.0.0:
|
|
||||||
resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==}
|
|
||||||
|
|
||||||
fast-deep-equal@3.1.3:
|
fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
@@ -5645,9 +5587,6 @@ packages:
|
|||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
is-node-process@1.2.0:
|
|
||||||
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
|
|
||||||
|
|
||||||
is-number@7.0.0:
|
is-number@7.0.0:
|
||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
@@ -5774,12 +5713,6 @@ packages:
|
|||||||
json-stable-stringify-without-jsonify@1.0.1:
|
json-stable-stringify-without-jsonify@1.0.1:
|
||||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
|
|
||||||
json-stringify-safe@5.0.1:
|
|
||||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
|
||||||
|
|
||||||
json-with-bigint@3.5.7:
|
|
||||||
resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==}
|
|
||||||
|
|
||||||
json5@2.2.3:
|
json5@2.2.3:
|
||||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -6359,10 +6292,6 @@ packages:
|
|||||||
sass:
|
sass:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
nock@14.0.11:
|
|
||||||
resolution: {integrity: sha512-u5xUnYE+UOOBA6SpELJheMCtj2Laqx15Vl70QxKo43Wz/6nMHXS7PrEioXLjXAwhmawdEMNImwKCcPhBJWbKVw==}
|
|
||||||
engines: {node: '>=18.20.0 <20 || >=20.12.1'}
|
|
||||||
|
|
||||||
node-abi@3.89.0:
|
node-abi@3.89.0:
|
||||||
resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==}
|
resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -6445,9 +6374,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
outvariant@1.4.3:
|
|
||||||
resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==}
|
|
||||||
|
|
||||||
oxc-parser@0.120.0:
|
oxc-parser@0.120.0:
|
||||||
resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==}
|
resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
@@ -6710,10 +6636,6 @@ packages:
|
|||||||
prop-types@15.8.1:
|
prop-types@15.8.1:
|
||||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||||
|
|
||||||
propagate@2.0.1:
|
|
||||||
resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==}
|
|
||||||
engines: {node: '>= 8'}
|
|
||||||
|
|
||||||
property-information@5.6.0:
|
property-information@5.6.0:
|
||||||
resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
|
resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
|
||||||
|
|
||||||
@@ -7251,9 +7173,6 @@ packages:
|
|||||||
react: ^18.0.0 || ^19.0.0
|
react: ^18.0.0 || ^19.0.0
|
||||||
react-dom: ^18.0.0 || ^19.0.0
|
react-dom: ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
strict-event-emitter@0.5.1:
|
|
||||||
resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==}
|
|
||||||
|
|
||||||
string-argv@0.3.2:
|
string-argv@0.3.2:
|
||||||
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
||||||
engines: {node: '>=0.6.19'}
|
engines: {node: '>=0.6.19'}
|
||||||
@@ -7640,9 +7559,6 @@ packages:
|
|||||||
unist-util-visit@5.1.0:
|
unist-util-visit@5.1.0:
|
||||||
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
|
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
|
||||||
|
|
||||||
universal-user-agent@7.0.3:
|
|
||||||
resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==}
|
|
||||||
|
|
||||||
universalify@2.0.1:
|
universalify@2.0.1:
|
||||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
@@ -9325,15 +9241,6 @@ snapshots:
|
|||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
|
|
||||||
'@mswjs/interceptors@0.41.3':
|
|
||||||
dependencies:
|
|
||||||
'@open-draft/deferred-promise': 2.2.0
|
|
||||||
'@open-draft/logger': 0.3.0
|
|
||||||
'@open-draft/until': 2.1.0
|
|
||||||
is-node-process: 1.2.0
|
|
||||||
outvariant: 1.4.3
|
|
||||||
strict-event-emitter: 0.5.1
|
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@1.1.1':
|
'@napi-rs/wasm-runtime@1.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/core': 1.9.0
|
'@emnapi/core': 1.9.0
|
||||||
@@ -9400,57 +9307,6 @@ snapshots:
|
|||||||
|
|
||||||
'@nolyfill/side-channel@1.0.44': {}
|
'@nolyfill/side-channel@1.0.44': {}
|
||||||
|
|
||||||
'@octokit/auth-token@6.0.0': {}
|
|
||||||
|
|
||||||
'@octokit/core@7.0.6':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/auth-token': 6.0.0
|
|
||||||
'@octokit/graphql': 9.0.3
|
|
||||||
'@octokit/request': 10.0.8
|
|
||||||
'@octokit/request-error': 7.1.0
|
|
||||||
'@octokit/types': 16.0.0
|
|
||||||
before-after-hook: 4.0.0
|
|
||||||
universal-user-agent: 7.0.3
|
|
||||||
|
|
||||||
'@octokit/endpoint@11.0.3':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/types': 16.0.0
|
|
||||||
universal-user-agent: 7.0.3
|
|
||||||
|
|
||||||
'@octokit/graphql@9.0.3':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/request': 10.0.8
|
|
||||||
'@octokit/types': 16.0.0
|
|
||||||
universal-user-agent: 7.0.3
|
|
||||||
|
|
||||||
'@octokit/openapi-types@27.0.0': {}
|
|
||||||
|
|
||||||
'@octokit/request-error@7.1.0':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/types': 16.0.0
|
|
||||||
|
|
||||||
'@octokit/request@10.0.8':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/endpoint': 11.0.3
|
|
||||||
'@octokit/request-error': 7.1.0
|
|
||||||
'@octokit/types': 16.0.0
|
|
||||||
fast-content-type-parse: 3.0.0
|
|
||||||
json-with-bigint: 3.5.7
|
|
||||||
universal-user-agent: 7.0.3
|
|
||||||
|
|
||||||
'@octokit/types@16.0.0':
|
|
||||||
dependencies:
|
|
||||||
'@octokit/openapi-types': 27.0.0
|
|
||||||
|
|
||||||
'@open-draft/deferred-promise@2.2.0': {}
|
|
||||||
|
|
||||||
'@open-draft/logger@0.3.0':
|
|
||||||
dependencies:
|
|
||||||
is-node-process: 1.2.0
|
|
||||||
outvariant: 1.4.3
|
|
||||||
|
|
||||||
'@open-draft/until@2.1.0': {}
|
|
||||||
|
|
||||||
'@orpc/client@1.13.9':
|
'@orpc/client@1.13.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@orpc/shared': 1.13.9
|
'@orpc/shared': 1.13.9
|
||||||
@@ -11553,8 +11409,6 @@ snapshots:
|
|||||||
|
|
||||||
baseline-browser-mapping@2.10.8: {}
|
baseline-browser-mapping@2.10.8: {}
|
||||||
|
|
||||||
before-after-hook@4.0.0: {}
|
|
||||||
|
|
||||||
bezier-easing@2.1.0: {}
|
bezier-easing@2.1.0: {}
|
||||||
|
|
||||||
bidi-js@1.0.3:
|
bidi-js@1.0.3:
|
||||||
@@ -12873,8 +12727,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
fast-content-type-parse@3.0.0: {}
|
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
|
|
||||||
fast-glob@3.3.1:
|
fast-glob@3.3.1:
|
||||||
@@ -13325,8 +13177,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-docker: 3.0.0
|
is-docker: 3.0.0
|
||||||
|
|
||||||
is-node-process@1.2.0: {}
|
|
||||||
|
|
||||||
is-number@7.0.0: {}
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
is-plain-obj@4.1.0: {}
|
is-plain-obj@4.1.0: {}
|
||||||
@@ -13438,10 +13288,6 @@ snapshots:
|
|||||||
|
|
||||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||||
|
|
||||||
json-stringify-safe@5.0.1: {}
|
|
||||||
|
|
||||||
json-with-bigint@3.5.7: {}
|
|
||||||
|
|
||||||
json5@2.2.3: {}
|
json5@2.2.3: {}
|
||||||
|
|
||||||
jsonc-eslint-parser@3.1.0:
|
jsonc-eslint-parser@3.1.0:
|
||||||
@@ -14326,12 +14172,6 @@ snapshots:
|
|||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
nock@14.0.11:
|
|
||||||
dependencies:
|
|
||||||
'@mswjs/interceptors': 0.41.3
|
|
||||||
json-stringify-safe: 5.0.1
|
|
||||||
propagate: 2.0.1
|
|
||||||
|
|
||||||
node-abi@3.89.0:
|
node-abi@3.89.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
@@ -14401,8 +14241,6 @@ snapshots:
|
|||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
word-wrap: 1.2.5
|
word-wrap: 1.2.5
|
||||||
|
|
||||||
outvariant@1.4.3: {}
|
|
||||||
|
|
||||||
oxc-parser@0.120.0:
|
oxc-parser@0.120.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@oxc-project/types': 0.120.0
|
'@oxc-project/types': 0.120.0
|
||||||
@@ -14752,8 +14590,6 @@ snapshots:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
|
||||||
propagate@2.0.1: {}
|
|
||||||
|
|
||||||
property-information@5.6.0:
|
property-information@5.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
xtend: 4.0.2
|
xtend: 4.0.2
|
||||||
@@ -15452,8 +15288,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
strict-event-emitter@0.5.1: {}
|
|
||||||
|
|
||||||
string-argv@0.3.2: {}
|
string-argv@0.3.2: {}
|
||||||
|
|
||||||
string-ts@2.3.1: {}
|
string-ts@2.3.1: {}
|
||||||
@@ -15857,8 +15691,6 @@ snapshots:
|
|||||||
unist-util-is: 6.0.1
|
unist-util-is: 6.0.1
|
||||||
unist-util-visit-parents: 6.0.2
|
unist-util-visit-parents: 6.0.2
|
||||||
|
|
||||||
universal-user-agent@7.0.3: {}
|
|
||||||
|
|
||||||
universalify@2.0.1: {}
|
universalify@2.0.1: {}
|
||||||
|
|
||||||
unpic@4.2.2: {}
|
unpic@4.2.2: {}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import type { NextRequest } from '@/next/server'
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import type { NextRequest } from 'next/server'
|
||||||
import { Buffer } from 'node:buffer'
|
import { Buffer } from 'node:buffer'
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import { NextResponse } from 'next/server'
|
||||||
import { env } from '@/env'
|
import { env } from '@/env'
|
||||||
import { NextResponse } from '@/next/server'
|
|
||||||
|
|
||||||
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com https://api2.amplitude.com *.amplitude.com'
|
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://ungh.cc https://api2.amplitude.com *.amplitude.com'
|
||||||
|
|
||||||
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
|
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
|
||||||
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
|
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
|
||||||
|
|||||||
Reference in New Issue
Block a user