diesel -> sqlx, patch cve (again lul)
This commit is contained in:
parent
a5cac129dd
commit
c3bb6d0d67
39 changed files with 1581 additions and 1207 deletions
|
@ -1 +1 @@
|
||||||
DATABASE_URL=postgres://username:password@localhost/diesel_demo
|
DATABASE_URL=sqlite://./database.db
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
/target
|
/target
|
||||||
|
|
||||||
/data
|
/data
|
||||||
|
*.db
|
||||||
|
|
||||||
.env
|
.env
|
||||||
config.toml
|
config.toml
|
899
Cargo.lock
generated
899
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -5,10 +5,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.21.3"
|
base64 = "0.21.3"
|
||||||
diesel = { version = "=2.1.0", features = ["postgres", "64-column-tables"] }
|
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
flate2 = "1.0.27"
|
flate2 = "1.0.27"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
migrate = "0.2.0"
|
||||||
password-auth = "0.3.0"
|
password-auth = "0.3.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.9.4"
|
regex = "1.9.4"
|
||||||
|
@ -19,4 +19,6 @@ roxmltree = "0.18.0"
|
||||||
serde = { version = "1.0.188", features = ["derive"] }
|
serde = { version = "1.0.188", features = ["derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
sha = "1.0.3"
|
sha = "1.0.3"
|
||||||
|
sqlx = { version = "0.7.2", features = ["runtime-tokio", "sqlite"] }
|
||||||
|
tempfile = "3.8.0"
|
||||||
toml = "0.7.6"
|
toml = "0.7.6"
|
||||||
|
|
5
build.rs
Normal file
5
build.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// generated by `sqlx migrate build-script`
|
||||||
|
fn main() {
|
||||||
|
// trigger recompilation when a new migration is added
|
||||||
|
println!("cargo:rerun-if-changed=migrations");
|
||||||
|
}
|
|
@ -9,10 +9,17 @@
|
||||||
# boomlings.com/database/
|
# boomlings.com/database/
|
||||||
# example.com/aaaaaaaaaa/
|
# example.com/aaaaaaaaaa/
|
||||||
# ^^^^^^^^^^^
|
# ^^^^^^^^^^^
|
||||||
# leaving as "/" will disable this
|
# leaving as empty will disable this
|
||||||
append_path = "/"
|
append_path = ""
|
||||||
# where can your server be accessible?
|
# where can your server be accessible (port)?
|
||||||
port = 8000
|
port = 8000
|
||||||
|
# where can your server be accessible (domain)?
|
||||||
|
# this is used for patching the executable
|
||||||
|
#
|
||||||
|
# example:
|
||||||
|
# boomlings.com
|
||||||
|
# localhost:8000
|
||||||
|
domain = "localhost:8000"
|
||||||
|
|
||||||
[accounts]
|
[accounts]
|
||||||
# allow new accounts to be created
|
# allow new accounts to be created
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# For documentation on how to configure this file,
|
|
||||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
|
||||||
|
|
||||||
[print_schema]
|
|
||||||
file = "src/db/schema.rs"
|
|
||||||
custom_type_derives = ["diesel::query_builder::QueryId"]
|
|
||||||
|
|
||||||
[migrations_directory]
|
|
||||||
dir = "migrations"
|
|
|
@ -1,6 +0,0 @@
|
||||||
-- This file was automatically created by Diesel to setup helper functions
|
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
|
||||||
-- changes will be added to existing projects as new migrations.
|
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
|
||||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
|
|
@ -1,36 +0,0 @@
|
||||||
-- This file was automatically created by Diesel to setup helper functions
|
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
|
||||||
-- changes will be added to existing projects as new migrations.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Sets up a trigger for the given table to automatically set a column called
|
|
||||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
|
||||||
-- in the modified columns)
|
|
||||||
--
|
|
||||||
-- # Example
|
|
||||||
--
|
|
||||||
-- ```sql
|
|
||||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
|
||||||
--
|
|
||||||
-- SELECT diesel_manage_updated_at('users');
|
|
||||||
-- ```
|
|
||||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
|
||||||
BEGIN
|
|
||||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
|
||||||
BEGIN
|
|
||||||
IF (
|
|
||||||
NEW IS DISTINCT FROM OLD AND
|
|
||||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
|
||||||
) THEN
|
|
||||||
NEW.updated_at := current_timestamp;
|
|
||||||
END IF;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
|
@ -1,3 +0,0 @@
|
||||||
DROP TABLE accounts;
|
|
||||||
|
|
||||||
DROP COLLATION case_insensitive;
|
|
1
migrations/20231018025924_accounts.down.sql
Normal file
1
migrations/20231018025924_accounts.down.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE accounts;
|
|
@ -1,13 +1,7 @@
|
||||||
CREATE COLLATION case_insensitive (
|
|
||||||
provider = icu,
|
|
||||||
locale = 'und-u-ks-level2',
|
|
||||||
deterministic = false
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE accounts (
|
CREATE TABLE accounts (
|
||||||
id SERIAL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
|
||||||
username VARCHAR(20) NOT NULL COLLATE case_insensitive UNIQUE,
|
username VARCHAR(20) NOT NULL COLLATE NOCASE UNIQUE,
|
||||||
gjp2 TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well)
|
gjp2 TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well)
|
||||||
password TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well)
|
password TEXT NOT NULL, -- argon2 hashed (rubrub uses bcrypt but oh well)
|
||||||
email VARCHAR(254) NOT NULL,
|
email VARCHAR(254) NOT NULL,
|
||||||
|
@ -25,5 +19,5 @@ CREATE TABLE accounts (
|
||||||
twitter_url VARCHAR(20),
|
twitter_url VARCHAR(20),
|
||||||
twitch_url VARCHAR(20),
|
twitch_url VARCHAR(20),
|
||||||
|
|
||||||
created_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS'))
|
created_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now'))
|
||||||
);
|
);
|
|
@ -1,14 +1,18 @@
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id SERIAL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
|
||||||
-- if `registered`, use account_id, else, use udid
|
-- on a registered account, account_id refers to the
|
||||||
|
-- account ID - however, pre 2.0, instead udid referred
|
||||||
|
-- to the user's UUID or UDID, depending on platform.
|
||||||
|
-- UUID and UDID are unique ids assigned for green
|
||||||
|
-- username users
|
||||||
|
--
|
||||||
|
-- in short, if `registered`, use account_id, else, use udid
|
||||||
udid TEXT,
|
udid TEXT,
|
||||||
account_id INTEGER references accounts(id),
|
account_id INTEGER references accounts(id),
|
||||||
registered INTEGER NOT NULL,
|
registered INTEGER NOT NULL,
|
||||||
|
|
||||||
-- idk why but we get weird errors if we use `COLLATE case_insensitive`
|
username TEXT NOT NULL COLLATE NOCASE,
|
||||||
-- maybe troubleshoot later, this works fine for now.
|
|
||||||
username TEXT NOT NULL, -- COLLATE case_insensitive,
|
|
||||||
|
|
||||||
stars INTEGER NOT NULL DEFAULT 0,
|
stars INTEGER NOT NULL DEFAULT 0,
|
||||||
demons INTEGER NOT NULL DEFAULT 0,
|
demons INTEGER NOT NULL DEFAULT 0,
|
||||||
|
@ -34,8 +38,8 @@ CREATE TABLE users (
|
||||||
special INTEGER NOT NULL DEFAULT 0,
|
special INTEGER NOT NULL DEFAULT 0,
|
||||||
glow INTEGER NOT NULL DEFAULT 0,
|
glow INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
created_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')),
|
created_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now')),
|
||||||
last_played TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')),
|
last_played TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now')),
|
||||||
|
|
||||||
is_banned INTEGER NOT NULL DEFAULT 0,
|
is_banned INTEGER NOT NULL DEFAULT 0,
|
||||||
is_banned_upload INTEGER NOT NULL DEFAULT 0
|
is_banned_upload INTEGER NOT NULL DEFAULT 0
|
|
@ -1,11 +1,11 @@
|
||||||
CREATE TABLE levels (
|
CREATE TABLE levels (
|
||||||
id SERIAL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
created_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')),
|
created_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now')),
|
||||||
modified_at TEXT NOT NULL DEFAULT (TO_CHAR(CURRENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.MS')),
|
modified_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'now')),
|
||||||
|
|
||||||
name VARCHAR(20) NOT NULL,
|
name VARCHAR(20) NOT NULL,
|
||||||
user_id INTEGER NOT NULL references users(id),
|
user_id INTEGER NOT NULL references users(id),
|
||||||
description VARCHAR(140) NOT NULL DEFAULT '',
|
description VARCHAR(140) NOT NULL DEFAULT "",
|
||||||
original INTEGER,
|
original INTEGER,
|
||||||
|
|
||||||
game_version INTEGER NOT NULL,
|
game_version INTEGER NOT NULL,
|
||||||
|
@ -16,8 +16,8 @@ CREATE TABLE levels (
|
||||||
unlisted INTEGER NOT NULL DEFAULT 0,
|
unlisted INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
version INTEGER NOT NULL DEFAULT 0,
|
version INTEGER NOT NULL DEFAULT 0,
|
||||||
extra_data BYTEA NOT NULL,
|
extra_data BLOB NOT NULL,
|
||||||
level_info BYTEA NOT NULL,
|
level_info BLOB NOT NULL,
|
||||||
|
|
||||||
editor_time INTEGER NOT NULL,
|
editor_time INTEGER NOT NULL,
|
||||||
editor_time_copies INTEGER NOT NULL,
|
editor_time_copies INTEGER NOT NULL,
|
||||||
|
@ -39,4 +39,4 @@ CREATE TABLE levels (
|
||||||
featured INTEGER NOT NULL DEFAULT 0,
|
featured INTEGER NOT NULL DEFAULT 0,
|
||||||
epic INTEGER NOT NULL DEFAULT 0,
|
epic INTEGER NOT NULL DEFAULT 0,
|
||||||
rated_coins INTEGER NOT NULL DEFAULT 0
|
rated_coins INTEGER NOT NULL DEFAULT 0
|
||||||
);
|
);
|
53
readme.md
53
readme.md
|
@ -6,7 +6,7 @@ this project is based off of (stolen from) the [crystal-gauntlet](https://git.oa
|
||||||
|
|
||||||
## why?
|
## why?
|
||||||
|
|
||||||
i've run out of ideas.
|
i'm trying to learn some rust, and this is a solid choice. most GDPS solutions out there are pretty garbage, to say the least.
|
||||||
|
|
||||||
### features
|
### features
|
||||||
|
|
||||||
|
@ -17,35 +17,40 @@ i've run out of ideas.
|
||||||
|
|
||||||
## build
|
## build
|
||||||
|
|
||||||
### migrating databases
|
`cargo build --release`
|
||||||
|
|
||||||
- run `cargo install diesel_cli --no-default-features --features postgres`
|
## configuration
|
||||||
- run `diesel migration run`
|
|
||||||
|
|
||||||
### testing
|
copy `.env.example` to `.env` and fill it out, same for `config.example.toml` to `config.toml`
|
||||||
|
|
||||||
- run `cargo run`
|
## setup
|
||||||
|
|
||||||
### building
|
make sure you have `sqlx-cli` by running `cargo install sqlx-cli`
|
||||||
|
|
||||||
- run `cargo build --release`
|
run `sqlx database setup`
|
||||||
|
|
||||||
|
then finally run the server with `cargo run`
|
||||||
|
|
||||||
|
## CLI tools
|
||||||
|
|
||||||
|
add `--help` or `-h` when calling the binary for special tools, such as patching executables.
|
||||||
|
|
||||||
## todo
|
## todo
|
||||||
|
|
||||||
- User icons in account management pages
|
- add get levels search back (find how to easily do dynamic queries)
|
||||||
- Flesh out account management page
|
- user icons in account management pages
|
||||||
- Account settings page
|
- account settings page
|
||||||
- Better web design (make formatting more consistant)
|
- better web design (make formatting more consistant)
|
||||||
- Use chrono for dates in database, add recent
|
- use chrono for dates in database
|
||||||
- 2.2's friends only unlisted
|
- 2.2's friends only unlisted
|
||||||
- Dailies, weeklies, events(?)
|
- dailies, weeklies, events(?)
|
||||||
- Moderation utilities
|
- moderation utilities
|
||||||
- Better song support
|
- better song support
|
||||||
- Cache authentication
|
- cache authentication (redis or mem)
|
||||||
- Panic less
|
- make a proper rank system (reuploading, uploading music, rating, etc.)
|
||||||
- Make a proper rank system (reuploading, uploading music, rating, etc.)
|
- use serde to make the forms whateverCaseThisIs rather than breaking our lint convention
|
||||||
- Use serde to make the forms whateverCaseThisIs rather than breaking our lint convention
|
- add back `realip` header support
|
||||||
- Swap to `sqlx` im gonna be honest `diesel` is pretty shit.
|
- add configurable form limits
|
||||||
- Swap to `sqlite` from `postgres`. Postgres feels too clunky and it just solos honestly
|
- nix
|
||||||
- Add back `realip` header support
|
- clean up difficulty/demon difficulties. It's fucking VILE.
|
||||||
- Add configurable form limits
|
- panic less, use results
|
|
@ -4,6 +4,8 @@ use std::sync::LazyLock;
|
||||||
|
|
||||||
use toml::Table;
|
use toml::Table;
|
||||||
|
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
pub static CONFIG: LazyLock<Table> = LazyLock::new(|| {
|
pub static CONFIG: LazyLock<Table> = LazyLock::new(|| {
|
||||||
let toml_str = fs::read_to_string("config.toml").expect("error finding toml config");
|
let toml_str = fs::read_to_string("config.toml").expect("error finding toml config");
|
||||||
let config: Table = toml::from_str(toml_str.as_str()).expect("error parsing toml config");
|
let config: Table = toml::from_str(toml_str.as_str()).expect("error parsing toml config");
|
||||||
|
@ -13,15 +15,20 @@ pub static CONFIG: LazyLock<Table> = LazyLock::new(|| {
|
||||||
|
|
||||||
pub fn config_get(key: &str) -> Option<&toml::Value> {
|
pub fn config_get(key: &str) -> Option<&toml::Value> {
|
||||||
let this = &CONFIG;
|
let this = &CONFIG;
|
||||||
let mut current = this.get(key)?;
|
let mut current_key = this.get(key.split(".").next()?)?;
|
||||||
for val in key.split(".").skip(1) {
|
for val in key.split(".").skip(1) {
|
||||||
current = current.as_table()?.get(val)?;
|
current_key = current_key.get(val)?;
|
||||||
}
|
}
|
||||||
Some(current)
|
Some(current_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config_get_with_default<T: serde::Deserialize<'static>>(key: &str, default: T) -> T {
|
pub fn config_get_with_default<'de, T>(key: &str, default: T) -> T
|
||||||
config_get(key)
|
where
|
||||||
.and_then(|v| v.clone().try_into().ok())
|
T: DeserializeOwned + 'de
|
||||||
.unwrap_or(default)
|
{
|
||||||
|
let val = config_get(key)
|
||||||
|
.and_then(|v| v.to_owned().try_into().expect("invalid toml val"))
|
||||||
|
.unwrap_or_else(|| default);
|
||||||
|
|
||||||
|
return val;
|
||||||
}
|
}
|
11
src/db.rs
11
src/db.rs
|
@ -1,14 +1,11 @@
|
||||||
use diesel::prelude::*;
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
pub mod models;
|
pub async fn establish_sqlite_conn() -> SqliteConnection {
|
||||||
pub mod schema;
|
|
||||||
|
|
||||||
pub fn establish_connection_pg() -> PgConnection {
|
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
PgConnection::establish(&database_url)
|
return SqliteConnection::connect(&database_url).await.expect("failed to connect to database");
|
||||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
|
||||||
}
|
}
|
149
src/db/models.rs
149
src/db/models.rs
|
@ -1,149 +0,0 @@
|
||||||
use diesel::prelude::*;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use super::schema::*;
|
|
||||||
|
|
||||||
#[derive(Queryable, Serialize)]
|
|
||||||
pub struct Account {
|
|
||||||
pub id: i32,
|
|
||||||
|
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
|
||||||
pub gjp2: String,
|
|
||||||
pub email: String,
|
|
||||||
|
|
||||||
pub is_admin: i32,
|
|
||||||
|
|
||||||
pub messages_enabled: i32,
|
|
||||||
pub comments_enabled: i32,
|
|
||||||
|
|
||||||
pub friend_requests_enabled: i32,
|
|
||||||
|
|
||||||
pub youtube_url: Option<String>,
|
|
||||||
pub twitter_url: Option<String>,
|
|
||||||
pub twitch_url: Option<String>,
|
|
||||||
|
|
||||||
pub created_at: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Deserialize)]
|
|
||||||
#[diesel(table_name = accounts)]
|
|
||||||
pub struct NewAccount {
|
|
||||||
pub username: String,
|
|
||||||
pub gjp2: String,
|
|
||||||
pub password: String,
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Serialize)]
|
|
||||||
pub struct User {
|
|
||||||
pub id: i32,
|
|
||||||
|
|
||||||
pub udid: Option<String>,
|
|
||||||
pub account_id: Option<i32>,
|
|
||||||
pub registered: i32,
|
|
||||||
|
|
||||||
pub username: String,
|
|
||||||
|
|
||||||
pub stars: i32,
|
|
||||||
pub demons: i32,
|
|
||||||
pub coins: i32,
|
|
||||||
pub user_coins: i32,
|
|
||||||
pub diamonds: i32,
|
|
||||||
pub orbs: i32,
|
|
||||||
pub creator_points: i32,
|
|
||||||
|
|
||||||
pub completed_levels: i32,
|
|
||||||
|
|
||||||
pub icon_type: i32,
|
|
||||||
pub color1: i32,
|
|
||||||
pub color2: i32,
|
|
||||||
pub cube: i32,
|
|
||||||
pub ship: i32,
|
|
||||||
pub ball: i32,
|
|
||||||
pub ufo: i32,
|
|
||||||
pub wave: i32,
|
|
||||||
pub robot: i32,
|
|
||||||
pub spider: i32,
|
|
||||||
pub explosion: i32,
|
|
||||||
pub special: i32,
|
|
||||||
pub glow: i32,
|
|
||||||
|
|
||||||
pub created_at: String,
|
|
||||||
pub last_played: String,
|
|
||||||
|
|
||||||
pub is_banned: i32,
|
|
||||||
pub is_banned_upload: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Deserialize)]
|
|
||||||
#[diesel(table_name = users)]
|
|
||||||
pub struct NewUser {
|
|
||||||
pub account_id: i32,
|
|
||||||
pub username: String,
|
|
||||||
pub registered: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Serialize)]
|
|
||||||
pub struct Level {
|
|
||||||
pub id: i32,
|
|
||||||
|
|
||||||
pub created_at: String,
|
|
||||||
pub modified_at: String,
|
|
||||||
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
pub user_id: i32,
|
|
||||||
|
|
||||||
pub description: String,
|
|
||||||
pub original: Option<i32>,
|
|
||||||
pub game_version: i32,
|
|
||||||
pub binary_version: i32,
|
|
||||||
pub password: Option<String>,
|
|
||||||
pub requested_stars: Option<i32>,
|
|
||||||
pub unlisted: i32,
|
|
||||||
pub version: i32,
|
|
||||||
pub extra_data: Vec<u8>,
|
|
||||||
pub level_info: Vec<u8>,
|
|
||||||
pub editor_time: i32,
|
|
||||||
pub editor_time_copies: i32,
|
|
||||||
pub song_id: i32,
|
|
||||||
pub length: i32,
|
|
||||||
pub objects: i32,
|
|
||||||
pub coins: i32,
|
|
||||||
pub has_ldm: i32,
|
|
||||||
pub two_player: i32,
|
|
||||||
pub downloads: i32,
|
|
||||||
pub likes: i32,
|
|
||||||
pub difficulty: Option<i32>,
|
|
||||||
pub community_difficulty: Option<i32>,
|
|
||||||
pub demon_difficulty: Option<i32>,
|
|
||||||
pub stars: Option<i32>,
|
|
||||||
pub featured: i32,
|
|
||||||
pub epic: i32,
|
|
||||||
pub rated_coins: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Deserialize)]
|
|
||||||
#[diesel(table_name = levels)]
|
|
||||||
pub struct NewLevel {
|
|
||||||
pub name: String,
|
|
||||||
pub user_id: i32,
|
|
||||||
pub description: String,
|
|
||||||
pub original: Option<i32>,
|
|
||||||
pub game_version: i32,
|
|
||||||
pub binary_version: i32,
|
|
||||||
pub password: Option<String>,
|
|
||||||
pub requested_stars: i32,
|
|
||||||
pub unlisted: i32,
|
|
||||||
pub version: i32,
|
|
||||||
pub extra_data: Vec<u8>,
|
|
||||||
pub level_info: Vec<u8>,
|
|
||||||
pub editor_time: i32,
|
|
||||||
pub editor_time_copies: i32,
|
|
||||||
pub song_id: i32,
|
|
||||||
pub length: i32,
|
|
||||||
pub objects: i32,
|
|
||||||
pub coins: i32,
|
|
||||||
pub has_ldm: i32,
|
|
||||||
pub two_player: i32
|
|
||||||
}
|
|
107
src/db/schema.rs
107
src/db/schema.rs
|
@ -1,107 +0,0 @@
|
||||||
// @generated automatically by Diesel CLI.
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
accounts (id) {
|
|
||||||
id -> Int4,
|
|
||||||
#[max_length = 20]
|
|
||||||
username -> Varchar,
|
|
||||||
gjp2 -> Text,
|
|
||||||
password -> Text,
|
|
||||||
#[max_length = 254]
|
|
||||||
email -> Varchar,
|
|
||||||
is_admin -> Int4,
|
|
||||||
messages_enabled -> Int4,
|
|
||||||
comments_enabled -> Int4,
|
|
||||||
friend_requests_enabled -> Int4,
|
|
||||||
#[max_length = 30]
|
|
||||||
youtube_url -> Nullable<Varchar>,
|
|
||||||
#[max_length = 20]
|
|
||||||
twitter_url -> Nullable<Varchar>,
|
|
||||||
#[max_length = 20]
|
|
||||||
twitch_url -> Nullable<Varchar>,
|
|
||||||
created_at -> Text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
levels (id) {
|
|
||||||
id -> Int4,
|
|
||||||
created_at -> Text,
|
|
||||||
modified_at -> Text,
|
|
||||||
#[max_length = 20]
|
|
||||||
name -> Varchar,
|
|
||||||
user_id -> Int4,
|
|
||||||
#[max_length = 140]
|
|
||||||
description -> Varchar,
|
|
||||||
original -> Nullable<Int4>,
|
|
||||||
game_version -> Int4,
|
|
||||||
binary_version -> Int4,
|
|
||||||
password -> Nullable<Text>,
|
|
||||||
requested_stars -> Nullable<Int4>,
|
|
||||||
unlisted -> Int4,
|
|
||||||
version -> Int4,
|
|
||||||
extra_data -> Bytea,
|
|
||||||
level_info -> Bytea,
|
|
||||||
editor_time -> Int4,
|
|
||||||
editor_time_copies -> Int4,
|
|
||||||
song_id -> Int4,
|
|
||||||
length -> Int4,
|
|
||||||
objects -> Int4,
|
|
||||||
coins -> Int4,
|
|
||||||
has_ldm -> Int4,
|
|
||||||
two_player -> Int4,
|
|
||||||
downloads -> Int4,
|
|
||||||
likes -> Int4,
|
|
||||||
difficulty -> Nullable<Int4>,
|
|
||||||
community_difficulty -> Nullable<Int4>,
|
|
||||||
demon_difficulty -> Nullable<Int4>,
|
|
||||||
stars -> Nullable<Int4>,
|
|
||||||
featured -> Int4,
|
|
||||||
epic -> Int4,
|
|
||||||
rated_coins -> Int4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
users (id) {
|
|
||||||
id -> Int4,
|
|
||||||
udid -> Nullable<Text>,
|
|
||||||
account_id -> Nullable<Int4>,
|
|
||||||
registered -> Int4,
|
|
||||||
username -> Text,
|
|
||||||
stars -> Int4,
|
|
||||||
demons -> Int4,
|
|
||||||
coins -> Int4,
|
|
||||||
user_coins -> Int4,
|
|
||||||
diamonds -> Int4,
|
|
||||||
orbs -> Int4,
|
|
||||||
creator_points -> Int4,
|
|
||||||
completed_levels -> Int4,
|
|
||||||
icon_type -> Int4,
|
|
||||||
color1 -> Int4,
|
|
||||||
color2 -> Int4,
|
|
||||||
cube -> Int4,
|
|
||||||
ship -> Int4,
|
|
||||||
ball -> Int4,
|
|
||||||
ufo -> Int4,
|
|
||||||
wave -> Int4,
|
|
||||||
robot -> Int4,
|
|
||||||
spider -> Int4,
|
|
||||||
explosion -> Int4,
|
|
||||||
special -> Int4,
|
|
||||||
glow -> Int4,
|
|
||||||
created_at -> Text,
|
|
||||||
last_played -> Text,
|
|
||||||
is_banned -> Int4,
|
|
||||||
is_banned_upload -> Int4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diesel::joinable!(levels -> users (user_id));
|
|
||||||
diesel::joinable!(users -> accounts (account_id));
|
|
||||||
|
|
||||||
diesel::allow_tables_to_appear_in_same_query!(
|
|
||||||
accounts,
|
|
||||||
levels,
|
|
||||||
users,
|
|
||||||
);
|
|
|
@ -1,3 +1,2 @@
|
||||||
pub mod login_account;
|
pub mod login_account;
|
||||||
pub mod register_account;
|
pub mod register_account;
|
||||||
pub mod update_account_settings;
|
|
|
@ -2,8 +2,6 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::helpers;
|
use crate::helpers;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
|
@ -17,46 +15,43 @@ pub struct FromLoginAccount {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/loginGJAccount.php", data = "<input>")]
|
#[post("/accounts/loginGJAccount.php", data = "<input>")]
|
||||||
pub fn login_account(input: Form<FromLoginAccount>) -> status::Custom<&'static str> {
|
pub async fn login_account(input: Form<FromLoginAccount>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
if input.userName != helpers::clean::clean_no_space(input.userName.as_ref()) {
|
let username = helpers::clean::clean_basic(input.userName.as_ref());
|
||||||
|
|
||||||
|
let password = input.password.clone();
|
||||||
|
let gjp = input.gjp.clone();
|
||||||
|
let gjp2 = input.gjp2.clone();
|
||||||
|
|
||||||
|
if input.userName != username {
|
||||||
return status::Custom(Status::Ok, "-4")
|
return status::Custom(Status::Ok, "-4")
|
||||||
}
|
}
|
||||||
|
|
||||||
// gjp2 checks dont matter, its hashed, gjp checks would break bc its base64, and why does this check exist if its just for logging in robtop this is useless it doesnt provide security we already did the security on the register account u fucking faggot im really bored of working on this but im also excited to see if it works deepwoken solos mid dash
|
// why does this check exist? it's kinda useless
|
||||||
match input.password.clone() {
|
if let Some(password) = password {
|
||||||
Some(password_val) => {
|
if password.len() < 6 {
|
||||||
if password_val.len() < 6 {
|
return status::Custom(Status::Ok, "-8")
|
||||||
return status::Custom(Status::Ok, "-8")
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.userName.len() < 3 {
|
if username.len() < 3 {
|
||||||
return status::Custom(Status::Ok, "-9")
|
return status::Custom(Status::Ok, "-9")
|
||||||
}
|
}
|
||||||
|
|
||||||
// account verification
|
let result = sqlx::query_scalar!("SELECT id FROM accounts WHERE username = ?", username)
|
||||||
{
|
.fetch_one(connection)
|
||||||
use db::schema::accounts::dsl::*;
|
.await;
|
||||||
|
|
||||||
let query_result = accounts
|
match result {
|
||||||
.select(id)
|
Ok(account_id_val) => {
|
||||||
.filter(username.eq(input.userName.clone()))
|
let user_id_val = helpers::accounts::get_user_id_from_account_id(account_id_val).await;
|
||||||
.get_result::<i32, >(connection);
|
|
||||||
|
|
||||||
match query_result {
|
match helpers::accounts::auth(account_id_val, input.password.clone(), input.gjp.clone(), input.gjp2.clone()).await {
|
||||||
Ok(account_id_val) => {
|
Ok(_) => return status::Custom(Status::Ok, Box::leak(format!("{},{}", user_id_val, account_id_val).into_boxed_str())),
|
||||||
let user_id_val = helpers::accounts::get_user_id_from_account_id(account_id_val);
|
Err(_) => return status::Custom(Status::Ok, "-11")
|
||||||
|
}
|
||||||
match helpers::accounts::auth(account_id_val, input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
|
},
|
||||||
Ok(_) => return status::Custom(Status::Ok, Box::leak(format!("{},{}", user_id_val, account_id_val).into_boxed_str())),
|
Err(_) => return status::Custom(Status::Ok, "-1")
|
||||||
Err(_) => return status::Custom(Status::Ok, "-11")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(_) => return status::Custom(Status::Ok, "-1")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,9 +2,6 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
use diesel::result::Error;
|
|
||||||
|
|
||||||
use password_auth::generate_hash;
|
use password_auth::generate_hash;
|
||||||
|
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
@ -19,77 +16,59 @@ pub struct FormRegisterAccount {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/accounts/registerGJAccount.php", data = "<input>")]
|
#[post("/accounts/registerGJAccount.php", data = "<input>")]
|
||||||
pub fn register_account(input: Form<FormRegisterAccount>) -> status::Custom<&'static str> {
|
pub async fn register_account(input: Form<FormRegisterAccount>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
|
let username = helpers::clean::clean_basic(input.userName.as_ref());
|
||||||
|
let password = input.password.clone();
|
||||||
|
let email = input.email.clone();
|
||||||
|
|
||||||
|
let hashed_password = generate_hash(password.clone());
|
||||||
|
let gjp2 = helpers::encryption::get_gjp2(password.clone());
|
||||||
|
|
||||||
if config::config_get_with_default("accounts.allow_registration", true) == false {
|
if config::config_get_with_default("accounts.allow_registration", true) == false {
|
||||||
return status::Custom(Status::Ok, "-1")
|
return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.userName != helpers::clean::clean_no_space(input.userName.as_ref()) {
|
if input.userName != username {
|
||||||
return status::Custom(Status::Ok, "-4")
|
return status::Custom(Status::Ok, "-4")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.password.len() < 6 {
|
if password.len() < 6 {
|
||||||
return status::Custom(Status::Ok, "-8")
|
return status::Custom(Status::Ok, "-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.userName.len() < 3 {
|
if username.len() < 3 {
|
||||||
return status::Custom(Status::Ok, "-9")
|
return status::Custom(Status::Ok, "-9")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.userName.len() > 20 {
|
if username.len() > 20 {
|
||||||
return status::Custom(Status::Ok, "-4")
|
return status::Custom(Status::Ok, "-4")
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.email.len() > 254 {
|
if email.len() > 254 {
|
||||||
return status::Custom(Status::Ok, "-6")
|
return status::Custom(Status::Ok, "-6")
|
||||||
}
|
}
|
||||||
|
|
||||||
// account management
|
// check if the username is already taken
|
||||||
use db::models::{Account, NewAccount};
|
sqlx::query_scalar!("SELECT COUNT(*) FROM accounts WHERE username = ?", username)
|
||||||
|
.fetch_one(&mut connection)
|
||||||
|
.await
|
||||||
|
.map_err(|_| status::Custom(Status::Ok, "-1"))
|
||||||
|
.expect("error getting the account count");
|
||||||
|
|
||||||
let inserted_account: Account;
|
let inserted_account = sqlx::query!("INSERT INTO accounts (username, password, email, gjp2) VALUES (?, ?, ?, ?)", username, hashed_password, email, gjp2)
|
||||||
|
.execute(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error saving the new account");
|
||||||
|
|
||||||
{
|
let inserted_account_id = inserted_account.last_insert_rowid();
|
||||||
use db::schema::accounts::dsl::*;
|
|
||||||
|
|
||||||
let account_name_usage = accounts.filter(username.eq(input.userName.clone())).count().get_result::<i64>(connection) as Result<i64, Error>;
|
|
||||||
let account_name_used = account_name_usage.expect("database name query error") != 0;
|
|
||||||
if account_name_used {
|
|
||||||
return status::Custom(Status::Ok, "-2")
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_account = NewAccount {
|
|
||||||
username: input.userName.clone(),
|
|
||||||
password: generate_hash(input.password.clone()),
|
|
||||||
gjp2: generate_hash(helpers::encryption::get_gjp2(input.password.clone())),
|
|
||||||
email: input.email.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
inserted_account = diesel::insert_into(accounts)
|
|
||||||
.values(&new_account)
|
|
||||||
.get_result::<Account, >(connection)
|
|
||||||
.expect("error saving the new account");
|
|
||||||
}
|
|
||||||
|
|
||||||
// user management
|
// user management
|
||||||
|
sqlx::query!("INSERT INTO users (account_id, username, registered) VALUES (?, ?, 1)", inserted_account_id, username)
|
||||||
{
|
.execute(&mut connection)
|
||||||
use db::models::{User, NewUser};
|
.await
|
||||||
use db::schema::users::dsl::*;
|
.expect("error saving the new user");
|
||||||
|
|
||||||
let new_user = NewUser {
|
return status::Custom(Status::Ok, "1");
|
||||||
account_id: inserted_account.id,
|
|
||||||
username: input.userName.clone(),
|
|
||||||
registered: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
diesel::insert_into(users)
|
|
||||||
.values(&new_user)
|
|
||||||
.get_result::<User, >(connection)
|
|
||||||
.expect("error saving the new user");
|
|
||||||
}
|
|
||||||
|
|
||||||
return status::Custom(Status::Ok, "1")
|
|
||||||
}
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
use rocket::form::Form;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::response::status;
|
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::helpers;
|
|
||||||
use crate::db;
|
|
||||||
|
|
||||||
#[derive(FromForm)]
|
|
||||||
pub struct FormUpdateAccountSettings {
|
|
||||||
accountID: i32,
|
|
||||||
|
|
||||||
mS: i32,
|
|
||||||
frS: i32,
|
|
||||||
cS: i32,
|
|
||||||
|
|
||||||
yt: String,
|
|
||||||
twitter: String,
|
|
||||||
twitch: String,
|
|
||||||
|
|
||||||
password: Option<String>,
|
|
||||||
gjp: Option<String>,
|
|
||||||
gjp2: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/updateGJAccSettings20.php", data = "<input>")]
|
|
||||||
pub fn update_account_settings(input: Form<FormUpdateAccountSettings>) -> status::Custom<&'static str> {
|
|
||||||
let connection = &mut db::establish_connection_pg();
|
|
||||||
|
|
||||||
// account verification
|
|
||||||
let (_user_id_val, account_id_val): (i32, i32);
|
|
||||||
|
|
||||||
match helpers::accounts::auth(input.accountID.clone(), input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
|
|
||||||
Ok((user_id, account_id)) => {
|
|
||||||
_user_id_val = user_id;
|
|
||||||
account_id_val = account_id;
|
|
||||||
},
|
|
||||||
Err(_) => return status::Custom(Status::Ok, "-1")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
use db::models::Account;
|
|
||||||
use db::schema::accounts::dsl::*;
|
|
||||||
|
|
||||||
diesel::update(accounts)
|
|
||||||
.filter(id.eq(account_id_val))
|
|
||||||
.set((
|
|
||||||
messages_enabled.eq(input.mS.clamp(0, 2)),
|
|
||||||
friend_requests_enabled.eq(input.frS.clamp(0, 1)),
|
|
||||||
comments_enabled.eq(input.cS.clamp(0, 2)),
|
|
||||||
youtube_url.eq(input.yt.chars().take(20).collect::<String>()),
|
|
||||||
twitch_url.eq(input.twitch.chars().take(25).collect::<String>()),
|
|
||||||
twitter_url.eq(input.twitter.chars().take(15).collect::<String>())
|
|
||||||
))
|
|
||||||
.get_result::<Account, >(connection)
|
|
||||||
.expect("failed to update account");
|
|
||||||
}
|
|
||||||
|
|
||||||
return status::Custom(Status::Ok, "1")
|
|
||||||
}
|
|
|
@ -2,8 +2,6 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
||||||
use flate2::read::{GzDecoder, ZlibDecoder};
|
use flate2::read::{GzDecoder, ZlibDecoder};
|
||||||
|
@ -24,16 +22,11 @@ pub struct FormDownloadLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/downloadGJLevel22.php", data = "<input>")]
|
#[post("/downloadGJLevel22.php", data = "<input>")]
|
||||||
pub fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static str> {
|
pub async fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
use db::schema::{levels, users};
|
|
||||||
use db::models::{Level, User};
|
|
||||||
|
|
||||||
let mut response: Vec<String> = Vec::new();
|
let mut response: Vec<String> = Vec::new();
|
||||||
|
|
||||||
let query = levels::table.into_boxed();
|
|
||||||
|
|
||||||
match input.levelID {
|
match input.levelID {
|
||||||
-1 => {
|
-1 => {
|
||||||
unimplemented!("no daily support")
|
unimplemented!("no daily support")
|
||||||
|
@ -49,168 +42,162 @@ pub fn download_level(input: Form<FormDownloadLevel>) -> status::Custom<&'static
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// database query
|
let result = sqlx::query!("SELECT levels.id, levels.name, levels.extra_data, levels.level_info, levels.password, levels.user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, levels.created_at, levels.modified_at, users.username, users.udid, users.account_id, users.registered, editor_time, editor_time_copies FROM levels JOIN users ON levels.user_id = users.id WHERE levels.id = ?", input.levelID)
|
||||||
{
|
.fetch_one(&mut connection)
|
||||||
let result = query
|
.await
|
||||||
.filter(levels::id.eq(input.levelID))
|
.expect("error loading levels");
|
||||||
.get_result::<Level, >(connection)
|
|
||||||
.expect("fatal error loading levels");
|
|
||||||
|
|
||||||
let user: User = users::table.find(result.user_id).get_result::<User, >(connection).expect("couldnt get user from lvl");
|
let set_difficulty = match result.difficulty {
|
||||||
let level: Level = result;
|
Some(diff) => {
|
||||||
|
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
||||||
let set_difficulty = match level.difficulty {
|
},
|
||||||
Some(diff) => {
|
None => None
|
||||||
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
};
|
||||||
},
|
let community_difficulty = match result.community_difficulty {
|
||||||
None => None
|
Some(diff) => {
|
||||||
};
|
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
||||||
let community_difficulty = match level.community_difficulty {
|
},
|
||||||
Some(diff) => {
|
None => None
|
||||||
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
};
|
||||||
},
|
let difficulty = match set_difficulty {
|
||||||
None => None
|
Some(diff) => {
|
||||||
};
|
Some(diff)
|
||||||
let difficulty = match set_difficulty {
|
},
|
||||||
Some(diff) => {
|
None => {
|
||||||
Some(diff)
|
match community_difficulty {
|
||||||
},
|
Some(diff) => {
|
||||||
None => {
|
Some(diff)
|
||||||
match community_difficulty {
|
},
|
||||||
Some(diff) => {
|
None => None
|
||||||
Some(diff)
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let demon_difficulty = match level.demon_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
Some(helpers::difficulty::DemonDifficulty::new(diff))
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
|
|
||||||
let xor_pass: String;
|
|
||||||
if input.gameVersion.unwrap_or(19) >= 20 {
|
|
||||||
xor_pass = general_purpose::URL_SAFE.encode(helpers::encryption::cyclic_xor_string(&level.password.clone().unwrap_or(String::from("0")), "26364"))
|
|
||||||
} else {
|
|
||||||
xor_pass = level.password.clone().unwrap_or(String::from("0"));
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
let demon_difficulty = match result.demon_difficulty {
|
||||||
|
Some(diff) => {
|
||||||
|
Some(helpers::difficulty::DemonDifficulty::new(diff))
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
let compressed_level_data = fs::read(format!("{}/{}/{}.lvl", config::config_get_with_default("db.data_folder", "data"), "levels", level.id)).expect("couldnt read level file");
|
let xor_pass: String;
|
||||||
|
if input.gameVersion.unwrap_or(19) >= 20 {
|
||||||
let uncompressed_level_data = String::from_utf8(if compressed_level_data.starts_with(&[0x1F, 0x8B]) {
|
xor_pass = general_purpose::URL_SAFE.encode(helpers::encryption::cyclic_xor_string(&result.password.clone().unwrap_or(String::from("0")), "26364"))
|
||||||
// gzip!!
|
} else {
|
||||||
let mut gz_decoder = GzDecoder::new(compressed_level_data.as_slice());
|
xor_pass = result.password.clone().unwrap_or(String::from("0"));
|
||||||
let mut decompressed_data = Vec::new();
|
|
||||||
gz_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
|
|
||||||
decompressed_data
|
|
||||||
} else if compressed_level_data.starts_with(&[0x78]) {
|
|
||||||
// zlib!!
|
|
||||||
let mut zlib_decoder = ZlibDecoder::new(compressed_level_data.as_slice());
|
|
||||||
let mut decompressed_data = Vec::new();
|
|
||||||
zlib_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
|
|
||||||
decompressed_data
|
|
||||||
} else {
|
|
||||||
panic!("invalid compression method")
|
|
||||||
}).expect("invalid utf-8 sequence");
|
|
||||||
|
|
||||||
let level_data = uncompressed_level_data.as_bytes();
|
|
||||||
|
|
||||||
response.push(helpers::format::format(hashmap! {
|
|
||||||
1 => level.id.to_string(),
|
|
||||||
2 => level.name,
|
|
||||||
3 => if input.gameVersion.unwrap_or(19) >= 20 {
|
|
||||||
general_purpose::URL_SAFE.encode(level.description)
|
|
||||||
} else {
|
|
||||||
level.description
|
|
||||||
},
|
|
||||||
4 => String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence"),
|
|
||||||
5 => level.version.to_string(),
|
|
||||||
6 => user.id.to_string(),
|
|
||||||
// this argument is weird. its the "difficulty divisor"
|
|
||||||
// used to be vote count but yeah
|
|
||||||
8 => 10.to_string(),
|
|
||||||
9 => (match difficulty {
|
|
||||||
Some(diff) => diff.to_star_difficulty(),
|
|
||||||
None => 0
|
|
||||||
} * 10).to_string(),
|
|
||||||
10 => level.downloads.to_string(),
|
|
||||||
12 => (if level.song_id < 50 { level.song_id } else { 0 }).to_string(),
|
|
||||||
13 => level.game_version.to_string(),
|
|
||||||
14 => level.likes.to_string(),
|
|
||||||
16 => (-level.likes).to_string(),
|
|
||||||
15 => level.length.to_string(),
|
|
||||||
17 => match difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => 0
|
|
||||||
}.to_string(),
|
|
||||||
18 => (if let Some(stars) = level.stars { stars } else { 0 }).to_string(),
|
|
||||||
19 => level.featured.to_string(),
|
|
||||||
25 => match difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
if diff == helpers::difficulty::LevelDifficulty::Auto {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => 0
|
|
||||||
}.to_string(),
|
|
||||||
27 => xor_pass,
|
|
||||||
28 => "1".to_string(), // unimplemented
|
|
||||||
29 => "1".to_string(), // unimplemented
|
|
||||||
30 => (if let Some(original) = level.original { original } else { 0 }).to_string(),
|
|
||||||
31 => level.two_player.to_string(),
|
|
||||||
35 => (if level.song_id >= 50 { level.song_id } else { 0 }).to_string(),
|
|
||||||
36 => String::from_utf8(if input.extras.is_some() { level.extra_data } else { Vec::new() }).expect("invalid utf-8 sequence"),
|
|
||||||
37 => level.coins.to_string(),
|
|
||||||
38 => level.rated_coins.to_string(),
|
|
||||||
39 => (if let Some(requested_stars) = level.requested_stars { requested_stars } else { 0 }).to_string(),
|
|
||||||
40 => level.has_ldm.to_string(),
|
|
||||||
41 => "".to_string(), // unimplemented
|
|
||||||
42 => level.epic.to_string(),
|
|
||||||
43 => match demon_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
diff
|
|
||||||
},
|
|
||||||
None => helpers::difficulty::DemonDifficulty::Hard
|
|
||||||
}.to_demon_difficulty().to_string(),
|
|
||||||
44 => "0".to_string(), // unimplemented
|
|
||||||
45 => level.objects.to_string(),
|
|
||||||
46 => level.editor_time.to_string(),
|
|
||||||
47 => level.editor_time_copies.to_string()
|
|
||||||
}));
|
|
||||||
response.push(helpers::encryption::gen_solo(String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence")));
|
|
||||||
|
|
||||||
let thing = [
|
|
||||||
user.id.to_string(),
|
|
||||||
level.stars.unwrap_or(0).to_string(),
|
|
||||||
match difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => 0
|
|
||||||
}.to_string(),
|
|
||||||
level.id.to_string(),
|
|
||||||
level.rated_coins.to_string(),
|
|
||||||
level.featured.to_string(),
|
|
||||||
level.password.unwrap_or(String::new()).to_string(),
|
|
||||||
0.to_string()
|
|
||||||
];
|
|
||||||
response.push(helpers::encryption::gen_solo_2(thing.join(",")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let compressed_level_data = fs::read(format!("{}/{}/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), "levels", result.id)).expect("couldnt read level file");
|
||||||
|
|
||||||
|
let uncompressed_level_data = String::from_utf8(if compressed_level_data.starts_with(&[0x1F, 0x8B]) {
|
||||||
|
// gzip!!
|
||||||
|
let mut gz_decoder = GzDecoder::new(compressed_level_data.as_slice());
|
||||||
|
let mut decompressed_data = Vec::new();
|
||||||
|
gz_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
|
||||||
|
decompressed_data
|
||||||
|
} else if compressed_level_data.starts_with(&[0x78]) {
|
||||||
|
// zlib!!
|
||||||
|
let mut zlib_decoder = ZlibDecoder::new(compressed_level_data.as_slice());
|
||||||
|
let mut decompressed_data = Vec::new();
|
||||||
|
zlib_decoder.read_to_end(&mut decompressed_data).expect("err uncompressing level");
|
||||||
|
decompressed_data
|
||||||
|
} else {
|
||||||
|
panic!("invalid compression method")
|
||||||
|
}).expect("invalid utf-8 sequence");
|
||||||
|
|
||||||
|
let level_data = uncompressed_level_data.as_bytes();
|
||||||
|
|
||||||
|
response.push(helpers::format::format(hashmap! {
|
||||||
|
1 => result.id.to_string(),
|
||||||
|
2 => result.name,
|
||||||
|
3 => if input.gameVersion.unwrap_or(19) >= 20 {
|
||||||
|
general_purpose::URL_SAFE.encode(result.description)
|
||||||
|
} else {
|
||||||
|
result.description
|
||||||
|
},
|
||||||
|
4 => String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence"),
|
||||||
|
5 => result.version.to_string(),
|
||||||
|
6 => result.user_id.to_string(),
|
||||||
|
// this argument is weird. its the "difficulty divisor"
|
||||||
|
// used to be vote count but yeah
|
||||||
|
8 => 10.to_string(),
|
||||||
|
9 => (match difficulty {
|
||||||
|
Some(diff) => diff.to_star_difficulty(),
|
||||||
|
None => 0
|
||||||
|
} * 10).to_string(),
|
||||||
|
10 => result.downloads.to_string(),
|
||||||
|
12 => (if result.song_id < 50 { result.song_id } else { 0 }).to_string(),
|
||||||
|
13 => result.game_version.to_string(),
|
||||||
|
14 => result.likes.to_string(),
|
||||||
|
16 => (-result.likes).to_string(),
|
||||||
|
15 => result.length.to_string(),
|
||||||
|
17 => match difficulty {
|
||||||
|
Some(diff) => {
|
||||||
|
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => 0
|
||||||
|
}.to_string(),
|
||||||
|
18 => (if let Some(stars) = result.stars { stars } else { 0 }).to_string(),
|
||||||
|
19 => result.featured.to_string(),
|
||||||
|
25 => match difficulty {
|
||||||
|
Some(diff) => {
|
||||||
|
if diff == helpers::difficulty::LevelDifficulty::Auto {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => 0
|
||||||
|
}.to_string(),
|
||||||
|
27 => xor_pass,
|
||||||
|
28 => "1".to_string(), // unimplemented
|
||||||
|
29 => "1".to_string(), // unimplemented
|
||||||
|
30 => (if let Some(original) = result.original { original } else { 0 }).to_string(),
|
||||||
|
31 => result.two_player.to_string(),
|
||||||
|
35 => (if result.song_id >= 50 { result.song_id } else { 0 }).to_string(),
|
||||||
|
36 => String::from_utf8(if input.extras.is_some() { result.extra_data } else { Vec::new() }).expect("invalid utf-8 sequence"),
|
||||||
|
37 => result.coins.to_string(),
|
||||||
|
38 => result.rated_coins.to_string(),
|
||||||
|
39 => (if let Some(requested_stars) = result.requested_stars { requested_stars } else { 0 }).to_string(),
|
||||||
|
40 => result.has_ldm.to_string(),
|
||||||
|
41 => "".to_string(), // unimplemented
|
||||||
|
42 => result.epic.to_string(),
|
||||||
|
43 => match demon_difficulty {
|
||||||
|
Some(diff) => {
|
||||||
|
diff
|
||||||
|
},
|
||||||
|
None => helpers::difficulty::DemonDifficulty::Hard
|
||||||
|
}.to_demon_difficulty().to_string(),
|
||||||
|
44 => "0".to_string(), // unimplemented
|
||||||
|
45 => result.objects.to_string(),
|
||||||
|
46 => result.editor_time.to_string(),
|
||||||
|
47 => result.editor_time_copies.to_string()
|
||||||
|
}));
|
||||||
|
response.push(helpers::encryption::gen_solo(String::from_utf8(level_data.to_vec()).expect("invalid utf-8 sequence")));
|
||||||
|
|
||||||
|
let thing = [
|
||||||
|
result.user_id.to_string(),
|
||||||
|
result.stars.unwrap_or(0).to_string(),
|
||||||
|
match difficulty {
|
||||||
|
Some(diff) => {
|
||||||
|
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => 0
|
||||||
|
}.to_string(),
|
||||||
|
result.id.to_string(),
|
||||||
|
result.rated_coins.to_string(),
|
||||||
|
result.featured.to_string(),
|
||||||
|
result.password.unwrap_or(String::new()).to_string(),
|
||||||
|
0.to_string()
|
||||||
|
];
|
||||||
|
response.push(helpers::encryption::gen_solo_2(thing.join(",")));
|
||||||
|
|
||||||
return status::Custom(Status::Ok, Box::leak(response.join("#").into_boxed_str()))
|
return status::Custom(Status::Ok, Box::leak(response.join("#").into_boxed_str()))
|
||||||
}
|
}
|
|
@ -2,19 +2,53 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
||||||
|
use sqlx::Type;
|
||||||
|
use sqlx::{Encode, Sqlite, query_builder::QueryBuilder, Execute};
|
||||||
|
|
||||||
use crate::helpers;
|
use crate::helpers;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
struct DynQuery {
|
||||||
|
id: i64,
|
||||||
|
name: String,
|
||||||
|
user_id: i64,
|
||||||
|
description: String,
|
||||||
|
original: Option<i32>,
|
||||||
|
game_version: i32,
|
||||||
|
requested_stars: Option<i32>,
|
||||||
|
version: i32,
|
||||||
|
song_id: i32,
|
||||||
|
length: i32,
|
||||||
|
objects: i32,
|
||||||
|
coins: i32,
|
||||||
|
has_ldm: i32,
|
||||||
|
two_player: i32,
|
||||||
|
downloads: i32,
|
||||||
|
likes: i32,
|
||||||
|
difficulty: Option<i64>,
|
||||||
|
community_difficulty: Option<i64>,
|
||||||
|
demon_difficulty: Option<i64>,
|
||||||
|
stars: Option<i32>,
|
||||||
|
featured: i32,
|
||||||
|
epic: i32,
|
||||||
|
rated_coins: i32,
|
||||||
|
user_username: String,
|
||||||
|
user_udid: Option<String>,
|
||||||
|
user_account_id: Option<i64>,
|
||||||
|
user_registered: i32,
|
||||||
|
editor_time: i32,
|
||||||
|
editor_time_copies: i32
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
pub struct FormGetLevels {
|
pub struct FormGetLevels {
|
||||||
page: i64,
|
page: Option<i64>,
|
||||||
str: String,
|
str: Option<String>,
|
||||||
|
|
||||||
accountID: Option<i32>,
|
accountID: Option<i64>,
|
||||||
gjp: Option<String>,
|
gjp: Option<String>,
|
||||||
gjp2: Option<String>,
|
gjp2: Option<String>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
|
@ -41,55 +75,57 @@ pub struct FormGetLevels {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/getGJLevels20.php", data = "<input>")]
|
#[post("/getGJLevels20.php", data = "<input>")]
|
||||||
pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
pub async fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
use db::schema::{levels, users};
|
|
||||||
use db::models::{Level, User};
|
|
||||||
|
|
||||||
let mut can_see_unlisted = false;
|
let mut can_see_unlisted = false;
|
||||||
|
|
||||||
let mut query = levels::table.into_boxed();
|
|
||||||
let mut count_query = levels::table.into_boxed();
|
|
||||||
|
|
||||||
if input.str != "" && input.r#type != Some(5) && input.r#type != Some(10) && input.r#type != Some(19) {
|
// WHERE [...]
|
||||||
match input.str.parse::<i32>() {
|
let mut query_params: Vec<&str> = vec![];
|
||||||
Ok(matched_id) => {
|
// Use this for binding on `query_params`
|
||||||
|
let mut query_params_bind: Vec<Box<dyn ToString + Send>> = vec![];
|
||||||
|
// ORDER BY [...]
|
||||||
|
let mut order = "levels.created_at DESC";
|
||||||
|
|
||||||
|
let page_offset = input.page.unwrap_or(0) * 10;
|
||||||
|
|
||||||
|
let search_query = input.str.clone().unwrap_or("".to_string());
|
||||||
|
|
||||||
|
if !search_query.is_empty() && input.r#type != Some(5) && input.r#type != Some(10) && input.r#type != Some(19) {
|
||||||
|
match search_query.parse::<i64>() {
|
||||||
|
Ok(id) => {
|
||||||
can_see_unlisted = true;
|
can_see_unlisted = true;
|
||||||
query = query.filter(levels::id.eq(matched_id));
|
query_params.push("levels.id = ?");
|
||||||
count_query = count_query.filter(levels::id.eq(matched_id))
|
query_params_bind.push(Box::new(id))
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
query = query.filter(levels::name.ilike(input.str.to_owned() + "%"));
|
query_params.push("levels.name LIKE ?");
|
||||||
count_query = count_query.filter(levels::name.ilike(input.str.to_owned() + "%"))
|
query_params_bind.push(Box::new(search_query.clone() + "%"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(1) = input.featured {
|
if let Some(1) = input.featured {
|
||||||
query = query.filter(levels::featured.eq(1));
|
query_params.push("featured = 1");
|
||||||
count_query = count_query.filter(levels::featured.eq(1))
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.original {
|
if let Some(1) = input.original {
|
||||||
query = query.filter(levels::original.is_null());
|
query_params.push("original IS NULL");
|
||||||
count_query = count_query.filter(levels::original.is_null())
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.coins {
|
if let Some(1) = input.coins {
|
||||||
query = query.filter(levels::rated_coins.eq(1).and(levels::coins.ne(0)));
|
query_params.push("rated_coins = 1 AND levels.coins != 0");
|
||||||
count_query = count_query.filter(levels::rated_coins.eq(1).and(levels::coins.ne(0)))
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.epic {
|
if let Some(1) = input.epic {
|
||||||
query = query.filter(levels::epic.eq(1));
|
query_params.push("epic = 1");
|
||||||
count_query = count_query.filter(levels::epic.eq(1))
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.uncompleted {
|
if let Some(1) = input.uncompleted {
|
||||||
match input.completedLevels.clone() {
|
match input.completedLevels.clone() {
|
||||||
Some(completed_levels) => {
|
Some(completed_levels) => {
|
||||||
let clean_levels: Vec<i32> = completed_levels[1..completed_levels.len() - 1].split(',')
|
let clean_levels: Vec<i64> = completed_levels[1..completed_levels.len() - 1].split(',')
|
||||||
.map(|s| s.parse::<i32>().expect("failed to parse i32"))
|
.map(|s| s.parse::<i64>().expect("failed to parse i64"))
|
||||||
.collect();
|
.collect();
|
||||||
query = query.filter(levels::id.ne_all(clean_levels.clone()));
|
let levels_str = clean_levels.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
|
||||||
count_query = count_query.filter(levels::id.ne_all(clean_levels))
|
query_params.push("levels.id NOT IN (?)");
|
||||||
|
query_params_bind.push(Box::new(levels_str));
|
||||||
},
|
},
|
||||||
None => return status::Custom(Status::Ok, "-1")
|
None => return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
|
@ -97,91 +133,92 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
if let Some(1) = input.onlyCompleted {
|
if let Some(1) = input.onlyCompleted {
|
||||||
match input.completedLevels.clone() {
|
match input.completedLevels.clone() {
|
||||||
Some(completed_levels) => {
|
Some(completed_levels) => {
|
||||||
let clean_levels: Vec<i32> = completed_levels[1..completed_levels.len() - 1].split(',')
|
let clean_levels: Vec<i64> = completed_levels[1..completed_levels.len() - 1].split(',')
|
||||||
.map(|s| s.parse::<i32>().expect("failed to parse i32"))
|
.map(|s| s.parse::<i64>().expect("failed to parse i64"))
|
||||||
.collect();
|
.collect();
|
||||||
query = query.filter(levels::id.eq_any(clean_levels.clone()));
|
let levels_str = clean_levels.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
|
||||||
count_query = count_query.filter(levels::id.eq_any(clean_levels))
|
query_params.push("levels.id IN (?)");
|
||||||
|
query_params_bind.push(Box::new(levels_str));
|
||||||
},
|
},
|
||||||
None => return status::Custom(Status::Ok, "-1")
|
None => return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(song_id) = input.song {
|
if let Some(song_id) = input.song {
|
||||||
if let Some(custom_song) = input.customSong {
|
if let Some(custom_song) = input.customSong {
|
||||||
query = query.filter(levels::song_id.eq(custom_song));
|
query_params.push("song_id = ?");
|
||||||
count_query = count_query.filter(levels::song_id.eq(custom_song))
|
query_params_bind.push(Box::new(custom_song));
|
||||||
} else {
|
} else {
|
||||||
query = query.filter(levels::song_id.eq(song_id));
|
query_params.push("song_id = ?");
|
||||||
count_query = count_query.filter(levels::song_id.eq(song_id));
|
query_params_bind.push(Box::new(song_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(1) = input.twoPlayer {
|
if let Some(1) = input.twoPlayer {
|
||||||
query = query.filter(levels::two_player.eq(1));
|
query_params.push("two_player = 1");
|
||||||
count_query = count_query.filter(levels::two_player.eq(1))
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.star {
|
if let Some(1) = input.star {
|
||||||
query = query.filter(levels::stars.is_not_null());
|
query_params.push("levels.stars IS NOT NULL");
|
||||||
count_query = count_query.filter(levels::stars.is_not_null())
|
|
||||||
}
|
}
|
||||||
if let Some(1) = input.noStar {
|
if let Some(1) = input.noStar {
|
||||||
query = query.filter(levels::stars.is_null());
|
query_params.push("levels.stars IS NULL");
|
||||||
count_query = count_query.filter(levels::stars.is_null())
|
|
||||||
}
|
}
|
||||||
if let Some(_gauntlet_id) = input.gauntlet {
|
if let Some(_gauntlet_id) = input.gauntlet {
|
||||||
unimplemented!("no gauntlet support")
|
unimplemented!("no gauntlet support")
|
||||||
}
|
}
|
||||||
if let Some(len) = input.len {
|
if let Some(len) = input.len {
|
||||||
query = query.filter(levels::length.eq(len));
|
query_params.push("levels.length = ?");
|
||||||
count_query = count_query.filter(levels::length.eq(len))
|
query_params_bind.push(Box::new(len));
|
||||||
}
|
}
|
||||||
if let Some(diff) = input.diff.clone() {
|
if let Some(diff) = input.diff.clone() {
|
||||||
if diff != "-" {
|
if diff != "-" {
|
||||||
match diff.as_str() {
|
match diff.as_str() {
|
||||||
"-1" => {
|
"-1" => {
|
||||||
query = query.filter(levels::difficulty.is_null().and(levels::community_difficulty.is_null()));
|
query_params.push("difficulty IS NULL AND community_difficulty IS NULL"); // NA
|
||||||
count_query = count_query.filter(levels::difficulty.is_null().and(levels::community_difficulty.is_null()))
|
|
||||||
},
|
},
|
||||||
"-2" => match input.demonFilter {
|
"-2" => match input.demonFilter {
|
||||||
Some(demon_filter) => {
|
Some(demon_filter) => {
|
||||||
match demon_filter {
|
match demon_filter {
|
||||||
1 => {
|
1 => {
|
||||||
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()));
|
query_params.push("demon_difficulty = ?");
|
||||||
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()))
|
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Easy.to_demon_difficulty()));
|
||||||
},
|
},
|
||||||
2 => {
|
2 => {
|
||||||
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()));
|
query_params.push("demon_difficulty = ?");
|
||||||
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()))
|
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Medium.to_demon_difficulty()));
|
||||||
},
|
},
|
||||||
3 => {
|
3 => {
|
||||||
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()));
|
query_params.push("demon_difficulty = ?");
|
||||||
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()))
|
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Hard.to_demon_difficulty()));
|
||||||
},
|
},
|
||||||
4 => {
|
4 => {
|
||||||
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()));
|
query_params.push("demon_difficulty = ?");
|
||||||
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()))
|
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Insane.to_demon_difficulty()));
|
||||||
},
|
},
|
||||||
5 => {
|
5 => {
|
||||||
query = query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()));
|
query_params.push("demon_difficulty = ?");
|
||||||
count_query = count_query.filter(levels::demon_difficulty.eq::<i32>(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()))
|
query_params_bind.push(Box::new(helpers::difficulty::DemonDifficulty::Extreme.to_demon_difficulty()));
|
||||||
},
|
},
|
||||||
_ => panic!("invalid demon filter!")
|
_ => panic!("invalid demon filter!")
|
||||||
}
|
}
|
||||||
query = query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()))));
|
query_params.push("difficulty = ? OR (difficulty IS NULL AND community_difficulty = ?)");
|
||||||
count_query = count_query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()))))
|
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()));
|
||||||
|
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Demon.to_star_difficulty()));
|
||||||
},
|
},
|
||||||
None => panic!("demon filter option with no demon filter argument")
|
None => panic!("demon filter option with no demon filter argument")
|
||||||
},
|
},
|
||||||
"-3" => {
|
"-3" => {
|
||||||
query = query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()))));
|
query_params.push("difficulty = ? OR (difficulty IS NULL AND community_difficulty = ?)");
|
||||||
count_query = count_query.filter(diesel::BoolExpressionMethods::or(levels::difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()), levels::difficulty.is_null().and(levels::community_difficulty.eq::<i32>(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()))))
|
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()));
|
||||||
|
query_params_bind.push(Box::new(helpers::difficulty::LevelDifficulty::Auto.to_star_difficulty()));
|
||||||
},
|
},
|
||||||
// easy, normal, hard, harder, insane
|
// easy, normal, hard, harder, insane
|
||||||
_ => {
|
_ => {
|
||||||
let diffs: Vec<i32> = diff.split(',')
|
let clean_diffs: Vec<i32> = diff.split(',')
|
||||||
.map(|v| v.parse::<i32>().expect("couldnt parse i32"))
|
.map(|v| v.parse::<i32>().expect("couldnt parse i32"))
|
||||||
.collect();
|
.collect();
|
||||||
query = query.filter(levels::difficulty.eq_any(diffs.clone()).or(levels::difficulty.is_null().and(levels::community_difficulty.eq_any(diffs.clone()))));
|
let diffs_str = clean_diffs.iter().map(|n| n.to_string()).collect::<Vec<String>>().join(", ");
|
||||||
count_query = count_query.filter(levels::difficulty.eq_any(diffs.clone()).or(levels::difficulty.is_null().and(levels::community_difficulty.eq_any(diffs))))
|
query_params.push("difficulty IN (?) OR (difficulty IS NULL AND community_difficulty IN (?))");
|
||||||
|
query_params_bind.push(Box::new(diffs_str.clone()));
|
||||||
|
query_params_bind.push(Box::new(diffs_str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,13 +228,11 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
match search_type {
|
match search_type {
|
||||||
// downloads
|
// downloads
|
||||||
1 => {
|
1 => {
|
||||||
query = query.order(levels::downloads.desc());
|
order = "levels.downloads DESC";
|
||||||
// count query order doesnt matter
|
|
||||||
},
|
},
|
||||||
// likes
|
// likes
|
||||||
2 => {
|
2 => {
|
||||||
query = query.order(levels::likes.desc());
|
order = "levels.likes DESC";
|
||||||
// count query order doesnt matter
|
|
||||||
},
|
},
|
||||||
// trending
|
// trending
|
||||||
3 => {
|
3 => {
|
||||||
|
@ -212,9 +247,9 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
5 => {
|
5 => {
|
||||||
if let Some(1) = input.local {
|
if let Some(1) = input.local {
|
||||||
if let Some(input_account_id) = input.accountID {
|
if let Some(input_account_id) = input.accountID {
|
||||||
let (user_id_val, _account_id_val): (i32, i32);
|
let (user_id_val, _account_id_val): (i64, i64);
|
||||||
|
|
||||||
match helpers::accounts::auth(input_account_id, input.password.clone(), input.gjp.clone(), input.gjp2.clone()) {
|
match helpers::accounts::auth(input_account_id, input.password.clone(), input.gjp.clone(), input.gjp2.clone()).await {
|
||||||
Ok((user_id_val_auth, account_id_val_auth)) => {
|
Ok((user_id_val_auth, account_id_val_auth)) => {
|
||||||
user_id_val = user_id_val_auth;
|
user_id_val = user_id_val_auth;
|
||||||
_account_id_val = account_id_val_auth;
|
_account_id_val = account_id_val_auth;
|
||||||
|
@ -222,37 +257,34 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
Err(_) => return status::Custom(Status::Ok, "-1")
|
Err(_) => return status::Custom(Status::Ok, "-1")
|
||||||
};
|
};
|
||||||
|
|
||||||
if user_id_val == input.str.parse::<i32>().expect("couldnt convert query input to i32") {
|
if user_id_val == search_query.parse::<i64>().expect("couldnt convert query input to i64") {
|
||||||
can_see_unlisted = true;
|
can_see_unlisted = true;
|
||||||
query = query.filter(levels::user_id.eq(user_id_val));
|
query_params.push("levels.user_id = ?");
|
||||||
count_query = count_query.filter(levels::user_id.eq(user_id_val))
|
query_params_bind.push(Box::new(user_id_val));
|
||||||
} else {
|
} else {
|
||||||
return status::Custom(Status::Ok, "-1")
|
return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let None = input.local {
|
if let None = input.local {
|
||||||
let user_id_val = input.str.parse::<i32>().expect("couldnt convert query input to i32");
|
let user_id_val = search_query.parse::<i64>().expect("couldnt convert query input to i64");
|
||||||
|
|
||||||
query = query.filter(levels::user_id.eq(user_id_val));
|
query_params.push("levels.user_id = ?");
|
||||||
count_query = count_query.filter(levels::user_id.eq(user_id_val))
|
query_params_bind.push(Box::new(user_id_val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// featured
|
// featured
|
||||||
// 17 is gdworld
|
// 17 is gdworld
|
||||||
6 | 17 => {
|
6 | 17 => {
|
||||||
query = query.filter(levels::featured.eq(1));
|
query_params.push("featured = 1");
|
||||||
count_query = count_query.filter(levels::featured.eq(1))
|
|
||||||
},
|
},
|
||||||
// epic / HoF
|
// epic / HoF
|
||||||
16 => {
|
16 => {
|
||||||
query = query.filter(levels::epic.eq(1));
|
query_params.push("epic = 1");
|
||||||
count_query = count_query.filter(levels::epic.eq(1))
|
|
||||||
},
|
},
|
||||||
// magic
|
// magic
|
||||||
7 => {
|
7 => {
|
||||||
query = query.filter(levels::objects.gt(4000));
|
query_params.push("objects > 4000");
|
||||||
count_query = count_query.filter(levels::objects.gt(4000))
|
|
||||||
},
|
},
|
||||||
// map packs 🙄😶
|
// map packs 🙄😶
|
||||||
10 | 19 => {
|
10 | 19 => {
|
||||||
|
@ -260,8 +292,7 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
},
|
},
|
||||||
// rated
|
// rated
|
||||||
11 => {
|
11 => {
|
||||||
query = query.filter(levels::stars.is_not_null());
|
query_params.push("levels.stars IS NOT NULL");
|
||||||
count_query = count_query.filter(levels::stars.is_not_null())
|
|
||||||
},
|
},
|
||||||
// followed
|
// followed
|
||||||
12 => {
|
12 => {
|
||||||
|
@ -286,72 +317,63 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
// default sort
|
// default sort
|
||||||
// 15 is gdworld
|
// 15 is gdworld
|
||||||
0 | 15 | _ => {
|
0 | 15 | _ => {
|
||||||
query = query.order(levels::likes.desc());
|
order = "likes DESC";
|
||||||
// count query order doesnt matter
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !can_see_unlisted {
|
if !can_see_unlisted {
|
||||||
query = query.filter(levels::unlisted.eq(0));
|
query_params.push("unlisted = 0");
|
||||||
count_query = count_query.filter(levels::unlisted.eq(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results: Vec<String> = [].to_vec();
|
let where_str = format!("WHERE ({})", query_params.join(" AND "));
|
||||||
let mut users: Vec<String> = [].to_vec();
|
let query_base = format!("FROM levels JOIN users ON levels.user_id = users.id {} ORDER BY {}", where_str, order);
|
||||||
let mut songs: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
let mut hash_data: Vec<(i32, i32, bool)> = [].to_vec();
|
let mut query_builder: QueryBuilder<Sqlite> = QueryBuilder::new(&format!("SELECT levels.id, levels.name, users.id as user_id, levels.description, levels.original, levels.game_version, levels.requested_stars, levels.version, levels.song_id, levels.length, levels.objects, levels.coins, levels.has_ldm, levels.two_player, levels.downloads, levels.likes, levels.difficulty, levels.community_difficulty, levels.demon_difficulty, levels.stars, levels.featured, levels.epic, levels.rated_coins, users.username as user_username, users.udid as user_udid, users.account_id as user_account_id, users.registered as user_registered, editor_time, editor_time_copies {}", query_base));
|
||||||
|
let mut query = query_builder.build_query_as::<DynQuery>();
|
||||||
|
|
||||||
|
for param in query_params_bind {
|
||||||
|
query = query.bind(param.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut results: Vec<String> = vec![];
|
||||||
|
let mut users: Vec<String> = vec![];
|
||||||
|
let mut songs: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let mut hash_data: Vec<(i64, i32, bool)> = vec![];
|
||||||
|
|
||||||
|
let count: i64 = sqlx::query_scalar(&format!("SELECT COUNT(*) {}", query_base))
|
||||||
|
.fetch_one(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error getting level count");
|
||||||
|
|
||||||
|
// for goop in {
|
||||||
|
// query
|
||||||
|
// .fetch_all(&mut connection)
|
||||||
|
// .await
|
||||||
|
// .expect("error loading levels")
|
||||||
|
// } {
|
||||||
|
// println!("lvl id: {:?}", goop.id);
|
||||||
|
// println!("user id: {:?}", goop.user_id);
|
||||||
|
// }
|
||||||
|
|
||||||
for result in {
|
for result in {
|
||||||
query
|
query
|
||||||
.order(levels::created_at.desc())
|
.fetch_all(&mut connection)
|
||||||
.offset(input.page * 10)
|
.await
|
||||||
.limit(10)
|
.expect("error loading levels")
|
||||||
.get_results::<Level, >(connection)
|
|
||||||
.expect("fatal error loading levels")
|
|
||||||
} {
|
} {
|
||||||
let user: User = users::table.find(result.user_id).get_result::<User, >(connection).expect("couldnt get user from lvl");
|
let set_difficulty = result.difficulty.map(helpers::difficulty::LevelDifficulty::new);
|
||||||
let level: Level = result;
|
let community_difficulty = result.community_difficulty.map(helpers::difficulty::LevelDifficulty::new);
|
||||||
|
let difficulty = set_difficulty.or(community_difficulty);
|
||||||
let set_difficulty = match level.difficulty {
|
let demon_difficulty = result.demon_difficulty.map(helpers::difficulty::DemonDifficulty::new);
|
||||||
Some(diff) => {
|
|
||||||
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
let community_difficulty = match level.community_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
Some(helpers::difficulty::LevelDifficulty::new(diff))
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
let difficulty = match set_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
Some(diff)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
match community_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
Some(diff)
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let demon_difficulty = match level.demon_difficulty {
|
|
||||||
Some(diff) => {
|
|
||||||
Some(helpers::difficulty::DemonDifficulty::new(diff))
|
|
||||||
},
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
|
|
||||||
results.push(helpers::format::format(hashmap! {
|
results.push(helpers::format::format(hashmap! {
|
||||||
1 => level.id.to_string(),
|
1 => result.id.to_string(),
|
||||||
2 => level.name,
|
2 => result.name,
|
||||||
3 => general_purpose::URL_SAFE.encode(level.description),
|
3 => general_purpose::URL_SAFE.encode(result.description),
|
||||||
5 => level.version.to_string(),
|
5 => result.version.to_string(),
|
||||||
6 => user.id.to_string(),
|
6 => result.user_id.to_string(),
|
||||||
// this argument is weird. its the "difficulty divisor"
|
// this argument is weird. its the "difficulty divisor"
|
||||||
// used to be vote count but yeah
|
// used to be vote count but yeah
|
||||||
8 => 10.to_string(),
|
8 => 10.to_string(),
|
||||||
|
@ -359,12 +381,12 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
Some(diff) => diff.to_star_difficulty(),
|
Some(diff) => diff.to_star_difficulty(),
|
||||||
None => 0
|
None => 0
|
||||||
} * 10).to_string(),
|
} * 10).to_string(),
|
||||||
10 => level.downloads.to_string(),
|
10 => result.downloads.to_string(),
|
||||||
12 => (if level.song_id < 50 { level.song_id } else { 0 }).to_string(),
|
12 => (if result.song_id < 50 { result.song_id } else { 0 }).to_string(),
|
||||||
13 => level.game_version.to_string(),
|
13 => result.game_version.to_string(),
|
||||||
14 => level.likes.to_string(),
|
14 => result.likes.to_string(),
|
||||||
16 => (-level.likes).to_string(),
|
16 => (-result.likes).to_string(),
|
||||||
15 => level.length.to_string(),
|
15 => result.length.to_string(),
|
||||||
17 => match difficulty {
|
17 => match difficulty {
|
||||||
Some(diff) => {
|
Some(diff) => {
|
||||||
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
if diff == helpers::difficulty::LevelDifficulty::Demon {
|
||||||
|
@ -375,8 +397,8 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
},
|
},
|
||||||
None => 0
|
None => 0
|
||||||
}.to_string(),
|
}.to_string(),
|
||||||
18 => (if let Some(stars) = level.stars { stars } else { 0 }).to_string(),
|
18 => (if let Some(stars) = result.stars { stars } else { 0 }).to_string(),
|
||||||
19 => level.featured.to_string(),
|
19 => result.featured.to_string(),
|
||||||
25 => match difficulty {
|
25 => match difficulty {
|
||||||
Some(diff) => {
|
Some(diff) => {
|
||||||
if diff == helpers::difficulty::LevelDifficulty::Auto {
|
if diff == helpers::difficulty::LevelDifficulty::Auto {
|
||||||
|
@ -387,54 +409,49 @@ pub fn get_levels(input: Form<FormGetLevels>) -> status::Custom<&'static str> {
|
||||||
},
|
},
|
||||||
None => 0
|
None => 0
|
||||||
}.to_string(),
|
}.to_string(),
|
||||||
30 => (if let Some(original) = level.original { original } else { 0 }).to_string(),
|
30 => (if let Some(original) = result.original { original } else { 0 }).to_string(),
|
||||||
31 => level.two_player.to_string(),
|
31 => result.two_player.to_string(),
|
||||||
35 => (if level.song_id >= 50 { level.song_id } else { 0 }).to_string(),
|
35 => (if result.song_id >= 50 { result.song_id } else { 0 }).to_string(),
|
||||||
37 => level.coins.to_string(),
|
37 => result.coins.to_string(),
|
||||||
38 => level.rated_coins.to_string(),
|
38 => result.rated_coins.to_string(),
|
||||||
39 => (if let Some(requested_stars) = level.requested_stars { requested_stars } else { 0 }).to_string(),
|
39 => (if let Some(requested_stars) = result.requested_stars { requested_stars } else { 0 }).to_string(),
|
||||||
40 => level.has_ldm.to_string(),
|
40 => result.has_ldm.to_string(),
|
||||||
42 => level.epic.to_string(),
|
42 => result.epic.to_string(),
|
||||||
43 => match demon_difficulty {
|
43 => match demon_difficulty {
|
||||||
Some(diff) => {
|
Some(diff) => {
|
||||||
diff
|
diff
|
||||||
},
|
},
|
||||||
None => helpers::difficulty::DemonDifficulty::Hard
|
None => helpers::difficulty::DemonDifficulty::Hard
|
||||||
}.to_demon_difficulty().to_string(),
|
}.to_demon_difficulty().to_string(),
|
||||||
45 => level.objects.to_string(),
|
45 => result.objects.to_string(),
|
||||||
46 => level.editor_time.to_string(),
|
46 => result.editor_time.to_string(),
|
||||||
47 => level.editor_time_copies.to_string()
|
47 => result.editor_time_copies.to_string()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
users.push(format!("{}:{}:{}", user.id, user.username, {
|
users.push(format!("{}:{}:{}", result.user_id, result.user_username, {
|
||||||
if user.registered == 1 {
|
if result.user_registered == 1 {
|
||||||
user.account_id.expect("wtf? registered user with no account id.").to_string()
|
result.user_account_id.expect("wtf? registered user with no account id.").to_string()
|
||||||
} else {
|
} else {
|
||||||
user.udid.expect("wtf? unregistered user with no udid.")
|
result.user_udid.expect("wtf? unregistered user with no udid.")
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
hash_data.push((
|
hash_data.push((
|
||||||
level.id,
|
result.id,
|
||||||
{ if let Some(stars) = level.stars {
|
{ if let Some(stars) = result.stars {
|
||||||
stars
|
stars
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}},
|
}},
|
||||||
{ if let 1 = level.rated_coins {
|
{ if let 1 = result.rated_coins {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}}
|
}}
|
||||||
));
|
));
|
||||||
};
|
}
|
||||||
|
|
||||||
let level_count = count_query
|
let search_meta = format!("{}:{}:{}", count, page_offset, 10);
|
||||||
.count()
|
|
||||||
.get_result::<i64, >(connection)
|
|
||||||
.expect("failed to get count of levels");
|
|
||||||
|
|
||||||
let search_meta = format!("{}:{}:{}", level_count, input.page * 10, 10);
|
|
||||||
|
|
||||||
let response = vec![results.join("|"), users.join("|"), songs.join("|"), search_meta, helpers::encryption::gen_multi(hash_data)].join("#");
|
let response = vec![results.join("|"), users.join("|"), songs.join("|"), search_meta, helpers::encryption::gen_multi(hash_data)].join("#");
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -14,7 +12,7 @@ use crate::db;
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
pub struct FormUploadLevel {
|
pub struct FormUploadLevel {
|
||||||
accountID: i32,
|
accountID: i64,
|
||||||
|
|
||||||
gjp: Option<String>,
|
gjp: Option<String>,
|
||||||
gjp2: Option<String>,
|
gjp2: Option<String>,
|
||||||
|
@ -24,7 +22,7 @@ pub struct FormUploadLevel {
|
||||||
audioTrack: i32,
|
audioTrack: i32,
|
||||||
levelName: String,
|
levelName: String,
|
||||||
levelDesc: String,
|
levelDesc: String,
|
||||||
levelID: i32,
|
levelID: i64,
|
||||||
levelVersion: i32,
|
levelVersion: i32,
|
||||||
levelInfo: String,
|
levelInfo: String,
|
||||||
levelString: String,
|
levelString: String,
|
||||||
|
@ -40,14 +38,14 @@ pub struct FormUploadLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/uploadGJLevel21.php", data = "<input>")]
|
#[post("/uploadGJLevel21.php", data = "<input>")]
|
||||||
pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str> {
|
pub async fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
// account verification
|
// account verification
|
||||||
let (user_id_val, _account_id_val): (i32, i32);
|
let (user_id_val, _account_id_val): (i64, i64);
|
||||||
|
|
||||||
// password argument is used for the level, so
|
// password argument is used for the level, so
|
||||||
match helpers::accounts::auth(input.accountID.clone(), None, input.gjp.clone(), input.gjp2.clone()) {
|
match helpers::accounts::auth(input.accountID.clone(), None, input.gjp.clone(), input.gjp2.clone()).await {
|
||||||
Ok((user_id, account_id)) => {
|
Ok((user_id, account_id)) => {
|
||||||
user_id_val = user_id;
|
user_id_val = user_id;
|
||||||
_account_id_val = account_id;
|
_account_id_val = account_id;
|
||||||
|
@ -91,112 +89,93 @@ pub fn upload_level(input: Form<FormUploadLevel>) -> status::Custom<&'static str
|
||||||
let objects_val = level_objects.len();
|
let objects_val = level_objects.len();
|
||||||
let two_player_val = if inner_level_string.get("kA10").unwrap_or(&String::from("0")).parse::<i32>().expect("kA10 not int") == 1 { 1 } else { 0 };
|
let two_player_val = if inner_level_string.get("kA10").unwrap_or(&String::from("0")).parse::<i32>().expect("kA10 not int") == 1 { 1 } else { 0 };
|
||||||
let level_length_val = helpers::levels::secs_to_time(level_length_secs);
|
let level_length_val = helpers::levels::secs_to_time(level_length_secs);
|
||||||
|
|
||||||
// blocking coins
|
// blocking coins
|
||||||
if coins_val > 3 {
|
if coins_val > 3 {
|
||||||
return status::Custom(Status::Ok, "-1")
|
return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// too many objects
|
// too many objects
|
||||||
if config::config_get_with_default("levels.max_objects", 0) != 0 && objects_val > config::config_get_with_default("levels.max_objects", 0) {
|
let max_objects = config::config_get_with_default("levels.max_objects", 0) as usize;
|
||||||
|
if max_objects != 0 && objects_val > max_objects {
|
||||||
return status::Custom(Status::Ok, "-1")
|
return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// forbidden object checking
|
// forbidden object checking
|
||||||
if let Some(_forbidden_object) = level_objects.iter().find(|obj| config::config_get_with_default("levels.blocklist", Vec::new() as Vec<i32>).contains(&obj.id())) {
|
if let Some(_obj) = level_objects.iter().find(|obj| config::config_get_with_default("levels.blocklist", Vec::new() as Vec<i32>).contains(&obj.id())) {
|
||||||
return status::Custom(Status::Ok, "-1")
|
return status::Custom(Status::Ok, "-1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ACE vulnerability check
|
|
||||||
if let Some(_ace_object) = level_objects.iter().find(|obj| obj.item_block_id() < Some(0) || obj.item_block_id() > Some(1100)) {
|
|
||||||
return status::Custom(Status::Ok, "-1")
|
|
||||||
}
|
|
||||||
|
|
||||||
// data base 🤣😁
|
|
||||||
|
|
||||||
{
|
// ACE vulnerability check
|
||||||
use db::models::{Level, NewLevel};
|
for obj in level_objects.iter().filter(|obj| obj.item_block_id().is_some()) {
|
||||||
use db::schema::levels::dsl::*;
|
if obj.item_block_id() < Some(0) || obj.item_block_id() > Some(1100) {
|
||||||
|
return status::Custom(Status::Ok, "-1");
|
||||||
if levels
|
}
|
||||||
.filter(id.eq(input.levelID))
|
|
||||||
.count()
|
|
||||||
.get_result::<i64>(connection)
|
|
||||||
.expect("couldnt get count of levels") > 0 {
|
|
||||||
// update level
|
|
||||||
let level_user_id = levels
|
|
||||||
.filter(id.eq(input.levelID))
|
|
||||||
.select(user_id)
|
|
||||||
.get_result::<i32>(connection)
|
|
||||||
.expect("couldnt query levels");
|
|
||||||
|
|
||||||
if level_user_id != user_id_val {
|
|
||||||
return status::Custom(Status::Ok, "-1")
|
|
||||||
}
|
|
||||||
|
|
||||||
let updated_level = diesel::update(levels)
|
|
||||||
.filter(id.eq(input.levelID))
|
|
||||||
.set((
|
|
||||||
description.eq(description_val.chars().take(140).collect::<String>()),
|
|
||||||
password.eq(input.password.clone()),
|
|
||||||
requested_stars.eq(match input.requestedStars {
|
|
||||||
Some(requested_stars_val) => requested_stars_val.clamp(0, 10),
|
|
||||||
None => 0
|
|
||||||
}),
|
|
||||||
version.eq(input.levelVersion),
|
|
||||||
extra_data.eq(extra_string.as_bytes().to_owned()),
|
|
||||||
level_info.eq(input.levelInfo.clone().into_bytes()),
|
|
||||||
editor_time.eq(input.wt.unwrap_or(0)),
|
|
||||||
editor_time_copies.eq(input.wt2.unwrap_or(0)),
|
|
||||||
song_id.eq(song_id_val),
|
|
||||||
length.eq(level_length_val),
|
|
||||||
objects.eq(objects_val as i32),
|
|
||||||
coins.eq(coins_val as i32),
|
|
||||||
has_ldm.eq(input.ldm.unwrap_or(0).clamp(0, 1)),
|
|
||||||
two_player.eq(two_player_val)
|
|
||||||
))
|
|
||||||
.get_result::<Level, >(connection)
|
|
||||||
.expect("failed to update level");
|
|
||||||
|
|
||||||
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data"), updated_level.id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
|
||||||
|
|
||||||
return status::Custom(Status::Ok, Box::leak(input.levelID.to_string().into_boxed_str()))
|
|
||||||
} else {
|
|
||||||
// upload level
|
|
||||||
let new_level = NewLevel {
|
|
||||||
name: helpers::clean::clean_basic(&input.levelName).chars().take(20).collect(),
|
|
||||||
user_id: user_id_val,
|
|
||||||
description: description_val.chars().take(140).collect(),
|
|
||||||
original: input.original,
|
|
||||||
game_version: input.gameVersion,
|
|
||||||
binary_version: input.binaryVersion.unwrap_or(0),
|
|
||||||
password: input.password.clone(),
|
|
||||||
requested_stars: match input.requestedStars {
|
|
||||||
Some(requested_stars_val) => requested_stars_val.clamp(0, 10),
|
|
||||||
None => 0
|
|
||||||
},
|
|
||||||
unlisted: input.unlisted.unwrap_or(0).clamp(0, 1),
|
|
||||||
version: input.levelVersion,
|
|
||||||
extra_data: extra_string.as_bytes().to_owned(),
|
|
||||||
level_info: input.levelInfo.clone().into_bytes(),
|
|
||||||
editor_time: input.wt.unwrap_or(0),
|
|
||||||
editor_time_copies: input.wt2.unwrap_or(0),
|
|
||||||
song_id: song_id_val,
|
|
||||||
length: level_length_val,
|
|
||||||
objects: objects_val as i32,
|
|
||||||
coins: coins_val as i32,
|
|
||||||
has_ldm: input.ldm.unwrap_or(0).clamp(0, 1),
|
|
||||||
two_player: two_player_val
|
|
||||||
};
|
|
||||||
|
|
||||||
let inserted_level = diesel::insert_into(levels)
|
|
||||||
.values(&new_level)
|
|
||||||
.get_result::<Level, >(connection)
|
|
||||||
.expect("failed to insert level");
|
|
||||||
|
|
||||||
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data"), inserted_level.id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
|
||||||
|
|
||||||
return status::Custom(Status::Ok, "1")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sqlx::query_scalar!("SELECT COUNT(*) FROM levels WHERE id = ?", input.levelID)
|
||||||
|
.fetch_one(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error getting level count") > 0 {
|
||||||
|
// update level
|
||||||
|
|
||||||
|
let level_user_id = sqlx::query!("SELECT user_id FROM levels WHERE id = ?", input.levelID)
|
||||||
|
.fetch_one(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error getting level user id")
|
||||||
|
.user_id;
|
||||||
|
|
||||||
|
if level_user_id != user_id_val {
|
||||||
|
return status::Custom(Status::Ok, "-1")
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_description = description_val.chars().take(140).collect::<String>();
|
||||||
|
let new_password = input.password.clone();
|
||||||
|
let new_requested_stars = match input.requestedStars { Some(requested_stars_val) => requested_stars_val.clamp(0, 10), None => 0 };
|
||||||
|
let new_extra_string = extra_string.as_bytes().to_owned();
|
||||||
|
let new_level_info = input.levelInfo.clone().into_bytes();
|
||||||
|
let new_editor_time = input.wt.unwrap_or(0);
|
||||||
|
let new_editor_time_copies = input.wt2.unwrap_or(0);
|
||||||
|
let new_objects = objects_val as i64;
|
||||||
|
let new_coins = coins_val as i64;
|
||||||
|
let new_ldm = input.ldm.unwrap_or(0).clamp(0, 1);
|
||||||
|
|
||||||
|
let updated_level = sqlx::query!("UPDATE levels SET description = ?, password = ?, requested_stars = ?, version = ?, extra_data = ?, level_info = ?, editor_time = ?, editor_time_copies = ?, song_id = ?, length = ?, objects = ?, coins = ?, has_ldm = ?, two_player = ? WHERE id = ?",new_description, new_password, new_requested_stars, input.levelVersion, new_extra_string, new_level_info, new_editor_time, new_editor_time_copies, song_id_val, level_length_val, new_objects, new_coins, new_ldm, two_player_val, input.levelID)
|
||||||
|
.execute(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error updating level");
|
||||||
|
|
||||||
|
let updated_level_id = updated_level.last_insert_rowid();
|
||||||
|
|
||||||
|
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), updated_level_id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
||||||
|
|
||||||
|
return status::Custom(Status::Ok, Box::leak(input.levelID.to_string().into_boxed_str()))
|
||||||
|
} else {
|
||||||
|
// insert level
|
||||||
|
|
||||||
|
let new_name = helpers::clean::clean_basic(&input.levelName).chars().take(20).collect::<String>();
|
||||||
|
let new_description = description_val.chars().take(140).collect::<String>();
|
||||||
|
let new_binary_version = input.binaryVersion.unwrap_or(0);
|
||||||
|
let new_password = input.password.clone();
|
||||||
|
let new_requested_stars = match input.requestedStars { Some(requested_stars_val) => requested_stars_val.clamp(0, 10), None => 0 };
|
||||||
|
let new_unlisted = input.unlisted.unwrap_or(0).clamp(0, 1);
|
||||||
|
let new_extra_string = extra_string.as_bytes().to_owned();
|
||||||
|
let new_level_info = input.levelInfo.clone().into_bytes();
|
||||||
|
let new_editor_time = input.wt.unwrap_or(0);
|
||||||
|
let new_editor_time_copies = input.wt2.unwrap_or(0);
|
||||||
|
let new_objects = objects_val as i64;
|
||||||
|
let new_coins = coins_val as i64;
|
||||||
|
let new_ldm = input.ldm.unwrap_or(0).clamp(0, 1);
|
||||||
|
|
||||||
|
let inserted_level = sqlx::query!("INSERT INTO levels (name, user_id, description, original, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new_name, user_id_val, new_description, input.original, input.gameVersion, new_binary_version, new_password, new_requested_stars, new_unlisted, input.levelVersion, new_extra_string, new_level_info, new_editor_time, new_editor_time_copies, song_id_val, level_length_val, new_objects, new_coins, new_ldm, two_player_val)
|
||||||
|
.execute(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error inserting level");
|
||||||
|
|
||||||
|
let inserted_level_id = inserted_level.last_insert_rowid();
|
||||||
|
|
||||||
|
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), inserted_level_id), general_purpose::URL_SAFE.decode(input.levelString.clone()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
||||||
|
|
||||||
|
return status::Custom(Status::Ok, Box::leak(inserted_level_id.to_string().into_boxed_str()))
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,8 +2,6 @@ use rocket::form::Form;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::helpers;
|
use crate::helpers;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
|
@ -14,31 +12,21 @@ pub struct FormGetUsers {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/getGJUsers20.php", data = "<input>")]
|
#[post("/getGJUsers20.php", data = "<input>")]
|
||||||
pub fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
|
pub async fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
// query users
|
let username = input.str.to_owned() + "%";
|
||||||
use db::schema::users::dsl::*;
|
let offset = input.page * 10;
|
||||||
use db::models::User;
|
|
||||||
|
|
||||||
let mut query_users = users.into_boxed();
|
let query_results = sqlx::query!("SELECT * FROM users WHERE id = ? OR username LIKE ? ORDER BY stars DESC LIMIT 10 OFFSET ?", input.str, username, offset)
|
||||||
|
.fetch_all(&mut connection)
|
||||||
match input.str.parse::<i32>() {
|
.await
|
||||||
Ok(id_value) => query_users = query_users.filter(id.eq(id_value)),
|
.expect("Fatal error loading users");
|
||||||
Err(_) => query_users = query_users.filter(username.ilike(input.str.to_owned() + "%"))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut results: Vec<String> = vec![];
|
let mut results: Vec<String> = vec![];
|
||||||
|
|
||||||
for result in {
|
for result in query_results {
|
||||||
query_users
|
let user = result;
|
||||||
.order(stars.desc())
|
|
||||||
.offset(input.page * 10)
|
|
||||||
.limit(10)
|
|
||||||
.get_results::<User, >(connection)
|
|
||||||
.expect("Fatal error loading users")
|
|
||||||
} {
|
|
||||||
let user: User = result;
|
|
||||||
|
|
||||||
let formatted_result = helpers::format::format(hashmap! {
|
let formatted_result = helpers::format::format(hashmap! {
|
||||||
1 => user.username,
|
1 => user.username,
|
||||||
|
@ -75,22 +63,15 @@ pub fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
|
||||||
results.push(formatted_result)
|
results.push(formatted_result)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut query_users_count = users.into_boxed();
|
let amount = sqlx::query_scalar!("SELECT COUNT(*) FROM users WHERE id = ? OR username LIKE ?", input.str, username)
|
||||||
|
.fetch_one(&mut connection)
|
||||||
match input.str.parse::<i32>() {
|
.await
|
||||||
Ok(id_value) => query_users_count = query_users_count.filter(id.eq(id_value)),
|
.expect("error loading users");
|
||||||
Err(_) => query_users_count = query_users_count.filter(username.ilike(input.str.to_owned() + "%"))
|
|
||||||
};
|
|
||||||
|
|
||||||
let amount = query_users_count
|
|
||||||
.count()
|
|
||||||
.get_result::<i64>(connection)
|
|
||||||
.expect("error querying user count");
|
|
||||||
|
|
||||||
let response = if results.is_empty() {
|
let response = if results.is_empty() {
|
||||||
String::from("-1")
|
String::from("-1")
|
||||||
} else {
|
} else {
|
||||||
vec![results.join("|"), format!("{}:{}:10", amount, input.page * 10)].join("#")
|
vec![results.join("|"), format!("{}:{}:10", amount, offset)].join("#")
|
||||||
};
|
};
|
||||||
|
|
||||||
return status::Custom(Status::Ok, Box::leak(response.into_boxed_str()))
|
return status::Custom(Status::Ok, Box::leak(response.into_boxed_str()))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use diesel::prelude::*;
|
|
||||||
use password_auth::verify_password;
|
use password_auth::verify_password;
|
||||||
|
|
||||||
use crate::{db, helpers};
|
use crate::db;
|
||||||
|
use crate::helpers;
|
||||||
|
|
||||||
// returns userid, accountid
|
// returns userid, accountid
|
||||||
pub enum AuthError {
|
pub enum AuthError {
|
||||||
|
@ -9,39 +9,36 @@ pub enum AuthError {
|
||||||
AccountNotFound
|
AccountNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auth(account_id: i32, password_val: Option<String>, gjp_val: Option<String>, gjp2_val: Option<String>) -> Result<(i32, i32), AuthError> {
|
pub async fn auth(account_id: i64, password_val: Option<String>, gjp_val: Option<String>, gjp2_val: Option<String>) -> Result<(i64, i64), AuthError> {
|
||||||
use db::schema::accounts::dsl::*;
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
let connection = &mut db::establish_connection_pg();
|
let query_result = sqlx::query!("SELECT password, gjp2 FROM accounts WHERE id = ?", account_id)
|
||||||
|
.fetch_one(connection)
|
||||||
let query_result = accounts
|
.await;
|
||||||
.select((password, gjp2))
|
|
||||||
.filter(id.eq(account_id))
|
|
||||||
.get_result::<(String, String)>(connection);
|
|
||||||
|
|
||||||
match query_result {
|
match query_result {
|
||||||
Ok((
|
Ok(result) => {
|
||||||
password_queried_val,
|
let password_queried_val = result.password;
|
||||||
gjp2_queried_val
|
let gjp2_queried_val = result.gjp2;
|
||||||
)) => {
|
|
||||||
match password_val {
|
match password_val {
|
||||||
Some(password_val) => {
|
Some(password_val) => {
|
||||||
match verify_password(password_val, &password_queried_val) {
|
match verify_password(password_val, &password_queried_val) {
|
||||||
Ok(_) => return Ok((get_user_id_from_account_id(account_id), account_id)),
|
Ok(_) => return Ok((get_user_id_from_account_id(account_id).await, account_id)),
|
||||||
Err(_) => return Err(AuthError::WrongPassword)
|
Err(_) => return Err(AuthError::WrongPassword)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => match gjp_val {
|
None => match gjp_val {
|
||||||
Some(gjp_val) => {
|
Some(gjp_val) => {
|
||||||
match verify_password(helpers::encryption::decode_gjp(gjp_val), &password_queried_val) {
|
match verify_password(helpers::encryption::decode_gjp(gjp_val), &password_queried_val) {
|
||||||
Ok(_) => return Ok((get_user_id_from_account_id(account_id), account_id)),
|
Ok(_) => return Ok((get_user_id_from_account_id(account_id).await, account_id)),
|
||||||
Err(_) => return Err(AuthError::WrongPassword)
|
Err(_) => return Err(AuthError::WrongPassword)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => match gjp2_val {
|
None => match gjp2_val {
|
||||||
Some(gjp2_val) => {
|
Some(gjp2_val) => {
|
||||||
match verify_password(gjp2_val, &gjp2_queried_val) {
|
match verify_password(gjp2_val, &gjp2_queried_val) {
|
||||||
Ok(_) => return Ok((get_user_id_from_account_id(account_id), account_id)),
|
Ok(_) => return Ok((get_user_id_from_account_id(account_id).await, account_id)),
|
||||||
Err(_) => return Err(AuthError::WrongPassword)
|
Err(_) => return Err(AuthError::WrongPassword)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -56,16 +53,14 @@ pub fn auth(account_id: i32, password_val: Option<String>, gjp_val: Option<Strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_user_id_from_account_id(ext_id: i32) -> i32 {
|
pub async fn get_user_id_from_account_id(ext_id: i64) -> i64 {
|
||||||
use db::schema::users::dsl::*;
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
let connection = &mut db::establish_connection_pg();
|
let user_id = sqlx::query!("SELECT id FROM users WHERE account_id = ?", ext_id)
|
||||||
|
.fetch_one(connection)
|
||||||
let user_id = users
|
.await
|
||||||
.filter(udid.eq(ext_id.to_string()).or(account_id.eq(ext_id)))
|
.expect("no user associated with account id??")
|
||||||
.select(id)
|
.id;
|
||||||
.get_result::<i32>(connection)
|
|
||||||
.expect("No user associated with account?!?!?");
|
|
||||||
|
|
||||||
return user_id
|
return user_id
|
||||||
}
|
}
|
|
@ -8,4 +8,9 @@ pub fn clean_no_space(string: &str) -> String {
|
||||||
pub fn clean_basic(string: &str) -> String {
|
pub fn clean_basic(string: &str) -> String {
|
||||||
let regex = Regex::new(r"[^A-Za-z0-9\-_ ]").unwrap();
|
let regex = Regex::new(r"[^A-Za-z0-9\-_ ]").unwrap();
|
||||||
return regex.replace_all(string, "").to_string();
|
return regex.replace_all(string, "").to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_char(string: &str) -> String {
|
||||||
|
let regex = Regex::new(r"[^A-Za-z0-9 ]").unwrap();
|
||||||
|
return regex.replace_all(string, "").to_string();
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ pub enum LevelDifficulty {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LevelDifficulty {
|
impl LevelDifficulty {
|
||||||
pub fn new(value: i32) -> LevelDifficulty {
|
pub fn new(value: i64) -> LevelDifficulty {
|
||||||
match value {
|
match value {
|
||||||
0 => LevelDifficulty::Auto,
|
0 => LevelDifficulty::Auto,
|
||||||
1 => LevelDifficulty::Easy,
|
1 => LevelDifficulty::Easy,
|
||||||
|
@ -23,7 +23,7 @@ impl LevelDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(self) -> i32 {
|
pub fn value(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
LevelDifficulty::Auto => 0,
|
LevelDifficulty::Auto => 0,
|
||||||
LevelDifficulty::Easy => 1,
|
LevelDifficulty::Easy => 1,
|
||||||
|
@ -35,7 +35,7 @@ impl LevelDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_star_difficulty(self) -> i32 {
|
pub fn to_star_difficulty(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
LevelDifficulty::Auto => 5,
|
LevelDifficulty::Auto => 5,
|
||||||
LevelDifficulty::Easy => 1,
|
LevelDifficulty::Easy => 1,
|
||||||
|
@ -47,7 +47,7 @@ impl LevelDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stars_to_diff(stars: i32) -> Self {
|
pub fn stars_to_diff(stars: i64) -> Self {
|
||||||
match stars {
|
match stars {
|
||||||
1 => LevelDifficulty::Auto,
|
1 => LevelDifficulty::Auto,
|
||||||
2 => LevelDifficulty::Easy,
|
2 => LevelDifficulty::Easy,
|
||||||
|
@ -70,7 +70,7 @@ pub enum DemonDifficulty {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DemonDifficulty {
|
impl DemonDifficulty {
|
||||||
pub fn new(value: i32) -> DemonDifficulty {
|
pub fn new(value: i64) -> DemonDifficulty {
|
||||||
match value {
|
match value {
|
||||||
0 => DemonDifficulty::Easy,
|
0 => DemonDifficulty::Easy,
|
||||||
1 => DemonDifficulty::Medium,
|
1 => DemonDifficulty::Medium,
|
||||||
|
@ -81,7 +81,7 @@ impl DemonDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(self) -> i32 {
|
pub fn value(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
DemonDifficulty::Easy => 0,
|
DemonDifficulty::Easy => 0,
|
||||||
DemonDifficulty::Medium => 1,
|
DemonDifficulty::Medium => 1,
|
||||||
|
@ -91,7 +91,7 @@ impl DemonDifficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_demon_difficulty(self) -> i32 {
|
pub fn to_demon_difficulty(self) -> i64 {
|
||||||
match self {
|
match self {
|
||||||
DemonDifficulty::Easy => 3,
|
DemonDifficulty::Easy => 3,
|
||||||
DemonDifficulty::Medium => 4,
|
DemonDifficulty::Medium => 4,
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn decode_gjp(gjp: String) -> String {
|
||||||
return xor_decoded
|
return xor_decoded
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_multi(level_hash_data: Vec<(i32, i32, bool)>) -> String {
|
pub fn gen_multi(level_hash_data: Vec<(i64, i32, bool)>) -> String {
|
||||||
let mut input_str = String::new();
|
let mut input_str = String::new();
|
||||||
|
|
||||||
for (_index, val) in level_hash_data.iter().enumerate() {
|
for (_index, val) in level_hash_data.iter().enumerate() {
|
||||||
|
|
|
@ -1,57 +1,51 @@
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
pub const REUPLOAD_USER_NAME: &str = "reupload";
|
pub const REUPLOAD_USER_NAME: &str = "reupload";
|
||||||
|
|
||||||
pub static REUPLOAD_ACCOUNT_ID: RwLock<i32> = RwLock::new(0);
|
pub static REUPLOAD_ACCOUNT_ID: RwLock<i64> = RwLock::new(0);
|
||||||
|
|
||||||
pub fn init() {
|
pub async fn init() {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let mut connection = db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
use db::schema::{accounts, users};
|
let result = sqlx::query!("SELECT id FROM accounts WHERE username = ?", REUPLOAD_USER_NAME)
|
||||||
use db::models::{Account, NewAccount, User, NewUser};
|
.fetch_one(&mut connection)
|
||||||
|
.await;
|
||||||
|
|
||||||
match accounts::table
|
match result {
|
||||||
.filter(accounts::username.eq(REUPLOAD_USER_NAME))
|
Ok(result) => {
|
||||||
.select(accounts::id)
|
let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock");
|
||||||
.get_result::<i32, >(connection) {
|
*write_lock = result.id;
|
||||||
Ok(reupload_acc_id) => {
|
},
|
||||||
let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock!!");
|
Err(_) => {
|
||||||
*write_lock = reupload_acc_id;
|
let new_account = sqlx::query!(
|
||||||
},
|
"INSERT INTO accounts (username, gjp2, password, email) VALUES (?, ?, ?, ?)",
|
||||||
Err(_) => {
|
REUPLOAD_USER_NAME,
|
||||||
let new_account = NewAccount {
|
"!",
|
||||||
username: REUPLOAD_USER_NAME.to_string(),
|
"!",
|
||||||
gjp2: "!".to_string(),
|
""
|
||||||
password: "!".to_string(),
|
)
|
||||||
email: "".to_string()
|
.execute(&mut connection)
|
||||||
};
|
.await
|
||||||
|
.expect("error saving the new account");
|
||||||
|
|
||||||
let inserted_account = diesel::insert_into(accounts::table)
|
let reupload_acc_id = new_account.last_insert_rowid() as i64;
|
||||||
.values(&new_account)
|
|
||||||
.get_result::<Account, >(connection)
|
|
||||||
.expect("error saving the new account");
|
|
||||||
|
|
||||||
let reupload_acc_id = inserted_account.id;
|
sqlx::query!(
|
||||||
|
"INSERT INTO users (account_id, username, registered) VALUES (?, ?, ?)",
|
||||||
|
reupload_acc_id,
|
||||||
|
REUPLOAD_USER_NAME,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.execute(&mut connection)
|
||||||
|
.await
|
||||||
|
.expect("error saving the new user");
|
||||||
|
|
||||||
let new_user = NewUser {
|
let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock");
|
||||||
account_id: inserted_account.id,
|
*write_lock = reupload_acc_id;
|
||||||
username: REUPLOAD_USER_NAME.to_string(),
|
|
||||||
registered: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
diesel::insert_into(users::table)
|
println!("created reupload account, id: {}", reupload_acc_id);
|
||||||
.values(&new_user)
|
|
||||||
.get_result::<User, >(connection)
|
|
||||||
.expect("error saving the new user");
|
|
||||||
|
|
||||||
let mut write_lock = REUPLOAD_ACCOUNT_ID.write().expect("poisoned lock!!");
|
|
||||||
*write_lock = reupload_acc_id;
|
|
||||||
|
|
||||||
println!("created reupload account, id: {}", reupload_acc_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
21
src/main.rs
21
src/main.rs
|
@ -12,11 +12,11 @@ use rocket::data::{Limits, ToByteUnit};
|
||||||
|
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
|
|
||||||
mod db;
|
|
||||||
mod helpers;
|
|
||||||
mod endpoints;
|
|
||||||
mod template_endpoints;
|
|
||||||
mod config;
|
mod config;
|
||||||
|
mod db;
|
||||||
|
mod endpoints;
|
||||||
|
mod helpers;
|
||||||
|
mod template_endpoints;
|
||||||
|
|
||||||
#[get("/<file..>")]
|
#[get("/<file..>")]
|
||||||
async fn files(file: PathBuf) -> Option<NamedFile> {
|
async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||||
|
@ -24,14 +24,14 @@ async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
// init stuff
|
// init stuff
|
||||||
crate::helpers::reupload::init();
|
crate::helpers::reupload::init().await;
|
||||||
|
|
||||||
// data directories
|
// data directories
|
||||||
// unhardcore this maybe?
|
// unhardcore this maybe?
|
||||||
fs::create_dir_all(config::config_get_with_default("db.data_folder", "data")).expect("failed to create data directory!");
|
fs::create_dir_all(config::config_get_with_default("db.data_folder", "data".to_string())).expect("failed to create data directory!");
|
||||||
fs::create_dir_all(format!("{}/levels", config::config_get_with_default("db.data_folder", "data"))).expect("failed to create data directory for levels");
|
fs::create_dir_all(format!("{}/levels", config::config_get_with_default("db.data_folder", "data".to_string()))).expect("failed to create data directory for levels");
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
// conf
|
// conf
|
||||||
|
@ -57,10 +57,9 @@ fn rocket() -> _ {
|
||||||
files
|
files
|
||||||
])
|
])
|
||||||
// https://www.youtube.com/watch?v=_pLrtsf5yfE
|
// https://www.youtube.com/watch?v=_pLrtsf5yfE
|
||||||
.mount(config::config_get_with_default("general.append_path", "/"), routes![
|
.mount(format!("/{}", config::config_get_with_default("general.append_path", "".to_string())), routes![
|
||||||
endpoints::accounts::login_account::login_account,
|
endpoints::accounts::login_account::login_account,
|
||||||
endpoints::accounts::register_account::register_account,
|
endpoints::accounts::register_account::register_account,
|
||||||
endpoints::accounts::update_account_settings::update_account_settings,
|
|
||||||
|
|
||||||
endpoints::users::get_users::get_users,
|
endpoints::users::get_users::get_users,
|
||||||
|
|
||||||
|
@ -68,6 +67,6 @@ fn rocket() -> _ {
|
||||||
endpoints::levels::get_levels::get_levels,
|
endpoints::levels::get_levels::get_levels,
|
||||||
endpoints::levels::upload_level::upload_level
|
endpoints::levels::upload_level::upload_level
|
||||||
])
|
])
|
||||||
// so templates work i think
|
// so templates work
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
}
|
}
|
|
@ -4,25 +4,20 @@ use rocket_dyn_templates::{Template, context};
|
||||||
|
|
||||||
use rocket::http::CookieJar;
|
use rocket::http::CookieJar;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
#[get("/accounts")]
|
#[get("/accounts")]
|
||||||
pub fn account_management(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
pub async fn account_management(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
let logged_in = crate::helpers::templates::authenticate(cookies);
|
let logged_in = crate::helpers::templates::authenticate(cookies);
|
||||||
|
|
||||||
match logged_in {
|
match logged_in {
|
||||||
Ok((username_val, _account_id_val, user_id_val)) => {
|
Ok((username_val, _account_id, user_id)) => {
|
||||||
use db::schema::users::dsl::*;
|
let result = sqlx::query!("SELECT stars, demons, coins, user_coins, diamonds, creator_points FROM users WHERE id = ?", user_id)
|
||||||
use db::models::User;
|
.fetch_one(connection)
|
||||||
|
.await
|
||||||
let result = users
|
.expect("couldnt query database");
|
||||||
.filter(id.eq(user_id_val))
|
|
||||||
.get_result::<User, >(connection)
|
|
||||||
.expect("couldnt find user with user id from account");
|
|
||||||
|
|
||||||
return Ok(Template::render("account_management", context! {
|
return Ok(Template::render("account_management", context! {
|
||||||
username: username_val,
|
username: username_val,
|
||||||
|
|
|
@ -8,8 +8,6 @@ use rocket::http::{Cookie, CookieJar};
|
||||||
|
|
||||||
use rocket::time::Duration;
|
use rocket::time::Duration;
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::helpers;
|
use crate::helpers;
|
||||||
|
|
||||||
|
@ -20,23 +18,22 @@ pub struct FormLogin {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<input>")]
|
#[post("/login", data = "<input>")]
|
||||||
pub fn post_login(cookies: &CookieJar<'_>, input: Form<FormLogin>) -> Template {
|
pub async fn post_login(cookies: &CookieJar<'_>, input: Form<FormLogin>) -> Template {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
use db::schema::accounts::dsl::*;
|
let result = sqlx::query!("SELECT id, username FROM accounts WHERE username = ?", input.username)
|
||||||
|
.fetch_one(connection)
|
||||||
let result = accounts
|
.await;
|
||||||
.select((id, username))
|
|
||||||
.filter(username.eq(input.username.clone()))
|
|
||||||
.get_result::<(i32, String), >(connection);
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(account_id_username_val) => {
|
Ok(result) => {
|
||||||
match helpers::accounts::auth(account_id_username_val.0, Some(input.password.clone()), None, None) {
|
let account_username = result.username;
|
||||||
|
|
||||||
|
match helpers::accounts::auth(result.id, Some(input.password.clone()), None, None).await {
|
||||||
Ok(account_id_user_id_val) => {
|
Ok(account_id_user_id_val) => {
|
||||||
cookies.add_private(Cookie::build(
|
cookies.add_private(Cookie::build(
|
||||||
"blackmail_data",
|
"blackmail_data",
|
||||||
format!("{}:{}:{}", account_id_username_val.1, account_id_user_id_val.0, account_id_user_id_val.1))
|
format!("{}:{}:{}", account_username, result.id, account_id_user_id_val.1))
|
||||||
.path("/")
|
.path("/")
|
||||||
.secure(false)
|
.secure(false)
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
|
|
|
@ -12,8 +12,6 @@ use std::fs;
|
||||||
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
use crate::helpers;
|
use crate::helpers;
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
|
@ -38,7 +36,7 @@ pub struct FormReupload {
|
||||||
|
|
||||||
#[post("/tools/reupload", data = "<input>")]
|
#[post("/tools/reupload", data = "<input>")]
|
||||||
pub async fn post_reupload(input: Form<FormReupload>) -> Template {
|
pub async fn post_reupload(input: Form<FormReupload>) -> Template {
|
||||||
let connection = &mut db::establish_connection_pg();
|
let connection = &mut db::establish_sqlite_conn().await;
|
||||||
|
|
||||||
let disabled = !config::config_get_with_default("levels.reupload", true);
|
let disabled = !config::config_get_with_default("levels.reupload", true);
|
||||||
|
|
||||||
|
@ -70,46 +68,64 @@ pub async fn post_reupload(input: Form<FormReupload>) -> Template {
|
||||||
|
|
||||||
let gmd_file = reqwest::get(format!("https://history.geometrydash.eu/level/{}/{}/download/", remote_level_id, level.id)).await.expect("failed to fetch gmd file from remote server");
|
let gmd_file = reqwest::get(format!("https://history.geometrydash.eu/level/{}/{}/download/", remote_level_id, level.id)).await.expect("failed to fetch gmd file from remote server");
|
||||||
let level_data = helpers::levels::gmd_parse(&gmd_file.text().await.expect("failed to parse gmd file as text"));
|
let level_data = helpers::levels::gmd_parse(&gmd_file.text().await.expect("failed to parse gmd file as text"));
|
||||||
|
|
||||||
use db::schema::levels::dsl::*;
|
|
||||||
use db::models::{Level, NewLevel};
|
|
||||||
|
|
||||||
let new_level = NewLevel {
|
let level_name = level_data.get("k2").expect("level name not found").to_string();
|
||||||
name: level_data.get("k2").expect("level name not found").to_string(),
|
let reupload_account_id = helpers::reupload::REUPLOAD_ACCOUNT_ID.read().expect("poisoned lock!!").to_string().parse::<i32>().expect("reupload account id not int (shouldnt ever happen)");
|
||||||
user_id: crate::helpers::reupload::REUPLOAD_ACCOUNT_ID.read().expect("poisoned lock!!").to_string().parse::<i32>().expect("reupload account id not int (shouldnt ever happen)"),
|
let level_description = String::from_utf8(general_purpose::URL_SAFE.decode(general_purpose::URL_SAFE.decode(level_data.get("k3").expect("level description not found")).expect("couldnt decode base64")).expect("couldnt decode base64")).expect("invalid utf-8 sequence (how)");
|
||||||
description: String::from_utf8(general_purpose::URL_SAFE.decode(general_purpose::URL_SAFE.decode(level_data.get("k3").expect("level description not found")).expect("couldnt decode base64")).expect("couldnt decode base64")).expect("invalid utf-8 sequence (how)"),
|
let level_game_version = level_data.get("k17").expect("level game version not found").to_string().parse::<i32>().expect("level game version not int");
|
||||||
original: None,
|
let level_binary_version = level_data.get("k50").unwrap_or(&String::from("0")).to_string().parse::<i32>().expect("level binary version not int");
|
||||||
game_version: level_data.get("k17").expect("level game version not found").to_string().parse::<i32>().expect("level game version not int"),
|
let level_password = level_data.get("k41").expect("level password not found").to_string();
|
||||||
binary_version: level_data.get("k50").unwrap_or(&String::from("0")).to_string().parse::<i32>().expect("level binary version not int"),
|
let level_requested_stars = level_data.get("k66").expect("level requested stars not found").to_string().parse::<i32>().expect("level requested stars not int");
|
||||||
password: Some(level_data.get("k41").expect("level password not found").to_string()),
|
let level_version = level_data.get("k16").expect("level version not found").to_string().parse::<i32>().expect("level version not int");
|
||||||
requested_stars: level_data.get("k66").expect("level requested stars not found").to_string().parse::<i32>().expect("level requested stars not int"),
|
let extra_string = level_data.get("extra_string").unwrap_or(&crate::helpers::levels::DEFAULT_EXTRA_STRING).to_string().into_bytes();
|
||||||
unlisted: 0,
|
let default_level_info = crate::helpers::levels::DEFAULT_LEVEL_INFO.to_string().into_bytes();
|
||||||
version: level_data.get("k16").expect("level version not found").to_string().parse::<i32>().expect("level version not int"),
|
let level_editor_time = level_data.get("k80").unwrap_or(&String::from("0")).parse::<i32>().expect("level editor time not int");
|
||||||
extra_data: level_data.get("extra_string").unwrap_or(&crate::helpers::levels::DEFAULT_EXTRA_STRING).to_string().into_bytes(),
|
let level_editor_time_copies = level_data.get("k81").unwrap_or(&String::from("0")).parse::<i32>().expect("level editor time copies not int");
|
||||||
level_info: crate::helpers::levels::DEFAULT_LEVEL_INFO.to_string().into_bytes(),
|
let level_song_id = if level_data.get("k8").unwrap_or(&String::from("0")).parse::<i32>().expect("level song id not int") == 0 {
|
||||||
editor_time: level_data.get("k80").unwrap_or(&String::from("0")).parse::<i32>().expect("level editor time not int"),
|
level_data.get("k45").expect("level song id doesnt fucking exist").parse::<i32>().expect("level song id not int")
|
||||||
editor_time_copies: level_data.get("k81").unwrap_or(&String::from("0")).parse::<i32>().expect("level editor time copies not int"),
|
} else {
|
||||||
song_id: if level_data.get("k8").unwrap_or(&String::from("0")).parse::<i32>().expect("level song id not int") == 0 {
|
level_data.get("k8").expect("level song id doesnt fucking exist").parse::<i32>().expect("level song id not int")
|
||||||
level_data.get("k45").expect("level song id doesnt fucking exist").parse::<i32>().expect("level song id not int")
|
|
||||||
} else {
|
|
||||||
level_data.get("k8").expect("level song id doesnt fucking exist").parse::<i32>().expect("level song id not int")
|
|
||||||
},
|
|
||||||
length: level.length.expect("level length doesnt fucking exist"),
|
|
||||||
objects: level_data.get("k48").expect("level object count doesnt exist").parse::<i32>().expect("object count not int"),
|
|
||||||
coins: level_data.get("k64").unwrap_or(&String::from("0")).parse::<i32>().expect("coins not int"),
|
|
||||||
has_ldm: level_data.get("k72").unwrap_or(&String::from("0")).parse::<i32>().expect("ldm not int"),
|
|
||||||
two_player: level_data.get("k43").unwrap_or(&String::from("0")).parse::<i32>().expect("two player not int")
|
|
||||||
};
|
};
|
||||||
|
let level_length = level.length.expect("level length doesnt fucking exist");
|
||||||
|
let level_object_count = level_data.get("k48").expect("level object count doesnt exist").parse::<i32>().expect("object count not int");
|
||||||
|
let level_coins = level_data.get("k64").unwrap_or(&String::from("0")).parse::<i32>().expect("coins not int");
|
||||||
|
let level_ldm = level_data.get("k72").unwrap_or(&String::from("0")).parse::<i32>().expect("ldm not int");
|
||||||
|
let level_two_player = level_data.get("k43").unwrap_or(&String::from("0")).parse::<i32>().expect("two player not int");
|
||||||
|
|
||||||
let inserted_level = diesel::insert_into(levels)
|
let inserted_level = sqlx::query!(
|
||||||
.values(&new_level)
|
"INSERT INTO levels (name, user_id, description, game_version, binary_version, password, requested_stars, unlisted, version, extra_data, level_info, editor_time, editor_time_copies, song_id, length, objects, coins, has_ldm, two_player)
|
||||||
.get_result::<Level, >(connection)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
.expect("failed to insert level");
|
level_name,
|
||||||
|
reupload_account_id,
|
||||||
|
level_description,
|
||||||
|
level_game_version,
|
||||||
|
level_binary_version,
|
||||||
|
level_password,
|
||||||
|
level_requested_stars,
|
||||||
|
0,
|
||||||
|
level_version,
|
||||||
|
extra_string,
|
||||||
|
default_level_info,
|
||||||
|
level_editor_time,
|
||||||
|
level_editor_time_copies,
|
||||||
|
level_song_id,
|
||||||
|
level_length,
|
||||||
|
level_object_count,
|
||||||
|
level_coins,
|
||||||
|
level_ldm,
|
||||||
|
level_two_player
|
||||||
|
)
|
||||||
|
.execute(connection)
|
||||||
|
.await
|
||||||
|
.expect("couldnt write to db");
|
||||||
|
|
||||||
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data"), inserted_level.id), general_purpose::URL_SAFE.decode(level_data.get("k4").expect("no level data?!").as_bytes()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
// sqlite doesnt have return clause :frown: maybe swap to custom id system
|
||||||
|
let inserted_id = inserted_level.last_insert_rowid();
|
||||||
|
|
||||||
|
fs::write(format!("{}/levels/{}.lvl", config::config_get_with_default("db.data_folder", "data".to_string()), inserted_id), general_purpose::URL_SAFE.decode(level_data.get("k4").expect("no level data?!").as_bytes()).expect("user provided invalid level string")).expect("couldnt write level to file");
|
||||||
|
|
||||||
return Template::render("reupload", context! {
|
return Template::render("reupload", context! {
|
||||||
level_id: inserted_level.id
|
level_id: inserted_id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue