2022-05-29 18:12:54 +00:00
|
|
|
|
import { getOverrideHandler, getOverridePreHandler } from "../config.js"
|
|
|
|
|
import { Player } from "../models/player.js"
|
|
|
|
|
import { createHash } from "crypto"
|
|
|
|
|
import { generateToken, uuid } from "../generator.js"
|
|
|
|
|
import { Token } from "../models/token.js"
|
|
|
|
|
|
|
|
|
|
const BASE_RESPONSE = {
|
|
|
|
|
err: {
|
|
|
|
|
type: "number",
|
|
|
|
|
description: "错误类型, 1.048596代表无错误",
|
|
|
|
|
example: 1.048596
|
|
|
|
|
},
|
|
|
|
|
msg: {
|
|
|
|
|
type: "string",
|
|
|
|
|
description: "错误信息,如果有错误则返回错误信息,否则返回空字符串 << 感谢 Copilot 的补全",
|
|
|
|
|
example: "你号被ban了"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const identifiers = new Map()
|
|
|
|
|
|
|
|
|
|
async function identifierValidator(req, rep) {
|
|
|
|
|
const identifier = req.headers['X-LSP-Idenitifier']
|
|
|
|
|
if(!identifier) {
|
|
|
|
|
return await rep.code(401).send({
|
|
|
|
|
err: 1.143688,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const login = {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
url: '/api/login',
|
|
|
|
|
schema: {
|
|
|
|
|
summary: "登录",
|
|
|
|
|
description: `登录到 webapi,后续请求需要携带请求头 'X-LSP-Idenitifier': '<token>'`,
|
|
|
|
|
tags: [ 'webapi' ],
|
|
|
|
|
body: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
username: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户名',
|
|
|
|
|
example: 'test'
|
|
|
|
|
},
|
|
|
|
|
password: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '密码',
|
|
|
|
|
example: '123456'
|
|
|
|
|
},
|
|
|
|
|
createToken: {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
description: '是否创建一个 accessToken',
|
|
|
|
|
example: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
response: {
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
...BASE_RESPONSE,
|
|
|
|
|
extra: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
description: '额外信息',
|
|
|
|
|
example: {
|
|
|
|
|
identifier: '<token>',
|
|
|
|
|
textures: {
|
|
|
|
|
skin: '<url>',
|
|
|
|
|
cape: '<url>'
|
|
|
|
|
},
|
|
|
|
|
username: '<username>',
|
|
|
|
|
uuid: '<uuid>'
|
|
|
|
|
},
|
|
|
|
|
properties: {
|
|
|
|
|
identifier: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'identifier,后面请求必须带 X-LSP-Idenitifier: <token> 请求头',
|
|
|
|
|
example: '<token>'
|
|
|
|
|
},
|
|
|
|
|
textures: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
description: '用户皮肤和披风',
|
|
|
|
|
example: {
|
|
|
|
|
skin: '<url>',
|
|
|
|
|
cape: '<url>'
|
|
|
|
|
},
|
|
|
|
|
properties: {
|
|
|
|
|
skin: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户皮肤',
|
|
|
|
|
example: '<url>',
|
|
|
|
|
optional: true,
|
|
|
|
|
},
|
|
|
|
|
cape: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户披风',
|
|
|
|
|
example: '<url>',
|
|
|
|
|
optional: true,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
username: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户名',
|
|
|
|
|
example: '<username>'
|
|
|
|
|
},
|
|
|
|
|
uuid: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户唯一标识',
|
|
|
|
|
example: '<uuid>'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
401: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
err: {
|
|
|
|
|
type: 'number',
|
|
|
|
|
description: '错误类型',
|
|
|
|
|
example: 1.048596
|
|
|
|
|
},
|
|
|
|
|
msg: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '错误内容,展示给用户看的',
|
|
|
|
|
example: "您输入的密码似乎是用户 lama 的,请确保用户名没有打错!"
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
preHandler: getOverridePreHandler('/api/login'),
|
|
|
|
|
handler: getOverrideHandler('/api/login') ?? async function(req, rep) {
|
|
|
|
|
const { username, password, createToken } = req.body;
|
|
|
|
|
const user = await Player.findOne({ email: username, password: createHash("sha256").update(password).digest('hex') });
|
|
|
|
|
if (!user) {
|
|
|
|
|
return rep.code(401).send({
|
|
|
|
|
err: 1.143688,
|
|
|
|
|
msg: "用户名或密码错误"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!user.permissions.some((it) => { s
|
|
|
|
|
return it.node === 'login' && it.allowed && (it.duration === 0 || it.startDate + it.duration > Date.now())
|
|
|
|
|
})) {
|
|
|
|
|
return await rep.code(401).send({
|
|
|
|
|
err: 0.337187,
|
|
|
|
|
msg: "泻药,宁滴账号已被封禁"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [token, key] = generateToken(`webapi:${user.username}`)
|
|
|
|
|
this.log.info(`/api/login > 为玩家 webapi:${user.username} 生成令牌: ${token} | 随机 key = ${key}`)
|
|
|
|
|
|
|
|
|
|
identifiers.set(token, {
|
|
|
|
|
uuid: user.uuid,
|
|
|
|
|
t: Date.now() + 1000 * 60 * 60 * 24 * 1,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if(createToken) {
|
|
|
|
|
new Token({
|
|
|
|
|
uuid: user.uuid,
|
|
|
|
|
token: token,
|
|
|
|
|
clientToken: `${req.headers['x-forwarded-for'] || req.ip}:${token.substring(3, 8)}`,
|
|
|
|
|
expireDate: Date.now() + 1000 * 60 * 60 * 24 * 15,
|
|
|
|
|
deadDate: Date.now() + 1000 * 60 * 60 * 24 * 30,
|
|
|
|
|
}).save()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await rep.code(200).send(JSON.stringify({
|
|
|
|
|
err: 1.048596,
|
|
|
|
|
msg: '',
|
|
|
|
|
extra: {
|
|
|
|
|
identifier: token,
|
|
|
|
|
textures: user.textures,
|
|
|
|
|
username: user.username,
|
|
|
|
|
uuid: user.uuid,
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const register = {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
url: '/api/register',
|
|
|
|
|
schema: {
|
|
|
|
|
summary: "注册",
|
|
|
|
|
description: `注册到 webapi,后续请求需要先登录获取identifier,然后携带请求头 'X-LSP-Idenitifier': '<token>',200正确返回 <<< Copilot自己补全的`,
|
|
|
|
|
tags: [ 'webapi' ],
|
|
|
|
|
body: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
username: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户名',
|
|
|
|
|
example: 'test'
|
|
|
|
|
},
|
|
|
|
|
password: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '密码',
|
|
|
|
|
example: '123456'
|
|
|
|
|
},
|
|
|
|
|
email: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '邮箱',
|
|
|
|
|
example: ''
|
|
|
|
|
},
|
|
|
|
|
telegramId: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'telegramId',
|
|
|
|
|
example: ''
|
|
|
|
|
},
|
|
|
|
|
textureMigrations: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
description: '纹理迁移',
|
|
|
|
|
optional: true,
|
|
|
|
|
properties: {
|
|
|
|
|
skin: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '皮肤',
|
|
|
|
|
optional: true,
|
|
|
|
|
example: 'https://assets.lama.icu/textures/skin/steve.png'
|
|
|
|
|
},
|
|
|
|
|
cape: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '披风',
|
|
|
|
|
optional: true,
|
|
|
|
|
example: 'https://assets.lama.icu/textures/cape/steve.png'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
response: {
|
|
|
|
|
200: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
...BASE_RESPONSE,
|
|
|
|
|
extra: {
|
|
|
|
|
username: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '用户名',
|
|
|
|
|
example: 'test'
|
|
|
|
|
},
|
|
|
|
|
password: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '密码',
|
|
|
|
|
example: '123456'
|
|
|
|
|
},
|
|
|
|
|
email: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '邮箱',
|
|
|
|
|
example: ''
|
|
|
|
|
},
|
|
|
|
|
telegramId: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'telegramId',
|
|
|
|
|
example: ''
|
|
|
|
|
},
|
|
|
|
|
textureMigrations: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
description: '纹理迁移',
|
|
|
|
|
optional: true,
|
|
|
|
|
properties: {
|
|
|
|
|
skin: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '皮肤',
|
|
|
|
|
optional: true,
|
|
|
|
|
example: 'https://assets.lama.icu/textures/skin/steve.png'
|
|
|
|
|
},
|
|
|
|
|
cape: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: '披风',
|
|
|
|
|
optional: true,
|
|
|
|
|
example: 'https://assets.lama.icu/textures/cape/steve.png'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
preHandler: getOverridePreHandler('/api/register'),
|
|
|
|
|
handler: getOverrideHandler('/api/register') ?? async function(req, rep) {
|
|
|
|
|
const { username, password, email, telegramId, textureMigrations } = req.body
|
|
|
|
|
const user = await Player.findOne({ $or: [
|
|
|
|
|
{ email: email }, { username: username }
|
|
|
|
|
] })
|
|
|
|
|
if (user) {
|
|
|
|
|
return await rep.code(401).send({
|
|
|
|
|
err: 1,
|
|
|
|
|
msg: "用户名已存在"
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(username == 0 || password == 0 || email == 0 || telegramId == 0) {
|
|
|
|
|
return await rep.code(401).send({
|
|
|
|
|
err: 1,
|
|
|
|
|
msg: "用户名/密码/邮箱/telegramId不能为空"
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const textues = { }
|
|
|
|
|
|
|
|
|
|
if(textureMigrations) {
|
|
|
|
|
if(textureMigrations.skin != 0 && textureMigrations.skin) {
|
|
|
|
|
textues.skin = textureMigrations.skin
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(textureMigrations.cape != 0 && textureMigrations.cape) {
|
|
|
|
|
textues.cape = textureMigrations.cape
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newUser = new Player({
|
|
|
|
|
username,
|
|
|
|
|
password: createHash("sha256").update(password).digest('hex'),
|
|
|
|
|
email,
|
|
|
|
|
uuid: uuid('LSPlayer:' + email),
|
|
|
|
|
textues,
|
|
|
|
|
registerDate: Date.now(),
|
|
|
|
|
permissions: [{ node: 'login', allowed: true, duration: -1, startDate: Date.now(), highPriority: false }],
|
|
|
|
|
telegramBind: {
|
|
|
|
|
username: telegramId,
|
|
|
|
|
verified: false
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
await newUser.save()
|
|
|
|
|
|
|
|
|
|
return await rep.code(200).send({
|
|
|
|
|
err: 1.048596,
|
|
|
|
|
msg: '',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|