Skip to content

Commit c1e7631

Browse files
authored
Merge pull request #9 from hackmdio/feature/ee-support
Support enterprise instance
2 parents c1875d3 + 7c3e2a7 commit c1e7631

File tree

6 files changed

+181
-43
lines changed

6 files changed

+181
-43
lines changed

src/api.ts

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import cheerio from 'cheerio'
12
import * as fs from 'fs-extra'
23
import nodeFetch from 'node-fetch'
34
import tough = require('tough-cookie')
@@ -8,7 +9,8 @@ import config from './config'
89

910
interface APIOptions {
1011
serverUrl: string
11-
cookiePath: string
12+
cookiePath: string,
13+
enterprise: boolean
1214
}
1315

1416
type nodeFetchType = (url: RequestInfo, init?: RequestInit | undefined) => Promise<Response>
@@ -36,10 +38,11 @@ export type HistoryItem = {
3638
*/
3739
class API {
3840
public readonly serverUrl: string
41+
private readonly enterprise: boolean
3942
private readonly _fetch: nodeFetchType
4043

41-
constructor() {
42-
const {serverUrl, cookiePath}: APIOptions = config
44+
constructor(config: APIOptions) {
45+
const {serverUrl, cookiePath, enterprise} = config
4346

4447
fs.ensureFileSync(cookiePath)
4548

@@ -48,32 +51,39 @@ class API {
4851

4952
this._fetch = fetch
5053
this.serverUrl = serverUrl
54+
this.enterprise = enterprise
5155
}
5256

5357
async login(email: string, password: string) {
5458
const response = await this.fetch(`${this.serverUrl}/login`, {
5559
method: 'post',
5660
body: encodeFormComponent({email, password}),
57-
headers: {
61+
headers: await this.wrapHeaders({
5862
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
59-
}
63+
})
6064
})
65+
6166
return response.status === 200
6267
}
6368

6469
async loginLdap(username: string, password: string) {
6570
const response = await this.fetch(`${this.serverUrl}/auth/ldap`, {
6671
method: 'post',
6772
body: encodeFormComponent({username, password}),
68-
headers: {
73+
headers: await this.wrapHeaders({
6974
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
70-
}
75+
})
7176
})
7277
return response.status === 200
7378
}
7479

7580
async logout() {
76-
const response = await this.fetch(`${this.serverUrl}/logout`)
81+
const response = await this.fetch(`${this.serverUrl}/logout`, {
82+
method: this.enterprise ? 'POST' : 'GET',
83+
headers: await this.wrapHeaders({
84+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
85+
})
86+
})
7787
return response.status === 200
7888
}
7989

@@ -93,14 +103,26 @@ class API {
93103
}
94104

95105
async newNote(body: string) {
96-
const contentType = 'text/markdown;charset=UTF-8'
97-
const response = await this.fetch(`${this.serverUrl}/new`, {
98-
method: 'POST',
99-
body,
100-
headers: {
101-
'Content-Type': contentType
102-
}
103-
})
106+
let response
107+
if (this.enterprise) {
108+
response = await this.fetch(`${this.serverUrl}/new`, {
109+
method: 'POST',
110+
body: encodeFormComponent({content: body}),
111+
headers: await this.wrapHeaders({
112+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
113+
})
114+
})
115+
} else {
116+
const contentType = 'text/markdown;charset=UTF-8'
117+
response = await this.fetch(`${this.serverUrl}/new`, {
118+
method: 'POST',
119+
body,
120+
headers: {
121+
'Content-Type': contentType
122+
}
123+
})
124+
}
125+
104126
if (response.status === 200) {
105127
return response.url
106128
} else {
@@ -149,8 +171,27 @@ class API {
149171
get domain() {
150172
return url.parse(this.serverUrl).host
151173
}
174+
175+
private async wrapHeaders(headers: any) {
176+
if (this.enterprise) {
177+
const csrf = await this.loadCSRFToken()
178+
return {
179+
...headers,
180+
'X-XSRF-Token': csrf
181+
}
182+
} else {
183+
return headers
184+
}
185+
}
186+
187+
private async loadCSRFToken() {
188+
const html = await this.fetch(`${this.serverUrl}`).then(r => r.text())
189+
const $ = cheerio.load(html)
190+
191+
return $('meta[name="csrf-token"]').attr('content') || ''
192+
}
152193
}
153194

154195
export default API
155196

156-
export const APIClient = new API()
197+
export const APIClient = new API(config)

src/commands/login.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Login as HMD successfully!
2828
let id = flags.id
2929

3030
if (!id) {
31-
if(flags.ldap) {
31+
if (flags.ldap) {
3232
const out = await inquirer.prompt({
3333
type: 'input',
3434
name: 'username',
@@ -60,7 +60,7 @@ Login as HMD successfully!
6060

6161
try {
6262
let success = false
63-
if(flags.ldap) {
63+
if (flags.ldap) {
6464
success = await APIClient.loginLdap(id, password)
6565
} else {
6666
success = await APIClient.login(id, password)

src/config.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import * as fs from 'fs-extra'
2-
import * as path from 'path'
3-
42
import defaults from 'lodash/defaults'
53
import {homedir} from 'os'
4+
import * as path from 'path'
65

76
let configDir
87
if (process.env.HMD_CLI_CONFIG_DIR) {
@@ -16,12 +15,25 @@ const defaultCookiePath = path.join(homedir(), '.codimd', 'cookies.json')
1615

1716
const defaultConfig = {
1817
cookiePath: defaultCookiePath,
19-
serverUrl: ''
18+
serverUrl: 'https://hackmd.io',
19+
enterprise: true
20+
}
21+
22+
function toBooleanConfig(configValue?: string | boolean) {
23+
if (configValue && typeof configValue === 'string') {
24+
return (configValue === 'true')
25+
}
26+
return configValue
2027
}
2128

2229
const envConfig = {
23-
cookiePath: process.env.CMD_CLI_COOKIE_PATH,
24-
serverUrl: process.env.CMD_CLI_SERVER_URL
30+
cookiePath: process.env.HMD_CLI_COOKIE_PATH || process.env.CMD_CLI_COOKIE_PATH,
31+
serverUrl: process.env.HMD_CLI_SERVER_URL || process.env.CMD_CLI_SERVER_URL,
32+
enterprise: (process.env.HMD_CLI_COOKIE_PATH || process.env.HMD_CLI_SERVER_URL)
33+
? true
34+
: (process.env.CMD_CLI_COOKIE_PATH || process.env.CMD_CLI_SERVER_URL)
35+
? false
36+
: toBooleanConfig(process.env.HMD_CLI_ENTERPRISE)
2537
}
2638

2739
// look for a readable config file; we can merge it with the env.
@@ -61,6 +73,7 @@ ${err}
6173
// prefer environment config over file config
6274
const config = defaults(envConfig, readConfig, defaultConfig)
6375

76+
// !FIXME This branching never meets because we have defaultConfig
6477
if (!config.serverUrl) {
6578
throw new Error(`
6679
@@ -91,6 +104,7 @@ ${err}
91104
try {
92105
fs.existsSync(config.cookiePath)
93106
hasExistingConfigFile = true
107+
// tslint:disable-next-line: no-unused
94108
} catch (ignored) {}
95109

96110
if (hasExistingCookieFile) {
@@ -120,6 +134,4 @@ ${err}
120134
}
121135
}
122136

123-
124-
125137
export default config

test/commands/hello.test.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

test/config.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {expect} from '@oclif/test'
2+
import * as fs from 'fs-extra'
3+
import * as path from 'path'
4+
5+
import {tempDir} from './utils'
6+
7+
const requireConfig = () => require('../src/config').default
8+
9+
const cleanup = () => {
10+
// delete config module cache
11+
delete require.cache[require.resolve('../src/config')]
12+
13+
// reset env
14+
process.env = {}
15+
}
16+
17+
const setupConfigFile = () => {
18+
const configDir = tempDir()
19+
process.env.HMD_CLI_CONFIG_DIR = configDir
20+
return path.join(configDir, 'config.json')
21+
}
22+
23+
describe('Config test', function () {
24+
beforeEach(function () {
25+
cleanup()
26+
this.configFilePath = setupConfigFile()
27+
})
28+
29+
it('should throw no config error if config.json not found and no serverUrl set in env', function () {
30+
expect(requireConfig)
31+
.to.throw(new RegExp(`Configuration file at ${this.configFilePath} not readable`))
32+
})
33+
34+
it('should throw read error if config.json is not valid JSON', function () {
35+
fs.writeFileSync(this.configFilePath, '.', 'utf8')
36+
37+
expect(requireConfig)
38+
.to.throw(/Could not read JSON config file at/)
39+
})
40+
41+
it.skip('should throw error if no serverUrl is set', function () {
42+
fs.writeFileSync(this.configFilePath, '{}', 'utf8')
43+
44+
expect(requireConfig)
45+
.to.throw(/Please specify CodiMD server URL either/)
46+
})
47+
48+
it('should set enterprise to true if HMD_CLI_COOKIE_PATH or HMD_CLI_SERVER_URL are supplied', function () {
49+
fs.writeFileSync(this.configFilePath, '{}', 'utf8')
50+
51+
process.env.HMD_CLI_COOKIE_PATH = tempDir()
52+
let config = requireConfig()
53+
54+
expect(config.cookiePath).to.eq(process.env.HMD_CLI_COOKIE_PATH)
55+
expect(config.enterprise).to.eq(true)
56+
57+
cleanup()
58+
59+
process.env.HMD_CLI_SERVER_URL = tempDir()
60+
config = requireConfig()
61+
62+
expect(config.serverUrl).to.eq(process.env.HMD_CLI_SERVER_URL)
63+
expect(config.enterprise).to.eq(true)
64+
})
65+
66+
it('should set enterprise to false if either CMD_CLI_COOKIE_PATH or CMD_CLI_SERVER_URL are supplied', function () {
67+
fs.writeFileSync(this.configFilePath, '{}', 'utf8')
68+
69+
process.env.CMD_CLI_SERVER_URL = tempDir()
70+
let config = requireConfig()
71+
72+
expect(config.serverUrl).to.eq(process.env.CMD_CLI_SERVER_URL)
73+
expect(config.enterprise).to.eq(false)
74+
75+
cleanup()
76+
77+
process.env.CMD_CLI_COOKIE_PATH = tempDir()
78+
config = requireConfig()
79+
80+
expect(config.cookiePath).to.eq(process.env.CMD_CLI_COOKIE_PATH)
81+
expect(config.enterprise).to.eq(false)
82+
})
83+
84+
it('should set enterprise with HMD_CLI_ENTERPRISE', function () {
85+
fs.writeFileSync(this.configFilePath, '{}', 'utf8')
86+
87+
process.env.HMD_CLI_ENTERPRISE = 'false'
88+
expect(requireConfig().enterprise).to.eq(false)
89+
90+
cleanup()
91+
92+
process.env.HMD_CLI_ENTERPRISE = 'true'
93+
expect(requireConfig().enterprise).to.eq(true)
94+
})
95+
})

test/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as fs from 'fs-extra'
2+
import * as os from 'os'
3+
import * as path from 'path'
4+
5+
export function tempDir() {
6+
return fs.mkdtempSync(path.join(os.tmpdir(), 'foo-'))
7+
}

0 commit comments

Comments
 (0)