lsp-yggdrasil/src/routes/web-api.js

339 lines
12 KiB
JavaScript
Raw Normal View History

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: '',
})
}
}