diff --git a/backend/scripts/manage.py b/backend/scripts/manage.py index ea90d3e86..c56b33389 100755 --- a/backend/scripts/manage.py +++ b/backend/scripts/manage.py @@ -11,6 +11,7 @@ import json import socket import sys +from tabulate import tabulate from getpass import getpass from urllib.parse import urlparse @@ -58,13 +59,17 @@ def print_error(res): break def run_cmd(params): - expr = "(app.srepl.ext/run-json-cmd {})".format(encode(params)) - res, failed = send_eval(expr) - if failed: - print_error(res) - sys.exit(-1) + try: + expr = "(app.srepl.ext/run-json-cmd {})".format(encode(params)) + res, failed = send_eval(expr) + if failed: + print_error(res) + sys.exit(-1) - return res + return res + except Exception as cause: + print("EXC:", str(cause)) + sys.exit(-2) def create_profile(fullname, email, password): params = { @@ -96,6 +101,34 @@ def update_profile(email, fullname, password, is_active): else: print(f"No profile found with email {email}") +def delete_profile(email, soft): + params = { + "cmd": "delete-profile", + "params": { + "email": email, + "soft": soft + } + } + + res = run_cmd(params) + if res is True: + print(f"Deleted") + else: + print(f"No profile found with email {email}") + +def search_profile(email): + params = { + "cmd": "search-profile", + "params": { + "email": email, + } + } + + res = run_cmd(params) + + if isinstance(res, list): + print(tabulate(res, headers="keys")) + def derive_password(password): params = { "cmd": "derive-password", @@ -107,11 +140,13 @@ def derive_password(password): res = run_cmd(params) print(f"Derived password: \"{res}\"") -available_commands = [ +available_commands = ( "create-profile", "update-profile", - "derive-password" -] + "delete-profile", + "search-profile", + "derive-password", +) parser = argparse.ArgumentParser( description=( @@ -121,10 +156,11 @@ parser = argparse.ArgumentParser( parser.add_argument("-V", "--version", action="version", version="Penpot CLI %%develop%%") parser.add_argument("action", action="store", choices=available_commands) -parser.add_argument("-n", "--fullname", help="Fullname", action="store") -parser.add_argument("-e", "--email", help="Email", action="store") -parser.add_argument("-p", "--password", help="Password", action="store") -parser.add_argument("-c", "--connect", help="Connect to PREPL", action="store", default="tcp://localhost:6063") +parser.add_argument("-f", "--force", help="force operation", action="store_true") +parser.add_argument("-n", "--fullname", help="fullname", action="store") +parser.add_argument("-e", "--email", help="email", action="store") +parser.add_argument("-p", "--password", help="password", action="store") +parser.add_argument("-c", "--connect", help="connect to PREPL", action="store", default="tcp://localhost:6063") args = parser.parse_args() @@ -165,3 +201,19 @@ elif args.action == "derive-password": password = getpass("Password: ") derive_password(password) + +elif args.action == "delete-profile": + email = args.email + soft = not args.force + + if email is None: + email = input("Email: ") + + delete_profile(email, soft) + +elif args.action == "search-profile": + email = args.email + if email is None: + email = input("Email: ") + + search_profile(email) diff --git a/backend/src/app/srepl/ext.clj b/backend/src/app/srepl/ext.clj index 40b44fafe..4c880ecb4 100644 --- a/backend/src/app/srepl/ext.clj +++ b/backend/src/app/srepl/ext.clj @@ -13,6 +13,7 @@ [app.db :as db] [app.rpc.commands.auth :as cmd.auth] [app.util.json :as json] + [app.util.time :as dt] [cuerdas.core :as str])) (defn- get-current-system @@ -63,9 +64,43 @@ params {:email email :deleted-at nil} - {:return-keys false})] + {::db/return-keys? false})] (pos? (:next.jdbc/update-count res)))))))) +(defmethod run-json-cmd* :delete-profile + [{:keys [email soft]}] + (when-not email + (ex/raise :type :assertion + :code :invalid-arguments + :hint "email should be provided")) + + (when-let [system (get-current-system)] + (db/with-atomic [conn (:app.db/pool system)] + + (let [res (if soft + (db/update! conn :profile + {:deleted-at (dt/now)} + {:email email :deleted-at nil} + {::db/return-keys? false}) + (db/delete! conn :profile + {:email email} + {::db/return-keys? false}))] + (pos? (:next.jdbc/update-count res)))))) + +(defmethod run-json-cmd* :search-profile + [{:keys [email]}] + (when-not email + (ex/raise :type :assertion + :code :invalid-arguments + :hint "email should be provided")) + + (when-let [system (get-current-system)] + (db/with-atomic [conn (:app.db/pool system)] + + (let [sql (str "select email, fullname, created_at, deleted_at from profile " + " where email similar to ? order by created_at desc limit 100")] + (db/exec! conn [sql email]))))) + (defmethod run-json-cmd* :derive-password [{:keys [password]}] (auth/derive-password password)) diff --git a/docker/images/Dockerfile.backend b/docker/images/Dockerfile.backend index 21c7aba40..76fbb8d00 100644 --- a/docker/images/Dockerfile.backend +++ b/docker/images/Dockerfile.backend @@ -25,6 +25,7 @@ RUN set -ex; \ woff-tools \ woff2 \ python3 \ + python3-tabulate \ fontforge \ ; \ echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \