Skip to content

Commit a4fdf54

Browse files
author
james_tsai
committed
feat: add getMe api
1 parent 412a754 commit a4fdf54

4 files changed

Lines changed: 137 additions & 252 deletions

File tree

nodejs/package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nodejs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"typescript": "^3.8.3"
3232
},
3333
"dependencies": {
34+
"axios": "^0.25.0",
3435
"cheerio": "^1.0.0-rc.3",
3536
"fetch-cookie": "^0.8.0",
3637
"fs-extra": "^9.0.0",

nodejs/src/index.ts

Lines changed: 32 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -1,262 +1,42 @@
1-
import cheerio from 'cheerio'
2-
import * as fs from 'fs-extra'
3-
import {homedir} from 'os'
4-
import * as path from 'path'
5-
import nodeFetch from 'node-fetch'
6-
import tough = require('tough-cookie')
7-
import FileCookieStore from 'tough-cookie-filestore'
8-
import * as url from 'url'
9-
10-
import { defaults } from './utils'
11-
12-
let version = ''
13-
try {
14-
version = require('../package.json').version
15-
} catch (err) {}
16-
17-
const defaultCookiePath = path.join(homedir(), '.hackmd', 'cookies.json')
18-
19-
const defaultConfig = {
20-
cookiePath: defaultCookiePath,
21-
serverUrl: 'https://hackmd.io',
22-
enterprise: true
23-
}
24-
25-
interface APIOptions {
26-
serverUrl: string
27-
cookiePath: string,
28-
enterprise: boolean
29-
}
30-
31-
type nodeFetchType = (url: RequestInfo, init?: RequestInit | undefined) => Promise<Response>
32-
33-
function encodeFormComponent(form: object) {
34-
return Object.entries(form).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&')
35-
}
36-
37-
export enum ExportType {
38-
PDF,
39-
SLIDE,
40-
MD,
41-
HTML
42-
}
43-
44-
export type HistoryItem = {
45-
id: string
46-
text: string
47-
time: number | string
48-
tags: string[]
49-
}
50-
51-
export type NewNoteOption = {
52-
team: string
53-
}
54-
55-
/**
56-
* codimd API Client
57-
*/
58-
class API {
59-
public readonly serverUrl: string
60-
public readonly enterprise: boolean
61-
private readonly _fetch: nodeFetchType
62-
63-
constructor(config: Partial<APIOptions> = {}) {
64-
const {serverUrl, cookiePath, enterprise} = defaults(config, defaultConfig)
65-
66-
fs.ensureFileSync(cookiePath)
67-
68-
const jar = new FileCookieStore(cookiePath)
69-
const fetch: nodeFetchType = require('fetch-cookie')(nodeFetch, new tough.CookieJar(jar as any))
70-
71-
this._fetch = fetch
72-
this.serverUrl = url.parse(serverUrl).href
73-
this.enterprise = enterprise
74-
}
75-
76-
async login(email: string, password: string) {
77-
await this.fetch(url.resolve(this.serverUrl, 'login'), {
78-
method: 'post',
79-
body: encodeFormComponent({email, password}),
80-
headers: await this.wrapHeaders({
81-
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
82-
})
83-
})
84-
}
85-
86-
async loginLdap(username: string, password: string) {
87-
await this.fetch(url.resolve(this.serverUrl, 'auth/ldap'), {
88-
method: 'post',
89-
body: encodeFormComponent({username, password}),
90-
headers: await this.wrapHeaders({
91-
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
92-
})
93-
})
94-
}
95-
96-
async logout() {
97-
const response = await this.fetch(url.resolve(this.serverUrl, 'logout'), {
98-
method: this.enterprise ? 'POST' : 'GET',
99-
headers: await this.wrapHeaders({
100-
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
101-
})
1+
import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios'
2+
import { User } from './type'
3+
4+
export class API {
5+
public axios: AxiosInstance
6+
constructor(accessToken: string) {
7+
this.axios = axios.create({
8+
baseURL: "http://localhost:3000/v1/api",
9+
headers:{
10+
"Content-Type": "application.json",
11+
}
10212
})
103-
return response.status === 200
104-
}
105-
106-
async isLogin() {
107-
try {
108-
const data = await this.getMe()
109-
return data.status === 'ok'
110-
} catch (err) {
111-
return false
112-
}
113-
}
114-
115-
async getMe() {
116-
const response = await this.fetch(url.resolve(this.serverUrl, 'me'), this.defaultFetchOptions)
117-
return response.json()
118-
}
119-
120-
async getHistory(): Promise<{ history: HistoryItem[] }> {
121-
const response = await this.fetch(url.resolve(this.serverUrl, 'history'), this.defaultFetchOptions)
122-
return response.json()
123-
}
124-
125-
async newNote(body: string, options?: NewNoteOption) {
126-
let response
127-
if (this.enterprise) {
128-
let newNoteUrl
129-
if (options?.team) {
130-
newNoteUrl = url.resolve(this.serverUrl, `team/${options.team}/new`)
131-
} else {
132-
newNoteUrl = url.resolve(this.serverUrl, 'new')
13+
this.axios.interceptors.request.use(
14+
(config: AxiosRequestConfig) =>{
15+
if (!config.headers) {
16+
config.headers = {};
17+
}
18+
19+
if (accessToken) {
20+
config.headers.Authorization = `Bearer ${accessToken}`;
21+
}
22+
23+
return config
24+
},
25+
(err: AxiosError) => {
26+
return Promise.reject(err)
13327
}
134-
135-
response = await this.fetch(newNoteUrl, {
136-
method: 'POST',
137-
body: encodeFormComponent({content: body}),
138-
headers: await this.wrapHeaders({
139-
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
140-
})
141-
})
142-
} else {
143-
const contentType = 'text/markdown;charset=UTF-8'
144-
response = await this.fetch(url.resolve(this.serverUrl, 'new'), {
145-
method: 'POST',
146-
body,
147-
headers: await this.wrapHeaders({
148-
'Content-Type': contentType
149-
})
150-
})
151-
}
152-
153-
if (response.status === 200) {
154-
return response.url
155-
} else {
156-
throw new Error('Create note failed')
157-
}
158-
}
159-
160-
async listNotes () {
161-
// https://hackmd.io/api/overview?v=1627369059523
162-
if (this.enterprise) {
163-
const response = await this.fetch(url.resolve(this.serverUrl, 'api/overview'), this.defaultFetchOptions)
164-
165-
return response.json()
166-
} else {
167-
throw new Error('Not support')
168-
}
28+
)
16929
}
17030

171-
async listTeamNotes (teamPath: string) {
172-
if (this.enterprise) {
173-
return this.fetch(url.resolve(this.serverUrl, `api/overview/team/${teamPath}`), this.defaultFetchOptions).then(res => res.json())
174-
} else {
175-
throw new Error('Not support')
176-
}
177-
}
178-
179-
private async exportRes(noteId: string, type: ExportType) {
180-
let res: Response
181-
switch (type) {
182-
case ExportType.PDF:
183-
res = await this.fetch(url.resolve(this.serverUrl, `${noteId}/pdf`), this.defaultFetchOptions)
184-
break
185-
case ExportType.HTML:
186-
res = await this.fetch(url.resolve(this.serverUrl, `s/${noteId}`), this.defaultFetchOptions)
187-
break
188-
case ExportType.SLIDE:
189-
res = await this.fetch(url.resolve(this.serverUrl, `${noteId}/slide`), this.defaultFetchOptions)
190-
break
191-
case ExportType.MD:
192-
default:
193-
res = await this.fetch(url.resolve(this.serverUrl, `${noteId}/download`), this.defaultFetchOptions)
194-
}
195-
196-
return res
197-
}
198-
199-
async exportString(noteId: string, type: ExportType) {
200-
const res = await this.exportRes(noteId, type)
201-
202-
return res.text()
203-
}
204-
205-
async exportStream (noteId: string, type: ExportType) {
206-
const res = await this.exportRes(noteId, type)
207-
208-
return res.body
209-
}
210-
211-
async getTeams () {
212-
let data
213-
try {
214-
data = await this.getMe()
215-
} catch (err) {
216-
return []
217-
}
218-
219-
return data.teams || []
220-
}
221-
222-
get fetch() {
223-
return this._fetch
224-
}
225-
226-
get domain() {
227-
return url.parse(this.serverUrl).host
31+
getMe = async () => {
32+
const { data } = await this.axios.get<User>("/me")
33+
return data
22834
}
35+
}
22936

230-
get defaultFetchOptions () {
231-
return {
232-
headers: {
233-
'User-Agent': `HackMD API Client ${version} Node.js`
234-
}
235-
}
236-
}
37+
const api = new API("60WUTLO5DIHIOHCIY9TO0QEDVATU55XBTE7JJJBX9UTG49M7Y1")
38+
api.getMe()
23739

238-
private async wrapHeaders(headers: any) {
239-
if (this.enterprise) {
240-
const csrf = await this.loadCSRFToken()
241-
return {
242-
...headers,
243-
'User-Agent': `HackMD API Client ${version} Node.js`,
244-
'X-XSRF-Token': csrf
245-
}
246-
} else {
247-
return {
248-
...headers,
249-
'User-Agent': `HackMD API Client ${version} Node.js`
250-
}
251-
}
252-
}
25340

254-
private async loadCSRFToken() {
255-
const html = await this.fetch(this.serverUrl).then(r => r.text())
256-
const $ = cheerio.load(html)
25741

258-
return $('meta[name="csrf-token"]').attr('content') || ''
259-
}
260-
}
26142

262-
export default API

0 commit comments

Comments
 (0)