|
1 | 1 | import * as path from "path"; |
2 | 2 | import * as fs from "fs"; |
3 | | -import { Config, ConfigKey, isBasicAuth, isTokenAuth } from "./types/Config"; |
| 3 | +import { Config } from "./types/Config"; |
4 | 4 | import * as inquirer from "inquirer"; |
5 | 5 | import signale from "signale"; |
| 6 | +import { FileConfig } from "./types/FileConfig"; |
6 | 7 |
|
7 | | - |
8 | | -function overwriteConfigKeyWithEnvVarIfPresent<T>(config: Partial<Config>, configKey: ConfigKey, envKey: string): Partial<Config> { |
9 | | - if (configKey === "pages") { |
10 | | - throw Error("Cannot override pages using environment variable"); |
11 | | - } |
12 | | - |
13 | | - if (process.env[envKey] !== undefined) { |
14 | | - return { ...config, [configKey]: process.env[envKey] }; |
15 | | - } else { |
16 | | - return config; |
17 | | - } |
18 | | -} |
19 | | - |
| 8 | +type AuthOptions = { |
| 9 | + user?: string; |
| 10 | + pass?: string; |
| 11 | + personalAccessToken?: string; |
| 12 | +}; |
20 | 13 |
|
21 | 14 | export class ConfigLoader { |
22 | | - static async load(configPath: string | null): Promise<Partial<Config>> { |
23 | | - let config = ConfigLoader.readConfigFromFile(configPath); |
24 | | - config = ConfigLoader.overwriteAuthFromConfigWithEnvIfPresent(config), |
25 | | - config = await ConfigLoader.promptUserAndPassIfNoCredentialsSet(config); |
26 | | - return config; |
| 15 | + static async load(configPath: string | null): Promise<Config> { |
| 16 | + const fileConfig = ConfigLoader.readConfigFromFile(configPath); |
| 17 | + const authOptions = await ConfigLoader.promptUserAndPassIfNotSet( |
| 18 | + ConfigLoader.useAuthOptionsFromEnvIfPresent(ConfigLoader.authOptionsFromFileConfig(fileConfig)), |
| 19 | + ); |
| 20 | + return ConfigLoader.createConfig(fileConfig, ConfigLoader.createAuthorizationToken(authOptions)); |
27 | 21 | } |
28 | 22 |
|
29 | | - private static readConfigFromFile(configPath: string | null): Partial<Config> { |
| 23 | + private static readConfigFromFile(configPath: string | null, authorizationToken?: string): FileConfig { |
30 | 24 | configPath = path.resolve(configPath || path.join("cosmere.json")); |
31 | 25 | if (!fs.existsSync(configPath!)) { |
32 | 26 | signale.fatal(`File "${configPath}" not found!`); |
33 | 27 | process.exit(1); |
34 | 28 | } |
35 | 29 |
|
36 | | - let config: Partial<Config> = JSON.parse(fs.readFileSync(configPath!, "utf8")); |
37 | | - if (config.pages !== undefined) { |
38 | | - for (const i in config.pages) { |
39 | | - config.pages[i].file = path.isAbsolute(config.pages[i].file) |
40 | | - ? config.pages[i].file |
41 | | - : path.resolve(path.dirname(configPath) + "/" + config.pages[i].file); |
42 | | - } |
| 30 | + let config = JSON.parse(fs.readFileSync(configPath!, "utf8")) as Omit<FileConfig, "configPath">; |
| 31 | + for (const i in config.pages) { |
| 32 | + config.pages[i].file = path.isAbsolute(config.pages[i].file) |
| 33 | + ? config.pages[i].file |
| 34 | + : path.resolve(path.dirname(configPath) + "/" + config.pages[i].file); |
43 | 35 | } |
44 | | - config.configPath = configPath; |
45 | | - return config; |
| 36 | + |
| 37 | + return { |
| 38 | + ...config, |
| 39 | + configPath, |
| 40 | + }; |
46 | 41 | } |
47 | 42 |
|
48 | | - private static overwriteAuthFromConfigWithEnvIfPresent(config: Partial<Config>): Partial<Config> { |
49 | | - config = overwriteConfigKeyWithEnvVarIfPresent(config, "user", "CONFLUENCE_USERNAME"); |
50 | | - config = overwriteConfigKeyWithEnvVarIfPresent(config, "pass", "CONFLUENCE_PASSWORD"); |
51 | | - config = overwriteConfigKeyWithEnvVarIfPresent(config, "authToken", "CONFLUENCE_AUTH_TOKEN"); |
52 | | - return config; |
| 43 | + private static createAuthorizationToken(authOptions: AuthOptions): string { |
| 44 | + if (authOptions.personalAccessToken) { |
| 45 | + return `Bearer ${authOptions.personalAccessToken}`; |
| 46 | + } |
| 47 | + |
| 48 | + if (authOptions.user && authOptions.user.length > 0 && authOptions.pass && authOptions.pass.length > 0) { |
| 49 | + const encodedBasicToken = Buffer.from(`${authOptions.user}:${authOptions.pass}`).toString("base64"); |
| 50 | + return `Basic ${encodedBasicToken}`; |
| 51 | + } |
| 52 | + |
| 53 | + signale.fatal( |
| 54 | + "Missing configuration! You must either provide a combination of your Confluence username and password or a personal access token.", |
| 55 | + ); |
| 56 | + process.exit(2); |
| 57 | + } |
| 58 | + |
| 59 | + private static useAuthOptionsFromEnvIfPresent(authOptions: AuthOptions): AuthOptions { |
| 60 | + return { |
| 61 | + user: process.env.CONFLUENCE_USERNAME || authOptions.user, |
| 62 | + pass: process.env.CONFLUENCE_PASSWORD || authOptions.pass, |
| 63 | + personalAccessToken: process.env.CONFLUENCE_PERSONAL_ACCESS_TOKEN || authOptions.personalAccessToken, |
| 64 | + }; |
53 | 65 | } |
54 | 66 |
|
55 | | - private static async promptUserAndPassIfNoCredentialsSet(config: Partial<Config>): Promise<Partial<Config>> { |
56 | | - if (isTokenAuth(config)) { |
57 | | - return config; |
| 67 | + private static async promptUserAndPassIfNotSet(authOptions: AuthOptions): Promise<AuthOptions> { |
| 68 | + if (authOptions.personalAccessToken && authOptions.personalAccessToken.length > 0) { |
| 69 | + return authOptions; |
58 | 70 | } |
59 | 71 |
|
60 | | - if (!("user" in config)) { |
61 | | - const answers = await inquirer.prompt([{ |
| 72 | + const prompts = []; |
| 73 | + if (!authOptions.user) { |
| 74 | + prompts.push({ |
62 | 75 | type: "input", |
63 | 76 | name: "user", |
64 | 77 | message: "Your Confluence username:", |
65 | | - }]); |
66 | | - (config as any).user = answers.user; |
| 78 | + }); |
67 | 79 | } |
68 | 80 |
|
69 | | - if (!("pass" in config)) { |
70 | | - const answers = await inquirer.prompt([{ |
| 81 | + if (!authOptions.pass) { |
| 82 | + prompts.push({ |
71 | 83 | type: "password", |
72 | 84 | name: "pass", |
73 | 85 | message: "Your Confluence password:", |
74 | | - }]); |
75 | | - (config as any).pass = answers.pass; |
| 86 | + }); |
76 | 87 | } |
77 | 88 |
|
78 | | - return config; |
| 89 | + const answers = await inquirer.prompt(prompts); |
| 90 | + return { |
| 91 | + user: authOptions.user || (answers.user as string), |
| 92 | + pass: authOptions.pass || (answers.pass as string), |
| 93 | + personalAccessToken: authOptions.personalAccessToken, |
| 94 | + }; |
| 95 | + } |
| 96 | + |
| 97 | + private static authOptionsFromFileConfig(fileConfig: FileConfig): AuthOptions { |
| 98 | + return { |
| 99 | + user: fileConfig.user, |
| 100 | + pass: fileConfig.pass, |
| 101 | + personalAccessToken: fileConfig.personalAccessToken, |
| 102 | + }; |
| 103 | + } |
| 104 | + |
| 105 | + private static createConfig(fileConfig: FileConfig, authorizationToken: string): Config { |
| 106 | + return { |
| 107 | + baseUrl: fileConfig.baseUrl, |
| 108 | + cachePath: fileConfig.cachePath, |
| 109 | + prefix: fileConfig.prefix, |
| 110 | + pages: fileConfig.pages, |
| 111 | + configPath: fileConfig.configPath, |
| 112 | + authorizationToken: authorizationToken, |
| 113 | + }; |
79 | 114 | } |
80 | 115 | } |
0 commit comments