read settings

This commit is contained in:
Hongbo Wu 2024-03-14 21:36:47 +08:00
parent 69e74d432d
commit 1085dcc824
4 changed files with 125 additions and 107 deletions

View file

@ -20,7 +20,7 @@ export interface RetrieveRequest {
export interface IntegrationClient {
name: string
_token: string
token: string
accessToken(): Promise<string | null>

View file

@ -5,6 +5,7 @@ import { Integration } from '../../entity/integration'
import { LibraryItem } from '../../entity/library_item'
import { env } from '../../env'
import { Merge } from '../../util'
import { highlightUrl } from '../../utils/helpers'
import { logger } from '../../utils/logger'
import { IntegrationClient } from './integration'
@ -91,35 +92,39 @@ interface NotionPage {
}>
}
type Property = 'highlights' | 'labels' | 'notes'
interface Settings {
parentPageId: string
parentDatabaseId: string
properties: Property[]
}
export class NotionClient implements IntegrationClient {
name = 'NOTION'
_headers = {
token: string
private headers = {
'Content-Type': 'application/json',
Accept: 'application/json',
'Notion-Version': '2022-06-28',
}
_timeout = 5000 // 5 seconds
_axios = axios.create({
private timeout = 5000 // 5 seconds
private axiosInstance = axios.create({
baseURL: 'https://api.notion.com/v1',
timeout: this._timeout,
timeout: this.timeout,
})
_token: string
_client: Client
_integrationData?: Merge<Integration, { settings?: Settings }>
private client: Client
private integrationData?: Merge<Integration, { settings?: Settings }>
constructor(token: string, integration?: Integration) {
this._token = token
this._client = new Client({
this.token = token
this.client = new Client({
auth: token,
timeoutMs: this._timeout,
timeoutMs: this.timeout,
})
this._integrationData = integration
this.integrationData = integration
}
accessToken = async (): Promise<string | null> => {
@ -129,16 +134,16 @@ export class NotionClient implements IntegrationClient {
`${env.notion.clientId}:${env.notion.clientSecret}`
).toString('base64')
const response = await this._axios.post<{ access_token: string }>(
const response = await this.axiosInstance.post<{ access_token: string }>(
'/oauth/token',
{
grant_type: 'authorization_code',
code: this._token,
code: this.token,
redirect_uri: `${env.client.url}/settings/integrations`,
},
{
headers: {
...this._headers,
...this.headers,
Authorization: `Basic ${encoded}`,
},
}
@ -160,90 +165,101 @@ export class NotionClient implements IntegrationClient {
private itemToNotionPage = (
item: LibraryItem,
databaseId: string
): NotionPage => ({
parent: {
database_id: databaseId,
},
icon: item.siteIcon
? {
external: {
url: item.siteIcon,
},
}
: undefined,
cover: item.thumbnail
? {
external: {
url: item.thumbnail,
},
}
: undefined,
properties: {
Title: {
title: [
{
text: {
content: item.title,
settings: Settings
): NotionPage => {
return {
parent: {
database_id: settings.parentDatabaseId,
},
icon: item.siteIcon
? {
external: {
url: item.siteIcon,
},
},
],
},
Author: {
rich_text: [
{
text: {
content: item.author || 'unknown',
},
},
],
},
'Original URL': {
url: item.originalUrl,
},
'Omnivore URL': {
url: `${env.client.url}/me/${item.slug}`,
},
Tags: item.labels
? { multi_select: item.labels.map((label) => ({ name: label.name })) }
}
: undefined,
},
children: item.highlights
? item.highlights.map((highlight) => ({
type: 'paragraph',
paragraph: {
rich_text: [
{
text: {
content: highlight.quote || '',
link: {
url: `${env.client.url}/me/${item.slug}#${highlight.id}`,
cover: item.thumbnail
? {
external: {
url: item.thumbnail,
},
}
: undefined,
properties: {
Title: {
title: [
{
text: {
content: item.title,
},
},
],
},
Author: {
rich_text: [
{
text: {
content: item.author || 'unknown',
},
},
],
},
'Original URL': {
url: item.originalUrl,
},
'Omnivore URL': {
url: `${env.client.url}/me/${item.slug}`,
},
Tags:
item.labels && settings.properties.includes('labels')
? {
multi_select: item.labels.map((label) => ({
name: label.name,
})),
}
: undefined,
},
children: item.highlights
? item.highlights.map((highlight) => ({
type: 'paragraph',
paragraph: {
rich_text: [
{
text: {
content: settings.properties.includes('highlights')
? highlight.quote || ''
: '',
link: {
url: highlightUrl(item.slug, highlight.id),
},
},
annotations: {
color: highlight.color as AnnotationColor,
},
},
annotations: {
color: highlight.color as AnnotationColor,
{
text: {
content: settings.properties.includes('notes')
? `\n${highlight.annotation || ''}`
: '',
},
annotations: {
italic: true,
},
},
},
{
text: {
content: `\n${highlight.annotation || ''}`,
},
annotations: {
italic: true,
},
},
],
},
}))
: undefined,
})
],
},
}))
: undefined,
}
}
private createPage = async (page: NotionPage) => {
await this._client.pages.create(page)
await this.client.pages.create(page)
}
private findPage = async (url: string, databaseId: string) => {
const response = await this._client.databases.query({
const response = await this.client.databases.query({
database_id: databaseId,
page_size: 1,
filter: {
@ -261,21 +277,22 @@ export class NotionClient implements IntegrationClient {
}
export = async (items: LibraryItem[]): Promise<boolean> => {
if (!this._integrationData || !this._integrationData.settings) {
const settings = this.integrationData?.settings
if (!this.integrationData || !settings) {
logger.error('Notion integration data not found')
return false
}
const pageId = this._integrationData.settings.parentPageId
const pageId = settings.parentPageId
if (!pageId) {
logger.error('Notion parent page id not found')
return false
}
let databaseId = this._integrationData.settings.parentDatabaseId
let databaseId = settings.parentDatabaseId
if (!databaseId) {
// create a database for the items
const database = await this._client.databases.create({
const database = await this.client.databases.create({
parent: {
page_id: pageId,
},
@ -315,33 +332,33 @@ export class NotionClient implements IntegrationClient {
// save the database id
databaseId = database.id
await updateIntegration(
this._integrationData.id,
this.integrationData.id,
{
settings: {
...this._integrationData.settings,
...this.integrationData.settings,
parentDatabaseId: databaseId,
},
},
this._integrationData.user.id
this.integrationData.user.id
)
}
await Promise.all(
items.map(async (item) => {
const notionPage = this.itemToNotionPage(item, databaseId)
const notionPage = this.itemToNotionPage(item, settings)
const url = notionPage.properties['Omnivore URL'].url
const existingPage = await this.findPage(url, databaseId)
if (existingPage) {
// update the page
await this._client.pages.update({
await this.client.pages.update({
page_id: existingPage.id,
properties: notionPage.properties,
})
// append the children
if (notionPage.children) {
await this._client.blocks.children.append({
await this.client.blocks.children.append({
block_id: existingPage.id,
children: notionPage.children,
})

View file

@ -5,7 +5,7 @@ import { IntegrationClient } from './integration'
export class PocketClient implements IntegrationClient {
name = 'POCKET'
_token: string
token: string
_axios = axios.create({
baseURL: 'https://getpocket.com/v3',
headers: {
@ -16,7 +16,7 @@ export class PocketClient implements IntegrationClient {
})
constructor(token: string) {
this._token = token
this.token = token
}
accessToken = async (): Promise<string | null> => {
@ -25,7 +25,7 @@ export class PocketClient implements IntegrationClient {
'/oauth/authorize',
{
consumer_key: env.pocket.consumerKey,
code: this._token,
code: this.token,
}
)
return response.data.access_token

View file

@ -33,6 +33,8 @@ interface ReadwiseHighlight {
export class ReadwiseClient implements IntegrationClient {
name = 'READWISE'
token: string
_headers = {
'Content-Type': 'application/json',
}
@ -40,10 +42,9 @@ export class ReadwiseClient implements IntegrationClient {
baseURL: 'https://readwise.io/api/v2',
timeout: 5000, // 5 seconds
})
_token: string
constructor(token: string) {
this._token = token
this.token = token
}
accessToken = async (): Promise<string | null> => {
@ -51,10 +52,10 @@ export class ReadwiseClient implements IntegrationClient {
const response = await this._axios.get('/auth', {
headers: {
...this._headers,
Authorization: `Token ${this._token}`,
Authorization: `Token ${this.token}`,
},
})
return response.status === 204 ? this._token : null
return response.status === 204 ? this.token : null
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error(error.response)
@ -121,7 +122,7 @@ export class ReadwiseClient implements IntegrationClient {
{
headers: {
...this._headers,
Authorization: `Token ${this._token}`,
Authorization: `Token ${this.token}`,
},
}
)