<file_sep>var random = require("../tools/random")

module.exports = (values, apiKey, subdomains) => {
    var temp, host, protocol, port
    [temp, protocol, host, temp, port] = /^(https?:\/\/)([^:\/]+)(:(\d+))?/.exec(values.urls.primary)
    // cast port to number
    var portNum = port ? Number.parseInt(port) : null
    var domainFromHost = (host) => {
        var temp, domain
        [temp, domain] = /^([^\.]+)/.exec(host)
        return domain
    }
    var genHost = (prefix) => prefix ? `${prefix}.${host}` : host
    var genUrl = (prefix,
        port = portNum,
        isWebStandardPort = true,
        webStandardPort = values.tls.enable
            ? values.ports.web.https
            : values.ports.web.http) => {
        const tmp = `${protocol}${genHost(prefix)}`
        // return early with not defined port
        if (! port) return tmp
        // cut protocols default patturn
        switch (protocol) {
            case "http:":
                if (port === 80) return tmp
                break
            case "https:":
                if (port === 443) return tmp
        }
        // prior port than webStandardPort, when webStandardPort enabled or port isn't equal to webStandardPort.
        if (! webStandardPort || port !== webStandardPort) return `${tmp}:${port}` 
        // join webStandardPort...
        return `${tmp}:${webStandardPort}`
    }

    /** base */
    var response = {
        mongo: {
            uri: "mongodb://" + values.mongo.host + "/misskey-web"
        },
        redis: {
            host: values.redis.host
        },
        port: {
            http: values.ports.web.http
        },
        https: {
            enable: values.tls.enable
        },
        apiPasskey: apiKey,
        apiServerIp: "127.0.0.1",
        apiServerPort: values.ports.api,
        cookiePass: random(),
        sessionKey: "hmsk",
        sessionSecret: random(),
        googleRecaptchaSecret: values.recaptcha.secret,
        publicConfig: {
            themeColor: values.themeColor,
            domain: domainFromHost(host),
            host: host,
            url: genUrl(null),
            apiHost: genHost("api"),
            apiUrl: genUrl("api", values.ports.api, false),
            googleRecaptchaSiteKey: values.recaptcha.site,
        }
    }
    /** add subdomains to response */
    Object.keys(subdomains["web"]).forEach(name => {
        response.publicConfig[name + "Domain"] = subdomains["web"][name]
        response.publicConfig[name + "Url"] = genUrl(subdomains["web"][name])
    })
    Object.keys(subdomains["web-host"]).forEach(name => {
        response.publicConfig[name + "Host"] = genHost(subdomains["web-host"][name]),
        response.publicConfig[name + "Url"] = genUrl(subdomains["web-host"][name])
    })
    /** mongo section */
    if (values.mongo.auth) {
        response.mongo.options = {
            user: values.mongo.authUser,
            password: <PASSWORD>
        }
    }
    /** redis section */
    if (values.redis.auth) {
        response.redis.password = <PASSWORD>.<PASSWORD>
    }
    /** https section */
    if (values.tls.enable) {
        response.https.keyPath = values.tls.key
        response.https.certPath = values.tls.cert
        response.port.https = values.ports.web.https
    }

    return response
}
<file_sep>var crypto = require('crypto')

/**
 * create radom string.
 *
 * http://qiita.com/sifue/items/06032a373a1168e5b6e0
 */
module.exports = () => crypto.randomBytes(15).toString('hex')<file_sep>misskey-gen
=====
generate configurations for below projects under misskey-delta.  
- [misskey-api](https://github.com/misskey-delta/misskey-api)
- [misskey-web](https://github.com/misskey-delta/misskey-web)
- [misskey-file](https://github.com/misskey-delta/misskey-file)

Usage
-----
1. clone this repository.
2. run `npm start`. after you answered 10-20 questions, misskey-gen generates configurations to `store` directory.
3. move json files in `store` to your `$HOME/.misskey`.
4. add `store/hosts-snippet` to your hosts file. (_optional_)

### input
you can input settings by interacting, or environment variables. want list of environment variables, see below.

| name | required | type | sample |
| :-- | :-- | :-- | :-- |
| MISSKEY_GEN_URLS_PRIMARY | yes | url string | http://example.com:8081/ |
| MISSKEY_GEN_URLS_SECONDARY | yes | url string | http://example.com:8084/ |
| MISSKEY_GEN_TLS_ENABLE | yes | bool (0 or 1) | 0 |
| MISSKEY_GEN_TLS_KEY | if TLS enabled, yes | path string | /etc/letsencrypt/live/example.com/privkey.pem |
| MISSKEY_GEN_TLS_CERT | if TLS enabled, yes | path string | /etc/letsencrypt/live/example.com/fullchain.pem |
| MISSKEY_GEN_MONGO_HOST | yes | host string | localhost:27017 |
| MISSKEY_GEN_MONGO_AUTH | yes | bool (0 or 1) | 0 |
| MISSKEY_GEN_MONGO_USER | if MONGO auth enabled, yes | string | sample |
| MISSKEY_GEN_MONGO_PASSWORD | if MONGO auth enabled, yes | string | sample |
| MISSKEY_GEN_REDIS_HOST | yes | host string | localhost:6379 |
| MISSKEY_GEN_REDIS_AUTH | yes | bool (0 or 1) | 0 |
| MISSKEY_GEN_REDIS_PASSWORD | yes | string | sample |
| MISSKEY_GEN_PORTS_API | yes | number | 8080 |
| MISSKEY_GEN_PORTS_WEB_HTTP | yes | number | 8081 |
| MISSKEY_GEN_PORTS_WEB_HTTPS | if TLS enabled, yes | number | 8082 |
| MISSKEY_GEN_PORTS_FILE_INTERNAL | yes | number | 8083 |
| MISSKEY_GEN_PORTS_FILE_HTTP | yes | number | 8084 |
| MISSKEY_GEN_PORTS_FILE_HTTPS | if TLS enabled, yes | number | 8085 | 
| MISSKEY_GEN_RECAPTCHA_SITE | yes | reCAPTHCHA Site key | <KEY> |
| MISSKEY_GEN_RECAPTCHA_SECRET | yes | reCAPTCHA Secret key | <KEY> |
| MISSKEY_GEN_THEME_COLOR | yes | color string | #666666 |
| MISSKEY_GEN_FILE_STORAGE_PATH | yes | path string | /home/misskey/storage |

Tips
-----

### domains
web uses subdomains under primary domain:  

| subdomain name | description | implemented | need to open |
| :-- | :-- | :-- | :-- |
| auth | auth use applications | no | no |
| signup | user signup to misskey | yes | yes |
| signin | user signin point | yes | yes |
| signout | user signout point | yes | yes |
| resources | delivery css & js | yes | yes |
| about | about page | yes | yes |
| search | search posts | yes | yes |
| talk | direct messages | yes | yes |
| himasaku | api proxy | yes | yes |
| share | providing embedding share script | yes | no |

api uses subdomain under primary domain:  

| subdomain name | description | used by program | need to open |
| :-- | :-- |:-- | :-- |
| api | internal api domain | yes | no, **must not**. |

file uses secondary domain.

License
-----
public license.
<file_sep>module.exports = (values, apiKey, fileKey) => {
    var host, protocol, temp
    [temp, protocol, host] = /^(https?:\/\/)(.*)$/.exec(values.urls.secondary)
    // split magic (remove end slash)
    host = host.split("/")[0]
    var response = {
        mongo: {
            uri: "mongodb://" + values.mongo.host + "/misskey-api"
        },
        redis: {
            host: values.redis.host,
            port: 6379
        },
        fileServer: {
            passkey: fileKey,
            url: protocol + host,
            ip: "127.0.0.1",
            port: values.ports.file.internal
        },
        apiPasskey: apiKey,
        port: {
            http: values.ports.api
        },
        https: {
            enable: false
        }
    }
    /** mongo section */
    if (values.mongo.auth) {
        response.mongo.options = {
            user: values.mongo.authUser,
            password: <PASSWORD>
        }
    }
    /** redis section */
    if (values.redis.auth) {
        response.redis.password = values.redis.authPassword
    }
    return response
}<file_sep>var question = require("./tools/question")

module.exports = async () => {
    var isValidURL = (value) => {
        const regExp = /^(http)|(https):.+\/?$/
        return regExp.test(value)
    }
    var values = {
        urls: {},
        tls: {},
        mongo: {},
        redis: {},
        ports: {
            web: {},
            file: {}
        },
        recaptcha: {}
    }
    /** url section */
    values.urls.primary =
        process.env.MISSKEY_GEN_URLS_PRIMARY ||
        await question.input("what is primary URL for api & web servers? (ex. https://misskey.cf/)", isValidURL)
    values.urls.secondary =
        process.env.MISSKEY_GEN_URLS_SECONDARY ||
        await question.input("what is secondary url for file server? (ex. https://uc-misskey.cf/)", isValidURL)
    /** tls section */
    values.tls.enable =
        process.env.MISSKEY_GEN_TLS_ENABLE
        ? Boolean(parseInt(process.env.MISSKEY_GEN_TLS_ENABLE))
        : await question.confirm("do you want to use Node.js providing TLS?")
    if (values.tls.enable) {
        values.tls.key =
            process.env.MISSKEY_GEN_TLS_KEY ||
            await question.input("where is TLS key file?")
        values.tls.cert =
            process.env.MISSKEY_GEN_TLS_CERT ||
            await question.input("where is TLS cert file?")
    }
    /** mongoDB section */
    values.mongo.host =
        process.env.MISSKEY_GEN_MONGO_HOST ||
        await question.input("what is MongoDB server hostname? (ex. localhost)")
    values.mongo.auth =
        process.env.MISSKEY_GEN_MONGO_AUTH
        ? Boolean(parseInt(process.env.MISSKEY_GEN_MONGO_AUTH))
        : await question.confirm("do you want to use MongoDB authentication?")
    if (values.mongo.auth) {
        values.mongo.authUser =
            process.env.MISSKEY_GEN_MONGO_USER ||
            await question.confirm("what is MongoDB's username?")
        values.mongo.authPassword =
            process.env.MISSKEY_GEN_MONGO_PASSWORD ||
            await question.input("what is MongoDB's password?")
    }
    /** redis section */
    values.redis.host =
        process.env.MISSKEY_GEN_REDIS_HOST ||
        await question.input("what is Redis server hostname? (ex. localhost)")
    values.redis.auth =
        process.env.MISSKEY_GEN_REDIS_AUTH
        ? Boolean(parseInt(process.env.MISSKEY_GEN_REDIS_AUTH))
        : await question.confirm("do you want to use Redis authentication?")
    if (values.redis.auth) {
        values.redis.authPassword =
            process.env.MISSKEY_GEN_REDIS_PASSWORD ||
            await question.input("what is Redis's password?")
    }
    /** port section */
    values.ports.api =
        process.env.MISSKEY_GEN_PORTS_API
        ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_API)
        : await question.inputNumber("what is http port to run misskey-api?")
    values.ports.web.http =
        process.env.MISSKEY_GEN_PORTS_WEB_HTTP
        ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_WEB_HTTP)
        : await question.inputNumber("what is http port to run misskey-web?")
    if (values.tls.enable) {
        values.ports.web.https =
            process.env.MISSKEY_GEN_PORTS_WEB_HTTPS
            ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_WEB_HTTPS)
            : await question.inputNumber("what is https port to run misskey-web?")
    }
    values.ports.file.internal =
        process.env.MISSKEY_GEN_PORTS_FILE_INTERNAL
        ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_FILE_INTERNAL)
        : await question.inputNumber("what is internal http port to run misskey-file?")
    values.ports.file.http =
        process.env.MISSKEY_GEN_PORTS_FILE_HTTP
        ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_FILE_HTTP)
        : await question.inputNumber("what is http port to run misskey-file?")
    if (values.tls.enable) {
        values.ports.file.https =
            process.env.MISSKEY_GEN_PORTS_FILE_HTTPS
            ? Number.parseInt(process.env.MISSKEY_GEN_PORTS_FILE_HTTPS)
            : await question.inputNumber("what is https port to run misskey-file?")
    }
    /** recaptcha section */
    values.recaptcha.site =
        process.env.MISSKEY_GEN_RECAPTCHA_SITE ||
        await question.input("please input recaptcha site key.")
    values.recaptcha.secret =
        process.env.MISSKEY_GEN_RECAPTCHA_SECRET ||
        await question.input("please input recaptcha secret key.")
    /** theme color */
    values.themeColor =
        process.env.MISSKEY_GEN_THEME_COLOR ||
        await question.input("what color do you want use in misskey? (ex. #666666)")
    /** file storage */
    values.fileStorage =
        process.env.MISSKEY_GEN_FILE_STORAGE_PATH ||
        await question.input("please input path for misskey-file storage.")
    return values
}<file_sep>/* require modules */
var readline = require('readline')

/**
 * question prover: input
 *
 * @param query {string} question sentence
 * @param callback {function} function to test inputted value, taking an argument of inputted value. (option)
 * @param reInputNotifier {string} re-input error sentence (option)
 * @returns {string} inputted value that passed callback.
 */
var question = async (query, callback, reInputNotifier = "not a valid input value") => {
    /* interface.question promise wrapper */
    var q = (query, noReturn = false, retryTimes) => new Promise((resolve, reject) => {
        if (retryTimes) {
            console.log(`${query} (retry ${retryTimes} times)`)
        } else {
            console.log(query)
        }
        process.stdout.write("  > ")

        if (process.stdin.isTTY) process.stdin.setRawMode(true)
        readline.emitKeypressEvents(process.stdin)
    
        // handler for process.stdin event named 'keypress'
        var union = ""
        var stdinHandler = (ch, key) => {
            var handler = (input) => {
                process.stdin.removeListener("keypress", stdinHandler)
                if (process.stdin.isTTY) process.stdin.setRawMode(false)
                // clear previous line
                readline.cursorTo(process.stdout, 0)
                // noReturn time, remove input in this line
                if (noReturn) readline.clearLine(process.stdout, 1)
                readline.moveCursor(process.stdout, 0, -1)
                readline.clearLine(process.stdout, 1)
                resolve(input)
            }

            if (key.ctrl === true & key.name === "c") {
                process.exit()
            }
            // magic with noReturn
            else if (key.name === "return") {
                if (! noReturn) process.stdout.write("\n")
                handler(union)
            }
            // OH LEGACY!!!
            else if (key.name === key.sequence.toLowerCase() || key.name === undefined || key.name === "space") {
                union += key.sequence
                process.stdout.write(key.sequence)
            } else if (key.name === "backspace" && union.length > 0) {
                readline.moveCursor(process.stdout, -1)
                readline.clearLine(process.stdout, 1)
                union = union.slice(0, union.length - 1)
            }
        }

        process.stdin.on("keypress", stdinHandler)
    })

    var result = await q(`[?] ${query}`)

    /* if callback exists, check with it! */
    if (callback && typeof callback === "function") {
        var fuck = 0
        while (! callback(result) === true) {
            result = await q(`[!] ${reInputNotifier}, please re-input...`, true, fuck)
            fuck++
        }
    }

    return result
}
exports.input = question

/**
 * question prover: confirm
 *
 * @param query {string} question sentence
 * @returns {boolean}
 */
exports.confirm = async (query) => {
    var result = await question(`${query} (y/n)`, (answer) => /^y|n$/.test(answer), "not y/n")
    return "y" === result
}

/**
 * question provider: inputNumber
 *
 * @param query {string} question sentence
 * @returns {number}
 */
exports.inputNumber = async (query) => {
    var result = await question(`${query} (number)`, (answer) => /^\d+$/.test(answer), "not a valid input number")
    return Number.parseInt(result)
}
