Software /
code /
prosody-modules
Comparison
mod_invites_page/mod_invites_page.lua @ 4094:dd00a2b9927c
mod_invites_page: New module to generate landing page for invites
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 11 Sep 2020 13:52:32 +0100 |
child | 4107:798f284717e7 |
comparison
equal
deleted
inserted
replaced
4093:a2116f5a7c8f | 4094:dd00a2b9927c |
---|---|
1 local st = require "util.stanza"; | |
2 local url_escape = require "util.http".urlencode; | |
3 | |
4 module:depends("http"); | |
5 local base_url = module.http_url and module:http_url(); | |
6 | |
7 local render_html_template = require"util.interpolation".new("%b{}", st.xml_escape, { | |
8 urlescape = url_escape; | |
9 lower = string.lower; | |
10 classname = function (s) return (s:gsub("%W+", "-")); end; | |
11 relurl = function (s) | |
12 if s:match("^%w+://") then | |
13 return s; | |
14 end | |
15 return base_url.."/"..s; | |
16 end; | |
17 }); | |
18 local render_url = require "util.interpolation".new("%b{}", url_escape, { | |
19 urlescape = url_escape; | |
20 noscheme = function (url) | |
21 return (url:gsub("^[^:]+:", "")); | |
22 end; | |
23 }); | |
24 | |
25 module:depends("register_apps"); | |
26 module:depends("invites_register_web"); | |
27 | |
28 local site_name = module:get_option_string("site_name", module.host); | |
29 local site_apps = module:shared("register_apps/apps"); | |
30 | |
31 local http_files; | |
32 | |
33 if prosody.shutdown then | |
34 module:depends("http"); | |
35 http_files = module:depends("http_files"); | |
36 end | |
37 local invites = module:depends("invites"); | |
38 | |
39 -- Point at eg https://github.com/ge0rg/easy-xmpp-invitation | |
40 -- This URL must always be absolute, as it is shared standalone | |
41 local invite_url_template = module:get_option_string("invites_page", base_url and (base_url.."?{invite.token}") or nil); | |
42 -- This URL is relative to the invite page, or can be absolute | |
43 local register_url_template = module:get_option_string("invites_registration_page", "register?t={invite.token}&c={app.id}"); | |
44 | |
45 local function add_landing_url(invite) | |
46 if not invite_url_template then return; end | |
47 -- TODO: we don't currently have a landing page for subscription-only invites, | |
48 -- so the user will only receive a URI. The client should be able to handle this | |
49 -- by automatically falling back to a client-specific landing page, per XEP-0401. | |
50 if not invite.allow_registration then return; end | |
51 invite.landing_page = render_url(invite_url_template, { host = module.host, invite = invite }); | |
52 end | |
53 | |
54 module:hook("invite-created", add_landing_url); | |
55 | |
56 local function render_app_urls(apps, invite_vars) | |
57 local rendered_apps = {}; | |
58 for _, unrendered_app in ipairs(apps) do | |
59 local app = setmetatable({}, { __index = unrendered_app }); | |
60 local template_vars = { app = app, invite = invite_vars, base_url = base_url }; | |
61 if app.magic_link_format then | |
62 -- Magic link generally links directly to third-party | |
63 app.proceed_url = render_url(app.magic_link_format or app.link or "#", template_vars); | |
64 elseif app.supports_preauth_uri then | |
65 -- Proceed to a page that guides the user to download, and then | |
66 -- click the URI button | |
67 app.proceed_url = render_url("{base_url!}/setup/{app.id}?{invite.token}", template_vars); | |
68 else | |
69 -- Manual means proceed to web registration, but include app id | |
70 -- so it can show post-registration instructions | |
71 app.proceed_url = render_url(register_url_template, template_vars); | |
72 end | |
73 table.insert(rendered_apps, app); | |
74 end | |
75 return rendered_apps; | |
76 end | |
77 | |
78 function serve_invite_page(event) | |
79 local invite_page_template = assert(module:load_resource("html/invite.html")):read("*a"); | |
80 local invalid_invite_page_template = assert(module:load_resource("html/invite_invalid.html")):read("*a"); | |
81 | |
82 local invite = invites.get(event.request.url.query); | |
83 if not invite then | |
84 return render_html_template(invalid_invite_page_template, { | |
85 site_name = site_name; | |
86 static = base_url.."/static"; | |
87 }); | |
88 end | |
89 | |
90 local template_vars = { | |
91 site_name = site_name; | |
92 token = invite.token; | |
93 uri = invite.uri; | |
94 type = invite.type; | |
95 jid = invite.jid; | |
96 inviter = invite.inviter; | |
97 static = base_url.."/static"; | |
98 }; | |
99 template_vars.apps = render_app_urls(site_apps, template_vars); | |
100 | |
101 local invite_page = render_html_template(invite_page_template, template_vars); | |
102 return invite_page; | |
103 end | |
104 | |
105 function serve_setup_page(event, app_id) | |
106 local invite_page_template = assert(module:load_resource("html/client.html")):read("*a"); | |
107 local invalid_invite_page_template = assert(module:load_resource("html/invite_invalid.html")):read("*a"); | |
108 | |
109 local invite = invites.get(event.request.url.query); | |
110 if not invite then | |
111 return render_html_template(invalid_invite_page_template, { | |
112 site_name = site_name; | |
113 static = base_url.."/static"; | |
114 }); | |
115 end | |
116 | |
117 local template_vars = { | |
118 site_name = site_name; | |
119 apps = site_apps; | |
120 token = invite.token; | |
121 uri = invite.uri; | |
122 type = invite.type; | |
123 jid = invite.jid; | |
124 static = base_url.."/static"; | |
125 }; | |
126 template_vars.app = render_app_urls({ site_apps[app_id] }, template_vars)[1]; | |
127 | |
128 local invite_page = render_html_template(invite_page_template, template_vars); | |
129 return invite_page; | |
130 end | |
131 | |
132 local mime_map = { | |
133 png = "image/png"; | |
134 svg = "image/svg+xml"; | |
135 js = "application/javascript"; | |
136 }; | |
137 | |
138 module:provides("http", { | |
139 route = { | |
140 ["GET"] = serve_invite_page; | |
141 ["GET /setup/*"] = serve_setup_page; | |
142 ["GET /static/*"] = http_files and http_files.serve({ path = module:get_directory().."/static", mime_map = mime_map }); | |
143 }; | |
144 }); |