重构用户和笔记相关的数据库模式,更新字段名称;优化路由描述,添加新搜索功能;调整浏览器启动参数,简化调试信息
This commit is contained in:
@@ -37,7 +37,7 @@ export const xhsNote = sqliteTable('xhs_note', {
|
|||||||
]));
|
]));
|
||||||
|
|
||||||
export const xhsUser = sqliteTable('xhs_user', {
|
export const xhsUser = sqliteTable('xhs_user', {
|
||||||
user_id: text('user_id').primaryKey(),
|
id: text('id').primaryKey(),
|
||||||
xsec_token: text('xsec_token'),
|
xsec_token: text('xsec_token'),
|
||||||
|
|
||||||
username: text('username'),
|
username: text('username'),
|
||||||
@@ -67,7 +67,7 @@ export const xhsUser = sqliteTable('xhs_user', {
|
|||||||
updatedAt: integer('updated_at').default(Date.now()).notNull(),
|
updatedAt: integer('updated_at').default(Date.now()).notNull(),
|
||||||
deletedAt: integer('deleted_at'),
|
deletedAt: integer('deleted_at'),
|
||||||
}, (table) => ([
|
}, (table) => ([
|
||||||
index('idx_xhs_user_user_id').on(table.user_id),
|
index('idx_xhs_user_id').on(table.id),
|
||||||
index('idx_xhs_user_tags').on(table.tags),
|
index('idx_xhs_user_tags').on(table.tags),
|
||||||
index('idx_xhs_user_bun_tags').on(table.bunTags),
|
index('idx_xhs_user_bun_tags').on(table.bunTags),
|
||||||
]));
|
]));
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ app.route({
|
|||||||
description: 'Token 权限验证,临时方案',
|
description: 'Token 权限验证,临时方案',
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
// token authentication
|
// token authentication
|
||||||
|
console.log('token', ctx.state);
|
||||||
|
ctx.state.token = 'abc';
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
const isPm2 = !!process.env.PM2_HOME;
|
const isPm2 = !!process.env.PM2_HOME;
|
||||||
if (import.meta.main || isPm2) {
|
if (import.meta.main || isPm2) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const main = async (opts?: {
|
|||||||
console.log(`端口: ${debugPort}`);
|
console.log(`端口: ${debugPort}`);
|
||||||
console.log(`用户数据目录: ${userDataDir}`);
|
console.log(`用户数据目录: ${userDataDir}`);
|
||||||
console.log(`无头模式: ${headless}`);
|
console.log(`无头模式: ${headless}`);
|
||||||
const userAgent = new UserAgent().toString();
|
// const userAgent = new UserAgent().toString();
|
||||||
|
|
||||||
const params = [
|
const params = [
|
||||||
`--remote-debugging-port=${debugPort}`,
|
`--remote-debugging-port=${debugPort}`,
|
||||||
@@ -55,37 +55,7 @@ export const main = async (opts?: {
|
|||||||
'--disable-setuid-sandbox',
|
'--disable-setuid-sandbox',
|
||||||
'--disable-dev-shm-usage',
|
'--disable-dev-shm-usage',
|
||||||
'--no-first-run',
|
'--no-first-run',
|
||||||
'--disable-session-crashed-bubble',
|
// `--user-agent=${userAgent}`,
|
||||||
'--disable-infobars',
|
|
||||||
'--disable-default-apps',
|
|
||||||
'--disable-blink-features=AutomationControlled',
|
|
||||||
'--exclude-switches=enable-automation',
|
|
||||||
'--disable-features=IsolateOrigins,site-per-process',
|
|
||||||
'--disable-web-security',
|
|
||||||
'--disable-features=VizDisplayCompositor',
|
|
||||||
`--user-agent=${userAgent}`,
|
|
||||||
'--disable-sync',
|
|
||||||
'--no-default-browser-check',
|
|
||||||
'--no-experiments',
|
|
||||||
'--disable-popup-blocking',
|
|
||||||
'--disable-prompt-on-repost',
|
|
||||||
'--disable-background-networking',
|
|
||||||
'--disable-component-update',
|
|
||||||
'--disable-extensions',
|
|
||||||
'--disable-bundled-ppapi-flash',
|
|
||||||
// 隐藏automation bar相关特征
|
|
||||||
'--disable-renderer-backgrounding',
|
|
||||||
'--disable-backgrounding-occluded-windows',
|
|
||||||
'--disable-breakpad',
|
|
||||||
'--disable-client-side-phishing-detection',
|
|
||||||
'--disable-component-extensions-with-background-pages',
|
|
||||||
'--disable-datasaver-prompt',
|
|
||||||
'--disable-device-discovery-notifications',
|
|
||||||
'--disable-hang-monitor',
|
|
||||||
'--disable-ipc-flooding-protection',
|
|
||||||
'--no-service-autorun',
|
|
||||||
// 禁用自动化识别
|
|
||||||
'--disable-automation',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// 如果需要无头模式,添加额外参数
|
// 如果需要无头模式,添加额外参数
|
||||||
@@ -95,9 +65,6 @@ export const main = async (opts?: {
|
|||||||
'--window-size=1920,1080',
|
'--window-size=1920,1080',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
params.push('about:blank');
|
|
||||||
|
|
||||||
console.log('启动参数:', params);
|
console.log('启动参数:', params);
|
||||||
if (opts?.kiosk) {
|
if (opts?.kiosk) {
|
||||||
params.push('--kiosk'); // 全屏模式,无修改边框
|
params.push('--kiosk'); // 全屏模式,无修改边框
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ export class Core<T = {}> {
|
|||||||
if (opts?.useDebugPort !== undefined) {
|
if (opts?.useDebugPort !== undefined) {
|
||||||
this.useDebugPort = opts.useDebugPort;
|
this.useDebugPort = opts.useDebugPort;
|
||||||
}
|
}
|
||||||
if (opts?.useCDPConnect !== undefined) {
|
this.useCDPConnect = opts?.useCDPConnect || true;
|
||||||
this.useCDPConnect = opts.useCDPConnect;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
async createBrowser() {
|
async createBrowser() {
|
||||||
const chrome = await main({ debugPort: this.debugPort, headless: this.headless });
|
const chrome = await main({ debugPort: this.debugPort, headless: this.headless });
|
||||||
@@ -63,31 +61,6 @@ export class Core<T = {}> {
|
|||||||
async init() {
|
async init() {
|
||||||
const debugPort = this.debugPort;
|
const debugPort = this.debugPort;
|
||||||
try {
|
try {
|
||||||
// 如果不使用CDP连接,直接用Playwright启动
|
|
||||||
if (!this.useCDPConnect) {
|
|
||||||
console.log('使用纯Playwright模式启动(无CDP),避免被检测...');
|
|
||||||
this.browser = await chromium.launch({
|
|
||||||
headless: this.headless,
|
|
||||||
args: [
|
|
||||||
`--user-data-dir=${path.join(process.cwd(), 'browser-context')}`,
|
|
||||||
'--no-sandbox',
|
|
||||||
'--disable-blink-features=AutomationControlled',
|
|
||||||
'--disable-infobars',
|
|
||||||
'--exclude-switches=enable-automation',
|
|
||||||
]
|
|
||||||
});
|
|
||||||
this.browserContext = await this.browser.newContext();
|
|
||||||
this.handleRequest(this.browserContext);
|
|
||||||
this.page = await this.browserContext.newPage();
|
|
||||||
|
|
||||||
// 应用隐身脚本
|
|
||||||
await this.stealthMode(this.page);
|
|
||||||
|
|
||||||
this.emitter.emit('connected');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === 以下为CDP连接模式(可选) ===
|
|
||||||
const stdout = execSync(`netstat -ano | findstr :${debugPort}`);
|
const stdout = execSync(`netstat -ano | findstr :${debugPort}`);
|
||||||
console.log(`端口 ${debugPort} 已在监听:\n${stdout}`);
|
console.log(`端口 ${debugPort} 已在监听:\n${stdout}`);
|
||||||
const debugHost = this.debugHost;
|
const debugHost = this.debugHost;
|
||||||
@@ -95,50 +68,10 @@ export class Core<T = {}> {
|
|||||||
console.log('成功连接到 Chrome CDP!');
|
console.log('成功连接到 Chrome CDP!');
|
||||||
this.browser = browser;
|
this.browser = browser;
|
||||||
this.browserContext = browser.contexts()[0];
|
this.browserContext = browser.contexts()[0];
|
||||||
|
|
||||||
// 关闭所有现存的页面,防止复用百度等默认页面
|
|
||||||
const existingPages = this.browserContext.pages();
|
|
||||||
for (const page of existingPages) {
|
|
||||||
await page.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleRequest(this.browserContext);
|
this.handleRequest(this.browserContext);
|
||||||
// 创建全新的空白页面
|
// 创建全新的空白页面
|
||||||
this.page = await this.browserContext.newPage();
|
this.page = await this.browserContext.newPage()
|
||||||
|
// await this.stealthMode(this.page);
|
||||||
// 在页面创建后立即设置CDP脚本注入(在导航前)
|
|
||||||
try {
|
|
||||||
const cdpSession = await this.browserContext.newCDPSession(this.page);
|
|
||||||
// 禁用webdriver特征 - 在页面加载前注入
|
|
||||||
await cdpSession.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
||||||
source: `Object.defineProperty(navigator, 'webdriver', { get: () => false })`
|
|
||||||
});
|
|
||||||
// 隐藏automation bar相关特征
|
|
||||||
await cdpSession.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
||||||
source: `
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.textContent = \`
|
|
||||||
[class*="automation"],
|
|
||||||
[id*="automation"],
|
|
||||||
.infobar,
|
|
||||||
#infobar-container,
|
|
||||||
.top-chrome-background,
|
|
||||||
.automation-bar {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
\`;
|
|
||||||
document.documentElement.appendChild(style);
|
|
||||||
`
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log('CDP session设置失败(非致命错误):', (e as Error).message.slice(0, 80));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导航到空白页面,清除任何缓存的导航
|
|
||||||
await this.page.goto('about:blank', { waitUntil: 'domcontentloaded' });
|
|
||||||
|
|
||||||
// 始终启用隐身模式以隐藏debugPort和automation特征
|
|
||||||
await this.stealthMode(this.page);
|
|
||||||
|
|
||||||
this.emitter.emit('connected');
|
this.emitter.emit('connected');
|
||||||
return;
|
return;
|
||||||
@@ -203,6 +136,7 @@ export class Core<T = {}> {
|
|||||||
return this.page!;
|
return this.page!;
|
||||||
}
|
}
|
||||||
throw new Error('无法连接到浏览器实例');
|
throw new Error('无法连接到浏览器实例');
|
||||||
|
|
||||||
}
|
}
|
||||||
async setReady(ready: boolean = true) {
|
async setReady(ready: boolean = true) {
|
||||||
if (this.recordReady !== ready) {
|
if (this.recordReady !== ready) {
|
||||||
@@ -250,7 +184,7 @@ export class Core<T = {}> {
|
|||||||
context.on('response', async response => {
|
context.on('response', async response => {
|
||||||
const url = response.url();
|
const url = response.url();
|
||||||
const recordReady = this.recordReady;
|
const recordReady = this.recordReady;
|
||||||
console.log('Response URL:', url);
|
// console.log('Response URL:', url);
|
||||||
for (let listener of this.listeners) {
|
for (let listener of this.listeners) {
|
||||||
const type = listener.type || 'both';
|
const type = listener.type || 'both';
|
||||||
if (type === 'request') continue;
|
if (type === 'request') continue;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { app, core, db } from '../../app.ts';
|
|||||||
app.route({
|
app.route({
|
||||||
path: 'good',
|
path: 'good',
|
||||||
key: 'searchInfo',
|
key: 'searchInfo',
|
||||||
description: '搜索小红书今日热门信息差内容。支持自定义关键词,参数keyword(字符串)可选,默认搜索"信息差"',
|
description: '搜索小红书今日热门信息差内容。参数是keyword,默认搜索"信息差"',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['小红书', '信息差', '热门'],
|
tags: ['小红书', '信息差', '热门'],
|
||||||
@@ -20,7 +20,7 @@ app.route({
|
|||||||
...rest,
|
...rest,
|
||||||
token: ctx.query?.token as string,
|
token: ctx.query?.token as string,
|
||||||
}
|
}
|
||||||
})
|
}, ctx)
|
||||||
ctx.forward(res)
|
ctx.forward(res)
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
|
|
||||||
@@ -28,11 +28,10 @@ app.route({
|
|||||||
app.route({
|
app.route({
|
||||||
path: 'good',
|
path: 'good',
|
||||||
key: 'searchWork',
|
key: 'searchWork',
|
||||||
description: '搜索小红书今日工作机会与招聘信息。支持自定义关键词搜索,默认搜索"工作 杭州"',
|
description: '搜索小红书今日工作机会与招聘信息。参数是keyword,默认搜索"工作 杭州"',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['小红书', '工作', '招聘'],
|
tags: ['小红书', '工作', '招聘'],
|
||||||
icon: 'search',
|
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const { keyword = '工作 杭州', ...rest } = ctx.query;
|
const { keyword = '工作 杭州', ...rest } = ctx.query;
|
||||||
@@ -45,6 +44,53 @@ app.route({
|
|||||||
...rest,
|
...rest,
|
||||||
token: ctx.query?.token as string,
|
token: ctx.query?.token as string,
|
||||||
}
|
}
|
||||||
})
|
}, ctx)
|
||||||
|
ctx.forward(res)
|
||||||
|
}).addTo(app);
|
||||||
|
|
||||||
|
app.route({
|
||||||
|
path: 'good',
|
||||||
|
key: 'searchDate',
|
||||||
|
description: '搜索小红书今日交友信息。参数是keyword,默认搜索"相亲 杭州"',
|
||||||
|
middleware: ['auth'],
|
||||||
|
metadata: {
|
||||||
|
tags: ['小红书', '约会', '交友', '相亲'],
|
||||||
|
}
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const { keyword = '相亲 杭州', ...rest } = ctx.query;
|
||||||
|
const res = await app.run({
|
||||||
|
path: 'xhs',
|
||||||
|
key: 'search-notes',
|
||||||
|
payload: {
|
||||||
|
keyword: keyword,
|
||||||
|
scrollTimes: 10,
|
||||||
|
...rest,
|
||||||
|
token: ctx.query?.token as string,
|
||||||
|
}
|
||||||
|
}, ctx)
|
||||||
|
ctx.forward(res)
|
||||||
|
}).addTo(app);
|
||||||
|
|
||||||
|
|
||||||
|
app.route({
|
||||||
|
path: 'good',
|
||||||
|
key: 'searchBean',
|
||||||
|
description: '搜索小红书的拼豆,参数是keyword,默认搜索"拼豆"',
|
||||||
|
middleware: ['auth'],
|
||||||
|
metadata: {
|
||||||
|
tags: ['小红书', '拼豆'],
|
||||||
|
}
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const { keyword = '拼豆', ...rest } = ctx.query;
|
||||||
|
const res = await app.run({
|
||||||
|
path: 'xhs',
|
||||||
|
key: 'search-notes',
|
||||||
|
payload: {
|
||||||
|
keyword: keyword,
|
||||||
|
scrollTimes: 10,
|
||||||
|
...rest,
|
||||||
|
token: ctx.query?.token as string,
|
||||||
|
}
|
||||||
|
}, ctx)
|
||||||
ctx.forward(res)
|
ctx.forward(res)
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
@@ -79,6 +79,7 @@ const hoverPickerExample = async (page: Page, opts?: HoverPickerOptions) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await sleep(2000); // 等待2秒以确保筛选生效
|
||||||
// 将鼠标移到页面外,移除 hover 状态
|
// 将鼠标移到页面外,移除 hover 状态
|
||||||
await page.mouse.move(0, 0);
|
await page.mouse.move(0, 0);
|
||||||
console.log('已移除 hover 状态');
|
console.log('已移除 hover 状态');
|
||||||
@@ -209,7 +210,7 @@ app.route({
|
|||||||
status: '正常笔记',
|
status: '正常笔记',
|
||||||
description: keyword || '',
|
description: keyword || '',
|
||||||
link: getNoteUrl(note),
|
link: getNoteUrl(note),
|
||||||
data: JSON.stringify({ note, keyword }),
|
data: JSON.stringify({ note, keyword, user }),
|
||||||
cover: getCover(note),
|
cover: getCover(note),
|
||||||
authorUrl: user.link,
|
authorUrl: user.link,
|
||||||
user_id: user.user?.user_id || '',
|
user_id: user.user?.user_id || '',
|
||||||
@@ -225,7 +226,7 @@ app.route({
|
|||||||
const user = userData.user;
|
const user = userData.user;
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
return {
|
return {
|
||||||
user_id: user?.user_id || '',
|
id: user?.user_id || '',
|
||||||
nickname: user?.nickname || '',
|
nickname: user?.nickname || '',
|
||||||
avatar: user?.avatar || '',
|
avatar: user?.avatar || '',
|
||||||
status: '笔记用户',
|
status: '笔记用户',
|
||||||
@@ -234,11 +235,11 @@ app.route({
|
|||||||
data: JSON.stringify({ user }),
|
data: JSON.stringify({ user }),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const userIds = notes.map(note => note.user_id).filter(id => id);
|
const userIds = notes.map(note => note.id).filter(id => id);
|
||||||
const userList = await db.select().from(xhsUser).where(sql`user_id IN (${userIds.join(',')})`);
|
const userList = await db.select().from(xhsUser).where(sql`id IN (${userIds.join(',')})`);
|
||||||
// 如果用户表有bun的tags,对关键字进行屏蔽,对应的笔记默认打上禁止标签
|
// 如果用户表有bun的tags,对关键字进行屏蔽,对应的笔记默认打上禁止标签
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const user = userList.find(u => u.user_id === note.user_id);
|
const user = userList.find(u => u.id === note.user_id);
|
||||||
if (user) {
|
if (user) {
|
||||||
const bunTags = user.bunTags || '-';
|
const bunTags = user.bunTags || '-';
|
||||||
if (bunTags.includes(keyword || '')) {
|
if (bunTags.includes(keyword || '')) {
|
||||||
@@ -250,15 +251,21 @@ app.route({
|
|||||||
target: xhsNote.id,
|
target: xhsNote.id,
|
||||||
set: {
|
set: {
|
||||||
summary: sql`excluded.summary`,
|
summary: sql`excluded.summary`,
|
||||||
|
cover: sql`excluded.cover`,
|
||||||
|
status: sql`excluded.status`,
|
||||||
|
data: sql`excluded.data`,
|
||||||
|
link: sql`excluded.link`,
|
||||||
|
description: sql`excluded.description`,
|
||||||
|
authorUrl: sql`excluded.author_url`,
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
},
|
},
|
||||||
}).execute();
|
}).execute();
|
||||||
|
|
||||||
console.log(`已保存 ${data.length} 条搜索笔记结果`);
|
console.log(`已保存 ${data.length} 条搜索笔记结果`);
|
||||||
// 保存用户信息,去重
|
// 保存用户信息,去重
|
||||||
const uniqueUsers = Array.from(new Map(notesUser.filter(u => u !== null).map(u => [u!.user_id, u!])).values());
|
const uniqueUsers = Array.from(new Map(notesUser.filter(u => u !== null).map(u => [u!.id, u!])).values());
|
||||||
await db.insert(xhsUser).values(uniqueUsers).onConflictDoUpdate({
|
await db.insert(xhsUser).values(uniqueUsers).onConflictDoUpdate({
|
||||||
target: xhsUser.user_id,
|
target: xhsUser.id,
|
||||||
set: {
|
set: {
|
||||||
nickname: sql`excluded.nickname`,
|
nickname: sql`excluded.nickname`,
|
||||||
avatar: sql`excluded.avatar`,
|
avatar: sql`excluded.avatar`,
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ app.route({
|
|||||||
path: 'xhs-users',
|
path: 'xhs-users',
|
||||||
key: 'list',
|
key: 'list',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
description: '获取小红书用户列表',
|
description: `获取小红书用户列表, 参数说明:
|
||||||
|
page: 页码,默认1
|
||||||
|
pageSize: 每页数量,默认20
|
||||||
|
search: 搜索关键词,模糊匹配昵称、用户名和描述
|
||||||
|
sort: 排序方式,ASC或DESC,默认DESC按更新时间降序
|
||||||
|
`,
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['小红书', '用户'],
|
tags: ['小红书', '用户'],
|
||||||
}
|
}
|
||||||
@@ -66,11 +71,11 @@ app.route({
|
|||||||
tags: ['小红书', '用户'],
|
tags: ['小红书', '用户'],
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const { user_id, createdAt, updatedAt, ...rest } = ctx.query.data || {};
|
const { id, createdAt, updatedAt, ...rest } = ctx.query.data || {};
|
||||||
let user;
|
let user;
|
||||||
if (!user_id) {
|
if (!id) {
|
||||||
user = await db.insert(xhsUser).values({
|
user = await db.insert(xhsUser).values({
|
||||||
user_id: rest.user_id || `user_${Date.now()}`,
|
id: rest.id || `user_${Date.now()}`,
|
||||||
nickname: rest.nickname || '',
|
nickname: rest.nickname || '',
|
||||||
username: rest.username || '',
|
username: rest.username || '',
|
||||||
avatar: rest.avatar || '',
|
avatar: rest.avatar || '',
|
||||||
@@ -85,7 +90,7 @@ app.route({
|
|||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
}).returning();
|
}).returning();
|
||||||
} else {
|
} else {
|
||||||
const existing = await db.select().from(xhsUser).where(eq(xhsUser.user_id, user_id)).limit(1);
|
const existing = await db.select().from(xhsUser).where(eq(xhsUser.id, id)).limit(1);
|
||||||
if (existing.length === 0) {
|
if (existing.length === 0) {
|
||||||
ctx.throw(404, '没有找到对应的用户');
|
ctx.throw(404, '没有找到对应的用户');
|
||||||
}
|
}
|
||||||
@@ -99,7 +104,7 @@ app.route({
|
|||||||
link: rest.link,
|
link: rest.link,
|
||||||
data: rest.data ? JSON.stringify(rest.data) : undefined,
|
data: rest.data ? JSON.stringify(rest.data) : undefined,
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
}).where(eq(xhsUser.user_id, user_id)).returning();
|
}).where(eq(xhsUser.id, id)).returning();
|
||||||
}
|
}
|
||||||
ctx.body = user;
|
ctx.body = user;
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
@@ -109,20 +114,20 @@ app.route({
|
|||||||
path: 'xhs-users',
|
path: 'xhs-users',
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
description: '删除小红书用户, 参数: data.user_id 用户ID',
|
description: '删除小红书用户, 参数: data.id 用户ID',
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['小红书', '用户'],
|
tags: ['小红书', '用户'],
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const { user_id } = ctx.query.data || {};
|
const { id } = ctx.query.data || {};
|
||||||
if (!user_id) {
|
if (!id) {
|
||||||
ctx.throw(400, 'user_id 参数缺失');
|
ctx.throw(400, 'id 参数缺失');
|
||||||
}
|
}
|
||||||
const existing = await db.select().from(xhsUser).where(eq(xhsUser.user_id, user_id)).limit(1);
|
const existing = await db.select().from(xhsUser).where(eq(xhsUser.id, id)).limit(1);
|
||||||
if (existing.length === 0) {
|
if (existing.length === 0) {
|
||||||
ctx.throw(404, '没有找到对应的用户');
|
ctx.throw(404, '没有找到对应的用户');
|
||||||
}
|
}
|
||||||
await db.delete(xhsUser).where(eq(xhsUser.user_id, user_id));
|
await db.delete(xhsUser).where(eq(xhsUser.id, id));
|
||||||
ctx.body = { success: true };
|
ctx.body = { success: true };
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
|
|
||||||
@@ -130,16 +135,16 @@ app.route({
|
|||||||
path: 'xhs-users',
|
path: 'xhs-users',
|
||||||
key: 'get',
|
key: 'get',
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
description: '获取单个小红书用户, 参数: data.user_id 用户ID',
|
description: '获取单个小红书用户, 参数: data.id 用户ID',
|
||||||
metadata: {
|
metadata: {
|
||||||
tags: ['小红书', '用户'],
|
tags: ['小红书', '用户'],
|
||||||
}
|
}
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const { user_id } = ctx.query.data || {};
|
const { id } = ctx.query.data || {};
|
||||||
if (!user_id) {
|
if (!id) {
|
||||||
ctx.throw(400, 'user_id 参数缺失');
|
ctx.throw(400, 'id 参数缺失');
|
||||||
}
|
}
|
||||||
const existing = await db.select().from(xhsUser).where(eq(xhsUser.user_id, user_id)).limit(1);
|
const existing = await db.select().from(xhsUser).where(eq(xhsUser.id, id)).limit(1);
|
||||||
if (existing.length === 0) {
|
if (existing.length === 0) {
|
||||||
ctx.throw(404, '没有找到对应的用户');
|
ctx.throw(404, '没有找到对应的用户');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user