source: Klonkt/src/views/pages/downloads.ejs@ 77b6fca

main
Last change on this file since 77b6fca was 91094a4, checked in by roboburr <roboburr@…>, 3 months ago

Download-for-email (premium feature #2)

Fan leaves their email -> receives the file; address is added to the mailing list
(source 'download', single opt-in so the download is not behind a confirm barrier).
Reuses SubscriberService (#1).

  • DB: audio_tracks.downloadable (default 0).
  • admin-audio: per-track no-JS toggle (POST /admin/audio/:id/downloadable) + ⬇ button in the admin list (premium-gated); downloadable also in the /api/:id whitelist.
  • routes/download.js (premium-gated): GET /downloads (list), GET /download/:id (capture page), POST /download/:id (save email + session grant), GET /download/:id/bestand (serves attachment from storage/audio, 15-min session window, path-traversal guards).
  • views: pages/downloads.ejs + pages/download.ejs (form/ready, auto-start download).
  • admin dashboard: ⬇ Downloads link (premium, non-hub).

node --check passed. Nothing else reuses this; #8 notify reuses subscribers.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 2.0 KB
RevLine 
[91094a4]1<%
2 var ts = (typeof dlTracks !== 'undefined') ? dlTracks : [];
3%>
4<section class="dls">
5 <h1 class="dls-h1">Downloads</h1>
6 <p class="dls-sub">Gratis te downloaden — laat je e-mailadres achter en je krijgt het bestand.</p>
7
8 <% if (!ts.length) { %>
9 <p class="dls-empty">Er staan momenteel geen downloads klaar.</p>
10 <% } else { %>
11 <ul class="dls-list">
12 <% ts.forEach(function(t){ %>
13 <li class="dls-item">
14 <% if (t.cover_url) { %><span class="dls-cover" style="background-image:url('<%= t.cover_url %>')"></span><% } else { %><span class="dls-cover dls-cover-empty">♪</span><% } %>
15 <span class="dls-meta">
16 <span class="dls-title"><%= t.title %></span>
17 <% if (t.artist) { %><span class="dls-artist"><%= t.artist %></span><% } %>
18 </span>
19 <a class="dls-btn" href="<%= siteUrlBase %>/download/<%= t.id %>">⬇ Download</a>
20 </li>
21 <% }); %>
22 </ul>
23 <% } %>
24</section>
25
26<style>
27 .dls { max-width: 680px; margin: 0 auto; padding: 32px 18px 64px; }
28 .dls-h1 { font-size: clamp(26px,5vw,38px); margin: 0 0 8px; }
29 .dls-sub { opacity: .8; margin: 0 0 24px; }
30 .dls-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
31 .dls-item { display: flex; align-items: center; gap: 14px; padding: 10px 12px; border: 1px solid rgba(128,128,128,.2); border-radius: 12px; }
32 .dls-cover { flex: 0 0 auto; width: 48px; height: 48px; border-radius: 8px; background-size: cover; background-position: center; display: grid; place-items: center; }
33 .dls-cover-empty { background: var(--accent,#6b8f71); color: #fff; opacity: .85; }
34 .dls-meta { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
35 .dls-title { font-weight: 600; }
36 .dls-artist { font-size: 12.5px; opacity: .65; }
37 .dls-btn { flex: 0 0 auto; padding: 9px 15px; border-radius: 999px; background: var(--accent,#6b8f71); color: #fff; text-decoration: none; font-weight: 600; font-size: 13.5px; }
38</style>
Note: See TracBrowser for help on using the repository browser.