Learn how to build digital downloads with Lovable using our step-by-step guide. Set up, design, and launch your engaging digital products with ease.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Setting Up Your Digital Downloads Project in Lovable
Digital Downloads
.lovable.config.js
in your project’s root directory. This file will list the external libraries your project requires.Create the file lovable.config.js
and add the following code:
module.exports = {
dependencies: {
crypto: "npm:crypto"
// If you need to add other libraries, include them here in the same format.
}
};
Creating the Main Application File
app.js
.Add the following snippet at the top of app.js
to import your dependencies and set up a basic server. Make sure to include this snippet at the very start of your file:
const http = require('http');
const crypto = require('crypto');
// Basic server setup using Lovable's built-in system
const port = process.env.PORT || 8080;
Implementing Secure Download Link Generation
app.js
, add a function to generate secure download links. This function will create a token based on the user and product information.Insert the following code snippet after your dependency imports:
function createSecureToken(userId, productId) {
// Combine user, product, and timestamp details for security
const data = userId + ":" + productId + ":" + Date.now();
return crypto.createHash('sha256').update(data).digest('hex');
}
function generateDownloadLink(userId, productId) {
const token = createSecureToken(userId, productId);
// For simplicity, we assume the token is stored or validated in our download handler
return `https://yourlovabledomain.com/download?token=${token}`;
}
Setting Up the Download Request Handler
downloadHandler.js
in your project’s root directory. This file will handle incoming download requests and validation.Create downloadHandler.js
and add the following code:
const http = require('http');
const url = require('url');
// In a real scenario, a file path lookup and token validation mechanism would be implemented
function validateToken(token) {
// Placeholder for token validation logic
// Typically, you’d check a database for token existence and expiration
return token && token.length === 64;
}
function getFilePathFromToken(token) {
// Dummy function – in real usage retrieve the file path associated with the token
return './downloads/sample-file.zip';
}
function handleDownloadRequest(req, res) {
const queryData = url.parse(req.url, true).query;
const token = queryData.token;
if (validateToken(token)) {
const filePath = getFilePathFromToken(token);
// Send the file for download. In Lovable, you may use built-in file streaming.
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename="download.zip"'
});
// For demonstration, we simulate file data.
res.end("Simulated file content.");
} else {
res.writeHead(403, {'Content-Type': 'text/plain'});
res.end('Invalid or expired download link.');
}
}
module.exports = {
handleDownloadRequest
};
Integrating the Download Handler with Your Server
app.js
file.Insert the following code snippet below your previously inserted functions in app.js
:
const { handleDownloadRequest } = require('./downloadHandler');
const server = http.createServer((req, res) => {
const urlParts = req.url.split('?')[0];
if (urlParts === '/download') {
handleDownloadRequest(req, res);
} else {
// Default response for other routes
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Welcome to the Digital Downloads Service
');
}
});
server.listen(port, () => {
console.log(`Server running at http://0.0.0.0:${port}/`);
});
Building the Front-End for Requesting Downloads
index.html
in your project’s root directory. This file will serve as the landing page where users can request digital downloads.For example, add the following content to index.html
:
Digital Downloads
Digital Downloads
Click the button below to get your download.
Testing Your Digital Downloads Feature
lovable.config.js
, app.js
, downloadHandler.js
, and index.html
).index.html
to test the download functionality.
Finalizing and Sharing Your Project
const express = require('express');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const app = express();
const digitalDownloads = {
'lovable001': {
title: 'Lovable Premium Digital Album',
filePath: path.join(\__dirname, 'downloads', 'lovable_album.zip'),
fileSize: fs.existsSync(path.join(\__dirname, 'downloads', 'lovable_album.zip'))
? fs.statSync(path.join(\__dirname, 'downloads', 'lovable_album.zip')).size
: 0
}
};
function generateToken(productId) {
const secret = 'SuperSecretSaltForLovable';
return crypto.createHash('sha256').update(productId + secret).digest('hex');
}
app.get('/api/download/:productId', (req, res) => {
const { productId } = req.params;
const product = digitalDownloads[productId];
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
const userToken = req.query.token;
const validToken = generateToken(productId);
if (!userToken || userToken !== validToken) {
return res.status(403).json({ error: 'Unauthorized access' });
}
res.setHeader('Content-Length', product.fileSize);
res.setHeader('Content-Disposition', `attachment; filename="${path.basename(product.filePath)}"`);
const readStream = fs.createReadStream(product.filePath);
readStream.on('error', () => res.status(500).end('Error reading file'));
readStream.pipe(res);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
import express from 'express';
import AWS from 'aws-sdk';
import crypto from 'crypto';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
AWS.config.update({
region: process.env.AWS\_REGION
});
const s3 = new AWS.S3();
const digitalProducts = {
"lovable1001": { s3Key: "products/lovable\_album.zip", title: "Lovable Premium Digital Album" }
};
const generateToken = (productId) =>
crypto.createHmac("sha256", process.env.TOKEN\_SECRET)
.update(productId)
.digest("hex");
app.get('/api/presign/:productId', async (req, res) => {
const { productId } = req.params;
const product = digitalProducts[productId];
if (!product) {
return res.status(404).json({ error: "Product not found" });
}
const { token } = req.query;
if (!token || token !== generateToken(productId)) {
return res.status(403).json({ error: "Unauthorized access" });
}
const params = {
Bucket: process.env.S3\_BUCKET,
Key: product.s3Key,
Expires: 600 // URL valid for 10 minutes
};
try {
const signedUrl = await s3.getSignedUrlPromise("getObject", params);
res.json({ downloadUrl: signedUrl });
} catch (error) {
res.status(500).json({ error: "Error generating download URL" });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
"use strict";
const express = require('express');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const sqlite3 = require('sqlite3').verbose();
const app = express();
const PORT = process.env.PORT || 3000;
const secret = "LovableDownloadSecret";
const db = new sqlite3.Database(':memory:');
db.serialize(() => {
db.run(\`CREATE TABLE downloads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
productId TEXT NOT NULL,
ip TEXT NOT NULL,
timestamp INTEGER NOT NULL
)\`);
});
const digitalProducts = {
"lovable2002": {
title: "Lovable Deluxe Digital Cover",
filePath: path.join(\__dirname, 'assets', 'lovable_deluxe.zip')
}
};
function createToken(productId) {
return crypto.createHmac('sha256', secret).update(productId).digest('hex');
}
function logDownload(productId, ip) {
const stmt = db.prepare("INSERT INTO downloads (productId, ip, timestamp) VALUES (?, ?, ?)");
stmt.run(productId, ip, Date.now());
stmt.finalize();
}
app.get('/api/download/log/:productId', (req, res) => {
const { productId } = req.params;
const token = req.query.token;
const expectedToken = createToken(productId);
if (!digitalProducts[productId]) {
return res.status(404).json({ error: "Product not found" });
}
if (!token || token !== expectedToken) {
return res.status(403).json({ error: "Invalid token" });
}
const product = digitalProducts[productId];
fs.access(product.filePath, fs.constants.R\_OK, (err) => {
if (err) return res.status(500).json({ error: "File access error" });
logDownload(productId, req.ip);
res.setHeader('Content-Disposition', `attachment; filename="${path.basename(product.filePath)}"`);
const stream = fs.createReadStream(product.filePath);
stream.on('error', () => res.status(500).end("Error reading file"));
stream.pipe(res);
});
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Understanding Digital Downloads with AI Code Generators
Prerequisites
Planning Your Digital Download Project
Generating Code with an AI Code Generator
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Template</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; }
.header { background: #333; color: #fff; padding: 20px; text-align: center; }
.content { padding: 20px; }
</style>
</head>
<body>
<div class="header">Welcome to My Digital Download</div>
<div class="content">
<p>This is a responsive HTML template generated using an AI code tool.</p>
</div>
</body>
</html>
Organizing Code and Documentation
Testing Your Digital Download
Securing and Packaging Your Digital Download
MIT License
Copyright (c) [Year] [Your Name]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction...
Deploying and Distributing Your Digital Download
Maintaining and Updating Your Digital Download
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.