Host QR codes locally
This commit is contained in:
parent
4287a329f5
commit
5a86c07eb9
7 changed files with 40 additions and 6 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -38,6 +38,7 @@
|
|||
"snowtransfer": "^0.10.5",
|
||||
"stream-mime-type": "^1.0.2",
|
||||
"try-to-catch": "^3.0.1",
|
||||
"uqr": "^0.1.2",
|
||||
"xxhash-wasm": "^1.0.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
@ -3237,6 +3238,12 @@
|
|||
"pathe": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uqr": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz",
|
||||
"integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"snowtransfer": "^0.10.5",
|
||||
"stream-mime-type": "^1.0.2",
|
||||
"try-to-catch": "^3.0.1",
|
||||
"uqr": "^0.1.2",
|
||||
"xxhash-wasm": "^1.0.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
|
@ -195,5 +195,6 @@ Total transitive production dependencies: 147
|
|||
* (0) prettier-bytes: It does what I want and has no dependencies.
|
||||
* (2) snowtransfer: Discord API library with bring-your-own-caching that I trust.
|
||||
* (0) try-to-catch: Not strictly necessary, but it's already pulled in by supertape, so I may as well.
|
||||
* (0) uqr: QR code SVG generator. Used on the website to scan in an invite link.
|
||||
* (0) xxhash-wasm: Used where cryptographically secure hashing is not required.
|
||||
* (0) zod: Input validation for the web server. It's popular and easy to use.
|
||||
|
|
|
@ -131,7 +131,7 @@ function getJoinedMembers(roomID) {
|
|||
* @returns {Promise<{chunk: Ty.Event.Outer<Ty.Event.M_Room_Member>[]}>}
|
||||
*/
|
||||
function getMembers(roomID, membership) {
|
||||
return mreq.mreq("GET", `/client/v3/rooms/${roomID}/members`, {membership})
|
||||
return mreq.mreq("GET", `/client/v3/rooms/${roomID}/members`, undefined, {membership})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,6 +45,8 @@ mixin matrix(row, radio=false, badge="")
|
|||
else
|
||||
.s-user-card--link.fs-body1
|
||||
a(href=`https://matrix.to/#/${row.room_id}`)= row.nick || row.name
|
||||
if row.join_rule === "invite"
|
||||
+badge-private
|
||||
|
||||
block body
|
||||
if !guild_id && session.data.managedGuilds
|
||||
|
@ -91,9 +93,9 @@ block body
|
|||
div
|
||||
-
|
||||
let size = 105
|
||||
let src = new URL(`https://api.qrserver.com/v1/create-qr-code/?qzone=1&format=svg&size=${size}x${size}`)
|
||||
src.searchParams.set("data", `https://bridge.cadence.moe/invite?nonce=${nonce}`)
|
||||
img(width=size height=size src=src.toString())
|
||||
let p = new URLSearchParams()
|
||||
p.set("data", `https://bridge.cadence.moe/invite?nonce=${nonce}`)
|
||||
img(width=size height=size src=`/qr?${p}`)
|
||||
|
||||
h2.mt48.fs-headline1 Moderation
|
||||
|
||||
|
@ -123,7 +125,10 @@ block body
|
|||
unlinkedChannels.sort((a, b) => getPosition(a) - getPosition(b))
|
||||
|
||||
let linkedRoomIDs = linkedChannels.map(c => c.room_id)
|
||||
let unlinkedRooms = rooms.filter(r => !linkedRoomIDs.includes(r.room_id))
|
||||
let unlinkedRooms = rooms.filter(r => !linkedRoomIDs.includes(r.room_id) && !r.room_type)
|
||||
// https://discord.com/developers/docs/topics/threads#active-archived-threads
|
||||
// need to filter out linked archived threads from unlinkedRooms, will just do that by comparing against the name
|
||||
unlinkedRooms = unlinkedRooms.filter(r => !r.name.match(/^\[(🔒)?⛓️\]/))
|
||||
.s-card.bs-sm.p0
|
||||
.s-table-container
|
||||
table.s-table.s-table__bx-simple
|
||||
|
@ -175,7 +180,7 @@ block body
|
|||
!= icons.Icons.IconLockSm
|
||||
.fl-grow1 Invite
|
||||
|
||||
p.s-description.m0 In-app direct invite from another user; /invite on Discord; web form
|
||||
p.s-description.m0 In-app direct invite from another user
|
||||
p.s-description.m0 Shareable invite links, like Discord
|
||||
p.s-description.m0 Publicly listed in directory, like Discord server discovery
|
||||
|
||||
|
|
19
src/web/routes/qr.js
Normal file
19
src/web/routes/qr.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
// @ts-check
|
||||
|
||||
const {z} = require("zod")
|
||||
const {defineEventHandler, getValidatedQuery} = require("h3")
|
||||
|
||||
const {as} = require("../../passthrough")
|
||||
|
||||
const uqr = require("uqr")
|
||||
|
||||
const schema = {
|
||||
qr: z.object({
|
||||
data: z.string().max(128)
|
||||
})
|
||||
}
|
||||
|
||||
as.router.get("/qr", defineEventHandler(async event => {
|
||||
const {data} = await getValidatedQuery(event, schema.qr.parse)
|
||||
return new Response(uqr.renderSVG(data, {pixelSize: 3}), {headers: {"content-type": "image/svg+xml"}})
|
||||
}))
|
|
@ -26,6 +26,7 @@ sync.require("./routes/download-discord")
|
|||
sync.require("./routes/invite")
|
||||
sync.require("./routes/guild-settings")
|
||||
sync.require("./routes/oauth")
|
||||
sync.require("./routes/qr")
|
||||
|
||||
// Files
|
||||
|
||||
|
|
Loading…
Reference in a new issue