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:
35
scripts/admin.ts
Normal file
35
scripts/admin.ts
Normal 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();
|
||||
8
scripts/docker-entrypoint.sh
Normal file
8
scripts/docker-entrypoint.sh
Normal 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
205
scripts/seed-demo.ts
Normal 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
59
scripts/update-packages.sh
Executable 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"
|
||||
Reference in New Issue
Block a user