@developers-joyride/shortify
Version:
High performance URL shortener library with multi-database support (MongoDB, SQLite, PostgreSQL, MySQL)
217 lines (190 loc) • 7.04 kB
text/typescript
import express, { Request, Response } from "express";
import Shortify from "../src";
import dotenv from "dotenv";
// Load environment variables
dotenv.config();
// Create Express app
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Create Shortify instance
const shortify = new Shortify(
process.env.BASE_URL || "http://localhost:3000/",
process.env.MONGODB_URI || "mongodb://localhost:27017/shortify"
);
// Connect to database before starting server
async function startServer() {
try {
// Connect to MongoDB
await shortify.connect();
console.log("Connected to MongoDB");
// Set up routes
// Home page - Simple form to create shortened URLs
app.get("/", (req, res) => {
res.send(`
<html>
<head>
<title>URL Shortener</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input[type="text"] { width: 100%; padding: 8px; }
input[type="number"] { width: 100px; padding: 8px; }
button { padding: 10px 15px; background: #4CAF50; color: white; border: none; cursor: pointer; }
.result { margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 4px; }
</style>
</head>
<body>
<h1>URL Shortener</h1>
<form action="/shorten" method="POST">
<div class="form-group">
<label for="url">URL to Shorten:</label>
<input type="text" id="url" name="url" placeholder="https://example.com" required>
</div>
<div class="form-group">
<label for="customId">Custom ID (optional):</label>
<input type="text" id="customId" name="customId" placeholder="my-custom-id">
</div>
<div class="form-group">
<label for="expiry">Expires in (days):</label>
<input type="number" id="expiry" name="expiry" min="1" value="30">
</div>
<button type="submit">Shorten URL</button>
</form>
</body>
</html>
`);
});
// Shorten URL endpoint
app.post("/shorten", async (req: Request, res: Response) => {
try {
const { url, customId, expiry } = req.body;
if (!url) {
return res.status(400).json({ error: "URL is required" });
}
const options: any = {};
if (customId) {
options.customUrlId = customId;
}
if (expiry) {
options.expiresInDays = parseInt(expiry);
}
const result = await shortify.shorten(url, options);
res.send(`
<html>
<head>
<title>URL Shortened</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.result { margin-top: 20px; padding: 15px; background: #f0f0f0; border-radius: 4px; }
.copy-btn { padding: 5px 10px; background: #4CAF50; color: white; border: none; cursor: pointer; margin-left: 10px; }
</style>
</head>
<body>
<h1>URL Shortened</h1>
<div class="result">
<p><strong>Original URL:</strong> ${result.originalUrl}</p>
<p>
<strong>Shortened URL:</strong>
<a href="${result.shortUrl}" target="_blank">${
result.shortUrl
}</a>
<button class="copy-btn" onclick="copyToClipboard('${
result.shortUrl
}')">Copy</button>
</p>
<p><strong>URL ID:</strong> ${result.urlId}</p>
${
result.expiresAt
? `<p><strong>Expires at:</strong> ${new Date(
result.expiresAt
).toLocaleString()}</p>`
: ""
}
</div>
<p><a href="/">← Back to homepage</a></p>
<script>
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
alert('URL copied to clipboard');
});
}
</script>
</body>
</html>
`);
} catch (error) {
console.error("Error shortening URL:", error);
res.status(500).json({ error: "Failed to shorten URL" });
}
});
// Redirect shortened URLs
app.get(
"/:urlId",
async (req: Request<{ urlId: string }>, res: Response) => {
try {
const { urlId } = req.params;
const originalUrl = await shortify.resolve(urlId);
if (originalUrl) {
return res.redirect(originalUrl);
} else {
return res.status(404).send(`
<html>
<head>
<title>URL Not Found</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; text-align: center; }
</style>
</head>
<body>
<h1>URL Not Found</h1>
<p>The shortened URL you're looking for doesn't exist or has expired.</p>
<p><a href="/">← Go to homepage</a></p>
</body>
</html>
`);
}
} catch (error) {
console.error("Error resolving URL:", error);
res.status(500).json({ error: "Failed to resolve URL" });
}
}
);
// API endpoint to get URL stats
app.get(
"/api/stats/:urlId",
async (req: Request<{ urlId: string }>, res: Response) => {
try {
const { urlId } = req.params;
const stats = await shortify.getStats(urlId);
if (stats) {
return res.json(stats);
} else {
return res.status(404).json({ error: "URL not found" });
}
} catch (error) {
console.error("Error getting URL stats:", error);
res.status(500).json({ error: "Failed to get URL stats" });
}
}
);
// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
} catch (error) {
console.error("Failed to start server:", error);
process.exit(1);
}
}
// Graceful shutdown
process.on("SIGINT", async () => {
console.log("Shutting down server...");
await shortify.disconnect();
console.log("Disconnected from MongoDB");
process.exit(0);
});
// Start the server
startServer();