馃 An object-oriented Gemini server for Deno!
gemini-protocol deno typescript gemini
at main 135 lines 3.5 kB view raw
1// This file is part of Laurali <https://github.com/gemrest/laurali>. 2// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU General Public License as published by 6// the Free Software Foundation, version 3. 7// 8// This program is distributed in the hope that it will be useful, but 9// WITHOUT ANY WARRANTY; without even the implied warranty of 10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11// General Public License for more details. 12// 13// You should have received a copy of the GNU General Public License 14// along with this program. If not, see <http://www.gnu.org/licenses/>. 15// 16// Copyright (C) 2022-2022 Fuwn <contact@fuwn.me> 17// SPDX-License-Identifier: GPL-3.0-only 18 19/** An OpenSSL keypair config for use with `generateKey` */ 20export interface KeyConfig { 21 /** 22 * The subject of an OpenSSL keypair 23 * @default "CN=localhost" 24 */ 25 subj?: string; 26 /** 27 * The elliptic curve of an OpenSSL keypair 28 * @default "secp256k1" 29 */ 30 ec_paramgen_curve?: string; 31 /** 32 * The lifetime in days of an OpenSSL keypair 33 * @default "365" 34 */ 35 days?: string; 36 /** The output file of an OpenSSL public key */ 37 out: string; 38 /** The output file of an OpenSSL private key */ 39 keyOut: string; 40 /** 41 * The format of an OpenSSL keypair 42 * @default "pem" 43 */ 44 format?: string; 45} 46 47/** 48 * Generate an OpenSSL keypair 49 * @param config The configuration of the OpenSSL keypair 50 */ 51export const generateKey = async ( 52 config: KeyConfig, 53): Promise<Deno.ProcessStatus> => { 54 return await Deno.run({ 55 cmd: [ 56 "openssl", 57 "req", 58 "-new", 59 "-subj", 60 config.subj || "/CN=localhost", 61 "-x509", 62 "-newkey", 63 "ec", 64 "-pkeyopt", 65 `ec_paramgen_curve:${config.ec_paramgen_curve || "secp384r1"}`, 66 "-days", 67 config.days || "365", 68 "-nodes", 69 "-out", 70 config.out, 71 "-keyout", 72 config.keyOut, 73 "-inform", 74 config.format || "pem", 75 ], 76 stdout: "piped", 77 }).status(); 78}; 79 80/** https://stackoverflow.com/a/61868755/14452787 */ 81const exists = async (filename: string): Promise<boolean> => { 82 try { 83 await Deno.stat(filename); 84 85 return true; 86 } catch (e) { 87 if (e instanceof Deno.errors.NotFound) { 88 return false; 89 } else { 90 throw e; 91 } 92 } 93}; 94 95if (import.meta.main) { 96 const { parse } = await import("https://deno.land/std@0.139.0/flags/mod.ts"); 97 98 await Deno.mkdir(".laurali", { recursive: true }); 99 100 const generate = async () => { 101 await generateKey({ 102 subj: `/CN=${ 103 prompt( 104 "Which common name (hostname) would you like to use for the " + 105 "generated key?", 106 ) 107 }`, 108 out: `.laurali/public.pem`, 109 keyOut: `.laurali/private.pem`, 110 }); 111 }; 112 113 if ( 114 (await exists(".laurali/public.pem") || 115 await exists(".laurali/private.pem")) && 116 !parse(Deno.args, { "boolean": true }).overwrite 117 ) { 118 const overwrite = prompt( 119 "鈿狅笍 锔廇 file already exists at .laurali/public.pem or" + 120 ".laurali/private.pem.\n Overwrite? [y/n (y = yes overwrite, n = " + 121 "no keep)]", 122 ); 123 124 if (overwrite === "y") { 125 generate(); 126 } else { 127 throw new Error( 128 "Requires overwrite permissions to file(s), run again with " + 129 "the --overwrite flag", 130 ); 131 } 132 } else { 133 generate(); 134 } 135}