Compare commits

...
Author SHA1 Message Date
copilot-swe-agent[bot]andhyoban b5a7e7aede refactor: use Promise.all for service worker unregistration
Co-authored-by: hyoban <38493346+hyoban@users.noreply.github.com>
2026-01-23 03:31:32 +00:00
copilot-swe-agent[bot]andhyoban 57df92aba7 feat: add error handling for service worker unregistration
Co-authored-by: hyoban <38493346+hyoban@users.noreply.github.com>
2026-01-23 03:29:07 +00:00
copilot-swe-agent[bot]andhyoban e6d925cad0 feat: proactively unregister service workers in dev mode
Co-authored-by: hyoban <38493346+hyoban@users.noreply.github.com>
2026-01-23 03:26:09 +00:00
copilot-swe-agent[bot] ed34e302f2 Initial plan 2026-01-23 03:21:35 +00:00
Stephen ZhouGitHubgemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
b6141a135c Apply suggestion from @gemini-code-assist[bot]
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-23 11:16:10 +08:00
Stephen Zhou 00ec2c2623 chore: disable serwist in dev 2026-01-23 11:11:39 +08:00
盐粒 YanliandGitHub c8ae6e39d2 fix: NextStep crash when target node is missing (#31416) 2026-01-23 10:15:20 +08:00
QuantumGhostandGitHub 61f8647f37 docs(api): mark SystemFeatureApi as unauthenticated by design (#31417)
The `/console/api/system-features` is required for the dashboard initialization. Authentication would create circular dependency (can't login without dashboard loading).

ref: CVE-2025-63387

Related: #31368
2026-01-22 22:33:59 +08:00
4 changed files with 57 additions and 11 deletions
+9 -1
View File
@@ -39,5 +39,13 @@ class SystemFeatureApi(Resource):
),
)
def get(self):
"""Get system-wide feature configuration"""
"""Get system-wide feature configuration
NOTE: This endpoint is unauthenticated by design, as it provides system features
data required for dashboard initialization.
Authentication would create circular dependency (can't login without dashboard loading).
Only non-sensitive configuration data should be returned by this endpoint.
"""
return FeatureService.get_system_features().model_dump()
+34 -1
View File
@@ -1,3 +1,36 @@
'use client'
export { SerwistProvider } from '@serwist/turbopack/react'
import { SerwistProvider } from '@serwist/turbopack/react'
import { useEffect } from 'react'
import { IS_DEV } from '@/config'
export function PWAProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
if (IS_DEV && 'serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations()
.then((registrations) => {
return Promise.all(
registrations.map(registration => registration.unregister()),
)
})
.catch((error) => {
// Silently fail if service worker unregistration fails
// This is a development-only optimization and shouldn't block the app
console.warn('Failed to unregister service workers:', error)
})
}
}, [])
if (IS_DEV) {
return <>{children}</>
}
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
const swUrl = `${basePath}/serwist/sw.js`
return (
<SerwistProvider swUrl={swUrl}>
{children}
</SerwistProvider>
)
}
@@ -44,11 +44,19 @@ const NextStep = ({
const connectedEdges = getConnectedEdges([selectedNode] as Node[], edges).filter(edge => edge.source === selectedNode!.id)
const list = useMemo(() => {
const resolveNextNodes = (connected: typeof connectedEdges) => {
return connected.reduce<Node[]>((acc, edge) => {
const nextNode = outgoers.find(outgoer => outgoer.id === edge.target)
if (nextNode)
acc.push(nextNode)
return acc
}, [])
}
let items = []
if (branches?.length) {
items = branches.map((branch, index) => {
const connected = connectedEdges.filter(edge => edge.sourceHandle === branch.id)
const nextNodes = connected.map(edge => outgoers.find(outgoer => outgoer.id === edge.target)!)
const nextNodes = resolveNextNodes(connected)
return {
branch: {
@@ -61,7 +69,7 @@ const NextStep = ({
}
else {
const connected = connectedEdges.filter(edge => edge.sourceHandle === 'source')
const nextNodes = connected.map(edge => outgoers.find(outgoer => outgoer.id === edge.target)!)
const nextNodes = resolveNextNodes(connected)
items = [{
branch: {
@@ -73,7 +81,7 @@ const NextStep = ({
if (data.error_strategy === ErrorHandleTypeEnum.failBranch && hasErrorHandleNode(data.type)) {
const connected = connectedEdges.filter(edge => edge.sourceHandle === ErrorHandleTypeEnum.failBranch)
const nextNodes = connected.map(edge => outgoers.find(outgoer => outgoer.id === edge.target)!)
const nextNodes = resolveNextNodes(connected)
items.push({
branch: {
+3 -6
View File
@@ -12,7 +12,7 @@ import { ToastProvider } from './components/base/toast'
import BrowserInitializer from './components/browser-initializer'
import { ReactScanLoader } from './components/devtools/react-scan/loader'
import { I18nServerProvider } from './components/provider/i18n-server'
import { SerwistProvider } from './components/provider/serwist'
import { PWAProvider } from './components/provider/serwist'
import SentryInitializer from './components/sentry-initializer'
import RoutePrefixHandle from './routePrefixHandle'
import './styles/globals.css'
@@ -40,9 +40,6 @@ const LocaleLayout = async ({
}) => {
const locale = await getLocaleOnServer()
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
const swUrl = `${basePath}/serwist/sw.js`
const datasetMap: Record<DatasetAttr, string | undefined> = {
[DatasetAttr.DATA_API_PREFIX]: process.env.NEXT_PUBLIC_API_PREFIX,
[DatasetAttr.DATA_PUBLIC_API_PREFIX]: process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
@@ -96,7 +93,7 @@ const LocaleLayout = async ({
className="color-scheme h-full select-auto"
{...datasetMap}
>
<SerwistProvider swUrl={swUrl}>
<PWAProvider>
<ReactScanLoader />
<JotaiProvider>
<ThemeProvider
@@ -124,7 +121,7 @@ const LocaleLayout = async ({
</ThemeProvider>
</JotaiProvider>
<RoutePrefixHandle />
</SerwistProvider>
</PWAProvider>
</body>
</html>
)