Software /
code /
prosody-modules
Changeset
5943:05125c29fd67
mod_invites_webgen: initial release
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Wed, 26 Feb 2025 17:36:14 +0700 |
parents | 5942:abd1bbe5006e |
children | 5944:d073ada49a86 |
files | mod_invites_webgen/README.markdown mod_invites_webgen/html/webgen.en.html mod_invites_webgen/html/webgen.html mod_invites_webgen/mod_invites_webgen.lua |
diffstat | 4 files changed, 395 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_webgen/README.markdown Wed Feb 26 17:36:14 2025 +0700 @@ -0,0 +1,61 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Enable create invitations via the web' +... + +Introduction +============ + +Not every client would implement the create invitation feature. Thus, this +module allows users to create new invites from the server's website. + +Details +======= + +The invitation will create account-and-contact invites (the "add to roster" +kind of invite). Once user have sucessfully enter their credential, the module +redirect them to the invite page. + +It depends on these module and their purpose: + +- [mod_http]: for obvious reason :). +- [mod_invites][doc:modules:mod_invites]: generate the actual invite. +- [mod_invites_register_web][doc:modules:mod_invites_register]: serve the + invite page. + +Configuration +============= + +The configuration utilizes the same `allow_user_invites` and +`allow_contact_invites` as [mod_invites_adhoc]. + +If you specify a `site_name`, it will uses that else it will fallback to the +`VirtualHost` that it was loaded onto. + +You can use your own html templates with `invites_template_html`. Names of the +files MUST match the default. More over, you can offer multiple (human) +languages by adding the `&l=` to the URL. Meaning this module will serve +`register.html` for your default URL: +``` + + https://prosody.example.net/?=aowiefjoaij + +``` + +And if you have a `register.en.html` in the directory you have specified in +your config file, it will be served at: +``` + + https://prosody.example.net/?=aowiefjoaij&l=en + +``` + +So in your `register.html`, you can point to the English version by using an +`<a>` tag like this: +``` + + <a href="/?={token}&l=en">English</a> + +``` +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_webgen/html/webgen.en.html Wed Feb 26 17:36:14 2025 +0700 @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8;" /> + +<title>Register</title> +<meta name="description" content="Register for standard account `@{site_name}`." /> + +<meta name="author" content="Trần H. Trung" /> +<meta name="HandheldFriendly" content="true" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> + +<style> +code { + font-family: Monospace; + color: #fff; + background: #000; + padding: 2px; + border-radius: 5px; +} +main { + width: 960px; + margin: auto; +} +h1, h2, h3, h4, h5, h6 { text-align: center; } +table { + width: 100%; + padding: 5px; + text-align: center; + background: #000; + color: #fff; +} +.even { background: #444; } +.odd { background: #555; } +table caption { + background: #000; + color: #fff; + margin: auto; + padding: 5px; +} +</style> +</head> + +<body> + +<div style="height: 2rem;"></div> + +<main> +<hr/> + +<h1>Register</h1> + +<h4><a href='/webgen?&l=en' title='tiếng Việt' rel='noopener noreferer' >« tiếng Việt »</a></h4> + +<p>To register for a <a href='' title='Server - Standard Address' rel='noopener noreferer' >account</a> +<code>@{site_name}</code>, you need <a href='' title='Server - Invites' rel='noopener noreferer' >an invite.</a></p> + +<p>To generate an invite with your account, you can authenticate below:</p> + +<form method="post"> + +<table> + +<caption align="bottom">…{message&{message}!}</caption> + +<colgroup> +<col width="52%" /> +<col width="48%" /> +</colgroup> + +<tr class="header"> +<th align="center">Username</th> +<th align="center">Password</th> +</tr> + +<tr class="odd"> +<td align="left"><div style="text-align: center;"> +<p><input required + name="username" + aria-describedby="usernameHelp" + minlength="1" + maxlength="32" + length="32"><code>@{site_name}</code> +</input></p></div></td> +<td align="left"><div style="text-align: center;"> +<p><input required + type="password" + name="password" + aria-describedby="passwordHelp" + autocomplete="new-password" + minlength="\{password_policy.length\}"> +</input></p></div></td> +</tr> + +<tr class='even'> +<td colspan="2"> +<h3> +<button type="submit"> +Generate +</button> +</h3> +</td> +</tr> +</table> + +</form> + +<p>If you're new to XMPP and yet to know anybody, <a href='' title='Trung - Contact' target='_blank' rel='noopener noreferer' >contact me.</a></p> + +<h4><a href='#top' title='top' rel='noopener noreferer' >~*~</a></h4> + +<hr/> +</main> + +<div style="height: 4rem;"></div> + +</body> + +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_webgen/html/webgen.html Wed Feb 26 17:36:14 2025 +0700 @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html lang="vi"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8;" /> + +<title>Đăng Ký</title> +<meta name="description" content="Đăng ký tài khoản tiêu chuẩn `@{site_name}`." /> + +<meta name="author" content="Trần H. Trung" /> +<meta name="HandheldFriendly" content="true" /><!-- mobile device --> +<meta name="viewport" content="width=device-width, initial-scale=1" /><!-- mobile device --> + +<style> +code { + font-family: Monospace; + color: #fff; + background: #000; + padding: 2px; + border-radius: 5px; +} +main { + width: 960px; + margin: auto; +} +h1, h2, h3, h4, h5, h6 { text-align: center; } +table { + width: 100%; + padding: 5px; + text-align: center; + background: #000; + color: #fff; +} +.even { background: #444; } +.odd { background: #555; } +table caption { + background: #000; + color: #fff; + margin: auto; + padding: 5px; +} +</style> +</head> + +<body> + +<div style="height: 2rem;"></div> + +<main> +<hr/> + +<h1>Đăng Ký</h1> + +<h4><a href='/webgen?&l=en' title='English' rel='noopener noreferer' >« English »</a></h4> + +<p>Để đăng ký <a href='' title='Tài Liệu - Tài Khoản' rel='noopener noreferer'>tài khoản +<code>@{site_name}</code></a>, bạn cần có <a href='' title='Tài Liệu - Mời Đăng Ký' rel='noopener noreferer' >lời mời.</a></p> + +<p>Để tạo lời mời với tài khoản của mình, bạn có thể xác danh dưới đây:</p> + +<form method="post"> + +<table> + +<caption align="bottom">…{message&{message}!}</caption> + +<colgroup> +<col width="52%" /> +<col width="48%" /> +</colgroup> + +<tr class="header"> +<th align="center">Tên Đăng Nhập</th> +<th align="center">Mật Khẩu</th> +</tr> + +<tr class="odd"> +<td align="left"><div style="text-align: center;"> +<p><input required + name="username" + aria-describedby="usernameHelp" + minlength="1" + maxlength="32" + length="32"><code>@{site_name}</code> +</input></p></div></td> +<td align="left"><div style="text-align: center;"> +<p><input required + type="password" + name="password" + aria-describedby="passwordHelp" + autocomplete="new-password" + minlength="\{password_policy.length\}"> +</input></p></div></td> +</tr> + +<tr class='even'> +<td colspan='2'> +<h3> +<button type="submit"> +Tạo +</button> +</h3> +</td> +</tr> + +</table> + +</form> + +<p>Nếu XMPP còn mới và bạn chưa quen ai, hãy <a href='' title='Liên Lạc' rel='noopener noreferer' >liên lạc.</a></p> + +<h4><a href='#top' title='top' rel='noopener noreferer' >~*~</a></h4> + +<hr/> +</main> +</div> + +<div style="height: 4rem;"></div> + +</body> + +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_webgen/mod_invites_webgen.lua Wed Feb 26 17:36:14 2025 +0700 @@ -0,0 +1,94 @@ +local http_formdecode = require "net.http".formdecode; +local usermanager = require "core.usermanager"; +local nodeprep = require "util.encodings".stringprep.nodeprep; +local st = require "util.stanza"; +local url_escape = require "util.http".urlencode; +local render_html_template = require "util.interpolation".new("%b{}", st.xml_escape, { + urlescape = url_escape; +}); + +local site_name = module:get_option_string("site_name", module.host); + +local invites = module:depends("invites"); +--local template_get = module:depends("invites_page").template_get; + +local templatePath = module:get_option_string("invites_template_html", "html"); +function template_get(filename, lang) + local template = lang and templatePath.."/"..filename.."."..lang..".html" + or templatePath.."/"..filename..".html"; + return assert(module:load_resource(template):read("*a")); +end + + +module:depends("http"); + +local default_path, lang; + +local function serve_webgen(event) + local query_params = event.request.url.query and http_formdecode(event.request.url.query); + lang = query_params and query_params.l; + + local webgen_template = template_get("webgen", lang); + event.response.headers["Content-Type"] = "text/html; charset=utf-8"; + + local webgen = render_html_template(webgen_template, { + site_name = site_name; + }); + return webgen; +end + +module:depends("invites_register_web"); + +local function handle_webgen(event) + local request, response = event.request, event.response; + local form_data = http_formdecode(request.body); + local username, password = form_data["username"], form_data["password"]; + + event.response.headers["Content-Type"] = "text/html; charset=utf-8"; + + local webgen_template = template_get("webgen", lang); + local htmlPath = module:http_url("webgen", "/webgen"); + + if not username or #username == 0 or not password or #password == 0 then + return render_html_template(webgen_template, { + site_name = site_name; + path = htmlPath; + msg_class = "alert-warning"; + message = "Please fill in all fields."; + }); + end + + local prepped_username = nodeprep(username); + local exists = usermanager.user_exists(prepped_username, module.host); + local pass = usermanager.test_password(prepped_username, module.host, password); + local allow_user = module:get_option_boolean("allow_user_invites", false); + local allow_contact = module:get_option_boolean("allow_contact_invites", false); + + local invite; + if exists and pass and allow_user and allow_contact then + invite = invites.create_contact(username, true, { source = "webgen" }); + else + return render_html_template(webgen_template, { + site_name = site_name; + path = htmlPath; + username = username; + msg_class = "alert-warning"; + message = "Invalid input!"; + }); + end + + if not invite then + return 500; + else + event.response.headers.Location = invite.landing_page or invite.uri; + return 303; + end +end + +module:provides("http", { + default_path = "webgen"; + route = { + ["GET"] = serve_webgen; + ["POST"] = handle_webgen; + }; +});