commit b109c6777bded022c2b14a89b0d8c45285a89d6c Author: Fenris Wolf Date: Mon Sep 30 09:11:50 2024 +0200 [ini] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a02bcd0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/temp/ +/build/ diff --git a/source/api.ts b/source/api.ts new file mode 100644 index 0000000..e1da7d5 --- /dev/null +++ b/source/api.ts @@ -0,0 +1,391 @@ +namespace _wiki_js_cli.api +{ + + /** + */ + var _login_token : (null | string) = null; + + + /** + */ + var _url_base : (null | string) = null; + + + /** + */ + export function init( + url_base : string + ) : Promise + { + _url_base = url_base; + return Promise.resolve(undefined); + } + + + /** + */ + async function call_generic_graphql( + graphql_query, + options + ) + { + options = Object.assign( + { + "variables": {}, + }, + options + ); + const http_request = { + "target": (_url_base + "/graphql"), + "method": "POST", + "headers": Object.assign( + { + "Content-Type": "application/json", + }, + ( + (_login_token === null) + ? + {} + : + {"Cookie": ("jwt=" + _login_token)} + ) + ), + "body": JSON.stringify( + [ + { + "operationName": null, + "variables": options.variables, + "extensions": {}, + "query": graphql_query, + } + ] + ), + }; + const http_response = await _wiki_js_cli.helpers.http.call(http_request); + const data = JSON.parse(http_response.body); + return Promise.resolve(data[0]["data"]); + } + + + /** + */ + export async function call_finalize( + admin_email : string, + admin_password : string, + site_url : string, + telemetry : boolean + ) : Promise + { + const http_request = { + "target": (_wiki_js_cli.conf.get().api.url_base + "/finalize"), + "method": "POST", + "headers": { + "Content-Type": "application/json", + }, + "body": JSON.stringify( + { + "adminEmail": admin_email, + "adminPassword": admin_password, + "adminPasswordConfirm": admin_password, + "siteUrl": site_url, + "telemetry": telemetry, + } + ), + }; + const http_response = await _wiki_js_cli.helpers.http.call(http_request); + const data = JSON.parse(http_response.body); + return Promise.resolve(undefined); + } + + + /** + * executes a local login + */ + export async function call_login_local( + username : string, + password : string + ) : Promise + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_login_local", + { + } + ); + return ( + call_generic_graphql( + "mutation ($username: String!, $password: String!, $strategy: String!) {authentication {login(username: $username, password: $password, strategy: $strategy) {responseResult {succeeded errorCode slug message __typename} jwt mustChangePwd mustProvideTFA mustSetupTFA continuationToken redirect tfaQRImage __typename} __typename}}", + { + "variables": { + "strategy": "local", + "username": username, + "password": password, + } + } + ) + .then( + (data) => { + if (data["authentication"]["login"]["responseResult"]["succeeded"]) { + _login_token = data["authentication"]["login"]["jwt"]; + return Promise.resolve(undefined); + } + else { + return Promise.reject(new Error("login failed")); + } + } + ) + ); + } + + + /** + */ + export function call_email_settings_set( + settings + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_email_settings_set", + { + "settings": settings, + } + ); + return ( + call_generic_graphql( + "mutation ($senderName: String!, $senderEmail: String!, $host: String!, $port: Int!, $name: String!, $secure: Boolean!, $verifySSL: Boolean!, $user: String!, $pass: String!, $useDKIM: Boolean!, $dkimDomainName: String!, $dkimKeySelector: String!, $dkimPrivateKey: String!) {mail {updateConfig(senderName: $senderName, senderEmail: $senderEmail, host: $host, port: $port, name: $name, secure: $secure, verifySSL: $verifySSL, user: $user, pass: $pass, useDKIM: $useDKIM, dkimDomainName: $dkimDomainName, dkimKeySelector: $dkimKeySelector, dkimPrivateKey: $dkimPrivateKey) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", + { + "variables": { + "senderName": settings.sender_name, + "senderEmail": settings.sender_email_address, + "host": settings.smtp_host, + "port": settings.smtp_port, + "name": settings.name, + "secure": settings.secure, + "verifySSL": settings.verify_ssl, + "user": settings.smtp_username, + "pass": settings.smtp_password, + "useDKIM": settings.use_dkim, + "dkimDomainName": settings.dkim_domain_name, + "dkimKeySelector": settings.dkim_key_selector, + "dkimPrivateKey": settings.dkim_private_key, + } + } + ) + ); + } + + + /** + */ + export function call_locale_download( + locale + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_locale_download", + { + "locale": locale, + } + ); + return ( + call_generic_graphql( + "mutation ($locale: String!) {localization {downloadLocale(locale: $locale) {responseResult {succeeded errorCode slug message __typename} __typename}__typename}}", + { + "variables": { + "locale": locale, + } + } + ) + ); + } + + + /** + */ + export function call_group_list( + name : string + ) : Promise + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_group_list", + { + } + ); + return ( + call_generic_graphql( + "{groups {list {id name isSystem userCount createdAt updatedAt __typename} __typename}}", + { + "variables": { + } + } + ) + ); + } + + + /** + */ + export function call_group_create( + name : string + ) : Promise + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_group_create", + { + "name": name, + } + ); + return ( + call_generic_graphql( + "mutation ($name: String!) {groups {create(name: $name) {responseResult {succeeded errorCode slug message __typename} group {id name createdAt updatedAt __typename} __typename} __typename}}", + { + "variables": { + "name": name, + } + } + ) + ); + } + + + /** + * @param permissions_general { + * Array< + * string + * > + * } + * @param permissions_page_specific { + * Array< + * { + * id:string; + * path:string; + * roles:Array< + * string + * >; + * match:string; + * deny:boolean; + * locales:Array< + * string + * >; + * } + * > + * } + */ + export function call_group_update( + group_id, + name, + permissions_general, + permissions_page_specific + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_group_update", + { + } + ); + return ( + call_generic_graphql( + "mutation ($id: Int!, $name: String!, $redirectOnLogin: String!, $permissions: [String]!, $pageRules: [PageRuleInput]!) {groups {update(id: $id, name: $name, redirectOnLogin: $redirectOnLogin, permissions: $permissions, pageRules: $pageRules) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", + { + "variables": { + "id": group_id, + "name": name, + "redirectOnLogin": "/", + "permissions": permissions_general, + "pageRules": permissions_page_specific, + } + } + ) + ); + } + + + /** + */ + export function call_authentication_strategy_list( + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_authentication_strategy_list", + { + } + ); + return ( + call_generic_graphql( + "{authentication {activeStrategies {key strategy {key title description useForm logo website __typename} config {key value __typename} order isEnabled displayName selfRegistration domainWhitelist autoEnrollGroups __typename} __typename}}", + { + } + ) + .then( + (data) => Promise.resolve(data["authentication"]["activeStrategies"]) + ) + ); + } + + + /** + */ + export function call_authentication_strategy_set( + strategies + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_authentication_strategy_set", + { + "strategies": strategies, + } + ); + return ( + call_generic_graphql( + "mutation ($strategies: [AuthenticationStrategyInput]!) {authentication {updateStrategies(strategies: $strategies) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", + { + "variables": { + "strategies": strategies, + } + } + ) + ); + } + + + /** + */ + export function call_theming_set( + dark_mode : boolean, + toc_position : ("left" | "right" | "off") + ) + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.info, + "api_call_theming_set", + { + "dark_mode": dark_mode, + "toc_position": toc_position, + } + ); + return ( + call_generic_graphql( + "mutation ($theme: String!, $iconset: String!, $darkMode: Boolean!, $tocPosition: String, $injectCSS: String, $injectHead: String, $injectBody: String) {theming {setConfig(theme: $theme, iconset: $iconset, darkMode: $darkMode, tocPosition: $tocPosition, injectCSS: $injectCSS, injectHead: $injectHead, injectBody: $injectBody) {responseResult {succeeded errorCode slug message __typename} __typename} __typename}}", + { + "variables": { + "theme": "default", + "iconset": "mdi", + "darkMode": dark_mode, + "tocPosition": toc_position, + "injectCSS": "", + "injectHead": "", + "injectBody": "" + } + } + ) + ); + } + +} diff --git a/source/base.ts b/source/base.ts new file mode 100644 index 0000000..b9fcca8 --- /dev/null +++ b/source/base.ts @@ -0,0 +1,6 @@ +declare var process; + +declare var require; + +type int = number; + diff --git a/source/conf.ts b/source/conf.ts new file mode 100644 index 0000000..6ec81c3 --- /dev/null +++ b/source/conf.ts @@ -0,0 +1,79 @@ +namespace _wiki_js_cli.conf +{ + + /** + */ + var _data = null; + + + /** + */ + export async function load( + path : string + ) : Promise + { + let data_raw : any; + if (path === null) { + data_raw = {}; + } + else { + const content : string = await _wiki_js_cli.helpers.file.read(path); + data_raw = JSON.parse(content); + } + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.debug, + "conf_raw", + data_raw + ); + _data = { + "api": { + "url_base": ( + data_raw?.api?.url_base + ?? + "http://localhost:3000" + ), + }, + "login": { + "username": ( + data_raw?.login?.username + ?? + "admin" + ), + "password": ( + data_raw?.login?.password + ?? + "admin" + ), + }, + "log": { + "threshold": ( + data_raw?.log?.threshold + ?? + "info" + ), + } + }; + return Promise.resolve(undefined); + } + + + /** + */ + export function set( + data : any + ) : void + { + _data = data; + } + + + /** + */ + export function get( + ) : any + { + return _data; + } + +} + diff --git a/source/helpers/args.ts b/source/helpers/args.ts new file mode 100644 index 0000000..6a776eb --- /dev/null +++ b/source/helpers/args.ts @@ -0,0 +1,72 @@ +namespace _wiki_js_cli.helpers.args +{ + + /** + */ + export function parse( + args_raw : Array + ) + { + let result : { + positional : Array< + string + >; + volatile : Record< + string, + Array< + string + > + >; + } = { + "positional": [], + "volatile": {}, + }; + let state : ("free" | "bound") = "free"; + let key : (null | string) = null; + args_raw.forEach( + (arg_raw) => { + switch (state) { + case "free": { + if (arg_raw.startsWith("-")) { + key = arg_raw.slice(1); + state = "bound"; + } + else { + if (key === null) { + result.positional.push(arg_raw); + key = null; + state = "free"; + } + else { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.warning, + "arg_discarded", + { + "arg_raw": arg_raw, + } + ); + key = null; + state = "free"; + } + } + break; + } + case "bound": { + if (! (key in result["volatile"])) { + result["volatile"][key] = []; + } + else { + // do nothing + } + result["volatile"][key].push(arg_raw); + key = null; + state = "free"; + } + } + } + ); + return result; + } + +} + diff --git a/source/helpers/file.ts b/source/helpers/file.ts new file mode 100644 index 0000000..3ee8918 --- /dev/null +++ b/source/helpers/file.ts @@ -0,0 +1,32 @@ +namespace _wiki_js_cli.helpers.file +{ + + /** + */ + export function read( + path : string + ) : Promise + { + const nm_fs = require("fs"); + return ( + new Promise( + (resolve, reject) => { + nm_fs.readFile( + path, + { + }, + (err, data) => { + if (err) { + reject(err); + } + else { + resolve(data.toString()); + } + } + ); + } + ) + ); + } + +} diff --git a/source/helpers/http.ts b/source/helpers/http.ts new file mode 100644 index 0000000..931efc7 --- /dev/null +++ b/source/helpers/http.ts @@ -0,0 +1,57 @@ +namespace _wiki_js_cli.helpers.http +{ + + /** + */ + export type type_http_request = { + target : string; + method : string; + headers : Record; + body : string; + }; + + + /** + */ + export type type_http_response = { + status_code : int; + headers : Record; + body : string; + }; + + + /** + */ + export async function call( + http_request : type_http_request + ) : Promise + { + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.debug, + "http_call_request", + http_request + ); + const fetch_request = new Request( + http_request.target, + { + "method": http_request.method, + "headers": http_request.headers, + "body": http_request.body, + } + ); + const fetch_response = await fetch(fetch_request); + const http_response = { + "status_code": fetch_response.status, + "headers": Object.fromEntries(fetch_response.headers.entries()), + "body": await fetch_response.text(), + }; + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.debug, + "http_call_response", + http_response + ); + return http_response; + } + +} + diff --git a/source/helpers/log.ts b/source/helpers/log.ts new file mode 100644 index 0000000..d79ebdd --- /dev/null +++ b/source/helpers/log.ts @@ -0,0 +1,69 @@ +namespace _wiki_js_cli.helpers.log +{ + + /** + */ + export enum enum_level { + debug = "debug", + info = "info", + notice = "notice", + warning = "warning", + error = "error", + } + + + /** + */ + const _level_order : Array = [ + enum_level.debug, + enum_level.info, + enum_level.notice, + enum_level.warning, + enum_level.error, + ]; + + + /** + */ + var _threshold : enum_level = enum_level.info; + + + /** + */ + export function setup( + threshold : enum_level + ) : Promise + { + _threshold = threshold; + return Promise.resolve(undefined); + } + + + /** + */ + export function write( + level : enum_level, + incident : string, + details : any + ) : void + { + if (_level_order.indexOf(level) < _level_order.indexOf(_threshold)) { + // do nothing + } + else { + process.stderr.write( + _wiki_js_cli.helpers.string.coin( + "\n<{{datetime}}> [{{level}}] {{incident}}\n{{details}}\n\n", + { + "datetime": (new Date()).toISOString(), + "level": level, + "incident": incident, + "details": JSON.stringify(details, undefined, "\t"), + } + ) + ); + } + } + +} + diff --git a/source/helpers/string.ts b/source/helpers/string.ts new file mode 100644 index 0000000..44dc96c --- /dev/null +++ b/source/helpers/string.ts @@ -0,0 +1,20 @@ +namespace _wiki_js_cli.helpers.string +{ + + /** + */ + export function coin( + template : string, + arguments_ : Record + ) : string + { + let result : string = template; + Object.entries(arguments_).forEach( + ([key, value]) => { + result = result.replace(new RegExp("{{" + key + "}}", "g"), value); + } + ); + return result; + } + +} diff --git a/source/logic.ts b/source/logic.ts new file mode 100644 index 0000000..b3a61df --- /dev/null +++ b/source/logic.ts @@ -0,0 +1,387 @@ +namespace _wiki_js_cli.logic +{ + + /** + */ + export async function login( + options : { + username ?: string; + password ?: string; + } = {} + ) : Promise + { + options = Object.assign( + { + "username": _wiki_js_cli.conf.get().login.username, + "password": _wiki_js_cli.conf.get().login.password, + }, + options + ); + await _wiki_js_cli.api.call_login_local( + options.username, + options.password + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function initialize( + admin_email_address : string, + admin_password : string, + options : { + site_url ?: string; + allow_telemetry ?: boolean; + } = {} + ) : Promise + { + options = Object.assign( + { + "site_url": "http://localhost:3000", + "allow_telemetry": false, + }, + options + ); + await _wiki_js_cli.api.call_finalize( + admin_email_address, + admin_password, + options.site_url, + options.allow_telemetry + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function email_settings_set( + smtp_host : string, + smtp_port : int, + smtp_username : string, + smtp_password : string, + sender_name : string, + sender_email_address : string, + options : { + } = {} + ) : Promise + { + options = Object.assign( + { + }, + options + ); + const result = await _wiki_js_cli.api.call_email_settings_set( + { + "sender_name": sender_name, + "sender_email_address": sender_email_address, + "smtp_host": smtp_host, + "smtp_port": smtp_port, + "secure": true, + "verify_ssl": true, + "smtp_username": smtp_username, + "smtp_password": smtp_password, + "name": "", + "use_dkim": false, + "dkim_domain_name": "", + "dkim_key_selector": "", + "dkim_private_key": "", + } + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function locale_add( + locale_code : string + ) : Promise + { + _wiki_js_cli.api.call_locale_download( + locale_code + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function group_identify( + name : string + ) : Promise + { + const data = await _wiki_js_cli.api.call_group_list( + name + ); + const hits : Array = ( + data["groups"]["list"] + .filter( + (entry) => (entry["name"] === name) + ) + ); + if (hits.length !== 1) { + return Promise.reject(new Error("not found or ambiguous")); + } + else { + return Promise.resolve(hits[0]["id"]); + } + } + + + /** + * returns the ID of the generated group + */ + export async function group_add( + name : string, + options : { + permissions_general ?: Array< + string + >; + permissions_specific ?: Array< + { + id : string; + path : string; + roles : Array< + string + >; + match : string; + deny : boolean; + locales : Array< + string + >; + } + >; + } = {} + ) : Promise + { + options = Object.assign( + { + "permissions_general": [], + "permissions_specific": [], + }, + options + ); + const result_1 = await _wiki_js_cli.api.call_group_create( + name + ); + const id : int = result_1["groups"]["create"]["group"]["id"]; + const result_2 = await _wiki_js_cli.api.call_group_update( + id, + name, + options.permissions_general, + options.permissions_specific + ); + return Promise.resolve(id); + } + + + /** + */ + export async function group_modify( + id : int, + options : { + permissions_general ?: Array< + string + >; + permissions_specific ?: Array< + { + id : string; + path : string; + roles : Array< + string + >; + match : string; + deny : boolean; + locales : Array< + string + >; + } + >; + } = {} + ) : Promise + { + options = Object.assign( + { + "permissions_general": [], + "permissions_specific": [], + }, + options + ); + const result = await _wiki_js_cli.api.call_group_update( + id, + name, + options.permissions_general, + options.permissions_specific + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function authentication_strategy_list( + ) : Promise> + { + const result = await _wiki_js_cli.api.call_authentication_strategy_list( + ); + return Promise.resolve(result); + } + + + /** + */ + export async function authentication_strategy_add( + strategy : { + key:string; + name:string; + client_id:string; + client_secret:string; + authorization_url:string; + token_url:string; + user_info_url:string; + group_assignments:Array; + } + ) : Promise + { + const current = await _wiki_js_cli.api.call_authentication_strategy_list( + ); + const result = await _wiki_js_cli.api.call_authentication_strategy_set( + ( + ( + current + .map( + (entry) => ({ + "key": entry["key"], + "strategyKey": entry["strategy"]["key"], + "displayName": entry["displayName"], + "order": entry["order"], + "isEnabled": entry["isEnabled"], + "config": ( + entry["config"] + .map( + (item) => ({ + "key": item["key"], + "value": JSON.stringify({"v": JSON.parse(item["value"])["value"]}), + }) + ) + ), + "selfRegistration": entry["selfRegistration"], + "domainWhitelist": entry["domainWhitelist"], + "autoEnrollGroups": entry["autoEnrollGroups"], + }) + ) + ) + .concat( + [ + { + "key": strategy.key, + "strategyKey": "oauth2", + "displayName": strategy.name, + "order": ( + ( + current + .map(x => x["order"]) + .reduce((x,y) => (((x === null) || (x < y)) ? y : x), null) + ) + + + 1 + ), + "isEnabled": true, + "config": [ + { + "key": "clientId", + "value": JSON.stringify({"v": strategy.client_id}), + }, + { + "key": "clientSecret", + "value": JSON.stringify({"v": strategy.client_secret}), + }, + { + "key": "authorizationURL", + "value": JSON.stringify({"v": strategy.authorization_url}), + }, + { + "key": "tokenURL", + "value": JSON.stringify({"v": strategy.token_url}), + }, + { + "key": "userInfoURL", + "value": JSON.stringify({"v": strategy.user_info_url}), + }, + { + "key": "userIdClaim", + "value": JSON.stringify({"v": "id"}), + }, + { + "key": "displayNameClaim", + "value": JSON.stringify({"v": "name"}), + }, + { + "key": "emailClaim", + "value": JSON.stringify({"v": "email"}), + }, + { + "key": "mapGroups", + "value": JSON.stringify({"v": false}), + }, + { + "key": "groupsClaim", + "value": JSON.stringify({"v": "groups"}), + }, + { + "key": "logoutURL", + "value": JSON.stringify({"v": ""}), + }, + { + "key": "scope", + "value": JSON.stringify({"v": "openid profile email"}), + }, + { + "key": "useQueryStringForAccessToken", + "value": JSON.stringify({"v": false}), + }, + { + "key": "enableCSRFProtection", + "value": JSON.stringify({"v": true}), + } + ], + "selfRegistration": true, + "domainWhitelist": [], + "autoEnrollGroups": await Promise.all( + strategy.group_assignments + .map(x => group_identify(x)) + ), + }, + ] + ) + ) + ); + return Promise.resolve(undefined); + } + + + /** + */ + export async function theming_set( + options : { + dark_mode ?: boolean; + toc_position ?: ("left" | "right" | "off"); + } = {} + ) : Promise + { + options = Object.assign( + { + "dark_mode": false, + "toc_position": "left", + }, + options + ); + const result = await _wiki_js_cli.api.call_theming_set( + options.dark_mode, + options.toc_position + ); + return Promise.resolve(result); + } + +} diff --git a/source/main.ts b/source/main.ts new file mode 100644 index 0000000..8309d94 --- /dev/null +++ b/source/main.ts @@ -0,0 +1,377 @@ +namespace _wiki_js_cli +{ + + /** + */ + function parse_boolean( + indicator : string + ) : boolean + { + if ( + (indicator === "1") + || + (indicator === "yes") + || + (indicator === "on") + || + (indicator === "true") + ) { + return true; + } + else { + if ( + (indicator === "0") + || + (indicator === "no") + || + (indicator === "off") + || + (indicator === "false") + ) { + return false; + } + else { + throw (new Error("invalid boolean indicator: " + indicator)); + } + } + } + + + /** + */ + export async function main( + args_raw : Array + ) : Promise + { + // args + const args = _wiki_js_cli.helpers.args.parse(args_raw); + const override_url_base : (null | string) = ( + ( + ("b" in args.volatile) + && + (args.volatile["b"].length >= 0) + ) + ? + args.volatile["b"][0] + : + null + ); + const override_username : (null | string) = ( + ( + ("u" in args.volatile) + && + (args.volatile["u"].length >= 0) + ) + ? + args.volatile["u"][0] + : + null + ); + const override_password : (null | string) = ( + ( + ("p" in args.volatile) + && + (args.volatile["p"].length >= 0) + ) + ? + args.volatile["p"][0] + : + null + ); + const override_log_level : (null | string) = ( + ( + ("l" in args.volatile) + && + (args.volatile["l"].length >= 0) + ) + ? + args.volatile["l"][0] + : + null + ); + + // conf + const conf_path : (null | string) = ( + ( + ("c" in args.volatile) + && + (args.volatile["c"].length >= 0) + ) + ? + args.volatile["c"][0] + : + null + ); + await _wiki_js_cli.conf.load(conf_path); + _wiki_js_cli.helpers.log.write( + _wiki_js_cli.helpers.log.enum_level.debug, + "conf", + _wiki_js_cli.conf.get() + ); + + // init + await _wiki_js_cli.helpers.log.setup( + override_log_level ?? _wiki_js_cli.conf.get().log.threshold + ); + await _wiki_js_cli.api.init( + (override_url_base ?? _wiki_js_cli.conf.get().api.url_base) + ); + + // exec + if (args.positional.length < 1) { + return Promise.reject("SYNTAX: [node] cli.js [-c ] [-b ] [-u ] [-p ] [ [ […]]]\n\n\t = init | email-settings-set | locale-add | group-add | group-modify | auth-strat-add-oauth2 | theming-set"); + } + else { + const action : string = args.positional[0]; + switch (action) { + default: { + return Promise.reject("invalid action: " + action); + break; + } + case "init": { + if (args.positional.length <= 2) { + return Promise.reject("SYNTAX: … init [ []]"); + } + else { + const admin_email_address : string = args.positional[1]; + const admin_password : string = args.positional[2]; + const site_url : (undefined | string) = ( + (args.positional.length >= 4) + ? + args.positional[3] + : + undefined + ); + const allow_telemetry : (undefined | boolean) = ( + (args.positional.length >= 5) + ? + parse_boolean(args.positional[4]) + : + undefined + ); + await _wiki_js_cli.logic.initialize( + admin_email_address, + admin_password, + { + "site_url": site_url, + "allow_telemetry": allow_telemetry + } + ); + return Promise.resolve(undefined); + } + break; + } + case "email-settings-set": { + if (args.positional.length <= 6) { + return Promise.reject("SYNTAX: … email-settings-set "); + } + else { + const smtp_host : string = args.positional[1]; + const smtp_port : int = parseInt(args.positional[2]); + const smtp_username : string = args.positional[3]; + const smtp_password : string = args.positional[4]; + const sender_name : string = args.positional[5]; + const sender_email_address : string = args.positional[6]; + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + await _wiki_js_cli.logic.email_settings_set( + smtp_host, + smtp_port, + smtp_username, + smtp_password, + sender_name, + sender_email_address, + { + } + ); + return Promise.resolve(undefined); + } + break; + } + case "locale-add": { + if (args.positional.length <= 1) { + return Promise.reject("SYNTAX: … locale-add "); + } + else { + const locale_code : string = args.positional[1]; + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + await _wiki_js_cli.logic.locale_add( + locale_code + ); + return Promise.resolve(undefined); + } + break; + } + case "group-add": { + if (args.positional.length <= 2) { + return Promise.reject("SYNTAX: … group-add "); + } + else { + const name = args.positional[1]; + const permissions = args.positional[2].split(","); + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + const result = await _wiki_js_cli.logic.group_add( + name, + { + "permissions_general": permissions, + "permissions_specific": [ + { + "id": "default", + "path": "", + "roles": permissions, + "match": "START", + "deny": false, + "locales": [] + } + ], + } + ); + process.stdout.write( + JSON.stringify( + result, + undefined, + "\t" + ) + + + "\n" + ); + } + break; + } + case "group-modify": { + if (args.positional.length <= 2) { + return Promise.reject("SYNTAX: … group-modify "); + } + else { + const name = args.positional[1]; + const permissions = args.positional[2].split(","); + const id : int = await _wiki_js_cli.logic.group_identify(name); + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + const result = await _wiki_js_cli.logic.group_modify( + id, + { + "permissions_general": permissions, + "permissions_specific": [ + { + "id": "default", + "path": "", + "roles": permissions, + "match": "START", + "deny": false, + "locales": [] + } + ], + } + ); + } + break; + } + case "auth-strat-add-oauth2": { + if (args.positional.length <= 8) { + return Promise.reject("SYNTAX: … auth-strat-add-oauth2 "); + } + else { + const strategy = { + "key": args.positional[1], + "name": args.positional[2], + "client_id": args.positional[3], + "client_secret": args.positional[4], + "authorization_url": args.positional[5], + "token_url": args.positional[6], + "user_info_url": args.positional[7], + "group_assignments": args.positional[8].split(","), + } + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + await _wiki_js_cli.logic.authentication_strategy_add( + strategy + ); + return Promise.resolve(undefined); + } + break; + } + case "theming-set": { + if (args.positional.length <= 1) { + return Promise.reject("SYNTAX: … theming-set []"); + } + else { + const dark_mode : boolean = parse_boolean(args.positional[1]); + const toc_position_raw : string = ( + (args.positional.length <= 2) + ? + "left" + : + args.positional[2] + ); + const toc_position : (null | ("left" | "right" | "off")) = (() => { + switch (toc_position_raw) { + case "left": return "left"; + case "right": return "right"; + case "hidden": return "off"; + default: {return null;} + } + }) (); + if (toc_position === null) { + return Promise.reject("invalid toc-position: " + toc_position_raw + "; valid values are: left,right,hidden"); + } + else { + await _wiki_js_cli.logic.login( + { + "username": override_username, + "password": override_password, + } + ); + await _wiki_js_cli.logic.theming_set( + { + "dark_mode": dark_mode, + "toc_position": toc_position, + } + ); + return Promise.resolve(undefined); + } + } + break; + } + } + } + } + +} + + +( + _wiki_js_cli.main(process.argv.slice(2)) + .then( + () => { + } + ) + .catch( + (reason) => { + process.stderr.write("-- " + String(reason) + "\n"); + } + ) +); + diff --git a/tools/build b/tools/build new file mode 100755 index 0000000..796073a --- /dev/null +++ b/tools/build @@ -0,0 +1,4 @@ +#!/usr/bin/env sh + +make -f tools/makefile + diff --git a/tools/makefile b/tools/makefile new file mode 100644 index 0000000..812f2dc --- /dev/null +++ b/tools/makefile @@ -0,0 +1,45 @@ +## vars + +dir_source := source +dir_temp := temp +dir_build := build + + +## commands + +cmd_mkdir := mkdir -p +cmd_tsc := tsc +cmd_cat := cat +cmd_chmod := chmod +cmd_echo := echo +cmd_log := echo "--" + + +## rules + +.PHONY: _default +_default: ${dir_build}/wiki-js-cli + +${dir_temp}/unlinked.js: \ + ${dir_source}/base.ts \ + ${dir_source}/helpers/string.ts \ + ${dir_source}/helpers/file.ts \ + ${dir_source}/helpers/log.ts \ + ${dir_source}/helpers/http.ts \ + ${dir_source}/helpers/args.ts \ + ${dir_source}/conf.ts \ + ${dir_source}/api.ts \ + ${dir_source}/logic.ts \ + ${dir_source}/main.ts + @ ${cmd_log} "compile …" + @ ${cmd_mkdir} $(dir $@) + @ ${cmd_tsc} --target es2020 $^ --outFile $@ + +${dir_build}/wiki-js-cli: \ + ${dir_temp}/unlinked.js + @ ${cmd_log} "link …" + @ ${cmd_mkdir} $(dir $@) + @ ${cmd_echo} "#!/usr/bin/env node" > $@ + @ ${cmd_cat} $^ >> $@ + @ ${cmd_chmod} +x $@ +