style: auto-format codebase with Prettier

Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
This commit is contained in:
Wuesteon 2025-11-27 18:33:16 +01:00
parent 0241f5554c
commit d36b321d9d
3952 changed files with 661498 additions and 739751 deletions

View file

@ -34,8 +34,8 @@ async function createUnifiedCardsCollection() {
cascadeDelete: true,
minSelect: null,
maxSelect: 1,
displayFields: ['email', 'username']
}
displayFields: ['email', 'username'],
},
},
// Card type and origin
@ -45,8 +45,8 @@ async function createUnifiedCardsCollection() {
required: true,
options: {
values: ['user', 'template', 'system'],
maxSelect: 1
}
maxSelect: 1,
},
},
{
name: 'source',
@ -54,8 +54,8 @@ async function createUnifiedCardsCollection() {
required: false,
options: {
values: ['created', 'duplicated', 'imported', 'migrated'],
maxSelect: 1
}
maxSelect: 1,
},
},
{
name: 'template_id',
@ -66,8 +66,8 @@ async function createUnifiedCardsCollection() {
cascadeDelete: false,
minSelect: null,
maxSelect: 1,
displayFields: ['metadata']
}
displayFields: ['metadata'],
},
},
// Card configuration (unified structure)
@ -76,24 +76,24 @@ async function createUnifiedCardsCollection() {
type: 'json',
required: true,
options: {
maxSize: 1000000 // 1MB max
}
maxSize: 1000000, // 1MB max
},
},
{
name: 'metadata',
type: 'json',
required: false,
options: {
maxSize: 100000 // 100KB max
}
maxSize: 100000, // 100KB max
},
},
{
name: 'constraints',
type: 'json',
required: false,
options: {
maxSize: 10000 // 10KB max
}
maxSize: 10000, // 10KB max
},
},
// Organization
@ -104,8 +104,8 @@ async function createUnifiedCardsCollection() {
options: {
min: 0,
max: 100,
pattern: ''
}
pattern: '',
},
},
{
name: 'position',
@ -114,8 +114,8 @@ async function createUnifiedCardsCollection() {
options: {
min: 0,
max: 9999,
noDecimal: true
}
noDecimal: true,
},
},
{
name: 'folder_id',
@ -126,8 +126,8 @@ async function createUnifiedCardsCollection() {
cascadeDelete: false,
minSelect: null,
maxSelect: 1,
displayFields: ['name']
}
displayFields: ['name'],
},
},
// Visibility and sharing
@ -137,18 +137,18 @@ async function createUnifiedCardsCollection() {
required: true,
options: {
values: ['private', 'public', 'unlisted'],
maxSelect: 1
}
maxSelect: 1,
},
},
{
name: 'is_featured',
type: 'bool',
required: false
required: false,
},
{
name: 'allow_duplication',
type: 'bool',
required: false
required: false,
},
// Statistics
@ -159,8 +159,8 @@ async function createUnifiedCardsCollection() {
options: {
min: 0,
max: 999999,
noDecimal: true
}
noDecimal: true,
},
},
{
name: 'likes_count',
@ -169,8 +169,8 @@ async function createUnifiedCardsCollection() {
options: {
min: 0,
max: 999999,
noDecimal: true
}
noDecimal: true,
},
},
// Search and categorization
@ -179,8 +179,8 @@ async function createUnifiedCardsCollection() {
type: 'json',
required: false,
options: {
maxSize: 10000
}
maxSize: 10000,
},
},
{
name: 'category',
@ -188,8 +188,8 @@ async function createUnifiedCardsCollection() {
required: false,
options: {
values: ['personal', 'creative', 'minimal', 'social', 'portfolio', 'other'],
maxSelect: 1
}
maxSelect: 1,
},
},
// Variant for styling
@ -199,9 +199,9 @@ async function createUnifiedCardsCollection() {
required: false,
options: {
values: ['default', 'compact', 'hero', 'minimal', 'glass', 'gradient'],
maxSelect: 1
}
}
maxSelect: 1,
},
},
],
// Indexes for performance
@ -211,7 +211,7 @@ async function createUnifiedCardsCollection() {
'CREATE INDEX idx_cards_page ON cards (page)',
'CREATE INDEX idx_cards_visibility ON cards (visibility)',
'CREATE INDEX idx_cards_template_id ON cards (template_id)',
'CREATE INDEX idx_cards_position ON cards (position)'
'CREATE INDEX idx_cards_position ON cards (position)',
],
// API Rules
@ -223,7 +223,7 @@ async function createUnifiedCardsCollection() {
'@request.auth.id = user_id || (@request.auth.id != "" && type = "system" && @request.auth.admin = true)',
deleteRule: '@request.auth.id = user_id && type != "system"',
options: {}
options: {},
};
// Create the collection
@ -268,17 +268,17 @@ async function createDefaultTemplates(pb) {
type: 'header',
props: {
title: 'John Doe',
subtitle: 'Software Developer'
subtitle: 'Software Developer',
},
order: 0
order: 0,
},
{
id: 'content-1',
type: 'content',
props: {
text: 'Passionate about creating amazing digital experiences.'
text: 'Passionate about creating amazing digital experiences.',
},
order: 1
order: 1,
},
{
id: 'links-1',
@ -286,25 +286,25 @@ async function createDefaultTemplates(pb) {
props: {
links: [
{ label: 'GitHub', href: 'https://github.com', icon: '🔗' },
{ label: 'LinkedIn', href: 'https://linkedin.com', icon: '💼' }
{ label: 'LinkedIn', href: 'https://linkedin.com', icon: '💼' },
],
style: 'button'
style: 'button',
},
order: 2
}
]
order: 2,
},
],
},
metadata: {
name: 'Simple Profile',
description: 'A clean and simple profile card',
version: '1.0.0',
tags: ['profile', 'minimal', 'clean']
tags: ['profile', 'minimal', 'clean'],
},
constraints: {
aspectRatio: '16/9'
aspectRatio: '16/9',
},
usage_count: 0,
likes_count: 0
likes_count: 0,
},
{
type: 'template',
@ -352,27 +352,27 @@ async function createDefaultTemplates(pb) {
{ name: 'name', type: 'text', label: 'Your Name' },
{ name: 'tagline', type: 'text', label: 'Tagline' },
{ name: 'email', type: 'text', label: 'Email' },
{ name: 'phone', type: 'text', label: 'Phone' }
{ name: 'phone', type: 'text', label: 'Phone' },
],
values: {
name: 'Your Name',
tagline: 'Your tagline here',
email: 'contact@example.com',
phone: '+1 234 567 890'
}
phone: '+1 234 567 890',
},
},
metadata: {
name: 'Professional Card',
description: 'Professional contact card template',
version: '1.0.0',
tags: ['professional', 'contact']
tags: ['professional', 'contact'],
},
constraints: {
aspectRatio: '16/9'
aspectRatio: '16/9',
},
usage_count: 0,
likes_count: 0
}
likes_count: 0,
},
];
for (const template of templates) {

View file

@ -20,17 +20,17 @@ const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
// Ensure directory exists
const iconsDir = path.join(__dirname, '..', 'static', 'icons');
if (!fs.existsSync(iconsDir)) {
fs.mkdirSync(iconsDir, { recursive: true });
fs.mkdirSync(iconsDir, { recursive: true });
}
// Generate SVG icons
sizes.forEach(size => {
const filename = `icon-${size}x${size}.svg`;
const filepath = path.join(iconsDir, filename);
const content = createSvgIcon(size);
fs.writeFileSync(filepath, content.trim());
console.log(`Generated ${filename}`);
sizes.forEach((size) => {
const filename = `icon-${size}x${size}.svg`;
const filepath = path.join(iconsDir, filename);
const content = createSvgIcon(size);
fs.writeFileSync(filepath, content.trim());
console.log(`Generated ${filename}`);
});
// Also create apple-touch-icon
@ -40,10 +40,10 @@ console.log('Generated apple-touch-icon.svg');
// Create maskable icon (with safe area padding)
const createMaskableIcon = (size) => {
const safeArea = size * 0.8; // 80% safe area
const padding = (size - safeArea) / 2;
return `
const safeArea = size * 0.8; // 80% safe area
const padding = (size - safeArea) / 2;
return `
<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
<rect width="${size}" height="${size}" fill="#3B82F6"/>
<rect x="${padding}" y="${padding}" width="${safeArea}" height="${safeArea}" fill="#3B82F6" rx="${safeArea * 0.15}"/>
@ -53,13 +53,13 @@ const createMaskableIcon = (size) => {
};
// Generate maskable icons
[192, 512].forEach(size => {
const filename = `icon-maskable-${size}x${size}.svg`;
const filepath = path.join(iconsDir, filename);
const content = createMaskableIcon(size);
fs.writeFileSync(filepath, content.trim());
console.log(`Generated ${filename}`);
[192, 512].forEach((size) => {
const filename = `icon-maskable-${size}x${size}.svg`;
const filepath = path.join(iconsDir, filename);
const content = createMaskableIcon(size);
fs.writeFileSync(filepath, content.trim());
console.log(`Generated ${filename}`);
});
console.log('\n✅ All PWA icons generated successfully!');
console.log('\n✅ All PWA icons generated successfully!');

View file

@ -11,64 +11,65 @@ async function migrate() {
try {
console.log('Starting migration...');
console.log('This script will update links with use_username=true to use the new format');
// Get all links with use_username=true
const links = await pb.collection('links').getFullList({
filter: 'use_username = true',
expand: 'user_id'
expand: 'user_id',
});
console.log(`Found ${links.length} links to migrate`);
let successCount = 0;
let errorCount = 0;
let skippedCount = 0;
for (const link of links) {
try {
// Get the username from the expanded user
const username = link.expand?.user_id?.username;
if (!username) {
console.error(`No username found for link ${link.id} (user: ${link.user_id})`);
errorCount++;
continue;
}
// Check if already migrated (contains slash)
if (link.short_code.includes('/')) {
console.log(`Link ${link.id} already migrated: ${link.short_code}`);
skippedCount++;
continue;
}
// Create new short_code with username prefix
const newShortCode = `${username}/${link.short_code}`;
console.log(`Updating link ${link.id}: ${link.short_code} -> ${newShortCode}`);
// Update the link
await pb.collection('links').update(link.id, {
short_code: newShortCode
short_code: newShortCode,
});
successCount++;
} catch (error) {
console.error(`Error migrating link ${link.id}:`, error.message);
errorCount++;
}
}
console.log('\n=== Migration Summary ===');
console.log(`✅ Successfully migrated: ${successCount} links`);
console.log(`⏭️ Skipped (already migrated): ${skippedCount} links`);
console.log(`❌ Errors: ${errorCount} links`);
if (successCount > 0) {
console.log('\n⚠ Important: The use_username field should be removed from the collection schema');
console.log(
'\n⚠ Important: The use_username field should be removed from the collection schema'
);
console.log('You can do this manually in PocketBase Admin UI');
}
} catch (error) {
console.error('Migration failed:', error);
process.exit(1);
@ -76,4 +77,4 @@ async function migrate() {
}
// Run migration
migrate();
migrate();

View file

@ -14,59 +14,58 @@ async function migrate() {
try {
// Authenticate as admin (update with your credentials)
await pb.admins.authWithPassword('admin@example.com', 'your-password');
console.log('Fetching all links with use_username=true...');
// Get all links with use_username=true
const links = await pb.collection('links').getFullList({
filter: 'use_username = true',
expand: 'user_id'
expand: 'user_id',
});
console.log(`Found ${links.length} links to migrate`);
let successCount = 0;
let errorCount = 0;
for (const link of links) {
try {
// Get the username from the expanded user
const username = link.expand?.user_id?.username;
if (!username) {
console.error(`No username found for link ${link.id} (user: ${link.user_id})`);
errorCount++;
continue;
}
// Create new short_code with username prefix
const newShortCode = `${username}/${link.short_code}`;
console.log(`Updating link ${link.id}: ${link.short_code} -> ${newShortCode}`);
// Update the link
await pb.collection('links').update(link.id, {
short_code: newShortCode
short_code: newShortCode,
});
successCount++;
} catch (error) {
console.error(`Error migrating link ${link.id}:`, error);
errorCount++;
}
}
console.log('\nMigration complete!');
console.log(`✅ Successfully migrated: ${successCount} links`);
console.log(`❌ Errors: ${errorCount} links`);
if (successCount > 0) {
console.log('\n⚠ Next steps:');
console.log('1. Remove the use_username field from the links collection in PocketBase admin');
console.log('2. Test that all migrated links still work');
console.log('3. Deploy the updated application code');
}
} catch (error) {
console.error('Migration failed:', error);
process.exit(1);
@ -74,4 +73,4 @@ async function migrate() {
}
// Run migration
migrate();
migrate();

View file

@ -15,32 +15,32 @@ const pb = new PocketBase(process.env.PUBLIC_POCKETBASE_URL || 'https://pb.ulo.a
async function migrate() {
console.log('Starting migration to workspace system...');
try {
// Authenticate as admin
const adminEmail = process.env.ADMIN_EMAIL;
const adminPassword = process.env.ADMIN_PASSWORD;
if (!adminEmail || !adminPassword) {
console.error('Please set ADMIN_EMAIL and ADMIN_PASSWORD in .env.production');
process.exit(1);
}
await pb.admins.authWithPassword(adminEmail, adminPassword);
console.log('✓ Authenticated as admin');
// Get all users
const users = await pb.collection('users').getFullList();
console.log(`Found ${users.length} users to migrate`);
// Create personal workspace for each user
for (const user of users) {
try {
// Check if personal workspace already exists
const existingWorkspaces = await pb.collection('workspaces').getList(1, 1, {
filter: `owner="${user.id}" && type="personal"`
filter: `owner="${user.id}" && type="personal"`,
});
if (existingWorkspaces.items.length === 0) {
// Create personal workspace
const workspace = await pb.collection('workspaces').create({
@ -48,18 +48,18 @@ async function migrate() {
owner: user.id,
type: 'personal',
subscription_status: user.subscription_status || 'free',
description: 'Personal workspace'
description: 'Personal workspace',
});
console.log(`✓ Created personal workspace for ${user.email}`);
// Add user as owner in workspace_members
await pb.collection('workspace_members').create({
workspace: workspace.id,
user: user.id,
role: 'owner',
invitation_status: 'accepted',
accepted_at: new Date().toISOString()
accepted_at: new Date().toISOString(),
});
} else {
console.log(`- Personal workspace already exists for ${user.email}`);
@ -68,25 +68,25 @@ async function migrate() {
console.error(`✗ Error creating workspace for ${user.email}:`, error.message);
}
}
// Migrate shared_access to workspace_members
console.log('\nMigrating shared access...');
try {
const sharedAccess = await pb.collection('shared_access').getFullList({
expand: 'owner,user'
expand: 'owner,user',
});
console.log(`Found ${sharedAccess.length} shared access records`);
for (const access of sharedAccess) {
try {
// Find or create team workspace for the owner
let teamWorkspace;
const existingTeamWorkspaces = await pb.collection('workspaces').getList(1, 1, {
filter: `owner="${access.owner}" && type="team"`
filter: `owner="${access.owner}" && type="team"`,
});
if (existingTeamWorkspaces.items.length === 0) {
// Create team workspace
const ownerData = access.expand?.owner;
@ -94,28 +94,28 @@ async function migrate() {
name: `${ownerData?.name || ownerData?.email}'s Team`,
owner: access.owner,
type: 'team',
description: 'Team workspace for collaboration'
description: 'Team workspace for collaboration',
});
console.log(`✓ Created team workspace for owner ${access.owner}`);
// Add owner as owner in workspace_members
await pb.collection('workspace_members').create({
workspace: teamWorkspace.id,
user: access.owner,
role: 'owner',
invitation_status: 'accepted',
accepted_at: new Date().toISOString()
accepted_at: new Date().toISOString(),
});
} else {
teamWorkspace = existingTeamWorkspaces.items[0];
}
// Check if member already exists
const existingMembers = await pb.collection('workspace_members').getList(1, 1, {
filter: `workspace="${teamWorkspace.id}" && user="${access.user}"`
filter: `workspace="${teamWorkspace.id}" && user="${access.user}"`,
});
if (existingMembers.items.length === 0) {
// Add team member
await pb.collection('workspace_members').create({
@ -126,9 +126,9 @@ async function migrate() {
invitation_status: access.invitation_status,
invitation_token: access.invitation_token,
invited_at: access.invited_at,
accepted_at: access.accepted_at
accepted_at: access.accepted_at,
});
console.log(`✓ Migrated team member ${access.user} to workspace ${teamWorkspace.id}`);
} else {
console.log(`- Team member ${access.user} already exists in workspace`);
@ -140,13 +140,12 @@ async function migrate() {
} catch (error) {
console.error('✗ Error fetching shared access:', error.message);
}
console.log('\n✅ Migration completed successfully!');
console.log('\nNext steps:');
console.log('1. Test the new workspace system');
console.log('2. Update any links/cards to reference workspace instead of owner');
console.log('3. Once verified, you can remove the old shared_access collection');
} catch (error) {
console.error('✗ Migration failed:', error);
process.exit(1);
@ -154,4 +153,4 @@ async function migrate() {
}
// Run migration
migrate().catch(console.error);
migrate().catch(console.error);

View file

@ -2,9 +2,9 @@
/**
* Seed Script for Local PocketBase Development
*
*
* This script creates test data for local development.
*
*
* Usage:
* 1. Make sure PocketBase is running locally (http://localhost:8090)
* 2. Run: node scripts/seed-local-db.js
@ -33,7 +33,7 @@ const testUsers = [
passwordConfirm: 'test123456',
username: 'testuser',
name: 'Test User',
emailVisibility: true
emailVisibility: true,
},
{
email: 'demo@localhost',
@ -41,8 +41,8 @@ const testUsers = [
passwordConfirm: 'demo123456',
username: 'demouser',
name: 'Demo User',
emailVisibility: true
}
emailVisibility: true,
},
];
const testLinks = [
@ -54,7 +54,7 @@ const testLinks = [
is_active: true,
click_limit: null,
expires_at: null,
password: null
password: null,
},
{
short_code: 'test2',
@ -64,7 +64,7 @@ const testLinks = [
is_active: true,
click_limit: 100,
expires_at: null,
password: null
password: null,
},
{
short_code: 'protected',
@ -74,7 +74,7 @@ const testLinks = [
is_active: true,
click_limit: null,
expires_at: null,
password: 'secret123'
password: 'secret123',
},
{
short_code: 'expired',
@ -84,8 +84,8 @@ const testLinks = [
is_active: true,
click_limit: null,
expires_at: new Date(Date.now() - 86400000).toISOString(), // Yesterday
password: null
}
password: null,
},
];
async function seedDatabase() {
@ -99,14 +99,14 @@ async function seedDatabase() {
console.log('⚠️ Admin auth failed. You may need to:');
console.log(' 1. Create admin account at http://localhost:8090/_/');
console.log(' 2. Update POCKETBASE_ADMIN_EMAIL and POCKETBASE_ADMIN_PASSWORD\n');
// Try to continue without admin auth (some operations might fail)
}
// Step 2: Create test users
console.log('👥 Creating test users...');
const createdUsers = [];
for (const userData of testUsers) {
try {
const user = await pb.collection('users').create(userData);
@ -118,7 +118,7 @@ async function seedDatabase() {
// Try to get existing user
try {
const users = await pb.collection('users').getList(1, 1, {
filter: `email = "${userData.email}"`
filter: `email = "${userData.email}"`,
});
if (users.items.length > 0) {
createdUsers.push(users.items[0]);
@ -135,22 +135,22 @@ async function seedDatabase() {
// Step 3: Create test links
console.log('🔗 Creating test links...');
// Use the first created user as the owner
const ownerId = createdUsers[0]?.id;
for (const linkData of testLinks) {
try {
// Add owner if we have one
if (ownerId) {
linkData.user_id = ownerId;
}
// Generate a random custom code if needed
if (!linkData.custom_code) {
linkData.custom_code = linkData.short_code;
}
const link = await pb.collection('links').create(linkData);
console.log(` ✅ Created link: ${linkData.short_code} -> ${linkData.original_url}`);
} catch (error) {
@ -165,16 +165,16 @@ async function seedDatabase() {
// Step 4: Create some test clicks
console.log('📊 Creating test click data...');
try {
// Get one of the links we created
const links = await pb.collection('links').getList(1, 1, {
filter: 'short_code = "test1"'
filter: 'short_code = "test1"',
});
if (links.items.length > 0) {
const link = links.items[0];
// Create some fake clicks
const clickData = [
{
@ -186,7 +186,7 @@ async function seedDatabase() {
os: 'macOS',
country: 'Germany',
city: 'Munich',
clicked_at: new Date().toISOString()
clicked_at: new Date().toISOString(),
},
{
link_id: link.id,
@ -197,10 +197,10 @@ async function seedDatabase() {
os: 'iOS',
country: 'USA',
city: 'New York',
clicked_at: new Date(Date.now() - 3600000).toISOString() // 1 hour ago
}
clicked_at: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago
},
];
for (const click of clickData) {
try {
await pb.collection('clicks').create(click);
@ -227,7 +227,6 @@ async function seedDatabase() {
console.log(' http://localhost:5173/protected - Password: secret123');
console.log(' http://localhost:5173/expired - Expired link\n');
console.log('👉 Next: Open http://localhost:5173 and test the app!');
} catch (error) {
console.error('❌ Seeding failed:', error);
process.exit(1);
@ -235,9 +234,11 @@ async function seedDatabase() {
}
// Run the seeding
seedDatabase().then(() => {
process.exit(0);
}).catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});
seedDatabase()
.then(() => {
process.exit(0);
})
.catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});

View file

@ -10,21 +10,21 @@ const path = require('path');
const envFiles = ['.env', '.env.development', '.env.production'];
envFiles.forEach(file => {
const filePath = path.join(__dirname, file);
if (fs.existsSync(filePath)) {
console.log(`\n${file} exists`);
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
lines.forEach(line => {
if (line.includes('POCKETBASE_URL')) {
console.log(` -> ${line}`);
}
});
}
envFiles.forEach((file) => {
const filePath = path.join(__dirname, file);
if (fs.existsSync(filePath)) {
console.log(`\n${file} exists`);
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
lines.forEach((line) => {
if (line.includes('POCKETBASE_URL')) {
console.log(` -> ${line}`);
}
});
}
});
console.log('\nNOTE: SvelteKit/Vite loads environment variables differently.');
console.log('The app should use:');
console.log('- Development: http://localhost:8090 (from .env or fallback)');
console.log('- Production: https://pb.ulo.ad (from .env.production)');
console.log('- Production: https://pb.ulo.ad (from .env.production)');

View file

@ -17,7 +17,7 @@ try {
passwordConfirm: 'testpassword123',
username: `testuser${Date.now()}`,
name: 'Test User',
emailVisibility: true
emailVisibility: true,
});
console.log('✅ User created successfully:', testUser.id);

View file

@ -18,13 +18,13 @@ async function test() {
// Test 3: Get folders for user
const folders = await pb.collection('folders').getList(1, 50, {
filter: `user_id="${user.id}" && is_public=true`
filter: `user_id="${user.id}" && is_public=true`,
});
console.log('Public folders:', folders.items.length);
// Test 4: Get links for user
const links = await pb.collection('links').getList(1, 100, {
filter: `user_id="${user.id}" && is_active=true`
filter: `user_id="${user.id}" && is_active=true`,
});
console.log('Active links:', links.items.length);
} catch (err) {

View file

@ -17,13 +17,13 @@ async function test() {
// Test 3: Get folders for user
const folders = await pb.collection('folders').getList(1, 50, {
filter: `user_id="${user.id}" && is_public=true`
filter: `user_id="${user.id}" && is_public=true`,
});
console.log('Public folders:', folders.items.length);
// Test 4: Get links for user
const links = await pb.collection('links').getList(1, 100, {
filter: `user_id="${user.id}" && is_active=true`
filter: `user_id="${user.id}" && is_active=true`,
});
console.log('Active links:', links.items.length);
} catch (err) {

View file

@ -27,8 +27,8 @@ async function testConnection() {
fields: usersCollection.schema.map((f) => ({
name: f.name,
type: f.type,
required: f.required
}))
required: f.required,
})),
});
// Test 4: Try to list users (might fail due to permissions)
@ -47,7 +47,7 @@ async function testConnection() {
email: testEmail,
password: 'Test123456!',
passwordConfirm: 'Test123456!',
username: `testuser${Date.now()}`
username: `testuser${Date.now()}`,
});
console.log('✓ Registration test successful, user created:', result.id);
// Clean up test user

View file

@ -87,7 +87,7 @@ async function testConnection() {
email: testEmail,
password: 'Test123456!',
passwordConfirm: 'Test123456!',
username: testUsername
username: testUsername,
});
console.log('✓ Registration successful! User ID:', result.id);
@ -115,8 +115,8 @@ async function testConnection() {
const response = await fetch(`${PROD_POCKETBASE_URL}/api/collections`, {
method: 'GET',
headers: {
Origin: 'https://your-frontend-domain.com'
}
Origin: 'https://your-frontend-domain.com',
},
});
console.log(
'✓ CORS headers present:',

View file

@ -10,7 +10,7 @@ console.log('==========================================\n');
async function testRedisConnection() {
console.log('1. Testing Redis Connection...');
const connected = await ensureRedisConnection();
if (connected) {
console.log('✅ Redis connected successfully');
console.log(` Host: ${process.env.REDIS_HOST || 'ycsoowwsc84s0s8gc8oooosk'}`);
@ -24,20 +24,20 @@ async function testRedisConnection() {
async function testBasicCacheOperations() {
console.log('2. Testing Basic Cache Operations...');
// Test set and get
const testKey = 'test:key';
const testValue = { message: 'Hello Redis', timestamp: Date.now() };
await cache.set(testKey, testValue, 60);
console.log(' Set test value in cache');
const retrieved = await cache.get(testKey);
console.log(' Retrieved value:', retrieved);
if (retrieved && retrieved.message === testValue.message) {
console.log('✅ Basic cache operations working\n');
// Clean up
await cache.del(testKey);
return true;
@ -49,21 +49,21 @@ async function testBasicCacheOperations() {
async function testLinkCaching() {
console.log('3. Testing Link Cache Functions...');
const testShortCode = 'test123';
const testUrl = 'https://example.com/test';
// Cache a redirect
await linkCache.cacheRedirect(testShortCode, testUrl);
console.log(` Cached redirect: ${testShortCode} -> ${testUrl}`);
// Try to retrieve it
const cachedUrl = await linkCache.getRedirectUrl(testShortCode);
console.log(` Retrieved URL: ${cachedUrl}`);
if (cachedUrl === testUrl) {
console.log('✅ Link caching working correctly\n');
// Clean up
await linkCache.invalidate(testShortCode);
return true;
@ -75,12 +75,12 @@ async function testLinkCaching() {
async function checkCachedLinks() {
console.log('4. Checking Currently Cached Links...');
try {
// Get all keys matching redirect pattern
const keys = await redis.keys('redirect:*');
console.log(` Found ${keys.length} cached redirects`);
if (keys.length > 0) {
console.log('\n Sample cached links:');
for (const key of keys.slice(0, 5)) {
@ -90,7 +90,7 @@ async function checkCachedLinks() {
console.log(` - ${shortCode}: ${url} (TTL: ${ttl}s)`);
}
}
// Check trending links
const trending = await linkCache.getTrendingLinks(5);
if (trending.length > 0) {
@ -99,7 +99,7 @@ async function checkCachedLinks() {
console.log(` ${i + 1}. ${code}`);
});
}
console.log('✅ Cache inspection complete\n');
return true;
} catch (error) {
@ -110,34 +110,34 @@ async function checkCachedLinks() {
async function performanceTest() {
console.log('5. Performance Test (Cache vs No Cache)...');
const testCode = 'perf-test';
const testUrl = 'https://example.com/performance';
// Test without cache (simulate)
const dbStart = Date.now();
await new Promise(resolve => setTimeout(resolve, 50)); // Simulate DB latency
await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate DB latency
const dbTime = Date.now() - dbStart;
console.log(` Database fetch simulation: ${dbTime}ms`);
// Cache the URL
await linkCache.cacheRedirect(testCode, testUrl);
// Test with cache
const cacheStart = Date.now();
await linkCache.getRedirectUrl(testCode);
const cacheTime = Date.now() - cacheStart;
console.log(` Cache fetch: ${cacheTime}ms`);
const improvement = Math.round((dbTime - cacheTime) / dbTime * 100);
const improvement = Math.round(((dbTime - cacheTime) / dbTime) * 100);
console.log(` 🚀 Performance improvement: ${improvement}%`);
if (cacheTime < dbTime) {
console.log('✅ Cache is faster than database\n');
} else {
console.log('⚠️ Cache performance needs investigation\n');
}
// Clean up
await linkCache.invalidate(testCode);
return true;
@ -146,13 +146,13 @@ async function performanceTest() {
async function monitorLiveTraffic() {
console.log('6. Monitoring Live Traffic (10 seconds)...');
console.log(' Open your app and click some links to see cache activity\n');
// Subscribe to Redis monitor for 10 seconds
const monitor = await redis.monitor();
let commandCount = 0;
let cacheHits = 0;
let cacheSets = 0;
monitor.on('monitor', (time, args) => {
const command = args[0];
if (command === 'get' && args[1]?.includes('redirect:')) {
@ -164,35 +164,35 @@ async function monitorLiveTraffic() {
}
commandCount++;
});
// Stop monitoring after 10 seconds
await new Promise(resolve => setTimeout(resolve, 10000));
await new Promise((resolve) => setTimeout(resolve, 10000));
monitor.disconnect();
console.log(`\n Monitoring complete:`);
console.log(` - Total Redis commands: ${commandCount}`);
console.log(` - Cache GETs: ${cacheHits}`);
console.log(` - Cache SETs: ${cacheSets}`);
console.log('✅ Live monitoring complete\n');
return true;
}
// Main test runner
async function runAllTests() {
console.log('Starting Redis Cache Tests...\n');
const tests = [
testRedisConnection,
testBasicCacheOperations,
testLinkCaching,
checkCachedLinks,
performanceTest
performanceTest,
];
let passed = 0;
let failed = 0;
for (const test of tests) {
try {
const result = await test();
@ -203,30 +203,30 @@ async function runAllTests() {
failed++;
}
}
console.log('==========================================');
console.log(`Test Results: ${passed} passed, ${failed} failed`);
// Optional: Run live monitoring
console.log('\nWould you like to monitor live traffic? (Ctrl+C to skip)');
console.log('Starting in 3 seconds...\n');
await new Promise(resolve => setTimeout(resolve, 3000));
await new Promise((resolve) => setTimeout(resolve, 3000));
try {
await monitorLiveTraffic();
} catch (error) {
console.log('Monitoring cancelled');
}
// Close Redis connection
redis.disconnect();
process.exit(failed > 0 ? 1 : 0);
}
// Run tests
runAllTests().catch(error => {
runAllTests().catch((error) => {
console.error('Test suite failed:', error);
redis.disconnect();
process.exit(1);
});
});

View file

@ -20,7 +20,7 @@ async function testRegistration() {
password: testPassword,
passwordConfirm: testPassword,
username: `user${timestamp}`,
emailVisibility: true
emailVisibility: true,
});
console.log('✅ User created successfully');

View file

@ -3,7 +3,7 @@ import PocketBase from 'pocketbase';
// Test verschiedene URL-Varianten
const urls = [
'http://pocketbase-xs0ccokk8s0goko4w40gwc0w.91.99.221.179.sslip.io', // ohne trailing slash
'http://pocketbase-xs0ccokk8s0goko4w40gwc0w.91.99.221.179.sslip.io/' // mit trailing slash
'http://pocketbase-xs0ccokk8s0goko4w40gwc0w.91.99.221.179.sslip.io/', // mit trailing slash
];
console.log('Testing different URL configurations...\n');
@ -30,7 +30,7 @@ async function testUrl(url) {
passwordConfirm: 'TestPass123!',
username: testUsername,
name: 'Test User',
emailVisibility: true
emailVisibility: true,
};
console.log(` Attempting registration with: ${testEmail}`);