-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
da90c17
commit ccf0c18
Showing
11 changed files
with
1,247 additions
and
406 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
const axios = require('axios') | ||
|
||
// 发送 Notion API 请求 | ||
async function postNotion(properties, databaseId, listContentMain, token) { | ||
const url = 'https://api.notion.com/v1/pages' | ||
|
||
const children = listContentMain | ||
.map(contentMain => { | ||
if (contentMain.type === 'paragraph') { | ||
return { | ||
object: 'block', | ||
type: 'paragraph', | ||
paragraph: { | ||
rich_text: [ | ||
{ type: 'text', text: { content: contentMain.content } } | ||
] | ||
} | ||
} | ||
} else if (['file', 'image'].includes(contentMain.type)) { | ||
return { | ||
object: 'block', | ||
type: contentMain.type, | ||
[contentMain.type]: { | ||
type: 'external', | ||
external: { url: contentMain.content } | ||
} | ||
} | ||
} | ||
return null | ||
}) | ||
.filter(Boolean) | ||
|
||
const payload = { | ||
parent: { database_id: databaseId }, | ||
properties, | ||
children | ||
} | ||
|
||
const headers = { | ||
accept: 'application/json', | ||
'Notion-Version': '2022-06-28', | ||
'content-type': 'application/json', | ||
Authorization: `Bearer ${token}` | ||
} | ||
|
||
try { | ||
const response = await axios.post(url, payload, { headers }) | ||
return response | ||
} catch (error) { | ||
console.error('写入Notion异常', error) | ||
throw new Error(`Error posting to Notion: ${error.message}`) | ||
} | ||
} | ||
|
||
// 处理响应结果 | ||
function responseResult(response) { | ||
if (response.status === 200) { | ||
console.log('成功...') | ||
console.log(response.data) | ||
} else { | ||
console.log('失败...') | ||
console.log(response.data) | ||
} | ||
} | ||
|
||
// 准备属性字段 | ||
function notionProperty(id, avatar, name, mail, lastLoginTime, token) { | ||
return { | ||
id: { | ||
rich_text: [ | ||
{ | ||
type: 'text', | ||
text: { | ||
content: id, | ||
link: null | ||
} | ||
} | ||
] | ||
}, | ||
avatar: { | ||
files: [ | ||
{ | ||
name: 'Project Alpha blueprint', | ||
external: { | ||
url: avatar | ||
} | ||
} | ||
] | ||
}, | ||
name: { | ||
title: [ | ||
{ | ||
text: { | ||
content: name | ||
} | ||
} | ||
] | ||
}, | ||
mail: { | ||
email: mail | ||
}, | ||
last_login_time: { | ||
date: { | ||
start: lastLoginTime | ||
} | ||
}, | ||
token: { | ||
rich_text: [ | ||
{ | ||
type: 'text', | ||
text: { | ||
content: token, | ||
link: null | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,57 @@ | ||
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' | ||
import { NextResponse } from 'next/server' | ||
|
||
/** | ||
* clerk 身份验证中间件 | ||
* Clerk 身份验证中间件 | ||
*/ | ||
export const config = { | ||
// 这里设置白名单,防止静态资源被拦截 | ||
matcher: ['/((?!.*\\..*|_next|/sign-in).*)', '/', '/(api|trpc)(.*)'] | ||
matcher: ['/((?!.*\\..*|_next|/sign-in|/auth).*)', '/', '/(api|trpc)(.*)'] | ||
} | ||
|
||
// 被保护的路由 | ||
|
||
// 限制登录访问的路由 | ||
const isTenantRoute = createRouteMatcher([ | ||
'/user/organization-selector(.*)', | ||
'/user/orgid/(.*)' | ||
]) | ||
|
||
// 被限制权限的路由 | ||
// 限制权限访问的路由 | ||
const isTenantAdminRoute = createRouteMatcher([ | ||
'/admin/(.*)/memberships', | ||
'/admin/(.*)/domain' | ||
]) | ||
|
||
// 路由登录及权限检查 | ||
export default clerkMiddleware( | ||
(auth, req) => { | ||
// Restrict admin routes to users with specific permissions | ||
if (isTenantAdminRoute(req)) { | ||
auth().protect(has => { | ||
return ( | ||
has({ permission: 'org:sys_memberships:manage' }) || | ||
has({ permission: 'org:sys_domains_manage' }) | ||
) | ||
}) | ||
} | ||
// Restrict organization routes to signed in users | ||
if (isTenantRoute(req)) auth().protect() | ||
} | ||
// { debug: process.env.npm_lifecycle_event === 'dev' } // 开发调试模式打印日志 | ||
) | ||
/** | ||
* 没有配置权限相关功能的返回 | ||
* @param req | ||
* @param ev | ||
* @returns | ||
*/ | ||
const noAuthMiddleware = async (req, ev) => { | ||
// 如果没有配置 Clerk 相关环境变量,返回一个默认响应或者继续处理请求 | ||
return NextResponse.next() | ||
} | ||
|
||
/** | ||
* 鉴权中间件 | ||
*/ | ||
const authMiddleware = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | ||
? clerkMiddleware( | ||
(auth, req) => { | ||
// 限制管理员路由访问权限 | ||
if (isTenantAdminRoute(req)) { | ||
auth().protect(has => { | ||
return ( | ||
has({ permission: 'org:sys_memberships:manage' }) || | ||
has({ permission: 'org:sys_domains_manage' }) | ||
) | ||
}) | ||
} | ||
// 限制组织路由访问权限 | ||
if (isTenantRoute(req)) auth().protect() | ||
} | ||
// { debug: process.env.npm_lifecycle_event === 'dev' } // 开发调试模式打印日志 | ||
) | ||
: noAuthMiddleware | ||
|
||
export default authMiddleware |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// pages/api/auth.js | ||
import axios from 'axios' | ||
import type { NextApiRequest, NextApiResponse } from 'next' | ||
|
||
/** | ||
* Notion授权返回结果 | ||
*/ | ||
export interface NotionTokenResponseData { | ||
access_token: string | ||
token_type: string | ||
bot_id: string | ||
workspace_name: string | ||
workspace_icon: string | ||
workspace_id: string | ||
owner: { | ||
type: string | ||
user: { | ||
object: string | ||
id: string | ||
name: string | ||
avatar_url: string | ||
type: string | ||
person: { | ||
email: string | ||
} | ||
} | ||
} | ||
duplicated_template_id: string | null | ||
request_id: string | ||
} | ||
|
||
export interface NotionTokenResponse { | ||
status: number | ||
statusText: string | ||
data: NotionTokenResponseData | ||
} | ||
|
||
/** | ||
* Notion授权回调 | ||
* @param req | ||
* @param res | ||
* @returns | ||
*/ | ||
export default async function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
try { | ||
const code = Array.isArray(req.query.code) | ||
? req.query.code[0] | ||
: req.query.code | ||
|
||
if (!code) { | ||
return res.status(400).json({ error: 'Invalid request, code is missing' }) | ||
} | ||
|
||
const params = await fetchToken(code) | ||
|
||
if (params?.status === 200) { | ||
const redirectQuery = { | ||
msg: '成功了' + JSON.stringify(params.data) | ||
} | ||
|
||
// 这里将用户数据写入到Notion数据库 | ||
res.redirect(302, `/auth/result?${new URLSearchParams(redirectQuery)}`) | ||
} else { | ||
const redirectQuery = { msg: params?.statusText || '请求异常' } | ||
res.redirect( | ||
302, | ||
`/auth/result?${new URLSearchParams(redirectQuery).toString()}` | ||
) | ||
} | ||
} catch (error) { | ||
console.error(error) | ||
res.status(500).json({ error: 'Internal Server Error' }) | ||
} | ||
} | ||
/** | ||
* 获取token | ||
* @param code | ||
* @returns | ||
*/ | ||
const fetchToken = async (code: string): Promise<NotionTokenResponse> => { | ||
const clientId = process.env.OAUTH_CLIENT_ID | ||
const clientSecret = process.env.OAUTH_CLIENT_SECRET | ||
const redirectUri = process.env.OAUTH_REDIRECT_URI | ||
const encoded = Buffer.from(`${clientId}:${clientSecret}`).toString('base64') | ||
|
||
try { | ||
const response = await axios.post<NotionTokenResponseData>( | ||
'https://api.notion.com/v1/oauth/token', | ||
{ | ||
grant_type: 'authorization_code', | ||
code: code, | ||
redirect_uri: redirectUri | ||
}, | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
Authorization: `Basic ${encoded}` | ||
} | ||
} | ||
) | ||
console.log('OAuth身份信息', response.data) | ||
return { | ||
status: response.status, | ||
statusText: response.statusText, | ||
data: response.data | ||
} | ||
} catch (error) { | ||
console.error('Error fetching token', error) | ||
return null | ||
} | ||
} |
Oops, something went wrong.