Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
m1k1o
GitHub Repository: m1k1o/neko
Path: blob/master/webpage/src/components/Configuration/index.tsx
1007 views
1
import React from 'react';
2
import Tabs from '@theme/Tabs';
3
import TabItem from '@theme/TabItem';
4
import CodeBlock from '@theme/CodeBlock';
5
6
interface ConfigOptionValue {
7
type?: string;
8
description?: string;
9
defaultValue?: string;
10
}
11
12
interface ConfigOption extends ConfigOptionValue {
13
key: string[];
14
}
15
16
function configKey(key: string | string[], value: ConfigOptionValue | any): ConfigOption {
17
if (typeof key === 'string') {
18
key = key.split('.');
19
}
20
if (typeof value === 'object') {
21
return {
22
key,
23
type: value.type || getType(value.defaultValue),
24
description: value.description,
25
defaultValue: value.defaultValue,
26
}
27
} else {
28
return {
29
key,
30
type: getType(value),
31
defaultValue: value,
32
}
33
}
34
}
35
36
function configKeys(optValues: Record<string, ConfigOptionValue | any>): ConfigOption[] {
37
let options: ConfigOption[] = [];
38
Object.entries(optValues).forEach(([key, value]) => {
39
options.push(configKey(key, value));
40
});
41
return options;
42
}
43
44
function filterKeys(options: ConfigOption[], filter: string): ConfigOption[] {
45
return options.filter(option => {
46
const key = option.key.join('.');
47
return key.startsWith(filter)
48
});
49
}
50
51
function defaultValue(value: ConfigOptionValue): string {
52
switch (value.type) {
53
case 'boolean':
54
return `${value.defaultValue || false}`;
55
case 'int':
56
case 'float':
57
case 'number':
58
return `${value.defaultValue || 0}`;
59
case 'duration':
60
case 'string':
61
return `${value.defaultValue ? `"${value.defaultValue}"` : '<string>'}`;
62
case 'strings':
63
return '<comma-separated list of strings>';
64
case 'object':
65
return '<json encoded object>';
66
case 'array':
67
return '<json encoded array>';
68
default:
69
return value.type ? `<${value.type}>` : '';
70
}
71
}
72
73
function getType(value: any): string {
74
if (Array.isArray(value)) {
75
return 'array';
76
}
77
return typeof value;
78
}
79
80
export function EnvironmentVariables({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
81
if (typeof comments === 'undefined') {
82
comments = true;
83
}
84
85
let code = '';
86
options.forEach(option => {
87
const description = option.description ? option.description : '';
88
const type = option.type ? ` (${option.type})` : '';
89
if (comments && description) {
90
code += `# ${description}${type}\n`;
91
}
92
code += `NEKO_${option.key.join('_').toUpperCase()}=${defaultValue(option)}\n`;
93
});
94
95
return (
96
<CodeBlock language="shell" {...props}>
97
{code}
98
</CodeBlock>
99
);
100
}
101
102
export function CommandLineArguments({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
103
if (typeof comments === 'undefined') {
104
comments = true;
105
}
106
107
let code = '';
108
options.forEach(option => {
109
const description = option.description ? option.description : '';
110
const type = option.type ? ` (${option.type})` : '';
111
if (comments && description) {
112
code += `# ${description}${type}\n`;
113
}
114
code += `--${option.key.join('.')} ${defaultValue(option)}\n`;
115
});
116
117
return (
118
<CodeBlock language="shell" {...props}>
119
{code}
120
</CodeBlock>
121
);
122
}
123
124
export function YamlFileContent({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
125
if (typeof comments === 'undefined') {
126
comments = true;
127
}
128
129
const final = Symbol('final');
130
131
const buildYaml = (obj: Record<string, any>, prefix = '') => {
132
let code = '';
133
Object.entries(obj).forEach(([key, option]) => {
134
if (typeof option === 'object' && !Array.isArray(option) && !option[final]) {
135
code += prefix+`${key}:\n`;
136
code += buildYaml(option, prefix + ' ');
137
} else {
138
const description = option.description ? option.description : '';
139
const type = option.type ? ` (${option.type})` : '';
140
if (comments && description) {
141
code += `${prefix}# ${description}${type}\n`;
142
}
143
let value: string;
144
if (option.type === 'strings') {
145
value = option.defaultValue ? `[ "${option.defaultValue}" ]` : '[ <string> ]';
146
} else if (option.type === 'object') {
147
value = "{}"
148
} else if (option.type === 'array') {
149
value = "[]"
150
} else {
151
value = defaultValue(option);
152
}
153
code += `${prefix}${key}: ${value}\n`;
154
}
155
});
156
return code;
157
};
158
159
const yamlCode = buildYaml(options.reduce((acc, option) => {
160
const keys = option.key;
161
let current = acc;
162
keys.forEach((key, index) => {
163
if (!current[key]) {
164
current[key] = index === keys.length - 1 ? option : {};
165
}
166
current = current[key];
167
});
168
current[final] = true;
169
return acc;
170
}, {}));
171
172
return (
173
<CodeBlock language="yaml" {...props}>
174
{yamlCode}
175
</CodeBlock>
176
);
177
}
178
179
type ConfigurationTabProps = {
180
options?: ConfigOption[] | Record<string, ConfigOptionValue | any>;
181
heading?: boolean;
182
comments?: boolean;
183
filter?: string | string[] | Record<string, ConfigOptionValue | any>;
184
};
185
186
export function ConfigurationTab({ options, heading, comments, filter, ...props }: ConfigurationTabProps) {
187
var configOptions: ConfigOption[] = [];
188
if (Array.isArray(options)) {
189
configOptions = options;
190
} else {
191
configOptions = configKeys(options)
192
}
193
if (typeof comments === 'undefined') {
194
comments = true;
195
}
196
if (typeof heading === 'undefined') {
197
heading = false;
198
}
199
200
if (Array.isArray(filter)) {
201
let filteredOptions: ConfigOption[] = [];
202
for (const f of filter) {
203
filteredOptions = [ ...filteredOptions, ...filterKeys(configOptions, f) ];
204
}
205
configOptions = filteredOptions;
206
} else if (typeof filter === 'string') {
207
configOptions = filterKeys(configOptions, filter);
208
} else if (typeof filter === 'object') {
209
let filteredOptions: ConfigOption[] = [];
210
for (const k in filter) {
211
let filtered = configOptions.find(option => {
212
return option.key.join('.') === k;
213
});
214
let replaced = configKey(k, filter[k]);
215
filteredOptions = [ ...filteredOptions, { ...filtered, ...replaced } ];
216
}
217
configOptions = filteredOptions;
218
}
219
220
return (
221
<Tabs groupId="configuration" defaultValue="yaml" values={[
222
{ label: 'YAML Configuration File', value: 'yaml' },
223
{ label: 'Environment Variables', value: 'env' },
224
{ label: 'Command Line Arguments', value: 'args' },
225
]} {...props}>
226
<TabItem value="env" label="Environment Variables">
227
{heading && (
228
<p>You can set the following environment variables in your <code>docker-compose.yaml</code> file or in your shell environment.</p>
229
)}
230
{EnvironmentVariables({ options: configOptions, comments })}
231
</TabItem>
232
<TabItem value="args" label="Command Line Arguments">
233
{heading && (
234
<p>You can list the following command line arguments using <code>neko serve --help</code>.</p>
235
)}
236
{CommandLineArguments({ options: configOptions, comments })}
237
</TabItem>
238
<TabItem value="yaml" label="YAML Configuration File">
239
{heading && (
240
<p>You can create a <code>/etc/neko/neko.yaml</code> file with the following configuration options.</p>
241
)}
242
{YamlFileContent({ options: configOptions, comments })}
243
</TabItem>
244
</Tabs>
245
);
246
}
247
248