getting user; mod: avatar, profileBg, profile now working
This commit is contained in:
+196
-29
@@ -34,6 +34,12 @@ shutdown_event: asyncio.Event = None
|
|||||||
def post(path, **data):
|
def post(path, **data):
|
||||||
return requests.post(f"{BASE_URL}{path}", data=data)
|
return requests.post(f"{BASE_URL}{path}", data=data)
|
||||||
|
|
||||||
|
def post_auth(path, **data):
|
||||||
|
return requests.post(f"{BASE_URL}{path}", data={"token": token, **data})
|
||||||
|
|
||||||
|
def post_auth_header(path, files=None, **data):
|
||||||
|
return requests.post(f"{BASE_URL}{path}", headers={"token": token}, data=data, files=files)
|
||||||
|
|
||||||
def fmt_msg(m: dict) -> str:
|
def fmt_msg(m: dict) -> str:
|
||||||
sender = m.get("sender", "?")
|
sender = m.get("sender", "?")
|
||||||
content = m.get("content", "")
|
content = m.get("content", "")
|
||||||
@@ -47,12 +53,19 @@ def fmt_msg(m: dict) -> str:
|
|||||||
|
|
||||||
# ── commands ─────────────────────────────────────────────────────────────────
|
# ── commands ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def cmd_login(args):
|
def cmd_new_user(args):
|
||||||
|
if len(args) < 2:
|
||||||
|
print("usage: /new/user <username> <password>")
|
||||||
|
return
|
||||||
|
r = post("/new/user", username=args[0], password=args[1])
|
||||||
|
print("registered" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_new_token(args):
|
||||||
global token, user_id
|
global token, user_id
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
print("usage: /login <username> <password>")
|
print("usage: /new/token <username> <password>")
|
||||||
return
|
return
|
||||||
r = post("/token", username=args[0], password=args[1])
|
r = post("/new/token", username=args[0], password=args[1])
|
||||||
if r.ok:
|
if r.ok:
|
||||||
data = r.json()
|
data = r.json()
|
||||||
token = data["token"]
|
token = data["token"]
|
||||||
@@ -64,31 +77,31 @@ def cmd_login(args):
|
|||||||
else:
|
else:
|
||||||
print(f"login failed: {r.text}")
|
print(f"login failed: {r.text}")
|
||||||
|
|
||||||
def cmd_send(args):
|
def cmd_msg_user(args):
|
||||||
if not token:
|
if not token:
|
||||||
print("not logged in")
|
print("not logged in")
|
||||||
return
|
return
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
print("usage: /send <connectionid> <message…>")
|
print("usage: /msg/user <connectionid> <message…>")
|
||||||
return
|
return
|
||||||
conn_id = args[0]
|
conn_id = args[0]
|
||||||
content = " ".join(args[1:])
|
content = " ".join(args[1:])
|
||||||
r = post("/message", token=token, connectionid=conn_id, msgContent=content)
|
r = post_auth("/msg/user", connectionid=conn_id, msgContent=content)
|
||||||
print("sent" if r.ok else f"error: {r.text}")
|
print("sent" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
def cmd_history(args):
|
def cmd_get_connection_messages(args):
|
||||||
if not token:
|
if not token:
|
||||||
print("not logged in")
|
print("not logged in")
|
||||||
return
|
return
|
||||||
if not args:
|
if not args:
|
||||||
print("usage: /history <connectionid> [count]")
|
print("usage: /get/connection/messages <connectionid> [count] [before]")
|
||||||
return
|
return
|
||||||
data = {"token": token, "connectionid": args[0]}
|
data = {"connectionid": args[0]}
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
data["messages"] = args[1]
|
data["messages"] = args[1]
|
||||||
if len(args) > 2:
|
if len(args) > 2:
|
||||||
data["before"] = args[2]
|
data["before"] = args[2]
|
||||||
r = requests.post(f"{BASE_URL}/get/connection/messages", data=data)
|
r = post_auth("/get/connection/messages", **data)
|
||||||
if r.ok:
|
if r.ok:
|
||||||
msgs = r.json() or []
|
msgs = r.json() or []
|
||||||
if not msgs:
|
if not msgs:
|
||||||
@@ -98,41 +111,198 @@ def cmd_history(args):
|
|||||||
else:
|
else:
|
||||||
print(f"error: {r.text}")
|
print(f"error: {r.text}")
|
||||||
|
|
||||||
def cmd_connections(args):
|
def cmd_get_connections(args):
|
||||||
if not token:
|
if not token:
|
||||||
print("not logged in")
|
print("not logged in")
|
||||||
return
|
return
|
||||||
r = post("/get/connections", token=token)
|
r = post_auth("/get/connections")
|
||||||
if r.ok:
|
if r.ok:
|
||||||
for c in (r.json() or {}).values():
|
for c in (r.json() or []):
|
||||||
print(f" {c['id']} requestor={c['requestorId']} recipient={c['recipientId']} state={c['state']}")
|
print(f" {c['id']} requestor={c['requestorId']} recipient={c['recipientId']} state={c['state']}")
|
||||||
else:
|
else:
|
||||||
print(f"error: {r.text}")
|
print(f"error: {r.text}")
|
||||||
|
|
||||||
def cmd_delconnection(args):
|
def cmd_new_connection(args):
|
||||||
if not token:
|
if not token:
|
||||||
print("not logged in")
|
print("not logged in")
|
||||||
return
|
return
|
||||||
if not args:
|
if not args:
|
||||||
print("usage: /delconnection <connectionid>")
|
print("usage: /new/connection <userid>")
|
||||||
return
|
return
|
||||||
r = post("/del/connection", token=token, connectionid=args[0])
|
r = post_auth("/new/connection", recipient=args[0])
|
||||||
|
print("connection requested" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_mod_connection_elevate(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /mod/connection/elevate <connectionid>")
|
||||||
|
return
|
||||||
|
r = post_auth("/mod/connection/elevate", connectionid=args[0])
|
||||||
|
print(f"ok: {r.text}" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_del_connection(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /del/connection <connectionid>")
|
||||||
|
return
|
||||||
|
r = post_auth("/del/connection", connectionid=args[0])
|
||||||
print("deleted" if r.ok else f"error: {r.text}")
|
print("deleted" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_del_user(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
r = post_auth("/del/user")
|
||||||
|
print("user deleted" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_get_user(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /get/user <userid>")
|
||||||
|
return
|
||||||
|
r = post_auth("/get/user", targetid=args[0])
|
||||||
|
if r.ok:
|
||||||
|
print(json.dumps(r.json(), indent=2))
|
||||||
|
else:
|
||||||
|
print(f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_mod_user_profile(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
data = {}
|
||||||
|
for arg in args:
|
||||||
|
if "=" in arg:
|
||||||
|
k, v = arg.split("=", 1)
|
||||||
|
data[k] = v
|
||||||
|
if not data:
|
||||||
|
print("usage: /mod/user/profile [pronouns=...] [description=...] [color=r,g,b,a]")
|
||||||
|
return
|
||||||
|
r = post_auth("/mod/user/profile", **data)
|
||||||
|
print("updated" if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_get_file(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if len(args) < 2:
|
||||||
|
print("usage: /get/file <connectionid> <key>")
|
||||||
|
return
|
||||||
|
r = post_auth_header("/get/file", connectionid=args[0], key=args[1])
|
||||||
|
if r.ok:
|
||||||
|
print(json.dumps(r.json(), indent=2))
|
||||||
|
else:
|
||||||
|
print(f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_new_file(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if len(args) < 2:
|
||||||
|
print("usage: /new/file <connectionid> <filepath>")
|
||||||
|
return
|
||||||
|
conn_id, filepath = args[0], args[1]
|
||||||
|
try:
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
r = post_auth_header("/new/file", files={"file": f}, connectionid=conn_id)
|
||||||
|
print(f"uploaded: {r.text}" if r.ok else f"error: {r.text}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"file not found: {filepath}")
|
||||||
|
|
||||||
|
def cmd_mod_user_avatar(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /mod/user/avatar <filepath>")
|
||||||
|
return
|
||||||
|
filepath = args[0]
|
||||||
|
try:
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
r = post_auth_header("/mod/user/avatar", files={"file": f})
|
||||||
|
print("avatar updated" if r.ok else f"error: {r.text}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"file not found: {filepath}")
|
||||||
|
|
||||||
|
def cmd_mod_user_profilebg(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /mod/user/profilebg <filepath>")
|
||||||
|
return
|
||||||
|
filepath = args[0]
|
||||||
|
try:
|
||||||
|
with open(filepath, "rb") as f:
|
||||||
|
r = post_auth_header("/mod/user/profilebg", files={"file": f})
|
||||||
|
print("profile background updated" if r.ok else f"error: {r.text}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"file not found: {filepath}")
|
||||||
|
|
||||||
|
def cmd_get_user_avatar(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /get/user/avatar <userid>")
|
||||||
|
return
|
||||||
|
r = post_auth_header("/get/user/avatar", userid=args[0])
|
||||||
|
print(r.text if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
|
def cmd_get_user_profilebg(args):
|
||||||
|
if not token:
|
||||||
|
print("not logged in")
|
||||||
|
return
|
||||||
|
if not args:
|
||||||
|
print("usage: /get/user/profilebg <userid>")
|
||||||
|
return
|
||||||
|
r = post_auth_header("/get/user/profilebg", userid=args[0])
|
||||||
|
print(r.text if r.ok else f"error: {r.text}")
|
||||||
|
|
||||||
COMMANDS = {
|
COMMANDS = {
|
||||||
"/login": cmd_login,
|
"/new/user": cmd_new_user,
|
||||||
"/send": cmd_send,
|
"/new/token": cmd_new_token,
|
||||||
"/history": cmd_history,
|
"/new/connection": cmd_new_connection,
|
||||||
"/connections": cmd_connections,
|
"/new/file": cmd_new_file,
|
||||||
"/delconnection": cmd_delconnection,
|
"/mod/user/profile": cmd_mod_user_profile,
|
||||||
|
"/mod/user/avatar": cmd_mod_user_avatar,
|
||||||
|
"/mod/user/profilebg": cmd_mod_user_profilebg,
|
||||||
|
"/mod/connection/elevate": cmd_mod_connection_elevate,
|
||||||
|
"/get/user": cmd_get_user,
|
||||||
|
"/get/connections": cmd_get_connections,
|
||||||
|
"/get/connection/messages": cmd_get_connection_messages,
|
||||||
|
"/get/file": cmd_get_file,
|
||||||
|
"/get/user/avatar": cmd_get_user_avatar,
|
||||||
|
"/get/user/profilebg": cmd_get_user_profilebg,
|
||||||
|
"/del/user": cmd_del_user,
|
||||||
|
"/del/connection": cmd_del_connection,
|
||||||
|
"/msg/user": cmd_msg_user,
|
||||||
}
|
}
|
||||||
|
|
||||||
HELP = """
|
HELP = """
|
||||||
/login <user> <pass> – authenticate
|
/new/user <user> <pass> – create account
|
||||||
/connections – list your connections
|
/new/token <user> <pass> – authenticate
|
||||||
/send <connectionid> <message…> – send a DM
|
/new/connection <userid> – send connection request
|
||||||
/history <connectionid> [count] [before] – fetch message history
|
/new/file <connectionid> <filepath> – upload attachment
|
||||||
/delconnection <connectionid> – delete a connection
|
/mod/user/profile [pronouns=...] [description=...] [color=r,g,b,a] – update profile
|
||||||
|
/mod/user/avatar <filepath> – set avatar image
|
||||||
|
/mod/user/profilebg <filepath> – set profile background
|
||||||
|
/mod/connection/elevate <connectionid> – elevate connection
|
||||||
|
/get/user <userid> – get user info
|
||||||
|
/get/connections – list your connections
|
||||||
|
/get/connection/messages <connectionid> [count] [before] – fetch message history
|
||||||
|
/get/file <connectionid> <key> – download attachment URL
|
||||||
|
/get/user/avatar <userid> – get avatar download URL
|
||||||
|
/get/user/profilebg <userid> – get profile background URL
|
||||||
|
/del/user – delete your account
|
||||||
|
/del/connection <connectionid> – delete a connection
|
||||||
|
/msg/user <connectionid> <message…> – send a DM
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ── websocket ─────────────────────────────────────────────────────────────────
|
# ── websocket ─────────────────────────────────────────────────────────────────
|
||||||
@@ -141,7 +311,6 @@ async def receiver(ws):
|
|||||||
async for raw in ws:
|
async for raw in ws:
|
||||||
try:
|
try:
|
||||||
data = json.loads(raw)
|
data = json.loads(raw)
|
||||||
# pushed DM
|
|
||||||
if "content" in data and "sender" in data:
|
if "content" in data and "sender" in data:
|
||||||
print(f"\n{fmt_msg(data)}", flush=True)
|
print(f"\n{fmt_msg(data)}", flush=True)
|
||||||
else:
|
else:
|
||||||
@@ -171,7 +340,6 @@ async def run():
|
|||||||
print("Alt+Enter = newline | Enter = send | /help | Ctrl+C = quit\n")
|
print("Alt+Enter = newline | Enter = send | /help | Ctrl+C = quit\n")
|
||||||
input_thread.start()
|
input_thread.start()
|
||||||
input_thread_started = True
|
input_thread_started = True
|
||||||
# re-auth after reconnect
|
|
||||||
if token:
|
if token:
|
||||||
await ws.send(json.dumps({"token": token}))
|
await ws.send(json.dumps({"token": token}))
|
||||||
|
|
||||||
@@ -219,7 +387,6 @@ def input_loop():
|
|||||||
if cmd in COMMANDS:
|
if cmd in COMMANDS:
|
||||||
COMMANDS[cmd](parts[1:])
|
COMMANDS[cmd](parts[1:])
|
||||||
else:
|
else:
|
||||||
# raw JSON passthrough
|
|
||||||
asyncio.run_coroutine_threadsafe(send_queue.put(text), loop)
|
asyncio.run_coroutine_threadsafe(send_queue.put(text), loop)
|
||||||
except (EOFError, KeyboardInterrupt):
|
except (EOFError, KeyboardInterrupt):
|
||||||
asyncio.run_coroutine_threadsafe(shutdown_event.set(), loop)
|
asyncio.run_coroutine_threadsafe(shutdown_event.set(), loop)
|
||||||
|
|||||||
+149
-138
@@ -66,29 +66,24 @@
|
|||||||
<div id="log"></div>
|
<div id="log"></div>
|
||||||
|
|
||||||
<div id="btn-row">
|
<div id="btn-row">
|
||||||
<button onclick="showForm('new-user')">POST /new/user</button>
|
<button data-form="new-user" onclick="showForm('new-user')">POST /new/user</button>
|
||||||
<button onclick="showForm('new-connection')">POST /new/connection</button>
|
<button data-form="new-token" onclick="showForm('new-token')">POST /new/token</button>
|
||||||
<button onclick="showForm('new-token')">POST /new/token</button>
|
<button data-form="new-connection" onclick="showForm('new-connection')">POST /new/connection</button>
|
||||||
<button onclick="showForm('new-group')">POST /new/group</button>
|
<button data-form="file-upload" onclick="showForm('file-upload')">POST /new/file</button>
|
||||||
<button onclick="showForm('mod-user-appearance')">POST /mod/user/appearence</button>
|
<button data-form="mod-user-profile" onclick="showForm('mod-user-profile')">POST /mod/user/profile</button>
|
||||||
<button onclick="showForm('mod-user-about')">POST /mod/user/about</button>
|
<button data-form="mod-user-avatar" onclick="showForm('mod-user-avatar')">POST /mod/user/avatar</button>
|
||||||
<button onclick="showForm('accept-connection')">POST /mod/connection/accept</button>
|
<button data-form="mod-user-profilebg" onclick="showForm('mod-user-profilebg')">POST /mod/user/profilebg</button>
|
||||||
<button onclick="showForm('add-users')">POST /mod/group/addusers</button>
|
<button data-form="mod-connection-elevate" onclick="showForm('mod-connection-elevate')">POST /mod/connection/elevate</button>
|
||||||
<button onclick="showForm('remove-users')">POST /mod/group/removeusers</button>
|
<button data-form="get-user" onclick="showForm('get-user')">POST /get/user</button>
|
||||||
<button onclick="showForm('group-color')">POST /mod/group/color</button>
|
<button data-form="get-connections" onclick="showForm('get-connections')">POST /get/connections</button>
|
||||||
<button onclick="showForm('group-owner')">POST /mod/group/owner</button>
|
<button data-form="get-connection-messages" onclick="showForm('get-connection-messages')">POST /get/connection/messages</button>
|
||||||
<button onclick="showForm('get-groups')">POST /get/groups</button>
|
<button data-form="file-download" onclick="showForm('file-download')">POST /get/file</button>
|
||||||
<button onclick="showForm('get-connections')">POST /get/connections</button>
|
<button data-form="get-user-avatar" onclick="showForm('get-user-avatar')">POST /get/user/avatar</button>
|
||||||
<button onclick="showForm('get-members')">POST /get/group/members</button>
|
<button data-form="get-user-profilebg" onclick="showForm('get-user-profilebg')">POST /get/user/profilebg</button>
|
||||||
<button onclick="showForm('del-user')" class="warn">POST /del/user</button>
|
<button data-form="del-user" class="warn" onclick="showForm('del-user')">POST /del/user</button>
|
||||||
<button onclick="showForm('del-group')" class="warn">POST /del/group</button>
|
<button data-form="del-connection" class="warn" onclick="showForm('del-connection')">POST /del/connection</button>
|
||||||
<button onclick="showForm('del-connection')" class="warn">POST /del/connection</button>
|
<button data-form="msg-user" onclick="showForm('msg-user')">POST /msg/user</button>
|
||||||
<button onclick="showForm('get-connection-messages')">POST /get/connection/messages</button>
|
<button data-form="websocket" onclick="showForm('websocket')">WS /ws</button>
|
||||||
<button onclick="showForm('msg-user')">POST /msg/user</button>
|
|
||||||
<button onclick="showForm('msg-group')">POST /msg/group</button>
|
|
||||||
<button onclick="showForm('file-upload')">POST /new/file</button>
|
|
||||||
<button onclick="showForm('file-download')">POST /get/file</button>
|
|
||||||
<button onclick="showForm('websocket')">WS /ws</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="form-panel">
|
<div id="form-panel">
|
||||||
@@ -116,14 +111,6 @@
|
|||||||
],
|
],
|
||||||
submit: () => httpPost('/new/user', { username:'nu-username', password:'nu-password', color:'nu-color' })
|
submit: () => httpPost('/new/user', { username:'nu-username', password:'nu-password', color:'nu-color' })
|
||||||
},
|
},
|
||||||
'new-connection': {
|
|
||||||
title: 'POST /new/connection — send connection request',
|
|
||||||
fields: [
|
|
||||||
{ id: 'nconn-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'nconn-recipient', label: 'recipient', ph: 'uint32' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/new/connection', { token:'nconn-token', recipient:'nconn-recipient' })
|
|
||||||
},
|
|
||||||
'new-token': {
|
'new-token': {
|
||||||
title: 'POST /new/token — login / get token',
|
title: 'POST /new/token — login / get token',
|
||||||
fields: [
|
fields: [
|
||||||
@@ -132,82 +119,59 @@
|
|||||||
],
|
],
|
||||||
submit: () => httpPost('/new/token', { username:'nt-username', password:'nt-password' })
|
submit: () => httpPost('/new/token', { username:'nt-username', password:'nt-password' })
|
||||||
},
|
},
|
||||||
'new-group': {
|
'new-connection': {
|
||||||
title: 'POST /new/group — create group',
|
title: 'POST /new/connection — send connection request',
|
||||||
fields: [
|
fields: [
|
||||||
{ id: 'ng-token', label: 'token', ph: '' },
|
{ id: 'nconn-token', label: 'token', ph: '' },
|
||||||
{ id: 'ng-name', label: 'name', ph: 'optional, default: Best group ever' },
|
{ id: 'nconn-recipient', label: 'recipient', ph: 'uint32' },
|
||||||
{ id: 'ng-color', label: 'color', ph: 'R,G,B optional', hint: 'R,G,B' },
|
|
||||||
{ id: 'ng-enableUserColors', label: 'enableUserColors', ph: '1 to enable (optional)' },
|
|
||||||
],
|
],
|
||||||
submit: () => httpPost('/new/group', { token:'ng-token', name:'ng-name', color:'ng-color', enableUserColors:'ng-enableUserColors' })
|
submit: () => httpPost('/new/connection', { token:'nconn-token', recipient:'nconn-recipient' })
|
||||||
},
|
},
|
||||||
'mod-user-appearance': {
|
'mod-user-profile': {
|
||||||
title: 'POST /mod/user/appearence — change user color',
|
title: 'POST /mod/user/profile — update profile',
|
||||||
fields: [
|
fields: [
|
||||||
{ id: 'mua-token', label: 'token', ph: '' },
|
{ id: 'mup-token', label: 'token', ph: '' },
|
||||||
{ id: 'mua-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
|
{ id: 'mup-pronouns', label: 'pronouns', ph: 'optional' },
|
||||||
|
{ id: 'mup-description', label: 'description', ph: 'optional' },
|
||||||
|
{ id: 'mup-color', label: 'color', ph: '255,100,50 optional', hint: 'R,G,B' },
|
||||||
],
|
],
|
||||||
submit: () => httpPost('/mod/user/appearence', { token:'mua-token', color:'mua-color' })
|
submit: () => httpPost('/mod/user/profile', { token:'mup-token', pronouns:'mup-pronouns', description:'mup-description', color:'mup-color' })
|
||||||
},
|
},
|
||||||
'mod-user-about': {
|
'mod-user-avatar': {
|
||||||
title: 'POST /mod/user/about — change user pronouns',
|
title: 'POST /mod/user/avatar — set avatar image (token in header)',
|
||||||
fields: [
|
renderCustom: () => `
|
||||||
{ id: 'mub-token', label: 'token', ph: '' },
|
<div class="field"><label>token</label><input id="mua-token" placeholder=""></div>
|
||||||
{ id: 'mub-pronouns', label: 'pronouns', ph: '2–25 chars' },
|
<div class="field"><label>file</label><input id="mua-file" type="file"></div>
|
||||||
],
|
<div class="form-actions">
|
||||||
submit: () => httpPost('/mod/user/about', { token:'mub-token', pronouns:'mub-pronouns' })
|
<button class="send" onclick="submitAvatarUpload()">Send</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
},
|
},
|
||||||
'accept-connection': {
|
'mod-user-profilebg': {
|
||||||
title: 'POST /mod/connection/accept — accept connection request',
|
title: 'POST /mod/user/profilebg — set profile background (token in header)',
|
||||||
fields: [
|
renderCustom: () => `
|
||||||
{ id: 'ca-token', label: 'token', ph: '' },
|
<div class="field"><label>token</label><input id="mpb-token" placeholder=""></div>
|
||||||
{ id: 'ca-connectionid', label: 'connectionid', ph: 'UUID' },
|
<div class="field"><label>file</label><input id="mpb-file" type="file"></div>
|
||||||
],
|
<div class="form-actions">
|
||||||
submit: () => httpPost('/mod/connection/accept', { token:'ca-token', connectionid:'ca-connectionid' })
|
<button class="send" onclick="submitProfileBgUpload()">Send</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
},
|
},
|
||||||
'add-users': {
|
'mod-connection-elevate': {
|
||||||
title: 'POST /mod/group/addusers — add users (owner only)',
|
title: 'POST /mod/connection/elevate — elevate connection',
|
||||||
fields: [
|
fields: [
|
||||||
{ id: 'au-token', label: 'token', ph: '' },
|
{ id: 'mce-token', label: 'token', ph: '' },
|
||||||
{ id: 'au-groupid', label: 'groupid', ph: 'uint32' },
|
{ id: 'mce-connectionid', label: 'connectionid', ph: 'UUID' },
|
||||||
{ id: 'au-users', label: 'users', ph: 'id1,id2,id3', hint: 'csv of uint32' },
|
|
||||||
],
|
],
|
||||||
submit: () => httpPost('/mod/group/addusers', { token:'au-token', groupid:'au-groupid', users:'au-users' })
|
submit: () => httpPost('/mod/connection/elevate', { token:'mce-token', connectionid:'mce-connectionid' })
|
||||||
},
|
},
|
||||||
'remove-users': {
|
'get-user': {
|
||||||
title: 'POST /mod/group/removeusers — remove users (owner only)',
|
title: 'POST /get/user — get user info',
|
||||||
fields: [
|
fields: [
|
||||||
{ id: 'ru-token', label: 'token', ph: '' },
|
{ id: 'gu-token', label: 'token', ph: '' },
|
||||||
{ id: 'ru-groupid', label: 'groupid', ph: 'uint32' },
|
{ id: 'gu-targetid', label: 'targetid', ph: 'uint32' },
|
||||||
{ id: 'ru-users', label: 'users', ph: 'id1,id2,id3', hint: 'csv of uint32' },
|
|
||||||
],
|
],
|
||||||
submit: () => httpPost('/mod/group/removeusers', { token:'ru-token', groupid:'ru-groupid', users:'ru-users' })
|
submit: () => httpPost('/get/user', { token:'gu-token', targetid:'gu-targetid' })
|
||||||
},
|
|
||||||
'group-color': {
|
|
||||||
title: 'POST /mod/group/color — change color (owner only)',
|
|
||||||
fields: [
|
|
||||||
{ id: 'gc-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'gc-groupid', label: 'groupid', ph: 'uint32' },
|
|
||||||
{ id: 'gc-color', label: 'color', ph: '255,100,50', hint: 'R,G,B' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/mod/group/color', { token:'gc-token', groupid:'gc-groupid', color:'gc-color' })
|
|
||||||
},
|
|
||||||
'group-owner': {
|
|
||||||
title: 'POST /mod/group/owner — change owner (owner only)',
|
|
||||||
fields: [
|
|
||||||
{ id: 'go-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'go-groupid', label: 'groupid', ph: 'uint32' },
|
|
||||||
{ id: 'go-newOwner', label: 'newOwner', ph: 'username of new owner' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/mod/group/owner', { token:'go-token', groupid:'go-groupid', newOwner:'go-newOwner' })
|
|
||||||
},
|
|
||||||
'get-groups': {
|
|
||||||
title: "POST /get/groups — get user's groups (no members)",
|
|
||||||
fields: [
|
|
||||||
{ id: 'gg-token', label: 'token', ph: '' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/get/groups', { token:'gg-token' })
|
|
||||||
},
|
},
|
||||||
'get-connections': {
|
'get-connections': {
|
||||||
title: "POST /get/connections — get user's connections",
|
title: "POST /get/connections — get user's connections",
|
||||||
@@ -216,37 +180,6 @@
|
|||||||
],
|
],
|
||||||
submit: () => httpPost('/get/connections', { token:'gconn-token' })
|
submit: () => httpPost('/get/connections', { token:'gconn-token' })
|
||||||
},
|
},
|
||||||
'get-members': {
|
|
||||||
title: 'POST /get/group/members — get group member IDs',
|
|
||||||
fields: [
|
|
||||||
{ id: 'gm-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'gm-group', label: 'group', ph: 'group ID (uint32)' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/get/group/members', { token:'gm-token', group:'gm-group' })
|
|
||||||
},
|
|
||||||
'del-user': {
|
|
||||||
title: 'POST /del/user — delete own account',
|
|
||||||
fields: [
|
|
||||||
{ id: 'du-token', label: 'token', ph: '' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/del/user', { token:'du-token' })
|
|
||||||
},
|
|
||||||
'del-group': {
|
|
||||||
title: 'POST /del/group — delete group (owner only)',
|
|
||||||
fields: [
|
|
||||||
{ id: 'dg-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'dg-groupid', label: 'groupid', ph: 'uint32' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/del/group', { token:'dg-token', groupid:'dg-groupid' })
|
|
||||||
},
|
|
||||||
'del-connection': {
|
|
||||||
title: 'POST /del/connection — delete a connection',
|
|
||||||
fields: [
|
|
||||||
{ id: 'dc-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'dc-connectionid', label: 'connectionid', ph: 'UUID' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/del/connection', { token:'dc-token', connectionid:'dc-connectionid' })
|
|
||||||
},
|
|
||||||
'get-connection-messages': {
|
'get-connection-messages': {
|
||||||
title: 'POST /get/connection/messages — fetch message history',
|
title: 'POST /get/connection/messages — fetch message history',
|
||||||
fields: [
|
fields: [
|
||||||
@@ -257,6 +190,41 @@
|
|||||||
],
|
],
|
||||||
submit: () => httpPost('/get/connection/messages', { token:'gcm-token', connectionid:'gcm-connectionid', messages:'gcm-messages', before:'gcm-before' })
|
submit: () => httpPost('/get/connection/messages', { token:'gcm-token', connectionid:'gcm-connectionid', messages:'gcm-messages', before:'gcm-before' })
|
||||||
},
|
},
|
||||||
|
'get-user-avatar': {
|
||||||
|
title: 'POST /get/user/avatar — get avatar URL (token in header)',
|
||||||
|
renderCustom: () => `
|
||||||
|
<div class="field"><label>token</label><input id="gua-token" placeholder=""></div>
|
||||||
|
<div class="field"><label>userid</label><input id="gua-userid" placeholder="uint32"></div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="send" onclick="submitGetUserAvatar()">Send</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
},
|
||||||
|
'get-user-profilebg': {
|
||||||
|
title: 'POST /get/user/profilebg — get profile background URL (token in header)',
|
||||||
|
renderCustom: () => `
|
||||||
|
<div class="field"><label>token</label><input id="gpb-token" placeholder=""></div>
|
||||||
|
<div class="field"><label>userid</label><input id="gpb-userid" placeholder="uint32"></div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="send" onclick="submitGetUserProfileBg()">Send</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
},
|
||||||
|
'del-user': {
|
||||||
|
title: 'POST /del/user — delete own account',
|
||||||
|
fields: [
|
||||||
|
{ id: 'du-token', label: 'token', ph: '' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/del/user', { token:'du-token' })
|
||||||
|
},
|
||||||
|
'del-connection': {
|
||||||
|
title: 'POST /del/connection — delete a connection',
|
||||||
|
fields: [
|
||||||
|
{ id: 'dc-token', label: 'token', ph: '' },
|
||||||
|
{ id: 'dc-connectionid', label: 'connectionid', ph: 'UUID' },
|
||||||
|
],
|
||||||
|
submit: () => httpPost('/del/connection', { token:'dc-token', connectionid:'dc-connectionid' })
|
||||||
|
},
|
||||||
'msg-user': {
|
'msg-user': {
|
||||||
title: 'POST /msg/user — send direct message to user',
|
title: 'POST /msg/user — send direct message to user',
|
||||||
fields: [
|
fields: [
|
||||||
@@ -267,15 +235,6 @@
|
|||||||
],
|
],
|
||||||
submit: () => httpPost('/msg/user', { token:'mu-token', connectionid:'mu-connectionid', msgContent:'mu-msgContent', attachedFile:'mu-attachedFile' })
|
submit: () => httpPost('/msg/user', { token:'mu-token', connectionid:'mu-connectionid', msgContent:'mu-msgContent', attachedFile:'mu-attachedFile' })
|
||||||
},
|
},
|
||||||
'msg-group': {
|
|
||||||
title: 'POST /msg/group — send message to group',
|
|
||||||
fields: [
|
|
||||||
{ id: 'mg-token', label: 'token', ph: '' },
|
|
||||||
{ id: 'mg-groupid', label: 'groupid', ph: 'uint32' },
|
|
||||||
{ id: 'mg-content', label: 'content', ph: 'message text' },
|
|
||||||
],
|
|
||||||
submit: () => httpPost('/msg/group', { token:'mg-token', groupid:'mg-groupid', content:'mg-content' })
|
|
||||||
},
|
|
||||||
'file-upload': {
|
'file-upload': {
|
||||||
title: 'POST /new/file — upload file (multipart, token in header)',
|
title: 'POST /new/file — upload file (multipart, token in header)',
|
||||||
renderCustom: () => `
|
renderCustom: () => `
|
||||||
@@ -339,10 +298,10 @@
|
|||||||
const def = formDefs[name];
|
const def = formDefs[name];
|
||||||
|
|
||||||
// activate clicked button
|
// activate clicked button
|
||||||
const idx = Object.keys(formDefs).indexOf(name);
|
const btn = document.querySelector(`#btn-row button[data-form="${name}"]`);
|
||||||
if (idx >= 0) {
|
if (btn) {
|
||||||
buttons[idx].classList.add('active');
|
btn.classList.add('active');
|
||||||
if (name === 'websocket') buttons[idx].classList.add('ws');
|
if (name === 'websocket') btn.classList.add('ws');
|
||||||
}
|
}
|
||||||
|
|
||||||
titleEl.textContent = def.title;
|
titleEl.textContent = def.title;
|
||||||
@@ -485,6 +444,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function submitAvatarUpload() {
|
||||||
|
const token = document.getElementById('mua-token').value;
|
||||||
|
const fileInput = document.getElementById('mua-file');
|
||||||
|
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', fileInput.files[0]);
|
||||||
|
log('HTTP /mod/user/avatar', `→ file=${fileInput.files[0].name}`, 'log-info');
|
||||||
|
try {
|
||||||
|
const resp = await fetch(baseUrl() + '/mod/user/avatar', { method: 'POST', headers: { token }, body: form });
|
||||||
|
const text = await resp.text();
|
||||||
|
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
|
||||||
|
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitProfileBgUpload() {
|
||||||
|
const token = document.getElementById('mpb-token').value;
|
||||||
|
const fileInput = document.getElementById('mpb-file');
|
||||||
|
if (!fileInput.files.length) { log('HTTP ERR', 'no file selected', 'log-err'); return; }
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('file', fileInput.files[0]);
|
||||||
|
log('HTTP /mod/user/profilebg', `→ file=${fileInput.files[0].name}`, 'log-info');
|
||||||
|
try {
|
||||||
|
const resp = await fetch(baseUrl() + '/mod/user/profilebg', { method: 'POST', headers: { token }, body: form });
|
||||||
|
const text = await resp.text();
|
||||||
|
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
|
||||||
|
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitGetUserAvatar() {
|
||||||
|
const token = document.getElementById('gua-token').value;
|
||||||
|
const userid = document.getElementById('gua-userid').value;
|
||||||
|
const params = new URLSearchParams({ userid });
|
||||||
|
log('HTTP /get/user/avatar', `→ ${params.toString()}`, 'log-info');
|
||||||
|
try {
|
||||||
|
const resp = await fetch(baseUrl() + '/get/user/avatar', { method: 'POST', headers: { token }, body: params });
|
||||||
|
const text = await resp.text();
|
||||||
|
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
|
||||||
|
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitGetUserProfileBg() {
|
||||||
|
const token = document.getElementById('gpb-token').value;
|
||||||
|
const userid = document.getElementById('gpb-userid').value;
|
||||||
|
const params = new URLSearchParams({ userid });
|
||||||
|
log('HTTP /get/user/profilebg', `→ ${params.toString()}`, 'log-info');
|
||||||
|
try {
|
||||||
|
const resp = await fetch(baseUrl() + '/get/user/profilebg', { method: 'POST', headers: { token }, body: params });
|
||||||
|
const text = await resp.text();
|
||||||
|
log(`HTTP ${resp.status}`, text, resp.ok ? 'log-http' : 'log-err');
|
||||||
|
} catch(e) { log('HTTP ERR', e.message, 'log-err'); }
|
||||||
|
}
|
||||||
|
|
||||||
function setWsStatus(connected) {
|
function setWsStatus(connected) {
|
||||||
const el = document.getElementById('ws-status');
|
const el = document.getElementById('ws-status');
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|||||||
@@ -32,16 +32,17 @@ func main() {
|
|||||||
http.HandleFunc("/new/connection", withCORS(httpRequest.HandleUserNewConnection))
|
http.HandleFunc("/new/connection", withCORS(httpRequest.HandleUserNewConnection))
|
||||||
http.HandleFunc("/new/token", withCORS(httpRequest.HandleUserNewToken))
|
http.HandleFunc("/new/token", withCORS(httpRequest.HandleUserNewToken))
|
||||||
http.HandleFunc("/new/file", withCORS(httpRequest.HandleAttachmentFileUpload))
|
http.HandleFunc("/new/file", withCORS(httpRequest.HandleAttachmentFileUpload))
|
||||||
http.HandleFunc("/mod/user/appearence", withCORS(httpRequest.HandleUserModProfile))
|
http.HandleFunc("/mod/user/profile", withCORS(httpRequest.HandleUserModProfile))
|
||||||
http.HandleFunc("/mod/user/avatar", withCORS(httpRequest.HandleUserModAvatar))
|
http.HandleFunc("/mod/user/avatar", withCORS(httpRequest.HandleUserModAvatar))
|
||||||
http.HandleFunc("/mod/user/profilebg", withCORS(httpRequest.HandleUserModProfileBg))
|
http.HandleFunc("/mod/user/profilebg", withCORS(httpRequest.HandleUserModProfileBg))
|
||||||
http.HandleFunc("/mod/user/about", withCORS(httpRequest.HandleUserModProfile))
|
http.HandleFunc("/mod/connection/elevate", withCORS(httpRequest.HandleUserElevateConnection))
|
||||||
http.HandleFunc("/mod/connection/accept", withCORS(httpRequest.HandleUserElevateConnection))
|
|
||||||
|
|
||||||
http.HandleFunc("/get/user", withCORS(httpRequest.HandleUserGetUser))
|
http.HandleFunc("/get/user", withCORS(httpRequest.HandleUserGetUser))
|
||||||
http.HandleFunc("/get/connections", withCORS(httpRequest.HandleUserGetConnections))
|
http.HandleFunc("/get/connections", withCORS(httpRequest.HandleUserGetConnections))
|
||||||
http.HandleFunc("/get/connection/messages", withCORS(httpRequest.HandleUserGetConnectionMessages))
|
http.HandleFunc("/get/connection/messages", withCORS(httpRequest.HandleUserGetConnectionMessages))
|
||||||
http.HandleFunc("/get/file", withCORS(httpRequest.HandleAttachmentFileDownload))
|
http.HandleFunc("/get/file", withCORS(httpRequest.HandleAttachmentFileDownload))
|
||||||
|
http.HandleFunc("/get/user/avatar", withCORS(httpRequest.HandleGetUserAvatar))
|
||||||
|
http.HandleFunc("/get/user/profilebg", withCORS(httpRequest.HandleGetUserProfileBg))
|
||||||
|
|
||||||
http.HandleFunc("/del/user", withCORS(httpRequest.HandleUserDelete))
|
http.HandleFunc("/del/user", withCORS(httpRequest.HandleUserDelete))
|
||||||
http.HandleFunc("/del/connection", withCORS(httpRequest.HandleUserDeleteConnection))
|
http.HandleFunc("/del/connection", withCORS(httpRequest.HandleUserDeleteConnection))
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ func HandleUserDeleteConnection(response http.ResponseWriter, request *http.Requ
|
|||||||
})
|
})
|
||||||
|
|
||||||
response.WriteHeader(http.StatusAccepted)
|
response.WriteHeader(http.StatusAccepted)
|
||||||
|
response.Write(conn.Id[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleUserElevateConnection(response http.ResponseWriter, request *http.Request) {
|
func HandleUserElevateConnection(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ func HandleAttachmentFileUpload(response http.ResponseWriter, request *http.Requ
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
contentType := header.Header.Get("Content-Type")
|
contentType := header.Header.Get("Content-Type")
|
||||||
key := minio.GetKey(conn.Id, contentType, minio.File)
|
key := minio.GetKey(minio.GetKeyOptions{
|
||||||
|
ConnectionId: conn.Id,
|
||||||
|
MimeType: contentType,
|
||||||
|
UploadType: minio.File,
|
||||||
|
})
|
||||||
|
|
||||||
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
||||||
"originalName": header.Filename,
|
"originalName": header.Filename,
|
||||||
@@ -85,7 +89,7 @@ func HandleGetUserAvatar(response http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, string(minio.UserAvatarPrefix)+target.Avatar)
|
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, target.Avatar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -123,7 +127,7 @@ func HandleGetUserProfileBg(response http.ResponseWriter, request *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, string(minio.UserProfileBgPrefix)+target.ProfileBg)
|
url, _, err := minio.GetDownloadUrlAndMetadata(ctx, target.ProfileBg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -209,11 +209,6 @@ func HandleUserModAvatar(response http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, ok := getConnectionWithResponseOnFail(&response, request, user)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, header, err := request.FormFile("file")
|
file, header, err := request.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "missing file", http.StatusBadRequest)
|
http.Error(response, "missing file", http.StatusBadRequest)
|
||||||
@@ -228,14 +223,18 @@ func HandleUserModAvatar(response http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user.Avatar != "" {
|
if user.Avatar != "" {
|
||||||
err = minio.Delete(ctx, string(minio.UserAvatarPrefix)+user.Avatar)
|
err = minio.Delete(ctx, user.Avatar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key := minio.GetKey(conn.Id, contentType, minio.File)
|
key := minio.GetKey(minio.GetKeyOptions{
|
||||||
|
UserId: user.Id,
|
||||||
|
MimeType: contentType,
|
||||||
|
UploadType: minio.UserAvatar,
|
||||||
|
})
|
||||||
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
||||||
"originalName": header.Filename,
|
"originalName": header.Filename,
|
||||||
"uploaderId": user.Id.String(),
|
"uploaderId": user.Id.String(),
|
||||||
@@ -244,7 +243,7 @@ func HandleUserModAvatar(response http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Avatar = key[len(minio.UserAvatarPrefix):]
|
user.Avatar = key
|
||||||
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{Avatar: true})
|
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{Avatar: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
@@ -273,11 +272,6 @@ func HandleUserModProfileBg(response http.ResponseWriter, request *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, ok := getConnectionWithResponseOnFail(&response, request, user)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, header, err := request.FormFile("file")
|
file, header, err := request.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "missing file", http.StatusBadRequest)
|
http.Error(response, "missing file", http.StatusBadRequest)
|
||||||
@@ -292,14 +286,18 @@ func HandleUserModProfileBg(response http.ResponseWriter, request *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user.ProfileBg != "" {
|
if user.ProfileBg != "" {
|
||||||
err = minio.Delete(ctx, string(minio.UserProfileBgPrefix)+user.ProfileBg)
|
err = minio.Delete(ctx, user.ProfileBg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key := minio.GetKey(conn.Id, contentType, minio.UserProfileBg)
|
key := minio.GetKey(minio.GetKeyOptions{
|
||||||
|
UserId: user.Id,
|
||||||
|
MimeType: contentType,
|
||||||
|
UploadType: minio.UserProfileBg,
|
||||||
|
})
|
||||||
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
if err = minio.Upload(ctx, key, file, header.Size, contentType, map[string]string{
|
||||||
"originalName": header.Filename,
|
"originalName": header.Filename,
|
||||||
"uploaderId": user.Id.String(),
|
"uploaderId": user.Id.String(),
|
||||||
@@ -308,7 +306,7 @@ func HandleUserModProfileBg(response http.ResponseWriter, request *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.ProfileBg = key[len(minio.UserProfileBgPrefix):]
|
user.ProfileBg = key
|
||||||
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{ProfileBg: true})
|
err = postgresql.UserUpdateProfile(ctx, user, types.UserProfileUpdateList{ProfileBg: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, "internal server error", http.StatusInternalServerError)
|
http.Error(response, "internal server error", http.StatusInternalServerError)
|
||||||
|
|||||||
+17
-8
@@ -33,20 +33,29 @@ const (
|
|||||||
UserProfileBgPrefix DataTypePrefix = "userProfileBg/"
|
UserProfileBgPrefix DataTypePrefix = "userProfileBg/"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetKey(connectionId uuid.UUID, mimeType string, uploadType DataType) string {
|
type GetKeyOptions struct {
|
||||||
extensions, err := mime.ExtensionsByType(mimeType)
|
UserId uuid.UUID
|
||||||
|
ConnectionId uuid.UUID
|
||||||
|
MimeType string
|
||||||
|
UploadType DataType
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetKey(opts GetKeyOptions) string {
|
||||||
|
extensions, err := mime.ExtensionsByType(opts.MimeType)
|
||||||
if err != nil || len(extensions) == 0 {
|
if err != nil || len(extensions) == 0 {
|
||||||
extensions = []string{".unknown"}
|
extensions = []string{".unknown"}
|
||||||
}
|
}
|
||||||
|
|
||||||
key := connectionId.String() + "/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + extensions[0]
|
key := "/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + extensions[0]
|
||||||
|
|
||||||
if uploadType == UserAvatar {
|
switch opts.UploadType {
|
||||||
return string(UserAvatarPrefix) + key
|
case UserAvatar:
|
||||||
} else if uploadType == UserProfileBg {
|
return string(UserAvatarPrefix) + opts.UserId.String() + key
|
||||||
return string(UserProfileBgPrefix) + key
|
case UserProfileBg:
|
||||||
|
return string(UserProfileBgPrefix) + opts.UserId.String() + key
|
||||||
|
default:
|
||||||
|
return string(FilePrefix) + opts.ConnectionId.String() + key
|
||||||
}
|
}
|
||||||
return string(FilePrefix) + key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(ctx context.Context) {
|
func Init(ctx context.Context) {
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ type User struct {
|
|||||||
Pronouns string `json:"pronouns"`
|
Pronouns string `json:"pronouns"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Avatar string `json:"avatar"`
|
Avatar string `json:"avatar"`
|
||||||
ProfileBg string `json:"profileBg"`
|
ProfileBg string `json:"profileBackground"`
|
||||||
PasswordHash string `json:"passwordHash"`
|
PasswordHash string `json:"-"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
WsConn *websocket.Conn `json:"-"`
|
WsConn *websocket.Conn `json:"-"`
|
||||||
Id uuid.UUID `json:"-"`
|
Id uuid.UUID `json:"-"`
|
||||||
|
|||||||
Reference in New Issue
Block a user