socket server with rust
This commit is contained in:
+1
-130
@@ -1,130 +1 @@
|
|||||||
# Logs
|
/target
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
.stylelintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
|
||||||
.temp
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
|
||||||
.docusaurus
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
||||||
|
|||||||
Generated
+2157
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "auraclient-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
mongodb = { version = "3.2.1", features = ["sync"] }
|
||||||
|
serde = "1.0.218"
|
||||||
@@ -1,2 +1,22 @@
|
|||||||
# auraclient-api
|
# SaturnClient API
|
||||||
The api for AuraClient which is a Minecraft mod Client
|
The api for AuraClient which is a Minecraft mod Client
|
||||||
|
|
||||||
|
# Protocol base
|
||||||
|
All whitespace is used
|
||||||
|
```
|
||||||
|
method
|
||||||
|
@key=value
|
||||||
|
```
|
||||||
|
For private requests (requires microsoft session tokens)
|
||||||
|
```
|
||||||
|
@SessionToken=<session-token>
|
||||||
|
```
|
||||||
|
|
||||||
|
# Example method
|
||||||
|
```
|
||||||
|
ping
|
||||||
|
```
|
||||||
|
- Response
|
||||||
|
```
|
||||||
|
pong
|
||||||
|
```
|
||||||
Generated
-1109
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "auraclient-api",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "The api for AuraClient which is a Minecraft mod Client",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "tsc && node dist/index.js",
|
|
||||||
"test": "tsc && node test.mjs"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/kleo-dev/auraclient-api.git"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/kleo-dev/auraclient-api/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/kleo-dev/auraclient-api#readme",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/express": "^5.0.0",
|
|
||||||
"@types/mongodb": "^4.0.6",
|
|
||||||
"@types/node": "^22.13.5",
|
|
||||||
"dotenv": "^16.4.7",
|
|
||||||
"express": "^4.21.2",
|
|
||||||
"mongodb": "^6.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub const CLOAKS: &[&str] = &["glitch"];
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { MongoClient } from "mongodb";
|
|
||||||
import { Cloak, Player, mongoUrl } from "./utils";
|
|
||||||
|
|
||||||
export const validCloaks: Cloak[] = ["glitch"];
|
|
||||||
|
|
||||||
const mongoClient = new MongoClient(mongoUrl);
|
|
||||||
|
|
||||||
const db = mongoClient.db("saturnclient");
|
|
||||||
|
|
||||||
export const players = db.collection<Player>("players");
|
|
||||||
-165
@@ -1,165 +0,0 @@
|
|||||||
import express from "express";
|
|
||||||
import { players, validCloaks } from "./database";
|
|
||||||
import { Cloak } from "./utils";
|
|
||||||
|
|
||||||
async function getPlayerData(token: string) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`https://api.minecraftservices.com/minecraft/profile`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer " + token,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Invalid session");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id, name } = await response.json();
|
|
||||||
|
|
||||||
return { uuid: id, name };
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error("Failed to validate session");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const port = 3000;
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.use(express.json());
|
|
||||||
|
|
||||||
app.get("/", (_, res) => {
|
|
||||||
res.send({
|
|
||||||
version: "0.0.1-alpha.0",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/player/:uuid", async (req, res) => {
|
|
||||||
const player = await players.findOne({
|
|
||||||
uuid: req.params.uuid.replaceAll("-", ""),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
res.send({
|
|
||||||
saturn: false,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
`https://api.minecraftservices.com/minecraft/profile/lookup/${player.uuid}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const { name } = await response.json();
|
|
||||||
|
|
||||||
res.send({
|
|
||||||
saturn: true,
|
|
||||||
cloak: player.cloak,
|
|
||||||
name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/auth", async (req, res) => {
|
|
||||||
const { token } = req.query;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { uuid, name } = await getPlayerData(token as string);
|
|
||||||
|
|
||||||
const player = await players.findOne({ uuid });
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
await players.insertOne({
|
|
||||||
uuid,
|
|
||||||
cloak: "",
|
|
||||||
cloaks: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send({
|
|
||||||
success: true,
|
|
||||||
uuid,
|
|
||||||
cloak: "",
|
|
||||||
cloaks: [],
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send({
|
|
||||||
success: true,
|
|
||||||
uuid,
|
|
||||||
name,
|
|
||||||
cloak: player.cloak,
|
|
||||||
cloaks: player.cloaks,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
res.status(500).send({
|
|
||||||
success: false,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/cloak/:cloak", async (req, res) => {
|
|
||||||
const { cloak } = req.params;
|
|
||||||
const { token } = req.query;
|
|
||||||
|
|
||||||
const { uuid } = await getPlayerData(token as string);
|
|
||||||
|
|
||||||
const player = await players.findOne({ uuid });
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
res.status(400).send({
|
|
||||||
success: false,
|
|
||||||
error: "Player not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validCloaks.includes(cloak as Cloak)) {
|
|
||||||
res.status(400).send({
|
|
||||||
success: false,
|
|
||||||
error: "Invalid cloak",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player.cloaks.includes(cloak as Cloak)) {
|
|
||||||
res.status(400).send({
|
|
||||||
success: false,
|
|
||||||
error: "You don't have this cloak",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await players.updateOne({ uuid }, { $set: { cloak: cloak as Cloak } });
|
|
||||||
|
|
||||||
console.log("Cloak set to " + cloak + " for " + uuid);
|
|
||||||
|
|
||||||
res.send({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/cloak/", async (req, res) => {
|
|
||||||
const { token } = req.query;
|
|
||||||
|
|
||||||
const { uuid } = await getPlayerData(token as string);
|
|
||||||
|
|
||||||
const player = await players.findOne({ uuid });
|
|
||||||
|
|
||||||
if (!player) {
|
|
||||||
res.status(400).send({
|
|
||||||
success: false,
|
|
||||||
error: "Player not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await players.updateOne({ uuid }, { $set: { cloak: "" } });
|
|
||||||
|
|
||||||
console.log("Cloak set to none for " + uuid);
|
|
||||||
|
|
||||||
res.send({ success: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(3000, () => {
|
|
||||||
console.log(`listening as http://localhost:${port}/`);
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use mongodb::sync::{Client, Collection};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub enum Response {
|
||||||
|
Pong,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Response::Pong => "pong".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Player {
|
||||||
|
pub uuid: String,
|
||||||
|
pub cloaks: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Database {
|
||||||
|
pub players: Collection<Player>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Database {
|
||||||
|
pub fn new(client: &Client) -> Self {
|
||||||
|
let db = client.database("saturn");
|
||||||
|
Self {
|
||||||
|
players: db.collection("players"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
pub mod assets;
|
||||||
|
pub mod io;
|
||||||
|
pub mod methods;
|
||||||
|
pub mod parser;
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::{TcpListener, TcpStream};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use io::Database;
|
||||||
|
use methods::handle_request;
|
||||||
|
use mongodb::sync::Client;
|
||||||
|
|
||||||
|
const MONGO_URI: &str = "mongodb://admin:admin@localhost/";
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
let client = Client::with_uri_str(MONGO_URI).unwrap();
|
||||||
|
let database = Arc::new(Database::new(&client));
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:8080")?;
|
||||||
|
|
||||||
|
println!("Server listening on port 8080");
|
||||||
|
|
||||||
|
for stream in listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => {
|
||||||
|
println!("New connection: {}", stream.peer_addr().unwrap());
|
||||||
|
thread::spawn({
|
||||||
|
let database = Arc::clone(&database);
|
||||||
|
move || handle_client(stream, database)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Connection failed: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_client(mut stream: TcpStream, database: Arc<Database>) {
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match stream.read(&mut buffer) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(n) => {
|
||||||
|
let recv = String::from_utf8_lossy(&buffer[..n]).to_string();
|
||||||
|
match parser::parse(&recv) {
|
||||||
|
Ok((method, params)) => {
|
||||||
|
match handle_request(Arc::clone(&database), method, params) {
|
||||||
|
Ok(response) => {
|
||||||
|
stream.write_all(response.to_string().as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error handling request: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error parsing request: {}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading from client: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Client disconnected");
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::io::{Database, Response};
|
||||||
|
|
||||||
|
pub fn handle_request(
|
||||||
|
database: Arc<Database>,
|
||||||
|
method: String,
|
||||||
|
params: HashMap<String, String>,
|
||||||
|
) -> Result<Response, String> {
|
||||||
|
match method.as_str() {
|
||||||
|
"ping" => Ok(Response::Pong),
|
||||||
|
_ => Err("Invalid request: ".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
|
pub trait ParamMap {
|
||||||
|
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParamMap for HashMap<String, String> {
|
||||||
|
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, String> {
|
||||||
|
match self.get(param) {
|
||||||
|
Some(value) => T::from_str(value).map_err(|_| format!("Invalid parameter: {}", param)),
|
||||||
|
None => Err(format!("Parameter not found: {}", param)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(s: &str) -> Result<(String, HashMap<String, String>), String> {
|
||||||
|
let mut parts = s.split("@");
|
||||||
|
|
||||||
|
let method = parts.next().unwrap().trim().to_lowercase();
|
||||||
|
|
||||||
|
let params = parts
|
||||||
|
.map(|p| {
|
||||||
|
let parts = p.split("=").collect::<Vec<&str>>();
|
||||||
|
(parts[0].trim().to_string(), parts[1].trim().to_string())
|
||||||
|
})
|
||||||
|
.collect::<HashMap<String, String>>();
|
||||||
|
|
||||||
|
Ok((method, params))
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { config } from "dotenv";
|
|
||||||
config();
|
|
||||||
|
|
||||||
function notFound(): string {
|
|
||||||
throw "'MONGO_URL' not found in .env";
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mongoUrl = process.env.MONGO_URL || notFound();
|
|
||||||
|
|
||||||
export type Cloak = "glitch" | "forrest" | "";
|
|
||||||
|
|
||||||
export interface Player {
|
|
||||||
uuid: string;
|
|
||||||
cloak: Cloak;
|
|
||||||
cloaks: Cloak[];
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
await fetch("http://localhost:3000/player/d362a042-28bd-49e1-a807-ae74dbe8aba9", {
|
|
||||||
method: "GET",
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "NodeNext",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"removeComments": true,
|
|
||||||
"preserveConstEnums": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"outDir": "dist/"
|
|
||||||
},
|
|
||||||
"include": ["src/**/*", "test.ts"],
|
|
||||||
"exclude": ["**/*.spec.ts"]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user