refactor(cli): add CLI utility entry point and fix rsa_private_key typo
- Correct typo: rsa -> rsa_private_key - Refactor cli.py so functions can be used by both interfaces - Introduce wikideck_cli.py as a CLI utility entry point - Public and private keys can now be saved separately
This commit is contained in:
parent
874cbff8c0
commit
d8a29c0a11
124
cli.py
124
cli.py
@ -17,8 +17,8 @@ WIKIDECK_URL = os.getenv('WIKIDECK_URL', 'http://localhost:8080/');
|
||||
|
||||
rsa_private_key: Optional[rsa.RSAPrivateKey] = None;
|
||||
msg_array: list[str] = [""];
|
||||
private_key_file: str = "private_key.pem";
|
||||
|
||||
private_key_file: str = "privatekey.pem";
|
||||
public_key_file: str = "publickey.pem";
|
||||
|
||||
|
||||
def main():
|
||||
@ -30,11 +30,12 @@ def main():
|
||||
|
||||
print("\n--- Wikideck CLI ---");
|
||||
print("1. Create RSA Keypair");
|
||||
print("2. Save RSA Keypair");
|
||||
print("2. Save Private Key");
|
||||
print("3. Load RSA Keypair");
|
||||
print("4. Mine a Block");
|
||||
print("5. Generate a Transaction");
|
||||
print("6. View Deck");
|
||||
print("4. Save Public Key")
|
||||
print("5. Mine a Block");
|
||||
print("6. Generate a Transaction");
|
||||
print("7. View Deck");
|
||||
print("0. Exit");
|
||||
print("\n-------------------");
|
||||
|
||||
@ -49,43 +50,62 @@ def main():
|
||||
if choice == 0:
|
||||
break;
|
||||
elif choice == 1:
|
||||
opt_generate_private_key(65537, 2048);
|
||||
opt_generate_keypair(65537, 2048);
|
||||
elif choice == 2:
|
||||
opt_save_private_key();
|
||||
opt_save_private_key(rsa_private_key);
|
||||
elif choice == 3:
|
||||
opt_load_private_key();
|
||||
elif choice == 4:
|
||||
opt_mine_block();
|
||||
opt_save_public_key(rsa_private_key);
|
||||
elif choice == 5:
|
||||
opt_generate_transaction();
|
||||
opt_mine_block();
|
||||
elif choice == 6:
|
||||
opt_generate_transaction();
|
||||
elif choice == 7:
|
||||
opt_view_deck();
|
||||
else:
|
||||
msg_array[0] = "Invalid Input. Please Choose a Number Between 0-6.";
|
||||
|
||||
clear_console();
|
||||
|
||||
def opt_generate_private_key(exponent, size):
|
||||
def opt_generate_keypair(exponent=65537, size=2048):
|
||||
global rsa_private_key;
|
||||
rsa_private_key = rsa.generate_private_key(
|
||||
public_exponent = exponent,
|
||||
key_size = size,
|
||||
)
|
||||
msg_array[0] = "RSA Keypair Generated";
|
||||
return rsa_private_key;
|
||||
|
||||
def opt_save_private_key():
|
||||
if rsa_private_key is None:
|
||||
msg_array[0] = "No RSA Keypair to Save. Please Generate one first.";
|
||||
def opt_save_private_key(rsa, filename=private_key_file):
|
||||
if rsa is None:
|
||||
msg_array[0] = "No RSA Keypair to extract private key. Please generate one first.";
|
||||
return None;
|
||||
else:
|
||||
with open(private_key_file, "wb") as file:
|
||||
pem = rsa_private_key.private_bytes(
|
||||
with open(filename, "wb") as file:
|
||||
pem = rsa.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
file.write(pem);
|
||||
msg_array[0] = f"RSA Private Key Saved to {private_key_file}";
|
||||
msg_array[0] = f"RSA Private Key Saved to {filename}";
|
||||
|
||||
def opt_save_public_key(rsa, filename=public_key_file):
|
||||
if rsa is None:
|
||||
msg_array[0] = "No RSA Keypair to extract public key. Please generate one first.";
|
||||
return None;
|
||||
|
||||
public_key = rsa.public_key();
|
||||
|
||||
public_pem = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
)
|
||||
|
||||
with open(filename, "wb") as file:
|
||||
file.write(public_pem);
|
||||
msg_array[0] = f"RSA Public Key Saved to {filename}";
|
||||
|
||||
def opt_load_private_key():
|
||||
global rsa_private_key;
|
||||
@ -100,10 +120,28 @@ def opt_load_private_key():
|
||||
)
|
||||
msg_array[0] = f"RSA Private Key Loaded From {private_key_file}";
|
||||
|
||||
def opt_mine_block():
|
||||
def get_private_key_from_file(path):
|
||||
if not os.path.exists(path):
|
||||
msg_array[0] = f"No key file found ({path} missing)";
|
||||
return None;
|
||||
|
||||
with open(path, "rb") as file:
|
||||
key = serialization.load_pem_private_key(
|
||||
file.read(),
|
||||
password=None,
|
||||
);
|
||||
msg_array[0] = f"RSA Private Key Gotten From {path}"; #inaccessible
|
||||
|
||||
return key;
|
||||
|
||||
def opt_mine_block(path = None):
|
||||
|
||||
global rsa_private_key;
|
||||
|
||||
if (path):
|
||||
rsa_private_key = get_private_key_from_file(path);
|
||||
if not (rsa_private_key):
|
||||
return None;
|
||||
|
||||
if rsa_private_key is None:
|
||||
msg_array[0] = "You must load or generate an RSA keypair first.";
|
||||
@ -127,29 +165,45 @@ def opt_mine_block():
|
||||
except Exception as e:
|
||||
msg_array[0] = f"Error during mining: {str(e)}";
|
||||
|
||||
def opt_generate_transaction():
|
||||
def opt_generate_transaction(path = None, recv = None):
|
||||
global rsa_private_key;
|
||||
|
||||
if (path):
|
||||
rsa_private_key = get_private_key_from_file(path);
|
||||
if not (rsa_private_key):
|
||||
return None;
|
||||
|
||||
|
||||
if rsa_private_key is None:
|
||||
msg_array[0] = "You must load or generate an RSA keypair first.";
|
||||
return;
|
||||
|
||||
print("\nPaste the recipient's RSA Public Key (PEM format). End with an Empty Line (Press enter twice): ");
|
||||
lines = [];
|
||||
if not (recv):
|
||||
print("\nPaste the recipient's RSA Public Key (PEM format). End with an Empty Line (Press enter twice): ");
|
||||
lines = [];
|
||||
while True:
|
||||
line = input();
|
||||
if not line.strip():
|
||||
break;
|
||||
lines.append(line);
|
||||
|
||||
while True:
|
||||
line = input();
|
||||
if not line.strip():
|
||||
break;
|
||||
lines.append(line);
|
||||
receiver_pem = "\n".join(lines);
|
||||
|
||||
try:
|
||||
receiver_key = serialization.load_pem_public_key(receiver_pem.encode("utf-8"));
|
||||
except Exception:
|
||||
msg_array[0] = "Invalid public key format";
|
||||
return;
|
||||
|
||||
receiver_pem = "\n".join(lines);
|
||||
|
||||
try:
|
||||
receiver_key = serialization.load_pem_public_key(receiver_pem.encode("utf-8"));
|
||||
except Exception:
|
||||
msg_array[0] = "Invalid public key format";
|
||||
return;
|
||||
else:
|
||||
if not os.path.isfile(recv):
|
||||
msg_array[0] = f"Reciever public key file not found {recv}";
|
||||
return None;
|
||||
with open(recv, "rb") as recv_file:
|
||||
receiver_key = serialization.load_pem_public_key(recv_file.read());
|
||||
|
||||
|
||||
|
||||
|
||||
try:
|
||||
|
||||
@ -193,6 +247,7 @@ def opt_generate_transaction():
|
||||
except Exception as e:
|
||||
msg_array[0] = f"Error creating transaction: {str(e)}";
|
||||
|
||||
|
||||
def opt_view_deck():
|
||||
|
||||
global rsa_private_key;
|
||||
@ -201,7 +256,7 @@ def opt_view_deck():
|
||||
return;
|
||||
|
||||
try:
|
||||
cards = fetch_cards_json(rsa);
|
||||
cards = fetch_cards_json(rsa_private_key);
|
||||
|
||||
|
||||
if len(cards) <= 0:
|
||||
@ -214,6 +269,7 @@ def opt_view_deck():
|
||||
except Exception as e:
|
||||
msg_array[0] = f"Error fetching cards: {str(e)}";
|
||||
|
||||
|
||||
def fetch_cards_json(rsa):
|
||||
pem = rsa.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
|
155
wikideck_cli.py
Executable file
155
wikideck_cli.py
Executable file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
from cli import opt_generate_keypair
|
||||
from cli import opt_save_private_key
|
||||
from cli import opt_load_private_key
|
||||
from cli import opt_save_public_key
|
||||
from cli import opt_mine_block
|
||||
from cli import opt_generate_transaction
|
||||
from cli import opt_view_deck
|
||||
from cli import msg_array
|
||||
from cli import get_private_key_from_file
|
||||
from cli import fetch_cards_json
|
||||
|
||||
DEFAULT_FILENAME: str = "privatekey";
|
||||
|
||||
def resolve_path(path: str | None) -> str | None:
|
||||
if not path or path.strip() == "":
|
||||
return None;
|
||||
|
||||
path = path.strip();
|
||||
|
||||
if not path.endswith(".pem"):
|
||||
path += ".pem";
|
||||
|
||||
if os.path.exists(path) and os.path.isfile(path):
|
||||
return path;
|
||||
|
||||
return None;
|
||||
|
||||
|
||||
def resolve_path_for_key_creation(filename:str | None) -> str:
|
||||
default_name = "privatekey.pem";
|
||||
|
||||
if not filename or filename.strip() == "":
|
||||
return default_name;
|
||||
|
||||
filename = filename.strip();
|
||||
|
||||
if os.path.isdir(filename):
|
||||
return os.path.join(filename, default_name);
|
||||
|
||||
if not filename.endswith(".pem"):
|
||||
filename += ".pem";
|
||||
|
||||
dir_path = os.path.dirname(filename);
|
||||
if dir_path and not os.path.exists(dir_path):
|
||||
os.makedirs(dir_path);
|
||||
|
||||
return filename;
|
||||
|
||||
def get_first_pem_file():
|
||||
pem_files = glob.glob("*.pem");
|
||||
|
||||
if pem_files:
|
||||
first_pem = min(pem_files, key=os.path.getmtime);
|
||||
return first_pem;
|
||||
else:
|
||||
return None;
|
||||
|
||||
def handle_createkey(args):
|
||||
rsa_key = opt_generate_keypair();
|
||||
filepath = resolve_path_for_key_creation(args.output);
|
||||
opt_save_private_key(rsa_key, filepath);
|
||||
print(msg_array[0]);
|
||||
public_path = f"pub_{filepath}"
|
||||
opt_save_public_key(rsa_key, public_path);
|
||||
|
||||
def handle_mine(args):
|
||||
file = resolve_path(args.use);
|
||||
if not file:
|
||||
file = get_first_pem_file();
|
||||
if not file:
|
||||
msg_array[0] = "No valid private key found";
|
||||
return;
|
||||
|
||||
opt_mine_block(file);
|
||||
|
||||
|
||||
def handle_tx(args):
|
||||
|
||||
file = resolve_path(args.use);
|
||||
if not file:
|
||||
file = get_first_pem_file();
|
||||
if not file:
|
||||
msg_array[0] = "No valid private key found";
|
||||
return;
|
||||
|
||||
recv = resolve_path(args.receiver);
|
||||
if args.receiver and not recv:
|
||||
msg_array[0] = "No valid public key found (receiver)";
|
||||
return;
|
||||
|
||||
opt_generate_transaction(file, recv);
|
||||
|
||||
|
||||
def handle_deck(args):
|
||||
file = resolve_path(args.use);
|
||||
if not file:
|
||||
file = get_first_pem_file();
|
||||
if not file:
|
||||
msg_array[0] = "No valid private key found";
|
||||
return;
|
||||
|
||||
rsa = get_private_key_from_file(file);
|
||||
if rsa is None:
|
||||
return;
|
||||
|
||||
cards = fetch_cards_json(rsa);
|
||||
|
||||
if len(cards) < 1:
|
||||
msg_array[0] = "You have no cards to view";
|
||||
return;
|
||||
|
||||
for i, card in enumerate(cards):
|
||||
print(f"{i+1}. Card ID: {card['cardId']}, Page ID: {card['pageId']}");
|
||||
|
||||
|
||||
def build_parser():
|
||||
parser = argparse.ArgumentParser(description="Wikideck CLI");
|
||||
subparsers = parser.add_subparsers(dest="command", required=True);
|
||||
|
||||
#createkey
|
||||
p_create = subparsers.add_parser("createkey",aliases=["ck"], help="Create an RSA keypair and save it to the current directory");
|
||||
p_create.add_argument("-O", "--output", help="Optional custom filename (.pem)");
|
||||
p_create.set_defaults(func=handle_createkey);
|
||||
|
||||
#mine
|
||||
p_mine = subparsers.add_parser("mine", help="Mine a block, using the first ./*.pem by default.");
|
||||
p_mine.add_argument("-u", "--use", help="Use specified keypair (path to .pem file)");
|
||||
p_mine.set_defaults(func=handle_mine);
|
||||
|
||||
#transaction
|
||||
p_transaction = subparsers.add_parser("transaction", aliases=["tx"], help="Generate a transaction, using the first ./*.pem by default.");
|
||||
p_transaction.add_argument("-u", "--use", help="Use specified keypair (path to .pem file)");
|
||||
p_transaction.add_argument("-to", "--receiver", help="Send to specified public key (path to .pem file)");
|
||||
p_transaction.set_defaults(func=handle_tx);
|
||||
|
||||
#deck
|
||||
p_deck = subparsers.add_parser("deck", help="Show all cards tied to the private key, using the first ./*.pem by default.");
|
||||
p_deck.add_argument("-u", "--use", help="Use specified keypair (path to .pem file)");
|
||||
p_deck.set_defaults(func=handle_deck);
|
||||
|
||||
return parser;
|
||||
|
||||
def main():
|
||||
parser = build_parser();
|
||||
args = parser.parse_args();
|
||||
args.func(args);
|
||||
print(msg_array[0]);
|
||||
|
||||
if __name__ == "__main__":
|
||||
main();
|
Loading…
Reference in New Issue
Block a user