login.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // 登录处理模块
  2. const formidable = require('formidable');
  3. const crypto = require('crypto');
  4. const { getDatabase } = require('./sql');
  5. // 固定验证码
  6. const FIXED_VERIFICATION_CODE = '9527';
  7. // 密码加密(使用 SHA256)
  8. function hashPassword(password) {
  9. return crypto.createHash('sha256').update(password).digest('hex');
  10. }
  11. // 设置 CORS 头的辅助函数
  12. function setCORSHeaders(res) {
  13. res.setHeader('Access-Control-Allow-Origin', '*');
  14. res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
  15. res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  16. }
  17. // 处理登录请求
  18. async function handleLoginRequest(req, res) {
  19. // 设置 CORS 头
  20. setCORSHeaders(res);
  21. if (req.method === 'OPTIONS') {
  22. res.writeHead(200);
  23. res.end();
  24. return;
  25. }
  26. if (req.method !== 'POST') {
  27. setCORSHeaders(res);
  28. res.writeHead(405, { 'Content-Type': 'application/json; charset=utf-8' });
  29. res.end(JSON.stringify({ success: false, message: 'Method not allowed' }));
  30. return;
  31. }
  32. try {
  33. const form = formidable.formidable({
  34. multiples: false
  35. });
  36. let fields;
  37. try {
  38. [fields] = await form.parse(req);
  39. } catch (parseError) {
  40. console.error('[Login] 表单解析错误:', parseError);
  41. setCORSHeaders(res);
  42. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  43. res.end(JSON.stringify({ success: false, message: '请求格式错误' }));
  44. return;
  45. }
  46. // 提取表单字段
  47. const loginType = Array.isArray(fields.loginType) ? fields.loginType[0] : (fields.loginType || 'account');
  48. const account = Array.isArray(fields.account) ? fields.account[0] : (fields.account || '');
  49. const password = Array.isArray(fields.password) ? fields.password[0] : (fields.password || '');
  50. const phone = Array.isArray(fields.phone) ? fields.phone[0] : (fields.phone || '');
  51. const code = Array.isArray(fields.code) ? fields.code[0] : (fields.code || '');
  52. // 获取数据库实例
  53. let db;
  54. try {
  55. db = await getDatabase();
  56. } catch (dbError) {
  57. console.error('[Login] 数据库连接错误:', dbError);
  58. setCORSHeaders(res);
  59. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  60. res.end(JSON.stringify({ success: false, message: '数据库连接失败' }));
  61. return;
  62. }
  63. console.log('[Login] 收到登录请求:', { loginType, account: account ? account.substring(0, 3) + '***' : '', hasPassword: !!password });
  64. let user = null;
  65. if (loginType === 'account') {
  66. // 账号密码登录
  67. if (!account || !password) {
  68. console.log('[Login] 缺少账号或密码');
  69. setCORSHeaders(res);
  70. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  71. res.end(JSON.stringify({ success: false, message: '请输入账号和密码' }));
  72. return;
  73. }
  74. // 查找用户(可能是用户名或手机号)
  75. user = db.findUserByUsername(account);
  76. if (!user) {
  77. user = db.findUserByPhone(account);
  78. }
  79. if (!user) {
  80. console.log('[Login] 用户不存在:', account);
  81. setCORSHeaders(res);
  82. res.writeHead(401, { 'Content-Type': 'application/json; charset=utf-8' });
  83. res.end(JSON.stringify({ success: false, message: '账号不存在', errorType: 'USER_NOT_FOUND' }));
  84. return;
  85. }
  86. // 验证密码
  87. const hashedPassword = hashPassword(password);
  88. console.log('[Login] 密码验证:', {
  89. storedHash: user.password ? user.password.substring(0, 10) + '...' : 'null',
  90. computedHash: hashedPassword.substring(0, 10) + '...',
  91. match: user.password === hashedPassword
  92. });
  93. if (user.password !== hashedPassword) {
  94. console.log('[Login] 密码不匹配');
  95. setCORSHeaders(res);
  96. res.writeHead(401, { 'Content-Type': 'application/json; charset=utf-8' });
  97. res.end(JSON.stringify({ success: false, message: '密码错误', errorType: 'WRONG_PASSWORD' }));
  98. return;
  99. }
  100. console.log('[Login] 登录成功:', user.username);
  101. } else if (loginType === 'phone') {
  102. // 手机验证码登录
  103. if (!phone || !code) {
  104. setCORSHeaders(res);
  105. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  106. res.end(JSON.stringify({ success: false, message: '请输入手机号和验证码' }));
  107. return;
  108. }
  109. // 验证手机号格式
  110. if (!/^1[3-9]\d{9}$/.test(phone)) {
  111. setCORSHeaders(res);
  112. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  113. res.end(JSON.stringify({ success: false, message: '请输入正确的手机号' }));
  114. return;
  115. }
  116. // 验证验证码(固定验证码 9527)
  117. if (code !== FIXED_VERIFICATION_CODE) {
  118. setCORSHeaders(res);
  119. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  120. res.end(JSON.stringify({ success: false, message: '验证码错误' }));
  121. return;
  122. }
  123. // 查找用户
  124. user = db.findUserByPhone(phone);
  125. if (!user) {
  126. setCORSHeaders(res);
  127. res.writeHead(401, { 'Content-Type': 'application/json; charset=utf-8' });
  128. res.end(JSON.stringify({ success: false, message: '手机未注册', errorType: 'PHONE_NOT_REGISTERED' }));
  129. return;
  130. }
  131. } else {
  132. setCORSHeaders(res);
  133. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  134. res.end(JSON.stringify({ success: false, message: '无效的登录类型' }));
  135. return;
  136. }
  137. // 登录成功
  138. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  139. res.end(JSON.stringify({
  140. success: true,
  141. message: '登录成功',
  142. user: {
  143. id: user.id,
  144. username: user.username,
  145. phone: user.phone,
  146. avatar: user.avatar
  147. }
  148. }));
  149. } catch (error) {
  150. console.error('[Login] 登录处理错误:', error);
  151. console.error('[Login] 错误堆栈:', error.stack);
  152. // 如果响应头还没有发送,发送错误响应
  153. if (!res.headersSent) {
  154. setCORSHeaders(res);
  155. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  156. res.end(JSON.stringify({
  157. success: false,
  158. message: '服务器错误: ' + (error.message || '未知错误')
  159. }));
  160. } else {
  161. console.error('[Login] 响应头已发送,无法发送错误响应');
  162. }
  163. }
  164. }
  165. // 处理检查手机号请求
  166. async function handleCheckPhoneRequest(req, res) {
  167. // 设置 CORS 头
  168. setCORSHeaders(res);
  169. if (req.method === 'OPTIONS') {
  170. res.writeHead(200);
  171. res.end();
  172. return;
  173. }
  174. if (req.method !== 'POST') {
  175. setCORSHeaders(res);
  176. res.writeHead(405, { 'Content-Type': 'application/json; charset=utf-8' });
  177. res.end(JSON.stringify({ success: false, message: 'Method not allowed' }));
  178. return;
  179. }
  180. try {
  181. const db = await getDatabase();
  182. let body = '';
  183. req.on('data', chunk => {
  184. body += chunk.toString();
  185. });
  186. req.on('end', async () => {
  187. try {
  188. const data = JSON.parse(body);
  189. const { phone, type } = data; // type: 'login' 或 'register'
  190. if (!phone) {
  191. setCORSHeaders(res);
  192. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  193. res.end(JSON.stringify({ success: false, message: '请输入手机号' }));
  194. return;
  195. }
  196. // 验证手机号格式
  197. if (!/^1[3-9]\d{9}$/.test(phone)) {
  198. setCORSHeaders(res);
  199. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  200. res.end(JSON.stringify({ success: false, message: '请输入正确的手机号' }));
  201. return;
  202. }
  203. // 查找用户
  204. const user = db.findUserByPhone(phone);
  205. const exists = !!user;
  206. // 根据类型判断
  207. if (type === 'login') {
  208. // 登录场景:手机号必须存在
  209. if (!exists) {
  210. setCORSHeaders(res);
  211. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  212. res.end(JSON.stringify({ success: false, message: '手机未注册', exists: false }));
  213. return;
  214. }
  215. } else if (type === 'register') {
  216. // 注册场景:手机号必须不存在
  217. if (exists) {
  218. setCORSHeaders(res);
  219. res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8' });
  220. res.end(JSON.stringify({ success: false, message: '手机号已被注册', exists: true }));
  221. return;
  222. }
  223. }
  224. // 检查通过
  225. setCORSHeaders(res);
  226. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  227. res.end(JSON.stringify({
  228. success: true,
  229. message: type === 'login' ? '手机号已注册' : '手机号可用',
  230. exists: exists
  231. }));
  232. } catch (parseError) {
  233. console.error('[CheckPhone] 解析请求数据失败:', parseError);
  234. setCORSHeaders(res);
  235. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  236. res.end(JSON.stringify({ success: false, message: '请求格式错误' }));
  237. }
  238. });
  239. } catch (error) {
  240. console.error('[CheckPhone] 检查手机号失败:', error);
  241. setCORSHeaders(res);
  242. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  243. res.end(JSON.stringify({ success: false, message: '服务器错误' }));
  244. }
  245. }
  246. module.exports = {
  247. handleLoginRequest,
  248. handleCheckPhoneRequest
  249. };