0.3 增加登录模块
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function setConvexEnv(name, value) {
|
||||
// 说明:使用 stdin 传值,避免以 `-----BEGIN ...` 开头的内容被 CLI 误当作参数/选项。
|
||||
execSync(`npx convex env set ${name}`, {
|
||||
input: String(value ?? ""),
|
||||
stdio: ['pipe', 'inherit', 'inherit'],
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
}
|
||||
|
||||
const privateKey = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPcrWDwjh74nu1
|
||||
gfYo9ywVIRMedfPwbNiDjHnovzHAIeToc1+Lw3ju/io9OME3KLWzIibUHX39LilK
|
||||
sSQKigJ3+i7J13IquJD++ozY9C8Dp/P34txVY+ECSQRdIBbETLJxYBWXOx6Ysw/d
|
||||
/CUuDOdEw2IhNvN97sTh5jUZe6f/H6mfCpJ3X5SZrqrIUTTL6r4Lj9ZS3PUX5ivU
|
||||
6sd2rXzpDsUQgrNgmvGN3w9nAHudxAUv/zOGa4802Z+z14LCBIX6v6PIK79YQ4s9
|
||||
/5hH73MesA6vgzWShJcyVy3guyde+duhpECo1q3d6dIZeqrazQwqAW3vcpmEFJdX
|
||||
jkpociDfAgMBAAECggEAAYtRE+mH1SGThlkvTrKWeWXBQG8xoJFzZTsiZtSEExbq
|
||||
UWxIh4cjqqL2znDpd5ALILIJ6/ejTxHrpN+yTSC+NQ9u6IJWusoA2ZXV5VH/nZD1
|
||||
yeHZ0FuCZRVnJB9/zz4qH5lSsi2TPz6SOagIuG2wIafeyw+94EmtOedSBAO2Q8NN
|
||||
3jHoINBCyRu6hU3ml0h7daoIhUw9ONI7MZUlYvuV7Ti3yf+czzpqwYx3gZA39sa8
|
||||
qUbJPFk6ts+CVjqSAdYVSfU3TC1Us1usOM3+04mACp3vc6ZUKxnB4Dsk/sSHj71n
|
||||
EcZ3fOR7EgjmXf6wBiq24T2+0UzoHW2yDaiAowr6iQKBgQDDs2eJTopFejt47EMm
|
||||
zY//e8fyUDKx07CWwUZKP78IMTZcp8BwHmKTSY+VbQLvVYnNxADkckYapokjU3a7
|
||||
GMOXxvKVLqfgU/oUBIkRFDRo+nasFCd5cPpnYjQ6lGalBkDghVlyvq7kKF/MPtIK
|
||||
dLqsV3tZDXoHX2mjTga94ygTZwKBgQC7pa+eZdWiz275mpYql9Gs6N3G3HhcpRxb
|
||||
oWYWekCtZQ9gtecpvA9e0tW3ShoF19ksWYGpM4vxAOXul5Ei9IPVVNHc7RvGF5CO
|
||||
0Zryx3qaVk7E6XQc3BQVVAenT2fNiv3+fkCMeAvcZBZOGiPPziCCT3wU3bd3tsn8
|
||||
EsMiAPvTyQKBgEs0iWhBr29NrsckfBXQTzMN/WOIIEMoJ6d3dKyZ3K6oQszOhmxP
|
||||
sPALB8uTjdotk/xoAzPHGlupfe/+ZhU2Sgvsn1JnEIprmyHQMGBI1G83OR2dzSGl
|
||||
IgVSvuF4IA3w3kOp2xr2Xj09qrrRtWPhQc9y+urY+/kTWIQyOvMD9WWnAoGADdUN
|
||||
2BBLqj++P3oMvcEJPMTBrGoOGU42g+6m1ttWLzH26zsdei8ZtvS1ulglCO87XBCR
|
||||
BUb+dtqJGIhls3zwxuYEvlNgK78K8ewzjtfzirL4BX3sCECU3mmeUtAAp98qD/uA
|
||||
iJpEzY83MbStlSDtto1jaSpa3uFDjGhZqAUIizkCgYABf/HSMwWsnvRgo0JMmDru
|
||||
3L/xScJiFVdFV9vNiSckjUYhx7lgFqEjsDEN1mZiRIa2UhLOqpo5S/cIXfMkeEPh
|
||||
9Rusf9STwNwOOyhFRrHCxqhTH6Ivnj6oZSM/UrS8GvCVMPWs1z8k45FWKI/IRXQq
|
||||
AT5PRKihrWbg63/rXaSCxw==
|
||||
-----END PRIVATE KEY-----`;
|
||||
|
||||
const jwks = '{"keys":[{"use":"sig","kty":"RSA","n":"j3K1g8I4e-J7tYH2KPcsFSETHnXz8GzYg4x56L8xwCHk6HNfi8N47v4qPTjBNyi1syIm1B19_S4pSrEkCooCd_ouyddyKriQ_vqM2PQvA6fz9-LcVWPhAkkEXSAWxEyycWAVlzsemLMP3fwlLgznRMNiITbzfe7E4eY1GXun_x-pnwqSd1-Uma6qyFE0y-q-C4_WUtz1F-Yr1OrHdq186Q7FEIKzYJrxjd8PZwB7ncQFL_8zhmuPNNmfs9eCwgSF-r-jyCu_WEOLPf-YR-9zHrAOr4M1koSXMlct4LsnXvnboaRAqNat3enSGXqq2s0MKgFt73KZhBSXV45KaHIg3w","e":"AQAB"}]}';
|
||||
|
||||
// 使用临时目录(跨平台兼容)
|
||||
const tmpDir = process.env.TMPDIR || process.env.TEMP || '/tmp';
|
||||
const keyFile = path.join(tmpDir, 'convex_jwt_key.txt');
|
||||
const jwksFile = path.join(tmpDir, 'convex_jwks.txt');
|
||||
|
||||
// 写入临时文件
|
||||
fs.writeFileSync(keyFile, privateKey);
|
||||
fs.writeFileSync(jwksFile, jwks);
|
||||
|
||||
console.log('Setting JWT_PRIVATE_KEY from file...');
|
||||
const keyContent = fs.readFileSync(keyFile, 'utf8');
|
||||
try {
|
||||
setConvexEnv("JWT_PRIVATE_KEY", keyContent);
|
||||
console.log('✓ JWT_PRIVATE_KEY set');
|
||||
} catch (e) {
|
||||
console.error('✗ Failed to set JWT_PRIVATE_KEY:', e.message);
|
||||
}
|
||||
|
||||
console.log('Setting JWKS...');
|
||||
try {
|
||||
setConvexEnv("JWKS", jwks);
|
||||
console.log('✓ JWKS set');
|
||||
} catch (e) {
|
||||
console.error('✗ Failed to set JWKS:', e.message);
|
||||
}
|
||||
|
||||
// 清理临时文件
|
||||
try {
|
||||
fs.unlinkSync(keyFile);
|
||||
fs.unlinkSync(jwksFile);
|
||||
} catch (e) {
|
||||
// 忽略清理错误
|
||||
}
|
||||
|
||||
console.log('Done! You can now test registration.');
|
||||
@@ -0,0 +1,13 @@
|
||||
import { exportJWK, exportPKCS8, generateKeyPair } from "jose";
|
||||
|
||||
const keys = await generateKeyPair("RS256", { extractable: true });
|
||||
const privateKey = await exportPKCS8(keys.privateKey);
|
||||
const publicKey = await exportJWK(keys.publicKey);
|
||||
const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
|
||||
|
||||
process.stdout.write(
|
||||
`JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`,
|
||||
);
|
||||
process.stdout.write("\n");
|
||||
process.stdout.write(`JWKS=${jwks}`);
|
||||
process.stdout.write("\n");
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 注册测试账号脚本
|
||||
*
|
||||
* 用途:快速注册测试账号,方便开发和测试
|
||||
* 运行:node scripts/register-test-user.js
|
||||
*/
|
||||
|
||||
const TEST_CREDENTIALS = {
|
||||
email: "test@example.com",
|
||||
password: "Test123456",
|
||||
name: "测试用户",
|
||||
};
|
||||
|
||||
async function registerTestUser() {
|
||||
const baseUrl = "http://localhost:3000";
|
||||
|
||||
console.log("正在注册测试账号...");
|
||||
console.log(`邮箱: ${TEST_CREDENTIALS.email}`);
|
||||
console.log(`密码: ${TEST_CREDENTIALS.password}`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/api/auth/signin`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: TEST_CREDENTIALS.email,
|
||||
password: TEST_CREDENTIALS.password,
|
||||
name: TEST_CREDENTIALS.name,
|
||||
flow: "signUp",
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log("✓ 测试账号注册成功!");
|
||||
console.log(`\n您现在可以使用以下凭据登录:`);
|
||||
console.log(` 邮箱: ${TEST_CREDENTIALS.email}`);
|
||||
console.log(` 密码: ${TEST_CREDENTIALS.password}`);
|
||||
console.log(`\n或在登录页面点击"测试账号快速登录"按钮。`);
|
||||
} else if (response.status === 501) {
|
||||
console.log("ℹ API 路由暂未实现,请通过浏览器手动注册:");
|
||||
console.log(` 1. 访问 http://localhost:3000/auth`);
|
||||
console.log(` 2. 点击"还没有账户?立即注册"`);
|
||||
console.log(` 3. 填写:`);
|
||||
console.log(` 邮箱: ${TEST_CREDENTIALS.email}`);
|
||||
console.log(` 密码: ${TEST_CREDENTIALS.password}`);
|
||||
console.log(` 姓名: ${TEST_CREDENTIALS.name}`);
|
||||
} else {
|
||||
console.error("✗ 注册失败:", result.error || result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("✗ 请求失败:", error.message);
|
||||
console.log("\n请确保开发服务器正在运行 (pnpm dev)");
|
||||
}
|
||||
}
|
||||
|
||||
registerTestUser();
|
||||
@@ -0,0 +1,57 @@
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function setConvexEnv(name, value) {
|
||||
// 说明:使用 stdin 传值,避免以 `-----BEGIN ...` 开头的内容被 CLI 误当作参数/选项。
|
||||
execSync(`npx convex env set ${name}`, {
|
||||
input: String(value ?? ""),
|
||||
stdio: ['pipe', 'inherit', 'inherit'],
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
}
|
||||
|
||||
const privateKey = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPcrWDwjh74nu1
|
||||
gfYo9ywVIRMedfPwbNiDjHnovzHAIeToc1+Lw3ju/io9OME3KLWzIibUHX39LilK
|
||||
sSQKigJ3+i7J13IquJD++ozY9C8Dp/P34txVY+ECSQRdIBbETLJxYBWXOx6Ysw/d
|
||||
/CUuDOdEw2IhNvN97sTh5jUZe6f/H6mfCpJ3X5SZrqrIUTTL6r4Lj9ZS3PUX5ivU
|
||||
6sd2rXzpDsUQgrNgmvGN3w9nAHudxAUv/zOGa4802Z+z14LCBIX6v6PIK79YQ4s9
|
||||
/5hH73MesA6vgzWShJcyVy3guyde+duhpECo1q3d6dIZeqrazQwqAW3vcpmEFJdX
|
||||
jkpociDfAgMBAAECggEAAYtRE+mH1SGThlkvTrKWeWXBQG8xoJFzZTsiZtSEExbq
|
||||
UWxIh4cjqqL2znDpd5ALILIJ6/ejTxHrpN+yTSC+NQ9u6IJWusoA2ZXV5VH/nZD1
|
||||
yeHZ0FuCZRVnJB9/zz4qH5lSsi2TPz6SOagIuG2wIafeyw+94EmtOedSBAO2Q8NN
|
||||
3jHoINBCyRu6hU3ml0h7daoIhUw9ONI7MZUlYvuV7Ti3yf+czzpqwYx3gZA39sa8
|
||||
qUbJPFk6ts+CVjqSAdYVSfU3TC1Us1usOM3+04mACp3vc6ZUKxnB4Dsk/sSHj71n
|
||||
EcZ3fOR7EgjmXf6wBiq24T2+0UzoHW2yDaiAowr6iQKBgQDDs2eJTopFejt47EMm
|
||||
zY//e8fyUDKx07CWwUZKP78IMTZcp8BwHmKTSY+VbQLvVYnNxADkckYapokjU3a7
|
||||
GMOXxvKVLqfgU/oUBIkRFDRo+nasFCd5cPpnYjQ6lGalBkDghVlyvq7kKF/MPtIK
|
||||
dLqsV3tZDXoHX2mjTga94ygTZwKBgQC7pa+eZdWiz275mpYql9Gs6N3G3HhcpRxb
|
||||
oWYWekCtZQ9gtecpvA9e0tW3ShoF19ksWYGpM4vxAOXul5Ei9IPVVNHc7RvGF5CO
|
||||
0Zryx3qaVk7E6XQc3BQVVAenT2fNiv3+fkCMeAvcZBZOGiPPziCCT3wU3bd3tsn8
|
||||
EsMiAPvTyQKBgEs0iWhBr29NrsckfBXQTzMN/WOIIEMoJ6d3dKyZ3K6oQszOhmxP
|
||||
sPALB8uTjdotk/xoAzPHGlupfe/+ZhU2Sgvsn1JnEIprmyHQMGBI1G83OR2dzSGl
|
||||
IgVSvuF4IA3w3kOp2xr2Xj09qrrRtWPhQc9y+urY+/kTWIQyOvMD9WWnAoGADdUN
|
||||
2BBLqj++P3oMvcEJPMTBrGoOGU42g+6m1ttWLzH26zsdei8ZtvS1ulglCO87XBCR
|
||||
BUb+dtqJGIhls3zwxuYEvlNgK78K8ewzjtfzirL4BX3sCECU3mmeUtAAp98qD/uA
|
||||
iJpEzY83MbStlSDtto1jaSpa3uFDjGhZqAUIizkCgYABf/HSMwWsnvRgo0JMmDru
|
||||
3L/xScJiFVdFV9vNiSckjUYhx7lgFqEjsDEN1mZiRIa2UhLOqpo5S/cIXfMkeEPh
|
||||
9Rusf9STwNwOOyhFRrHCxqhTH6Ivnj6oZSM/UrS8GvCVMPWs1z8k45FWKI/IRXQq
|
||||
AT5PRKihrWbg63/rXaSCxw==
|
||||
-----END PRIVATE KEY-----`;
|
||||
|
||||
const jwks = '{"keys":[{"use":"sig","kty":"RSA","n":"j3K1g8I4e-J7tYH2KPcsFSETHnXz8GzYg4x56L8xwCHk6HNfi8N47v4qPTjBNyi1syIm1B19_S4pSrEkCooCd_ouyddyKriQ_vqM2PQvA6fz9-LcVWPhAkkEXSAWxEyycWAVlzsemLMP3fwlLgznRMNiITbzfe7E4eY1GXun_x-pnwqSd1-Uma6qyFE0y-q-C4_WUtz1F-Yr1OrHdq186Q7FEIKzYJrxjd8PZwB7ncQFL_8zhmuPNNmfs9eCwgSF-r-jyCu_WEOLPf-YR-9zHrAOr4M1koSXMlct4LsnXvnboaRAqNat3enSGXqq2s0MKgFt73KZhBSXV45KaHIg3w","e":"AQAB"}]}';
|
||||
|
||||
console.log('Setting JWT_PRIVATE_KEY...');
|
||||
try {
|
||||
setConvexEnv("JWT_PRIVATE_KEY", privateKey);
|
||||
} catch (e) {
|
||||
console.error('Failed to set JWT_PRIVATE_KEY:', e.message);
|
||||
}
|
||||
|
||||
console.log('Setting JWKS...');
|
||||
try {
|
||||
setConvexEnv("JWKS", jwks);
|
||||
} catch (e) {
|
||||
console.error('Failed to set JWKS:', e.message);
|
||||
}
|
||||
|
||||
console.log('Done!');
|
||||
Reference in New Issue
Block a user