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

339 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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