Skip to content

Commit d6acba5

Browse files
tusharshah21joelamouche
authored andcommitted
Add ADMIN_ADDRESSES to env.example and test script for admin endpoint
1 parent 50479d4 commit d6acba5

2 files changed

Lines changed: 147 additions & 1 deletion

File tree

backend/.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ DATABASE_URL=postgres://guild_user:guild_password@localhost:5432/guild_genesis
66

77
# JWT Configuration
88
JWT_SECRET=your-super-secret-jwt-key-change-in-production
9-
JWT_EXPIRATION=86400
9+
JWT_EXPIRATION=86400
10+
11+
# Admin Configuration (comma-separated wallet addresses)
12+
# ADMIN_ADDRESSES=0xYourAdminAddress1,0xAnotherAdminAddress2

scripts/test_admin_delete.sh

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Test the /admin/profiles/:address delete endpoint.
5+
# Requirements: curl, node, npm. Installs ethers locally into /tmp by default.
6+
#
7+
# Inputs (env):
8+
# ADMIN_ADDRESS (required) - admin wallet address
9+
# ADMIN_PRIVATE_KEY (required) - admin wallet private key (0x-prefixed)
10+
# TARGET_ADDRESS (required) - target profile address to delete
11+
# API_URL (optional) - defaults to http://localhost:3001
12+
13+
API_URL="${API_URL:-http://localhost:3001}"
14+
ADMIN_ADDRESS="${ADMIN_ADDRESS:-}"
15+
ADMIN_PRIVATE_KEY="${ADMIN_PRIVATE_KEY:-}"
16+
TARGET_ADDRESS="${TARGET_ADDRESS:-}"
17+
18+
# If not provided via env, prompt interactively
19+
if [[ -z "${ADMIN_ADDRESS}" ]]; then
20+
read -r -p "Enter ADMIN_ADDRESS (0x...): " ADMIN_ADDRESS
21+
fi
22+
if [[ -z "${ADMIN_PRIVATE_KEY}" ]]; then
23+
read -r -s -p "Enter ADMIN_PRIVATE_KEY (0x..., hidden): " ADMIN_PRIVATE_KEY
24+
echo
25+
fi
26+
if [[ -z "${TARGET_ADDRESS}" ]]; then
27+
read -r -p "Enter TARGET_ADDRESS to delete (0x...): " TARGET_ADDRESS
28+
fi
29+
30+
if [[ -z "${ADMIN_ADDRESS}" || -z "${ADMIN_PRIVATE_KEY}" || -z "${TARGET_ADDRESS}" ]]; then
31+
echo "ADMIN_ADDRESS, ADMIN_PRIVATE_KEY and TARGET_ADDRESS are required. Aborting."
32+
exit 1
33+
fi
34+
35+
# Ensure we have ethers available
36+
TOOLS_DIR="${TOOLS_DIR:-/tmp/theguildgenesis-login}"
37+
export NODE_PATH="${TOOLS_DIR}/node_modules${NODE_PATH:+:${NODE_PATH}}"
38+
export PATH="${TOOLS_DIR}/node_modules/.bin:${PATH}"
39+
if ! node -e "require('ethers')" >/dev/null 2>&1; then
40+
echo "Installing ethers@6 to ${TOOLS_DIR}..."
41+
mkdir -p "${TOOLS_DIR}"
42+
npm install --prefix "${TOOLS_DIR}" ethers@6 >/dev/null
43+
fi
44+
45+
echo "Fetching nonce for admin ${ADMIN_ADDRESS}..."
46+
nonce_resp="$(curl -sS "${API_URL}/auth/nonce/${ADMIN_ADDRESS}")"
47+
echo "Nonce response: ${nonce_resp}"
48+
49+
# Parse nonce
50+
nonce="$(RESP="${nonce_resp}" python3 - <<'PY'
51+
import json, os
52+
data = json.loads(os.environ["RESP"])
53+
print(data["nonce"])
54+
PY
55+
)"
56+
if [[ -z "${nonce}" ]]; then
57+
echo "Failed to parse nonce from response"
58+
exit 1
59+
fi
60+
61+
message=$'Sign this message to authenticate with The Guild.\n\nNonce: '"${nonce}"
62+
63+
echo "Signing nonce..."
64+
signature="$(
65+
ADDRESS="${ADMIN_ADDRESS}" PRIVATE_KEY="${ADMIN_PRIVATE_KEY}" MESSAGE="${message}" \
66+
node - <<'NODE'
67+
const { Wallet, hashMessage } = require('ethers');
68+
69+
const address = process.env.ADDRESS;
70+
const pk = process.env.PRIVATE_KEY;
71+
const message = process.env.MESSAGE;
72+
73+
if (!address || !pk || !message) {
74+
console.error("Missing ADDRESS, PRIVATE_KEY or MESSAGE");
75+
process.exit(1);
76+
}
77+
78+
const wallet = new Wallet(pk);
79+
if (wallet.address.toLowerCase() !== address.toLowerCase()) {
80+
console.error(`Private key does not match address. Wallet: ${wallet.address}, Provided: ${address}`);
81+
process.exit(1);
82+
}
83+
84+
(async () => {
85+
const sig = await wallet.signMessage(message);
86+
console.log(sig);
87+
})();
88+
NODE
89+
)"
90+
91+
echo "Signature: ${signature}"
92+
93+
echo "Logging in as admin..."
94+
login_tmp="$(mktemp)"
95+
http_status="$(curl -sS -o "${login_tmp}" -w "%{http_code}" -X POST \
96+
-H "x-eth-address: ${ADMIN_ADDRESS}" \
97+
-H "x-eth-signature: ${signature}" \
98+
"${API_URL}/auth/login")"
99+
login_resp="$(cat "${login_tmp}")"
100+
rm -f "${login_tmp}"
101+
102+
echo "Login HTTP ${http_status}: ${login_resp}"
103+
if [[ "${http_status}" != "200" ]]; then
104+
echo "Login failed with status ${http_status}"
105+
exit 1
106+
fi
107+
108+
jwt="$(RESP="${login_resp}" python3 - <<'PY'
109+
import json, os
110+
data = json.loads(os.environ["RESP"])
111+
print(data["token"])
112+
PY
113+
)"
114+
if [[ -z "${jwt}" ]]; then
115+
echo "Failed to parse JWT from login response"
116+
exit 1
117+
fi
118+
119+
echo "JWT obtained: ${jwt:0:20}..."
120+
121+
echo "Deleting profile ${TARGET_ADDRESS} via admin endpoint..."
122+
delete_tmp="$(mktemp)"
123+
delete_status="$(curl -sS -o "${delete_tmp}" -w "%{http_code}" -X DELETE \
124+
-H "Authorization: Bearer ${jwt}" \
125+
"${API_URL}/admin/profiles/${TARGET_ADDRESS}")"
126+
delete_resp="$(cat "${delete_tmp}")"
127+
rm -f "${delete_tmp}"
128+
129+
echo "Delete HTTP ${delete_status}: ${delete_resp}"
130+
if [[ "${delete_status}" == "204" ]]; then
131+
echo "✅ Profile deleted successfully!"
132+
elif [[ "${delete_status}" == "404" ]]; then
133+
echo "⚠️ Profile not found"
134+
elif [[ "${delete_status}" == "403" ]]; then
135+
echo "❌ Forbidden - address ${ADMIN_ADDRESS} is not an admin"
136+
elif [[ "${delete_status}" == "401" ]]; then
137+
echo "❌ Unauthorized - authentication failed"
138+
else
139+
echo "❌ Delete failed with status ${delete_status}"
140+
exit 1
141+
fi
142+
143+
echo "Done."

0 commit comments

Comments
 (0)