Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d7c50dc
chore: bump the dev-dependencies group with 11 updates
dependabot[bot] May 25, 2026
d517b15
chore: bump @clerk/localizations from 3.17.1 to 3.37.6
dependabot[bot] May 25, 2026
f52e903
chore: bump @supabase/supabase-js from 2.106.0 to 2.106.2
dependabot[bot] May 25, 2026
a58200d
Merge pull request #4096 from notionnext-org/dependabot/npm_and_yarn/…
tangly1024 May 26, 2026
1a4f536
Merge pull request #4095 from notionnext-org/dependabot/npm_and_yarn/…
tangly1024 May 26, 2026
4edfe60
Merge pull request #4094 from notionnext-org/dependabot/npm_and_yarn/…
tangly1024 May 26, 2026
179950c
ci: create PR for automatic version bumps
May 26, 2026
9a83ca0
fix: resolve qs to patched version
May 26, 2026
a0b953e
Merge pull request #4 from XiaoFeng9009/XiaoFeng9009-patch-base
XiaoFeng9009 May 26, 2026
aa416a2
chore(release): bump package.json to 4.9.5.4 [skip-version]
github-actions[bot] May 26, 2026
7b77d22
Merge branch 'notionnext-org:main' into main
XiaoFeng9009 May 26, 2026
63d1953
chore(release): bump package.json to 4.9.5.5 [skip-version]
github-actions[bot] May 26, 2026
be6420b
Merge pull request #4104 from notionnext-org/codex/fix-version-bump-w…
tangly1024 May 26, 2026
2c922a8
Merge pull request #4105 from notionnext-org/codex/fix-qs-resolution
tangly1024 May 26, 2026
4e031e9
ci: push version bump branch without creating PR
May 26, 2026
a58b539
Merge pull request #4106 from notionnext-org/codex/fix-version-bump-b…
tangly1024 May 26, 2026
ca9e9a7
chore(release): bump package.json to 4.9.5.4 [skip-version]
github-actions[bot] May 26, 2026
bcd5a4a
Merge pull request #4107 from notionnext-org/chore/bump-package-version
tangly1024 May 26, 2026
cebd234
fix: resolve patched transitive dependencies
May 26, 2026
9331e87
Merge pull request #4108 from notionnext-org/codex/fix-open-security-…
tangly1024 May 26, 2026
abdf5b2
chore(release): bump package.json to 4.9.5.5
tangly1024 May 26, 2026
cb3302e
ci: trigger checks for version bump
tangly1024 May 26, 2026
8b10fff
chore(release): bump package.json to 4.9.5.5 [skip-version]
tangly1024 May 26, 2026
52fef9d
fix(endspace): support custom nested menus
tangly1024 May 26, 2026
bf2e060
Merge branch 'main' into codex/fix-endspace-custom-menu
tangly1024 May 26, 2026
97d59ef
Merge pull request #4112 from notionnext-org/codex/fix-endspace-custo…
tangly1024 May 26, 2026
e9af046
feat: add community backend support — Member & Event data types
qianzhu18 May 26, 2026
fe69af8
Merge branch 'notionnext-org:main' into main
XiaoFeng9009 May 27, 2026
d6b2689
fix: wire community member data source
tangly1024 May 27, 2026
2a0ecd1
Merge pull request #4113 from qianzhu18/feat/community-backend
tangly1024 May 27, 2026
89ac165
chore(release): bump package.json to 4.9.5.6
tangly1024 May 27, 2026
41ca6db
chore(release): bump package.json to 4.9.5.6 [skip-version]
tangly1024 May 27, 2026
0bdbc1f
fix: resolve tmp to patched version
tangly1024 May 27, 2026
38b4ee9
Merge pull request #4115 from notionnext-org/codex/fix-tmp-resolution
tangly1024 May 27, 2026
c2a6a2d
chore(release): bump package.json to 4.9.5.7
tangly1024 May 27, 2026
44b735f
chore(release): bump package.json to 4.9.5.7 [skip-version]
tangly1024 May 27, 2026
3c16f54
Merge branch 'notionnext-org:main' into main
XiaoFeng9009 May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions .github/workflows/bump-version-on-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,27 @@ jobs:
fetch-depth: 0

- name: Bump package.json patch (last segment)
run: node scripts/bump-package-patch-version.js
id: bump
run: |
node scripts/bump-package-patch-version.js
VERSION=$(node -p "require('./package.json').version")
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Commit and push if changed
- name: Push version bump branch
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git diff --quiet package.json; then
echo "No version change (unexpected)."
exit 0
fi
NEW_VER=$(node -p "require('./package.json').version")
BRANCH="chore/bump-package-version"
git checkout -B "${BRANCH}"
git add package.json
git commit -m "chore(release): bump package.json to ${NEW_VER} [skip-version]"
git push
git commit -m "chore(release): bump package.json to ${{ steps.bump.outputs.version }} [skip-version]"
git push --force-with-lease origin "${BRANCH}"
{
echo "Version bump branch pushed: \`${BRANCH}\`"
echo ""
echo "Create a pull request from \`${BRANCH}\` to \`main\` to publish version ${{ steps.bump.outputs.version }}."
} >> "$GITHUB_STEP_SUMMARY"
89 changes: 89 additions & 0 deletions __tests__/lib/db/notion/memberDataSource.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {
fetchMembersFromOfficialAPI,
mapOfficialMemberPage
} from '@/lib/db/notion/memberDataSource'

const originalEnv = process.env
const originalFetch = global.fetch

function memberPage({ id, title, slug, status = 'Published' }) {
return {
id,
created_time: '2026-01-01T00:00:00.000Z',
last_edited_time: '2026-01-02T00:00:00.000Z',
properties: {
title: { type: 'title', title: [{ plain_text: title }] },
slug: { type: 'rich_text', rich_text: [{ plain_text: slug }] },
status: { type: 'select', select: { name: status } },
featured: { type: 'checkbox', checkbox: true }
}
}
}

describe('memberDataSource', () => {
beforeEach(() => {
process.env = { ...originalEnv }
global.fetch = jest.fn()
})

afterEach(() => {
process.env = originalEnv
global.fetch = originalFetch
})

it('maps an official Notion member page to site data', () => {
const mapped = mapOfficialMemberPage(
memberPage({ id: 'member-1', title: 'Ada', slug: 'ada' })
)

expect(mapped).toMatchObject({
id: 'member-1',
title: 'Ada',
slug: 'members/ada',
href: '/members/ada',
type: 'Member',
status: 'Published',
featured: true
})
})

it('paginates official API results and filters unpublished members', async () => {
process.env.NOTION_API_TOKEN = 'secret'
process.env.NOTION_MEMBERS_DATA_SOURCE_ID = 'source-id'

global.fetch
.mockResolvedValueOnce({
ok: true,
json: async () => ({
has_more: true,
next_cursor: 'cursor-2',
results: [
memberPage({ id: 'member-1', title: 'Ada', slug: 'ada' })
]
})
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
has_more: false,
next_cursor: null,
results: [
memberPage({
id: 'member-2',
title: 'Draft',
slug: 'draft',
status: 'Invisible'
})
]
})
})

const members = await fetchMembersFromOfficialAPI()

expect(global.fetch).toHaveBeenCalledTimes(2)
expect(JSON.parse(global.fetch.mock.calls[1][1].body)).toMatchObject({
start_cursor: 'cursor-2'
})
expect(members.map(member => member.id)).toEqual(['member-1'])
})
})
4 changes: 4 additions & 0 deletions conf/notion.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ module.exports = {
type_menu: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU || 'Menu', // 当type文章类型与此值相同时,为菜单。
type_sub_menu:
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。
type_member:
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MEMBER || 'Member', // 社区成员资料
type_event:
process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_EVENT || 'Event', // 社区活动
title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title', // 文章标题
status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status',
status_publish:
Expand Down
69 changes: 69 additions & 0 deletions lib/db/SiteDataApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { fetchPageFromNotion } from './notion/getNotionPost'
import { processPostData } from '../utils/post'
import { adapterNotionBlockMap } from '../utils/notion.util'
import { sortPinnedPostsByLatestUpdate } from '@/lib/utils/pinnedPosts'
import { fetchMembersFromOfficialAPI } from './notion/memberDataSource'
// import pLimit from 'p-limit'

export { getAllTags } from './notion/getAllTags'
Expand Down Expand Up @@ -182,6 +183,8 @@ const EmptyData = pageId => ({
rawMetadata: {},
customNav: [],
customMenu: [],
allMembers: [],
allEvents: [],
postCount: 1,
pageIds: [],
latestPosts: []
Expand Down Expand Up @@ -393,6 +396,26 @@ async function convertNotionToSiteData(
)
}
collectionData.forEach(element => adjustPageProperties(element, NOTION_CONFIG))

const officialMembers = await fetchMembersFromOfficialAPI({
typeProperty: BLOG.NOTION_PROPERTY_NAME.type,
statusProperty: BLOG.NOTION_PROPERTY_NAME.status,
typeValue: BLOG.NOTION_PROPERTY_NAME.type_member,
statusValue: BLOG.NOTION_PROPERTY_NAME.status_publish
})
if (officialMembers.length > 0) {
const existingMembers = new Set(
collectionData
.filter(item => item?.type === 'Member')
.flatMap(item => [item.id, item.slug].filter(Boolean))
)
officialMembers.forEach(member => {
if (!existingMembers.has(member.id) && !existingMembers.has(member.slug)) {
collectionData.push(member)
}
})
}

const siteInfo = getSiteInfo({ collection, block, rawMetadata, NOTION_CONFIG })
const stepEnd9 = Date.now()
console.log(`[${traceId}] ⏱ 配置站点信息耗时: ${stepEnd9 - stepStart9}ms @ ${new Date().toISOString()}`)
Expand Down Expand Up @@ -432,6 +455,11 @@ async function convertNotionToSiteData(
latestPostCount: siteConfig('LATEST_POST_COUNT', 6, NOTION_CONFIG)
})
const allNavPages = getNavPages({ allPages })

// ── 社区数据:Member / Event ──
const allMembers = getAllMembers({ allPages })
const allEvents = getAllEvents({ allPages })

const stepEnd11 = Date.now()
console.log(`[${traceId}] ⏱ 其他数据生成耗时: ${stepEnd11 - stepStart11}ms @ ${new Date().toISOString()}`)
const overallEnd = Date.now()
Expand All @@ -442,6 +470,8 @@ async function convertNotionToSiteData(
notice,
siteInfo,
allPages,
allMembers,
allEvents,
allNavPages,
collection,
collectionQuery,
Expand Down Expand Up @@ -490,16 +520,20 @@ function handleDataBeforeReturn(db) {
db.tagOptions = cleanTagOptions(db?.tagOptions)
db.allNavPages = cleanPages(db?.allNavPages, db.tagOptions)
db.allPages = cleanPages(db.allPages, db.tagOptions)
db.allMembers = cleanPages(db.allMembers, db.tagOptions)
db.allEvents = cleanPages(db.allEvents, db.tagOptions)
db.latestPosts = cleanPages(db.latestPosts, db.tagOptions)

// 定时发布:检查发布时间窗口,超出范围的隐藏
// 仅对 Post/Page 生效,Event 和 Member 不受此限制
const POST_SCHEDULE_PUBLISH = siteConfig(
'POST_SCHEDULE_PUBLISH',
null,
db.NOTION_CONFIG
)
if (POST_SCHEDULE_PUBLISH) {
db.allPages?.forEach(p => {
if (p.type === 'Event' || p.type === 'Member') return
if (!isInRange(p.title, p.date)) {
console.log('[定时发布] 隐藏-->', p.title, p.date)
p.status = 'Invisible'
Expand Down Expand Up @@ -799,3 +833,38 @@ export function getNavPages({ allPages }) {
ext: item.ext || {}
}))
}

/**
* 获取所有已发布的社区成员
* 从 allPages 中筛选 type=Member && status=Published 的条目
*/
export function getAllMembers({ allPages }) {
if (!Array.isArray(allPages)) return []
return allPages
.filter(page => page?.type === 'Member' && page?.status === 'Published')
.sort((a, b) => {
// Featured 优先
const aFeatured = Boolean(a.featured)
const bFeatured = Boolean(b.featured)
if (aFeatured !== bFeatured) return bFeatured ? 1 : -1
// Verified 优先
const aVerified = Boolean(a.verified)
const bVerified = Boolean(b.verified)
if (aVerified !== bVerified) return bVerified ? 1 : -1
// sortOrder 升序
if (a.sortOrder != null && b.sortOrder != null) return a.sortOrder - b.sortOrder
// 发布时间倒序
return (b?.publishDate ?? 0) - (a?.publishDate ?? 0)
})
}

/**
* 获取所有已发布的社区活动
* 从 allPages 中筛选 type=Event && status=Published 的条目
*/
export function getAllEvents({ allPages }) {
if (!Array.isArray(allPages)) return []
return allPages
.filter(page => page?.type === 'Event' && page?.status === 'Published')
.sort((a, b) => (b?.publishDate ?? 0) - (a?.publishDate ?? 0))
}
4 changes: 3 additions & 1 deletion lib/db/notion/getPageProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ function mapProperties(properties) {
[BLOG.NOTION_PROPERTY_NAME.type_page]: 'Page',
[BLOG.NOTION_PROPERTY_NAME.type_notice]: 'Notice',
[BLOG.NOTION_PROPERTY_NAME.type_menu]: 'Menu',
[BLOG.NOTION_PROPERTY_NAME.type_sub_menu]: 'SubMenu'
[BLOG.NOTION_PROPERTY_NAME.type_sub_menu]: 'SubMenu',
[BLOG.NOTION_PROPERTY_NAME.type_member]: 'Member',
[BLOG.NOTION_PROPERTY_NAME.type_event]: 'Event'
}

const statusMap = {
Expand Down
Loading
Loading