feat: rebrand Hemmelig to paste.es for cloudhost.es

- Set Spanish as default language with ephemeral/encrypted privacy focus
- Translate all user-facing strings and legal pages to Spanish
- Replace Norwegian flag with Spanish flag in footer
- Remove Hemmelig/terces.cloud links, add cloudhost.es sponsorship
- Rewrite PrivacyPage: zero data collection, ephemeral design emphasis
- Rewrite TermsPage: Spanish law, RGPD, paste.es/CloudHost.es references
- Update PWA manifest, HTML meta tags, package.json branding
- Rename webhook headers to X-Paste-Event / X-Paste-Signature
- Update API docs title and contact to paste.es / cloudhost.es

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 09:30:19 +01:00
commit bc9f96cbd4
268 changed files with 45773 additions and 0 deletions

35
scripts/admin.ts Normal file
View File

@@ -0,0 +1,35 @@
import db from '../api/lib/db';
async function main() {
const email = process.argv[2];
if (!email) {
console.error('Please provide an email address.');
process.exit(1);
}
try {
const user = await db.user.findUnique({
where: { email },
});
if (!user) {
console.error(`User with email "${email}" not found.`);
process.exit(1);
}
await db.user.update({
where: { email },
data: { role: 'admin' },
});
console.log(`User "${email}" has been granted admin privileges.`);
} catch (error) {
console.error('An error occurred:', error);
process.exit(1);
} finally {
await db.$disconnect();
}
}
main();

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
# Fix permissions on mounted volumes (runs as root)
chown -R app:app /app/database /app/uploads 2>/dev/null || true
# Run migrations and start app as app user
exec gosu app sh -c 'npx prisma migrate deploy && exec npx tsx server.ts'

205
scripts/seed-demo.ts Normal file
View File

@@ -0,0 +1,205 @@
import crypto from 'crypto';
import db from '../api/lib/db';
const DAYS_TO_GENERATE = 30;
const VISITOR_PATHS = ['/', '/secret'];
function randomInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function randomDate(daysAgo: number): Date {
const date = new Date();
date.setDate(date.getDate() - daysAgo);
date.setHours(randomInt(0, 23), randomInt(0, 59), randomInt(0, 59));
return date;
}
function generateUniqueVisitorId(): string {
return crypto.randomBytes(16).toString('hex');
}
// ============================================
// Instance Settings
// ============================================
async function seedInstanceSettings() {
console.log('Seeding instance settings...');
const existing = await db.instanceSettings.findFirst();
if (existing) {
console.log(' Instance settings already exist, skipping...');
console.log(' Done!\n');
return existing;
}
const settings = await db.instanceSettings.create({
data: {
instanceName: 'Hemmelig Demo',
instanceDescription: 'A demo instance of Hemmelig for testing and development.',
allowRegistration: true,
requireEmailVerification: false,
defaultSecretExpiration: 72,
maxSecretSize: 1024,
allowPasswordProtection: true,
allowIpRestriction: true,
enableRateLimiting: true,
rateLimitRequests: 100,
rateLimitWindow: 60,
requireInviteCode: false,
webhookEnabled: false,
},
});
console.log(' Created instance settings');
console.log(' Done!\n');
return settings;
}
// ============================================
// Secrets
// ============================================
async function seedSecrets() {
console.log('Seeding secrets...');
const records = [];
for (let daysAgo = 0; daysAgo < DAYS_TO_GENERATE; daysAgo++) {
// Generate between 1-15 secrets per day
const secretsCount = randomInt(1, 15);
for (let i = 0; i < secretsCount; i++) {
const createdAt = randomDate(daysAgo);
// Random expiration: 1 hour, 1 day, 1 week, or more
const expirationHours = [1, 24, 168, 336, 672][randomInt(0, 4)];
const expiresAt = new Date(createdAt.getTime() + expirationHours * 60 * 60 * 1000);
// Random features
const hasPassword = Math.random() < 0.3;
const hasIpRange = Math.random() < 0.1;
const isBurnable = Math.random() < 0.4;
// Random views
const views = randomInt(1, 20);
// Generate dummy encrypted data
const secret = Buffer.from(crypto.randomBytes(32));
const title = Buffer.from(crypto.randomBytes(16));
const salt = crypto.randomBytes(16).toString('hex');
records.push({
secret,
title,
salt,
views,
password: hasPassword ? crypto.randomBytes(32).toString('hex') : null,
ipRange: hasIpRange ? '192.168.1.0/24' : '',
isBurnable,
createdAt,
expiresAt,
});
}
}
// Batch insert
const batchSize = 100;
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
await db.secrets.createMany({ data: batch });
process.stdout.write(
`\r Created ${Math.min(i + batchSize, records.length)}/${records.length} secrets`
);
}
console.log('\n Done!\n');
}
// ============================================
// Visitor Analytics
// ============================================
async function seedVisitorAnalytics() {
console.log('Seeding visitor analytics...');
const records = [];
for (let daysAgo = 0; daysAgo < DAYS_TO_GENERATE; daysAgo++) {
// Generate between 5-50 unique visitors per day
const uniqueVisitors = randomInt(5, 50);
const visitorIds = Array.from({ length: uniqueVisitors }, () => generateUniqueVisitorId());
for (const visitorId of visitorIds) {
// Each visitor views 1-5 pages
const pageViews = randomInt(1, 5);
for (let i = 0; i < pageViews; i++) {
const path = VISITOR_PATHS[randomInt(0, VISITOR_PATHS.length - 1)];
records.push({
path,
uniqueId: visitorId,
timestamp: randomDate(daysAgo),
});
}
}
}
// Batch insert
const batchSize = 500;
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
await db.visitorAnalytics.createMany({ data: batch });
process.stdout.write(
`\r Created ${Math.min(i + batchSize, records.length)}/${records.length} visitor records`
);
}
console.log('\n Done!\n');
}
// ============================================
// Clear Data
// ============================================
async function clearDemoData() {
console.log('Clearing existing demo data...');
await db.visitorAnalytics.deleteMany({});
console.log(' Cleared visitor analytics');
// Only delete anonymous secrets (no userId)
await db.secrets.deleteMany({ where: { userId: null } });
console.log(' Cleared anonymous secrets');
await db.instanceSettings.deleteMany({});
console.log(' Cleared instance settings');
console.log(' Done!\n');
}
// ============================================
// Main
// ============================================
async function main() {
const args = process.argv.slice(2);
const shouldClear = args.includes('--clear');
console.log('\n🌱 Hemmelig Demo Database Seeder\n');
console.log('This will populate the database with demo data for development.\n');
try {
if (shouldClear) {
await clearDemoData();
}
await seedInstanceSettings();
await seedSecrets();
await seedVisitorAnalytics();
console.log('✅ Demo database seeded successfully!\n');
} catch (error) {
console.error('\n❌ An error occurred:', error);
process.exit(1);
} finally {
await db.$disconnect();
}
}
main();

59
scripts/update-packages.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Script to update all packages in package.json to their latest versions
PACKAGE_JSON="package.json"
if [ ! -f "$PACKAGE_JSON" ]; then
echo "Error: package.json not found"
exit 1
fi
update_packages() {
local section=$1
echo ""
echo "=== Updating $section ==="
echo ""
# Extract package names from the section
packages=$(jq -r ".$section // {} | keys[]" "$PACKAGE_JSON" 2>/dev/null)
if [ -z "$packages" ]; then
echo "No packages found in $section"
return
fi
for package in $packages; do
current=$(jq -r ".$section[\"$package\"]" "$PACKAGE_JSON")
# Get latest version from npm
latest=$(npm view "$package" version 2>/dev/null)
if [ -z "$latest" ]; then
echo "$package: Could not fetch latest version"
continue
fi
# Compare versions (strip ^ or ~ from current)
current_clean=$(echo "$current" | sed 's/^[\^~]//')
if [ "$current_clean" = "$latest" ]; then
echo "$package: $current (up to date)"
else
echo "$package: $current → ^$latest"
# Update package.json using jq
tmp=$(mktemp)
jq ".$section[\"$package\"] = \"^$latest\"" "$PACKAGE_JSON" > "$tmp" && mv "$tmp" "$PACKAGE_JSON"
fi
done
}
echo "Checking for package updates..."
update_packages "dependencies"
update_packages "devDependencies"
echo ""
echo "=== Done ==="
echo ""
echo "Run 'npm install' to install updated packages"