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 });