This commit is contained in:
grabbit 2025-08-09 10:04:14 +08:00
parent 3a014a3d20
commit 12708e6149
6 changed files with 833 additions and 64 deletions

View File

@ -0,0 +1,434 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分布式流星监测网络</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0a0a23 0%, #1a1a3a 50%, #2a2a4a 100%);
color: white;
overflow-x: hidden;
}
.starfield {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.star {
position: absolute;
background: white;
border-radius: 50%;
animation: twinkle 2s infinite alternate;
}
@keyframes twinkle {
0% { opacity: 0.3; transform: scale(1); }
100% { opacity: 1; transform: scale(1.2); }
}
.meteor {
position: absolute;
width: 2px;
height: 100px;
background: linear-gradient(to bottom, #ff6b35, transparent);
animation: meteor-fall 3s linear infinite;
}
@keyframes meteor-fall {
0% {
transform: translateY(-100px) translateX(0);
opacity: 1;
}
100% {
transform: translateY(100vh) translateX(-200px);
opacity: 0;
}
}
.hero {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 2rem;
position: relative;
}
.hero-title {
font-size: 4rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(45deg, #ffffff, #ff6b35);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
0% { text-shadow: 0 0 20px rgba(255, 107, 53, 0.5); }
100% { text-shadow: 0 0 40px rgba(255, 107, 53, 0.8); }
}
.hero-subtitle {
font-size: 1.5rem;
color: #cccccc;
margin-bottom: 3rem;
max-width: 600px;
}
.split-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
max-width: 1200px;
margin: 4rem auto;
padding: 0 2rem;
}
.science-visual {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.constellation {
width: 100%;
height: 300px;
position: relative;
background: radial-gradient(circle at center, rgba(255, 107, 53, 0.1), transparent);
}
.constellation-dot {
position: absolute;
width: 6px;
height: 6px;
background: #ff6b35;
border-radius: 50%;
box-shadow: 0 0 15px #ff6b35;
}
.constellation-line {
position: absolute;
height: 1px;
background: linear-gradient(to right, #ff6b35, transparent);
transform-origin: left;
}
.value-prop {
display: flex;
flex-direction: column;
justify-content: center;
}
.value-prop h2 {
font-size: 2.5rem;
margin-bottom: 1.5rem;
color: #ff6b35;
}
.value-prop p {
font-size: 1.2rem;
line-height: 1.6;
color: #cccccc;
margin-bottom: 2rem;
}
.feature-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
max-width: 1200px;
margin: 4rem auto;
padding: 0 2rem;
}
.feature-card {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 2rem;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
cursor: pointer;
}
.feature-card:hover {
transform: translateY(-10px);
box-shadow: 0 20px 40px rgba(255, 107, 53, 0.2);
border-color: #ff6b35;
}
.feature-icon {
width: 60px;
height: 60px;
background: linear-gradient(45deg, #ff6b35, #ff8c42);
border-radius: 15px;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
}
.feature-card h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #ffffff;
}
.feature-card p {
color: #cccccc;
line-height: 1.6;
}
.cta-section {
text-align: center;
padding: 4rem 2rem;
max-width: 800px;
margin: 0 auto;
}
.cta-buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.btn {
padding: 1rem 2rem;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 600;
text-decoration: none;
transition: all 0.3s ease;
border: none;
cursor: pointer;
}
.btn-primary {
background: linear-gradient(45deg, #ff6b35, #ff8c42);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(255, 107, 53, 0.4);
}
.btn-secondary {
background: transparent;
color: #ff6b35;
border: 2px solid #ff6b35;
}
.btn-secondary:hover {
background: #ff6b35;
color: white;
}
@media (max-width: 768px) {
.hero-title {
font-size: 2.5rem;
}
.split-section {
grid-template-columns: 1fr;
gap: 2rem;
}
.cta-buttons {
flex-direction: column;
align-items: center;
}
}
</style>
</head>
<body>
<div class="starfield" id="starfield"></div>
<main>
<section class="hero">
<h1 class="hero-title">分布式流星监测网络</h1>
<p class="hero-subtitle">
连接全球观测者,记录天空中每一道流星的轨迹,为科学研究提供珍贵数据
</p>
</section>
<section class="split-section">
<div class="science-visual">
<div class="constellation" id="constellation">
<!-- 星座连线将通过JavaScript生成 -->
</div>
<h3 style="text-align: center; margin-top: 1rem; color: #ff6b35;">实时监测网络</h3>
</div>
<div class="value-prop">
<h2>科学价值</h2>
<p>
通过分布式监测网络,我们能够精确追踪流星轨迹,分析其起源、速度和成分。
每一次观测都为天体物理学研究贡献宝贵数据。
</p>
<p>
加入我们的网络,成为公民科学家,让您的观测为人类对宇宙的理解做出贡献。
</p>
</div>
</section>
<section class="feature-cards">
<div class="feature-card">
<div class="feature-icon">🛰️</div>
<h3>设备网络</h3>
<p>分布在全球的高精度监测设备24小时不间断捕捉流星事件构建完整的观测网络。</p>
</div>
<div class="feature-card">
<div class="feature-icon">📸</div>
<h3>事件画廊</h3>
<p>精彩的流星照片和视频集合,每一帧都记录着宇宙的壮丽瞬间,见证天空的奇迹。</p>
</div>
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>研究数据</h3>
<p>开放的科学数据平台,为天文学家和研究人员提供准确、详细的流星观测数据。</p>
</div>
</section>
<section class="cta-section">
<h2 style="font-size: 2.5rem; margin-bottom: 1.5rem; color: #ff6b35;">
开始您的星空探索之旅
</h2>
<p style="font-size: 1.2rem; color: #cccccc; margin-bottom: 2rem;">
注册账户,访问实时数据,加入全球观测者社区
</p>
<div class="cta-buttons">
<a href="#" class="btn btn-primary">立即注册</a>
<a href="#" class="btn btn-secondary">了解更多</a>
</div>
</section>
</main>
<script>
// 生成星空背景
function createStarfield() {
const starfield = document.getElementById('starfield');
const numStars = 100;
for (let i = 0; i < numStars; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.width = Math.random() * 3 + 1 + 'px';
star.style.height = star.style.width;
star.style.animationDelay = Math.random() * 2 + 's';
starfield.appendChild(star);
}
// 添加流星
setInterval(() => {
const meteor = document.createElement('div');
meteor.className = 'meteor';
meteor.style.left = Math.random() * 100 + '%';
meteor.style.animationDuration = (Math.random() * 2 + 2) + 's';
starfield.appendChild(meteor);
setTimeout(() => {
meteor.remove();
}, 5000);
}, 3000);
}
// 生成星座图案
function createConstellation() {
const constellation = document.getElementById('constellation');
const dots = [
{x: 20, y: 30}, {x: 40, y: 20}, {x: 60, y: 40},
{x: 30, y: 60}, {x: 70, y: 50}, {x: 50, y: 80},
{x: 80, y: 70}, {x: 15, y: 75}, {x: 85, y: 25}
];
// 创建星点
dots.forEach((dot, index) => {
const dotEl = document.createElement('div');
dotEl.className = 'constellation-dot';
dotEl.style.left = dot.x + '%';
dotEl.style.top = dot.y + '%';
dotEl.style.animationDelay = index * 0.2 + 's';
constellation.appendChild(dotEl);
});
// 创建连线
const connections = [
[0, 1], [1, 2], [2, 4], [4, 6], [3, 5], [5, 7], [0, 3]
];
connections.forEach(([start, end]) => {
const line = document.createElement('div');
line.className = 'constellation-line';
const startDot = dots[start];
const endDot = dots[end];
const dx = endDot.x - startDot.x;
const dy = endDot.y - startDot.y;
const length = Math.sqrt(dx * dx + dy * dy);
const angle = Math.atan2(dy, dx) * 180 / Math.PI;
line.style.left = startDot.x + '%';
line.style.top = startDot.y + '%';
line.style.width = length + '%';
line.style.transform = `rotate(${angle}deg)`;
constellation.appendChild(line);
});
}
// 平滑滚动动画
function handleScrollAnimations() {
const cards = document.querySelectorAll('.feature-card');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, { threshold: 0.1 });
cards.forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(50px)';
card.style.transition = 'all 0.6s ease';
observer.observe(card);
});
}
// 初始化
document.addEventListener('DOMContentLoaded', () => {
createStarfield();
createConstellation();
handleScrollAnimations();
});
</script>
</body>
</html>

View File

@ -1,26 +1,26 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
--card: #ffffff;
--card-foreground: #171717;
--popover: #ffffff;
--popover-foreground: #171717;
--primary: #171717;
--primary-foreground: #fafafa;
--secondary: #f5f5f5;
--secondary-foreground: #171717;
--muted: #f5f5f5;
--muted-foreground: #737373;
--accent: #f5f5f5;
--accent-foreground: #171717;
--background: #0a0a23;
--foreground: #ffffff;
--card: rgba(255, 255, 255, 0.05);
--card-foreground: #ffffff;
--popover: rgba(255, 255, 255, 0.05);
--popover-foreground: #ffffff;
--primary: #ff6b35;
--primary-foreground: #ffffff;
--secondary: rgba(255, 255, 255, 0.1);
--secondary-foreground: #ffffff;
--muted: rgba(255, 255, 255, 0.05);
--muted-foreground: #cccccc;
--accent: #ff6b35;
--accent-foreground: #ffffff;
--destructive: #ef4444;
--destructive-foreground: #fafafa;
--border: #e5e5e5;
--input: #e5e5e5;
--ring: #171717;
--radius: 0.5rem;
--destructive-foreground: #ffffff;
--border: rgba(255, 255, 255, 0.1);
--input: rgba(255, 255, 255, 0.1);
--ring: #ff6b35;
--radius: 1rem;
}
@theme inline {
@ -50,30 +50,99 @@
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--card: #0a0a0a;
--card-foreground: #ededed;
--popover: #0a0a0a;
--popover-foreground: #ededed;
--primary: #ededed;
--primary-foreground: #0a0a0a;
--secondary: #262626;
--secondary-foreground: #ededed;
--muted: #262626;
--muted-foreground: #a3a3a3;
--accent: #262626;
--accent-foreground: #ededed;
--destructive: #dc2626;
--destructive-foreground: #ededed;
--border: #262626;
--input: #262626;
--ring: #d4d4d8;
--background: #0a0a23;
--foreground: #ffffff;
--card: rgba(255, 255, 255, 0.05);
--card-foreground: #ffffff;
--popover: rgba(255, 255, 255, 0.05);
--popover-foreground: #ffffff;
--primary: #ff6b35;
--primary-foreground: #ffffff;
--secondary: rgba(255, 255, 255, 0.1);
--secondary-foreground: #ffffff;
--muted: rgba(255, 255, 255, 0.05);
--muted-foreground: #cccccc;
--accent: #ff6b35;
--accent-foreground: #ffffff;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
--border: rgba(255, 255, 255, 0.1);
--input: rgba(255, 255, 255, 0.1);
--ring: #ff6b35;
}
}
body {
background: var(--background);
background: linear-gradient(135deg, #0a0a23 0%, #1a1a3a 50%, #2a2a4a 100%);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow-x: hidden;
}
.starfield {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
}
.star {
position: absolute;
background: white;
border-radius: 50%;
animation: twinkle 2s infinite alternate;
}
@keyframes twinkle {
0% { opacity: 0.3; transform: scale(1); }
100% { opacity: 1; transform: scale(1.2); }
}
.meteor {
position: absolute;
width: 2px;
height: 100px;
background: linear-gradient(to bottom, #ff6b35, transparent);
animation: meteor-fall 3s linear infinite;
}
@keyframes meteor-fall {
0% {
transform: translateY(-100px) translateX(0);
opacity: 1;
}
100% {
transform: translateY(100vh) translateX(-200px);
opacity: 0;
}
}
.hero-title-gradient {
background: linear-gradient(45deg, #ffffff, #ff6b35);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
0% { text-shadow: 0 0 20px rgba(255, 107, 53, 0.5); }
100% { text-shadow: 0 0 40px rgba(255, 107, 53, 0.8); }
}
.glass-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
transition: all 0.3s ease;
}
.glass-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(255, 107, 53, 0.2);
border-color: #ff6b35;
}

View File

@ -15,8 +15,8 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "分布式流星监测网络 - Distributed Meteor Monitoring Network",
description: "连接全球观测者,记录天空中每一道流星的轨迹,为科学研究提供珍贵数据",
};
export default function RootLayout({
@ -25,7 +25,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="zh-CN">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>

View File

@ -3,18 +3,58 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { useAuth } from "@/contexts/auth-context"
import { useEffect } from "react"
export default function Home() {
const { isAuthenticated, user } = useAuth()
useEffect(() => {
createStarfield()
}, [])
const createStarfield = () => {
const starfield = document.getElementById('starfield')
if (!starfield) return
const numStars = 100
for (let i = 0; i < numStars; i++) {
const star = document.createElement('div')
star.className = 'star'
star.style.left = Math.random() * 100 + '%'
star.style.top = Math.random() * 100 + '%'
star.style.width = Math.random() * 3 + 1 + 'px'
star.style.height = star.style.width
star.style.animationDelay = Math.random() * 2 + 's'
starfield.appendChild(star)
}
// Add falling meteors
const createMeteor = () => {
const meteor = document.createElement('div')
meteor.className = 'meteor'
meteor.style.left = Math.random() * 100 + '%'
meteor.style.animationDuration = (Math.random() * 2 + 2) + 's'
starfield.appendChild(meteor)
setTimeout(() => {
meteor.remove()
}, 5000)
}
setInterval(createMeteor, 3000)
}
return (
<div className="min-h-screen flex flex-col">
<div className="min-h-screen flex flex-col relative">
<div id="starfield" className="starfield"></div>
{/* Navigation */}
<header className="absolute top-0 right-0 p-6 z-10">
<div className="flex items-center gap-4">
{isAuthenticated ? (
<>
<span className="text-sm text-gray-600 dark:text-gray-300">
<span className="text-sm text-gray-300">
Welcome, {user?.email}
</span>
<Button asChild>
@ -34,28 +74,88 @@ export default function Home() {
</div>
</header>
{/* Main Content */}
<div className="flex-1 flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800">
<main className="text-center px-4">
<h1 className="text-6xl font-bold text-gray-900 dark:text-white mb-4">
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8">
Distributed Meteor Monitoring Network
</p>
{!isAuthenticated && (
<div className="flex items-center justify-center gap-4">
<Button size="lg" asChild>
<Link href="/register">Join the Network</Link>
{/* Hero Section */}
<section className="min-h-screen flex flex-col justify-center items-center text-center px-4 relative">
<h1 className="text-6xl font-bold mb-4 hero-title-gradient">
</h1>
<p className="text-xl text-gray-300 mb-8 max-w-2xl">
</p>
<p className="text-lg text-gray-400 mb-8">
Distributed Meteor Monitoring Network
</p>
{!isAuthenticated && (
<div className="flex items-center justify-center gap-4 flex-wrap">
<Button size="lg" asChild className="bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700">
<Link href="/register"></Link>
</Button>
<Button variant="outline" size="lg" asChild className="border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white">
<Link href="/login">Sign In</Link>
</Button>
</div>
)}
</section>
{/* Feature Cards Section */}
<section className="py-16 px-4">
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="glass-card p-8 text-center">
<div className="w-16 h-16 bg-gradient-to-r from-orange-500 to-orange-600 rounded-2xl mx-auto mb-6 flex items-center justify-center text-2xl">
🛰
</div>
<h3 className="text-xl font-semibold mb-4 text-white"></h3>
<p className="text-gray-300 leading-relaxed">
24
</p>
</div>
<div className="glass-card p-8 text-center">
<div className="w-16 h-16 bg-gradient-to-r from-orange-500 to-orange-600 rounded-2xl mx-auto mb-6 flex items-center justify-center text-2xl">
📸
</div>
<h3 className="text-xl font-semibold mb-4 text-white"></h3>
<p className="text-gray-300 leading-relaxed">
</p>
</div>
<div className="glass-card p-8 text-center">
<div className="w-16 h-16 bg-gradient-to-r from-orange-500 to-orange-600 rounded-2xl mx-auto mb-6 flex items-center justify-center text-2xl">
📊
</div>
<h3 className="text-xl font-semibold mb-4 text-white"></h3>
<p className="text-gray-300 leading-relaxed">
</p>
</div>
</div>
</div>
</section>
{/* CTA Section */}
{!isAuthenticated && (
<section className="py-16 px-4 text-center">
<div className="max-w-4xl mx-auto">
<h2 className="text-4xl font-bold mb-6 text-orange-500">
</h2>
<p className="text-xl text-gray-300 mb-8">
访
</p>
<div className="flex items-center justify-center gap-4 flex-wrap">
<Button size="lg" asChild className="bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 px-8 py-3 text-lg">
<Link href="/register"></Link>
</Button>
<Button variant="outline" size="lg" asChild>
<Link href="/login">Sign In</Link>
<Button variant="outline" size="lg" asChild className="border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white px-8 py-3 text-lg">
<Link href="/gallery"></Link>
</Button>
</div>
)}
</main>
</div>
</div>
</section>
)}
</div>
)
}

View File

@ -0,0 +1,136 @@
#!/usr/bin/env node
/**
* 创建付费测试用户的脚本
*
* 使用方法
* node scripts/create-premium-user.js [email] [password] [displayName]
*
* 例子
* node scripts/create-premium-user.js premium@test.com TestPass123 "Premium User"
*/
const { Client } = require('pg');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '..', '.env') });
async function createPremiumUser(email, password, displayName) {
const client = new Client({
connectionString: process.env.DATABASE_URL
});
try {
await client.connect();
console.log('✅ Connected to database');
// 1. 首先注册普通用户
console.log(`\n📝 Registering user: ${email}`);
const registerResponse = await fetch('http://localhost:3001/api/v1/auth/register-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
displayName
})
});
if (!registerResponse.ok) {
const error = await registerResponse.json();
throw new Error(`Registration failed: ${error.message}`);
}
const registerData = await registerResponse.json();
const userId = registerData.userId;
console.log(`✅ User registered successfully with ID: ${userId}`);
// 2. 更新用户为付费用户
const fakeCustomerId = 'cus_test_premium_' + Date.now();
const fakeSubscriptionId = 'sub_test_premium_' + Date.now();
const updateQuery = `
UPDATE user_profiles
SET
payment_provider_customer_id = $1,
payment_provider_subscription_id = $2,
updated_at = NOW()
WHERE id = $3
`;
const result = await client.query(updateQuery, [fakeCustomerId, fakeSubscriptionId, userId]);
if (result.rowCount > 0) {
console.log('✅ User upgraded to premium successfully!');
// 3. 测试登录和profile获取
console.log('\n🔐 Testing login...');
const loginResponse = await fetch('http://localhost:3001/api/v1/auth/login-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password
})
});
if (loginResponse.ok) {
const loginData = await loginResponse.json();
console.log('✅ Login successful');
// 测试profile
const profileResponse = await fetch('http://localhost:3001/api/v1/auth/profile', {
method: 'GET',
headers: {
'Authorization': `Bearer ${loginData.accessToken}`,
'Content-Type': 'application/json',
}
});
if (profileResponse.ok) {
const profileData = await profileResponse.json();
console.log('✅ Profile retrieved successfully');
console.log('\n🎉 Premium user created successfully!');
console.log('='.repeat(50));
console.log(`👤 User Details:`);
console.log(` Email: ${email}`);
console.log(` Password: ${password}`);
console.log(` Display Name: ${displayName}`);
console.log(` User ID: ${userId}`);
console.log(` Customer ID: ${fakeCustomerId}`);
console.log(` Subscription ID: ${fakeSubscriptionId}`);
console.log(` Subscription Status: ${profileData.subscriptionStatus}`);
console.log(` Has Active Subscription: ${profileData.hasActiveSubscription}`);
console.log('='.repeat(50));
}
}
} else {
console.log('❌ Failed to upgrade user to premium');
}
} catch (error) {
console.error('❌ Error:', error.message);
process.exit(1);
} finally {
await client.end();
}
}
// 命令行参数处理
const args = process.argv.slice(2);
const email = args[0] || 'premium' + Date.now() + '@test.com';
const password = args[1] || 'TestPassword123';
const displayName = args[2] || 'Premium User';
console.log('🚀 Creating Premium User...');
console.log(`Email: ${email}`);
console.log(`Password: ${password}`);
console.log(`Display Name: ${displayName}`);
createPremiumUser(email, password, displayName);

30
test-api.js Normal file
View File

@ -0,0 +1,30 @@
const fetch = require('node-fetch');
async function testAPI() {
try {
console.log('Testing register-email API...');
const response = await fetch('http://localhost:3001/api/v1/auth/register-email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'testuser@example.com',
password: 'TestPass123',
displayName: 'Test User'
})
});
console.log('Response status:', response.status);
console.log('Response headers:', Object.fromEntries(response.headers));
const data = await response.text();
console.log('Response body:', data);
} catch (error) {
console.error('Error:', error.message);
}
}
testAPI();