Add image-designer skill, align billing to DeepSeek ×1, and enrich Plaza demo.
Introduce AI image generation with chat shortcut and agent API, improve MindSpace chat-to-page save resolution, and seed Plaza covers with production deploy scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+34
-20
@@ -1,6 +1,7 @@
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import argon2 from 'argon2';
|
||||
import { computeDeltaCostCents, loadBillingConfig, normalizeTokenState } from './billing.mjs';
|
||||
import { buildInsufficientBalancePayload, loadRechargeConfig } from './billing-recharge.mjs';
|
||||
import {
|
||||
@@ -64,25 +65,38 @@ function hashPasswordPbkdf2(password, salt) {
|
||||
return crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
|
||||
}
|
||||
|
||||
function hashPasswordArgon2id(password, salt) {
|
||||
return crypto
|
||||
.argon2Sync(PASSWORD_ALGORITHM_ARGON2ID, {
|
||||
message: password,
|
||||
nonce: Buffer.from(salt, 'hex'),
|
||||
async function hashPasswordArgon2id(password, salt) {
|
||||
if (typeof crypto.argon2Sync === 'function') {
|
||||
return crypto
|
||||
.argon2Sync(PASSWORD_ALGORITHM_ARGON2ID, {
|
||||
message: password,
|
||||
nonce: Buffer.from(salt, 'hex'),
|
||||
parallelism: ARGON2_PARALLELISM,
|
||||
tagLength: ARGON2_TAG_LENGTH,
|
||||
memory: ARGON2_MEMORY,
|
||||
passes: ARGON2_PASSES,
|
||||
})
|
||||
.toString('hex');
|
||||
}
|
||||
return (
|
||||
await argon2.hash(password, {
|
||||
type: argon2.argon2id,
|
||||
salt: Buffer.from(salt, 'hex'),
|
||||
memoryCost: ARGON2_MEMORY,
|
||||
timeCost: ARGON2_PASSES,
|
||||
parallelism: ARGON2_PARALLELISM,
|
||||
tagLength: ARGON2_TAG_LENGTH,
|
||||
memory: ARGON2_MEMORY,
|
||||
passes: ARGON2_PASSES,
|
||||
hashLength: ARGON2_TAG_LENGTH,
|
||||
raw: true,
|
||||
})
|
||||
.toString('hex');
|
||||
).toString('hex');
|
||||
}
|
||||
|
||||
function createPasswordRecord(password, algorithm = PASSWORD_ALGORITHM_ARGON2ID) {
|
||||
async function createPasswordRecord(password, algorithm = PASSWORD_ALGORITHM_ARGON2ID) {
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
if (algorithm === PASSWORD_ALGORITHM_ARGON2ID) {
|
||||
return {
|
||||
salt,
|
||||
passwordHash: hashPasswordArgon2id(password, salt),
|
||||
passwordHash: await hashPasswordArgon2id(password, salt),
|
||||
passwordAlgorithm: PASSWORD_ALGORITHM_ARGON2ID,
|
||||
};
|
||||
}
|
||||
@@ -93,10 +107,10 @@ function createPasswordRecord(password, algorithm = PASSWORD_ALGORITHM_ARGON2ID)
|
||||
};
|
||||
}
|
||||
|
||||
function verifyPassword(password, row) {
|
||||
async function verifyPassword(password, row) {
|
||||
const algorithm = row.password_algorithm || PASSWORD_ALGORITHM_PBKDF2;
|
||||
if (algorithm === PASSWORD_ALGORITHM_ARGON2ID) {
|
||||
return safeEqual(hashPasswordArgon2id(password, row.salt), row.password_hash);
|
||||
return safeEqual(await hashPasswordArgon2id(password, row.salt), row.password_hash);
|
||||
}
|
||||
return safeEqual(hashPasswordPbkdf2(password, row.salt), row.password_hash);
|
||||
}
|
||||
@@ -337,7 +351,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
return { ok: false, message: '请输入有效邮箱' };
|
||||
}
|
||||
|
||||
const { salt, passwordHash, passwordAlgorithm } = createPasswordRecord(password);
|
||||
const { salt, passwordHash, passwordAlgorithm } = await createPasswordRecord(password);
|
||||
const userId = crypto.randomUUID();
|
||||
const layout = await publishLayoutFor({ id: userId, username: normalized });
|
||||
const workspaceRoot = layout.publishDir;
|
||||
@@ -433,7 +447,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
return { ok: false, message: '用户名或密码错误' };
|
||||
}
|
||||
|
||||
if (!verifyPassword(password, row)) {
|
||||
if (!(await verifyPassword(password, row))) {
|
||||
const current =
|
||||
failure && failure.resetAt > now
|
||||
? failure
|
||||
@@ -447,7 +461,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
}
|
||||
|
||||
if ((row.password_algorithm || PASSWORD_ALGORITHM_PBKDF2) !== PASSWORD_ALGORITHM_ARGON2ID) {
|
||||
const nextPassword = createPasswordRecord(password);
|
||||
const nextPassword = await createPasswordRecord(password);
|
||||
await pool.query(
|
||||
`UPDATE h5_users
|
||||
SET salt = ?, password_hash = ?, password_algorithm = ?, updated_at = ?
|
||||
@@ -490,7 +504,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
return { ok: false, message: '账户已禁用,请联系管理员' };
|
||||
}
|
||||
|
||||
const { salt, passwordHash, passwordAlgorithm } = createPasswordRecord(password);
|
||||
const { salt, passwordHash, passwordAlgorithm } = await createPasswordRecord(password);
|
||||
const now = Date.now();
|
||||
await pool.query(
|
||||
`UPDATE h5_users SET salt = ?, password_hash = ?, password_algorithm = ?, updated_at = ? WHERE id = ?`,
|
||||
@@ -720,7 +734,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
const root = isAdmin
|
||||
? path.resolve(workspaceRoot || path.join(usersRoot, normalized))
|
||||
: (await publishLayoutFor({ id: userId, username: normalized })).publishDir;
|
||||
const { salt, passwordHash, passwordAlgorithm } = createPasswordRecord(password);
|
||||
const { salt, passwordHash, passwordAlgorithm } = await createPasswordRecord(password);
|
||||
const now = Date.now();
|
||||
|
||||
const conn = await pool.getConnection();
|
||||
@@ -1263,7 +1277,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
);
|
||||
if (rows.length === 0) return;
|
||||
|
||||
const { salt, passwordHash, passwordAlgorithm } = createPasswordRecord(adminPassword);
|
||||
const { salt, passwordHash, passwordAlgorithm } = await createPasswordRecord(adminPassword);
|
||||
const now = Date.now();
|
||||
await pool.query(
|
||||
`UPDATE h5_users SET salt = ?, password_hash = ?, password_algorithm = ?, updated_at = ? WHERE id = ?`,
|
||||
@@ -1923,7 +1937,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
}) => {
|
||||
const normalized = await generateWechatUsername(openid);
|
||||
const randomPassword = crypto.randomBytes(24).toString('base64url');
|
||||
const { salt, passwordHash, passwordAlgorithm } = createPasswordRecord(randomPassword);
|
||||
const { salt, passwordHash, passwordAlgorithm } = await createPasswordRecord(randomPassword);
|
||||
const userId = crypto.randomUUID();
|
||||
const layout = await publishLayoutFor({ id: userId, username: normalized });
|
||||
const workspaceRoot = layout.publishDir;
|
||||
|
||||
Reference in New Issue
Block a user