mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-07-12 00:32:20 +08:00
Compare commits
8 Commits
d61b27af8f
...
956634d8ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
956634d8ab | ||
|
|
8a1d33bd2b | ||
|
|
aef2c4986d | ||
|
|
b35aa35b1a | ||
|
|
0ef0a83685 | ||
|
|
586f4612ed | ||
|
|
f0183370be | ||
|
|
f4fe727a0e |
@@ -18,11 +18,6 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
|
run: curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
|
||||||
|
|
||||||
- name: Set up Node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '18'
|
|
||||||
|
|
||||||
- name: Install android NDK
|
- name: Install android NDK
|
||||||
id: setup-ndk
|
id: setup-ndk
|
||||||
uses: https://github.com/nttld/setup-ndk@v1
|
uses: https://github.com/nttld/setup-ndk@v1
|
||||||
@@ -33,12 +28,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir out
|
mkdir out
|
||||||
|
|
||||||
- name: Build webui
|
|
||||||
run: |
|
|
||||||
cd webui
|
|
||||||
npm install
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
- name: Install cargo ndk
|
- name: Install cargo ndk
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
|||||||
[submodule "assets"]
|
[submodule "assets"]
|
||||||
path = assets
|
path = assets
|
||||||
url = https://git.ethanthesleepy.one/ethanaobrien/sif2-runtime-files
|
url = https://git.ethanthesleepy.one/ethanaobrien/sif2-runtime-files
|
||||||
|
[submodule "webui"]
|
||||||
|
path = webui
|
||||||
|
url = https://git.ethanthesleepy.one/ethanaobrien/ew-webui
|
||||||
|
|||||||
690
Cargo.lock
generated
690
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
@@ -4,12 +4,12 @@ version = "1.0.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "4.12"
|
actix-web = "4.13"
|
||||||
rusqlite = { version = "0.37.0", features = ["bundled"] }
|
rusqlite = { version = "0.38.0", features = ["bundled"] }
|
||||||
clap = { version = "4.5.37", features = ["derive"]}
|
clap = { version = "4.5.59", features = ["derive"]}
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
rand = "0.9.2"
|
rand = "0.10.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
@@ -17,8 +17,8 @@ md5 = "0.8.0"
|
|||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
sha1 = "0.10.6"
|
sha1 = "0.10.6"
|
||||||
substring = "1.4.5"
|
substring = "1.4.5"
|
||||||
uuid = { version = "1.18.1", features = ["v7"] }
|
uuid = { version = "1.21.0", features = ["v7"] }
|
||||||
rsa = { version = "0.9.9", features = ["sha1"] }
|
rsa = { version = "0.9.10", features = ["sha1"] }
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
include-flate-codegen = "0.3.1"
|
include-flate-codegen = "0.3.1"
|
||||||
@@ -26,22 +26,23 @@ libflate = "2.2.1"
|
|||||||
cbc = { version = "0.1.2", features = ["alloc"] }
|
cbc = { version = "0.1.2", features = ["alloc"] }
|
||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
pem = "3.0.6"
|
pem = "3.0.6"
|
||||||
ureq = "3.1.4"
|
ureq = "3.2.0"
|
||||||
include_dir = {version = "0.7.4", optional = true }
|
mime_guess = "2.0.5"
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
|
||||||
[target.aarch64-linux-android.dependencies]
|
[target.aarch64-linux-android.dependencies]
|
||||||
jni = { version = "0.21.1", features = ["invocation", "default"], optional = true }
|
jni = { version = "0.21.0", features = ["invocation", "default"], optional = true }
|
||||||
|
|
||||||
[target.aarch64-apple-ios.dependencies]
|
[target.aarch64-apple-ios.dependencies]
|
||||||
objc2 = { version = "0.6.3", optional = true }
|
objc2 = { version = "0.6.3", optional = true }
|
||||||
objc2-foundation = { version = "0.3.2", features = ["NSFileManager"], optional = true }
|
objc2-foundation = { version = "0.3.2", features = ["NSFileManager"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.2"
|
||||||
|
|
||||||
# To enable this library you MUST comment out lib block below and add --features library
|
# To enable this library you MUST comment out lib block below and add --features library
|
||||||
[features]
|
[features]
|
||||||
library = ["jni", "objc2", "objc2-foundation", "include_dir"]
|
library = ["jni", "objc2", "objc2-foundation"]
|
||||||
|
|
||||||
#[lib]
|
#[lib]
|
||||||
#crate-type = ["cdylib"]
|
#crate-type = ["cdylib"]
|
||||||
|
|||||||
4
build.rs
4
build.rs
@@ -9,4 +9,8 @@ fn main() {
|
|||||||
.compile("libc_code.a");
|
.compile("libc_code.a");
|
||||||
println!("cargo:rerun-if-changed=src/log.c");
|
println!("cargo:rerun-if-changed=src/log.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !std::fs::exists("webui/index.html").unwrap_or(false) {
|
||||||
|
panic!("Could not compile crate! Missing webui! Did you pull submodules?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,12 @@ FROM docker.io/library/debian:latest AS builder
|
|||||||
# First - build
|
# First - build
|
||||||
|
|
||||||
RUN apt update && apt install -y curl libssl-dev perl git gcc make
|
RUN apt update && apt install -y curl libssl-dev perl git gcc make
|
||||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash
|
|
||||||
RUN apt update && apt install -y nodejs
|
|
||||||
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
|
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
|
||||||
|
|
||||||
WORKDIR /ew/
|
WORKDIR /ew/
|
||||||
|
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
|
|
||||||
WORKDIR /ew/webui/
|
|
||||||
|
|
||||||
RUN npm i && npm run build
|
|
||||||
|
|
||||||
WORKDIR /ew/
|
WORKDIR /ew/
|
||||||
|
|
||||||
RUN . "$HOME/.cargo/env" && cargo build --release
|
RUN . "$HOME/.cargo/env" && cargo build --release
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3'
|
|
||||||
services:
|
services:
|
||||||
sif2-ew:
|
sif2-ew:
|
||||||
image: sif2-ew:latest
|
image: sif2-ew:latest
|
||||||
@@ -14,6 +13,7 @@ services:
|
|||||||
HIDDEN: false # Will disable the webui
|
HIDDEN: false # Will disable the webui
|
||||||
DISABLE_IMPORTS: false # Will disable account imports
|
DISABLE_IMPORTS: false # Will disable account imports
|
||||||
DISABLE_EXPORTS: false # Will disable account exports
|
DISABLE_EXPORTS: false # Will disable account exports
|
||||||
|
#IMAGE_ASSET_PATH: /images/ # Images for cards in webui (will default to the public server)
|
||||||
|
|
||||||
# Everything below is for the "Help" page
|
# Everything below is for the "Help" page
|
||||||
#ANDROID_GLOBAL: "link.to/patched/android/global.apk"
|
#ANDROID_GLOBAL: "link.to/patched/android/global.apk"
|
||||||
@@ -25,4 +25,7 @@ services:
|
|||||||
- 8080:8080
|
- 8080:8080
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/data
|
- ./data:/data
|
||||||
|
# You can download from https://git.ethanthesleepy.one/ethanaobrien/ew-webui/releases/download/images/card-thumbnails.zip
|
||||||
|
# No, you should not have a nested folder EXAMPLE: `images/FILENAME.webp`
|
||||||
|
#- ./images:/images
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -25,4 +25,6 @@ asset_android_en=$([ "$EN_ANDROID_ASSET_HASH" != "" ] && echo "--en-android-asse
|
|||||||
|
|
||||||
asset_ios_en=$([ "$EN_IOS_ASSET_HASH" != "" ] && echo "--en-ios-asset-hash $EN_IOS_ASSET_HASH" || echo "")
|
asset_ios_en=$([ "$EN_IOS_ASSET_HASH" != "" ] && echo "--en-ios-asset-hash $EN_IOS_ASSET_HASH" || echo "")
|
||||||
|
|
||||||
/root/ew/ew --path $directory --port $port --npps4 $npps4 $asset_android_jp $asset_ios_jp $asset_android_en $asset_ios_en $exports $imports $purge $hidden $https --global-android "$ANDROID_GLOBAL" --japan-android "$ANDROID_JAPAN" --global-ios "$IOS_GLOBAL" --japan-ios "$IOS_JAPAN" --assets-url "$ASSET_URL" --max-time $maxTime
|
image_asset_path=$([ "$IMAGE_ASSET_PATH" != "" ] && echo "--image-asset-path $IMAGE_ASSET_PATH" || echo "")
|
||||||
|
|
||||||
|
/root/ew/ew --path $directory --port $port --npps4 $npps4 $asset_android_jp $asset_ios_jp $asset_android_en $asset_ios_en $exports $imports $purge $hidden $https $image_asset_path --global-android "$ANDROID_GLOBAL" --japan-android "$ANDROID_JAPAN" --global-ios "$IOS_GLOBAL" --japan-ios "$IOS_JAPAN" --assets-url "$ASSET_URL" --max-time $maxTime
|
||||||
|
|||||||
24
readme.md
24
readme.md
@@ -5,27 +5,15 @@ A (mostly functioning) server for Love Live! School idol festival 2 MIRACLE LIVE
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
### Linux
|
### Requirements
|
||||||
|
|
||||||
#### Requirements
|
|
||||||
- [perl](https://www.perl.org/get.html) (This is normally pre-installed)
|
|
||||||
- [rust](https://www.rust-lang.org/tools/install)
|
|
||||||
- [npm](https://www.npmjs.com/)
|
|
||||||
- The [libssl-dev](https://packages.debian.org/buster/libssl-dev) package. This will vary across distros.
|
|
||||||
|
|
||||||
`apt install -y npm libssl-dev perl`
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
#### Requirements
|
|
||||||
- [Strawberry Perl](https://strawberryperl.com/)
|
|
||||||
- [rust](https://www.rust-lang.org/tools/install)
|
- [rust](https://www.rust-lang.org/tools/install)
|
||||||
|
|
||||||
### Packaging/Running
|
### Packaging/Running
|
||||||
|
|
||||||
**Build npm:**
|
**Build:**
|
||||||
`cd webui && npm install && npm run build`
|
|
||||||
|
|
||||||
**Build Rust:**
|
|
||||||
Debug: `cargo run`
|
Debug: `cargo run`
|
||||||
Release: `cargo build --release`
|
Release: `cargo build --release`
|
||||||
|
|
||||||
|
**Docker:**
|
||||||
|
`docker build --tag ew --file docker/Dockerfile .`
|
||||||
|
Will create docker image with tag "ew"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use aes::cipher::BlockEncryptMut;
|
use aes::cipher::BlockEncryptMut;
|
||||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
|
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
|
||||||
|
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ pub async fn run_server(in_thread: bool) -> std::io::Result<()> {
|
|||||||
srv.call(req)
|
srv.call(req)
|
||||||
})
|
})
|
||||||
.app_data(web::PayloadConfig::default().limit(1024 * 1024 * 25))
|
.app_data(web::PayloadConfig::default().limit(1024 * 1024 * 25))
|
||||||
.service(static_handlers::css)
|
|
||||||
.service(static_handlers::maintenance)
|
.service(static_handlers::maintenance)
|
||||||
.service(static_handlers::js)
|
|
||||||
.service(static_handlers::files_jp)
|
.service(static_handlers::files_jp)
|
||||||
.service(static_handlers::files_gl)
|
.service(static_handlers::files_gl)
|
||||||
.default_service(web::route().to(router::request))
|
.default_service(web::route().to(router::request))
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ pub struct Args {
|
|||||||
pub en_android_asset_hash: String,
|
pub en_android_asset_hash: String,
|
||||||
|
|
||||||
#[arg(long, default_value = "", help = "Asset hash for JP Android client.")]
|
#[arg(long, default_value = "", help = "Asset hash for JP Android client.")]
|
||||||
pub jp_android_asset_hash: String
|
pub jp_android_asset_hash: String,
|
||||||
|
|
||||||
|
#[arg(long, default_value = "", help = "Path to image assets.")]
|
||||||
|
pub image_asset_path: String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_args() -> Args {
|
pub fn get_args() -> Args {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use json::{JsonValue, object, array};
|
use json::{JsonValue, object, array};
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
|
|
||||||
use crate::encryption;
|
use crate::encryption;
|
||||||
use crate::include_file;
|
use crate::include_file;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use json::{array, object, JsonValue};
|
use json::{array, object, JsonValue};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use actix_web::{HttpRequest, http::header::{HeaderMap, HeaderValue}};
|
use actix_web::{HttpRequest, http::header::{HeaderMap, HeaderValue}};
|
||||||
use crate::encryption;
|
use crate::encryption;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use json::{object, array, JsonValue};
|
use json::{object, array, JsonValue};
|
||||||
use actix_web::{HttpRequest};
|
use actix_web::{HttpRequest};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::router::{global, userdata, items, databases};
|
use crate::router::{global, userdata, items, databases};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use json::{array, object, JsonValue};
|
use json::{array, object, JsonValue};
|
||||||
use actix_web::{HttpRequest};
|
use actix_web::{HttpRequest};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
|
|
||||||
use crate::router::{global, userdata, items, databases};
|
use crate::router::{global, userdata, items, databases};
|
||||||
use crate::encryption;
|
use crate::encryption;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ pub mod user;
|
|||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use json::{JsonValue, array, object};
|
use json::{JsonValue, array, object};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
|
|
||||||
use crate::router::global;
|
use crate::router::global;
|
||||||
use crate::router::items;
|
use crate::router::items;
|
||||||
@@ -430,7 +430,8 @@ pub fn webui_login(uid: i64, password: &str) -> Result<String, String> {
|
|||||||
let new_token = create_webui_token();
|
let new_token = create_webui_token();
|
||||||
|
|
||||||
DATABASE.lock_and_exec("DELETE FROM webui WHERE user_id=?1", params!(uid));
|
DATABASE.lock_and_exec("DELETE FROM webui WHERE user_id=?1", params!(uid));
|
||||||
DATABASE.lock_and_exec("INSERT INTO webui (user_id, token, last_login) VALUES (?1, ?2, ?3)", params!(uid, new_token, global::timestamp()));
|
// This could overflow given enough time... though rusqlite doesnt currently support ToSql on a u64...... maybe someday
|
||||||
|
DATABASE.lock_and_exec("INSERT INTO webui (user_id, token, last_login) VALUES (?1, ?2, ?3)", params!(uid, new_token, global::timestamp() as i64));
|
||||||
Ok(new_token)
|
Ok(new_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
use json::{JsonValue, object};
|
use json::{JsonValue, object};
|
||||||
use crate::router::userdata;
|
use crate::router::userdata;
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ use actix_web::{
|
|||||||
};
|
};
|
||||||
use json::{JsonValue, object};
|
use json::{JsonValue, object};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use include_dir::{include_dir, Dir};
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use crate::include_file;
|
use crate::include_file;
|
||||||
use crate::router::{userdata, items};
|
use crate::router::{userdata, items};
|
||||||
|
|
||||||
//pub const FRONTEND_DOMAIN: &str = "https://sif2-api.ethanthesleepy.one";
|
|
||||||
|
|
||||||
fn get_config() -> JsonValue {
|
fn get_config() -> JsonValue {
|
||||||
let args = crate::get_args();
|
let args = crate::get_args();
|
||||||
object!{
|
object!{
|
||||||
@@ -35,7 +35,6 @@ fn error(msg: &str) -> HttpResponse {
|
|||||||
message: msg
|
message: msg
|
||||||
};
|
};
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
@@ -53,8 +52,6 @@ pub fn login(_req: HttpRequest, body: String) -> HttpResponse {
|
|||||||
};
|
};
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(("Set-Cookie", format!("ew_token={}; SameSite=Strict; HttpOnly", token.unwrap())))
|
.insert_header(("Set-Cookie", format!("ew_token={}; SameSite=Strict; HttpOnly", token.unwrap())))
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
@@ -79,7 +76,6 @@ pub fn import(_req: HttpRequest, body: String) -> HttpResponse {
|
|||||||
};
|
};
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +97,6 @@ pub fn user(req: HttpRequest) -> HttpResponse {
|
|||||||
data: data
|
data: data
|
||||||
};
|
};
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
@@ -116,8 +110,6 @@ pub fn start_loginbonus(req: HttpRequest, body: String) -> HttpResponse {
|
|||||||
let resp = userdata::webui_start_loginbonus(body["bonus_id"].as_i64().unwrap(), &token.unwrap());
|
let resp = userdata::webui_start_loginbonus(body["bonus_id"].as_i64().unwrap(), &token.unwrap());
|
||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
@@ -131,8 +123,6 @@ pub fn set_time(req: HttpRequest, body: String) -> HttpResponse {
|
|||||||
let resp = userdata::set_server_time(body["timestamp"].as_i64().unwrap(), &token.unwrap());
|
let resp = userdata::set_server_time(body["timestamp"].as_i64().unwrap(), &token.unwrap());
|
||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
@@ -146,34 +136,63 @@ pub fn logout(req: HttpRequest) -> HttpResponse {
|
|||||||
result: "OK"
|
result: "OK"
|
||||||
};
|
};
|
||||||
HttpResponse::Found()
|
HttpResponse::Found()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.insert_header(("Set-Cookie", "ew_token=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT"))
|
.insert_header(("Set-Cookie", "ew_token=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT"))
|
||||||
.insert_header(("Location", "/"))
|
.insert_header(("Location", "/login.html"))
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WEBUI_ASSETS: Dir<'_> = include_dir!("webui/");
|
||||||
|
|
||||||
pub fn main(req: HttpRequest) -> HttpResponse {
|
pub fn main(req: HttpRequest) -> HttpResponse {
|
||||||
if req.path() == "/" {
|
let path = if req.path().ends_with("/") { format!("{}index.html", req.path()) } else { req.path().to_string() };
|
||||||
|
let mut chars = path.chars();
|
||||||
|
chars.next();
|
||||||
|
let path = chars.as_str();
|
||||||
|
|
||||||
|
if path == "login.html" {
|
||||||
let token = get_login_token(&req);
|
let token = get_login_token(&req);
|
||||||
if token.is_some() {
|
if token.is_some() {
|
||||||
let data = userdata::webui_get_user(&token.unwrap());
|
let data = userdata::webui_get_user(&token.unwrap());
|
||||||
if data.is_some() {
|
if data.is_some() {
|
||||||
return HttpResponse::Found()
|
return HttpResponse::Found()
|
||||||
.insert_header(("Location", "/home/"))
|
.insert_header(("Location", "/account.html"))
|
||||||
.body("");
|
.body("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.path() != "/" && req.path() != "/home/" && req.path() != "/import/" && req.path() != "/help/" {
|
|
||||||
return HttpResponse::Found()
|
if let Some(file) = WEBUI_ASSETS.get_file(&path) {
|
||||||
.insert_header(("Location", "/"))
|
let body = file.contents();
|
||||||
.body("");
|
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||||
|
return HttpResponse::Ok()
|
||||||
|
.insert_header(ContentType(mime))
|
||||||
|
.insert_header(("content-length", body.len()))
|
||||||
|
.body(body);
|
||||||
|
} else if path.starts_with("webui/images/card-thumbnails") {
|
||||||
|
let args = crate::get_args();
|
||||||
|
|
||||||
|
let file_name = path.split("/").last().unwrap_or("");
|
||||||
|
let file_path = format!("{}/{}", args.image_asset_path, file_name).replace("//", "/");
|
||||||
|
if args.image_asset_path != "" && let Ok(body) = fs::read(file_path) {
|
||||||
|
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||||
|
return HttpResponse::Ok()
|
||||||
|
.insert_header(ContentType(mime))
|
||||||
|
.insert_header(("content-length", body.len()))
|
||||||
|
.body(body);
|
||||||
|
} else {
|
||||||
|
if args.image_asset_path != "" {
|
||||||
|
println!("File '{file_name}' was requested, but no file was found on the disk!");
|
||||||
|
}
|
||||||
|
return HttpResponse::SeeOther()
|
||||||
|
.insert_header(("location", format!("https://sif2-api.ethanthesleepy.one{}", req.path())))
|
||||||
|
.body("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
HttpResponse::Ok()
|
|
||||||
.insert_header(ContentType::html())
|
HttpResponse::Found()
|
||||||
.body(include_file!("webui/dist/index.html"))
|
.insert_header(("Location", "/"))
|
||||||
|
.body("")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export(req: HttpRequest) -> HttpResponse {
|
pub fn export(req: HttpRequest) -> HttpResponse {
|
||||||
@@ -213,7 +232,6 @@ pub fn server_info(_req: HttpRequest) -> HttpResponse {
|
|||||||
};
|
};
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +262,6 @@ pub fn get_card_info(req: HttpRequest) -> HttpResponse {
|
|||||||
|
|
||||||
return HttpResponse::Ok()
|
return HttpResponse::Ok()
|
||||||
.content_type(ContentType::json())
|
.content_type(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(resp));
|
.body(json::stringify(resp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,20 +285,20 @@ pub fn get_card_info(req: HttpRequest) -> HttpResponse {
|
|||||||
|
|
||||||
if page_items.is_empty() {
|
if page_items.is_empty() {
|
||||||
return HttpResponse::NotFound()
|
return HttpResponse::NotFound()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.finish();
|
.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_pages = (total_len as f64 / max as f64).ceil() as usize;
|
let total_pages = (total_len as f64 / max as f64).ceil() as usize;
|
||||||
|
let args = crate::get_args();
|
||||||
|
|
||||||
let resp = object!{
|
let resp = object!{
|
||||||
total_pages: total_pages,
|
total_pages: total_pages,
|
||||||
current: page_items
|
current: page_items,
|
||||||
|
image_path: args.image_asset_path
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type(ContentType::json())
|
.content_type(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +323,6 @@ pub fn get_music_info(req: HttpRequest) -> HttpResponse {
|
|||||||
|
|
||||||
if page_items.is_empty() {
|
if page_items.is_empty() {
|
||||||
return HttpResponse::NotFound()
|
return HttpResponse::NotFound()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.finish();
|
.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +336,6 @@ pub fn get_music_info(req: HttpRequest) -> HttpResponse {
|
|||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type(ContentType::json())
|
.content_type(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,14 +347,12 @@ lazy_static! {
|
|||||||
pub fn list_login_bonus(_req: HttpRequest) -> HttpResponse {
|
pub fn list_login_bonus(_req: HttpRequest) -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type(ContentType::json())
|
.content_type(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(LOGIN_BONUS.clone()))
|
.body(json::stringify(LOGIN_BONUS.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_items(_req: HttpRequest) -> HttpResponse {
|
pub fn list_items(_req: HttpRequest) -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.content_type(ContentType::json())
|
.content_type(ContentType::json())
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
.body(json::stringify(ITEM.clone()))
|
.body(json::stringify(ITEM.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +392,6 @@ pub fn cheat(req: HttpRequest, _body: String) -> HttpResponse {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
//.insert_header(("Access-Control-Allow-Origin", FRONTEND_DOMAIN))
|
|
||||||
//.insert_header(("Access-Control-Allow-Credentials", "true"))
|
|
||||||
.insert_header(ContentType::json())
|
.insert_header(ContentType::json())
|
||||||
.body(json::stringify(resp))
|
.body(json::stringify(resp))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,7 @@ use actix_web::{
|
|||||||
http::header::ContentType
|
http::header::ContentType
|
||||||
};
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use crate::include_file;
|
|
||||||
|
|
||||||
#[get("/index.css")]
|
|
||||||
async fn css(_req: HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.insert_header(ContentType(mime::TEXT_CSS))
|
|
||||||
.body(include_file!("webui/dist/index.css"))
|
|
||||||
}
|
|
||||||
#[get("/index.js")]
|
|
||||||
async fn js(_req: HttpRequest) -> HttpResponse {
|
|
||||||
HttpResponse::Ok()
|
|
||||||
.insert_header(ContentType(mime::APPLICATION_JAVASCRIPT_UTF_8))
|
|
||||||
.body(include_file!("webui/dist/index.js"))
|
|
||||||
}
|
|
||||||
#[get("/maintenance/maintenance.json")]
|
#[get("/maintenance/maintenance.json")]
|
||||||
async fn maintenance(_req: HttpRequest) -> HttpResponse {
|
async fn maintenance(_req: HttpRequest) -> HttpResponse {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
|
|||||||
1
webui
Submodule
1
webui
Submodule
Submodule webui added at 7fba8d617e
@@ -1,21 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react/jsx-runtime',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
],
|
|
||||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
|
||||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
|
||||||
settings: { react: { version: '18.2' } },
|
|
||||||
plugins: ['react-refresh'],
|
|
||||||
rules: {
|
|
||||||
'react/jsx-no-target-blank': 'off',
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
24
webui/.gitignore
vendored
24
webui/.gitignore
vendored
@@ -1,24 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>EW private server</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
4554
webui/package-lock.json
generated
4554
webui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "webui",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-bootstrap": "^2.10.2",
|
|
||||||
"react-dom": "^18.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/react": "^18.2.66",
|
|
||||||
"@types/react-dom": "^18.2.22",
|
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
|
||||||
"eslint": "^8.57.0",
|
|
||||||
"eslint-plugin-react": "^7.34.1",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.6",
|
|
||||||
"vite": "^5.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
const serverUrl = "";
|
|
||||||
|
|
||||||
async function api(url, body) {
|
|
||||||
try {
|
|
||||||
let options = body ? {method: "POST", body: JSON.stringify(body)} : {}
|
|
||||||
const resp = await fetch(serverUrl + url, options);
|
|
||||||
const text = await resp.text();
|
|
||||||
return JSON.parse(text);
|
|
||||||
} catch(e) {
|
|
||||||
return {
|
|
||||||
result: "ERR",
|
|
||||||
message: e.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default api;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
#home {
|
|
||||||
width: 90%;
|
|
||||||
margin: 50px auto;
|
|
||||||
background-color: #43A047;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
padding: 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logout {
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: underline;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 16px;
|
|
||||||
transition-duration: 0.4s;
|
|
||||||
float: right;
|
|
||||||
background-color: yellow;
|
|
||||||
border-radius: 30px;
|
|
||||||
padding: 5px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logout:hover {
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error p {
|
|
||||||
color: orange;
|
|
||||||
grid-template-columns: auto auto auto;
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import { useState, useParams, useEffect } from 'react'
|
|
||||||
import './Help.css'
|
|
||||||
import Request from '../Request.jsx'
|
|
||||||
let init = false;
|
|
||||||
|
|
||||||
function Help() {
|
|
||||||
const [downloadUrl, setDownloadUrl] = useState(<div>Your server admin has no pre-patched apks to download. See the question below..</div>);
|
|
||||||
const [downloadUrliOSGL, setDownloadUrliOSGL] = useState("https://ethanthesleepy.one/public/lovelive/sif2/sif2-gl.ipa");
|
|
||||||
const [downloadUrliOSJP, setDownloadUrliOSJP] = useState("https://ethanthesleepy.one/public/lovelive/sif2/sif2-jp.ipa");
|
|
||||||
|
|
||||||
const [assetUrl, setAssetUrl] = useState("https://sif2.sif.moe");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
if (init) return;
|
|
||||||
init = true;
|
|
||||||
let resp = await Request("/api/webui/serverInfo");
|
|
||||||
if (resp.result !== "OK") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!resp.data.links) return;
|
|
||||||
if (resp.data.links.global && resp.data.links.japan) {
|
|
||||||
setDownloadUrl(
|
|
||||||
<div>Your server admin has a link to download! Download <a href={resp.data.links.japan}>Japan</a> or <a href={resp.data.links.global}>Global</a></div>
|
|
||||||
);
|
|
||||||
} else if (resp.data.links.global) {
|
|
||||||
setDownloadUrl(
|
|
||||||
<div>Your server admin has a link to download! Download <a href={resp.data.links.global}>Global</a></div>
|
|
||||||
);
|
|
||||||
} else if (resp.data.links.japan) {
|
|
||||||
setDownloadUrl(
|
|
||||||
<div>Your server admin has a link to download! Download <a href={resp.data.links.japan}>Japan</a></div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (resp.data.links.assets) {
|
|
||||||
setAssetUrl(resp.data.links.assets);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!resp.data.links.ios) return;
|
|
||||||
if (resp.data.links.ios.japan) {
|
|
||||||
setDownloadUrliOSJP(resp.data.links.ios.japan);
|
|
||||||
}
|
|
||||||
if (resp.data.links.ios.global) {
|
|
||||||
setDownloadUrliOSGL(resp.data.links.ios.global);
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="home">
|
|
||||||
<h1>Help/About</h1>
|
|
||||||
<h2>What is "ew"? What is this server for?</h2>
|
|
||||||
<p>"ew" is a private server, written in Rust, for the short lived game "Love Live! School idol festival 2 MIRACLE LIVE!", a Love Live! themed mobile rhythm game.</p>
|
|
||||||
|
|
||||||
<h2>I'm just trying to play on this server, how do I install the app? (Android)</h2>
|
|
||||||
<p>{downloadUrl}</p>
|
|
||||||
|
|
||||||
<h2>My server admin has no download URLs, how do I patch the apk? (Android)</h2>
|
|
||||||
<p>You can use <a href="https://arasfon.ru/sif2/patcher/">@arasfon's sif2 apk patcher</a>. Plug and the asset url, which is "{assetUrl}", into the "Assets URL" textbox, and then the server url (Which is likely "{window.location.origin}", though this may not be the case). Select your game version, set "header format" to "Lowercase", and press patch. Once that done, use <a href="https://github.com/patrickfav/uber-apk-signer">uber-apk-signer</a> to sign the apk. Then, get it onto your phone and install!</p>
|
|
||||||
|
|
||||||
<h2>How do I install the app? (iOS)</h2>
|
|
||||||
<p>Running on iOS is much simpler than Android, thanks to triangle on the discord. You first download an ipa file for <a href={downloadUrliOSGL}>global</a> or <a href={downloadUrliOSJP}>Japan</a>, and use <a href="https://sideloadly.io/">Sideloadly</a> (or your preferred application installer) to install the app. Then open settings, navigate to the app you just installed, and input the server url (Which is likely "{window.location.origin}", though this may not be the case), and the asset url, which is "{assetUrl}". If you have any errors opening the app, make sure none of the urls in settings end with a slash (/).</p>
|
|
||||||
|
|
||||||
<h2>Help! I'm trying to open the app and it shows as "unavailable" (iOS)</h2>
|
|
||||||
<p>Do not delete it, Just re-sideload the app. This is an Apple security feature.</p>
|
|
||||||
|
|
||||||
<h2>How well does this work?</h2>
|
|
||||||
<p>Works well enough. The server itself takes up not even 20mb of storage, and it's written in rust. I personally think it's pretty well written.</p>
|
|
||||||
|
|
||||||
<h2>Could my computer/laptop run a server?</h2>
|
|
||||||
<p>Very very likely. If the platform is <a href="https://doc.rust-lang.org/nightly/rustc/platform-support.html">supported by rust</a>, then the answer is yes! It is recommended to manually compile the project until I get the time to setup actions. <a href="https://git.ethanthesleepy.one/ethanaobrien/ew">ew repo</a></p>
|
|
||||||
|
|
||||||
<h2>Is the server down right now? I can't connect</h2>
|
|
||||||
<p>Assuming you have just loaded this page on the server you use, then the answer is no, otherwise please contact your server admin.</p>
|
|
||||||
|
|
||||||
<h2>Do events work?</h2>
|
|
||||||
<p>Most events do not, though most should not crash the game. Star events are partially implemented. You can get your rank up, and compete with other players in a ranking table, but no rewards are currently implemented.</p>
|
|
||||||
|
|
||||||
<h2>But then, how do I get event URs?</h2>
|
|
||||||
<p>There are serial codes for several things, one of which includes all the event URs. I don't remember what does what but it is recommended to look at the serial code file to get the latest codes.</p>
|
|
||||||
|
|
||||||
<h2>Why does the game crash when I do x?</h2>
|
|
||||||
<p>This likely means something on the server is broken. If you're self hosting, please contact me via matrix. Otherwise, contact your server admin and ask them to report the issue.</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Help;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
#home {
|
|
||||||
width: 90%;
|
|
||||||
margin: 50px auto;
|
|
||||||
background-color: #43A047;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
padding: 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logout {
|
|
||||||
border: none;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: underline;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 16px;
|
|
||||||
transition-duration: 0.4s;
|
|
||||||
float: right;
|
|
||||||
background-color: yellow;
|
|
||||||
border-radius: 30px;
|
|
||||||
padding: 5px 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logout:hover {
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error p {
|
|
||||||
color: orange;
|
|
||||||
grid-template-columns: auto auto auto;
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
import { useState, useParams, useEffect } from 'react'
|
|
||||||
import './Home.css'
|
|
||||||
import Request from '../Request.jsx'
|
|
||||||
let bonusItems = [];
|
|
||||||
|
|
||||||
function getMigrationID(uid) {
|
|
||||||
return uid.toString()
|
|
||||||
.replaceAll('1', "A")
|
|
||||||
.replaceAll('2', "G")
|
|
||||||
.replaceAll('3', "W")
|
|
||||||
.replaceAll('4', "Q")
|
|
||||||
.replaceAll('5', "Y")
|
|
||||||
.replaceAll('6', "6")
|
|
||||||
.replaceAll('7', "I")
|
|
||||||
.replaceAll('8', "P")
|
|
||||||
.replaceAll('9', "U")
|
|
||||||
.replaceAll('0', "M")
|
|
||||||
+ "7";
|
|
||||||
}
|
|
||||||
|
|
||||||
function Bonus() {
|
|
||||||
const [inputValue, setInputValue] = useState('');
|
|
||||||
const error = useState("");
|
|
||||||
|
|
||||||
let itemz = [];
|
|
||||||
bonusItems.forEach((e) => {
|
|
||||||
itemz.push(e.master_login_bonus_id);
|
|
||||||
})
|
|
||||||
|
|
||||||
const [submittedItems, setSubmittedItems] = useState(itemz);
|
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
let input = parseInt(inputValue.trim());
|
|
||||||
if (isNaN(input) || submittedItems.includes(input)) return;
|
|
||||||
let resp = await Request("/api/webui/startLoginbonus", {
|
|
||||||
bonus_id: input
|
|
||||||
});
|
|
||||||
if (resp.result !== "OK") {
|
|
||||||
error[1](resp.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error[1]("");
|
|
||||||
setSubmittedItems([...submittedItems, resp.id]);
|
|
||||||
setInputValue('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveItem = (index) => {
|
|
||||||
const updatedItems = [...submittedItems];
|
|
||||||
updatedItems.splice(index, 1);
|
|
||||||
setSubmittedItems(updatedItems);
|
|
||||||
};
|
|
||||||
// <button onClick={() => handleRemoveItem(index)}>X</button>
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>Current login bonus list</h2>
|
|
||||||
<div id="error"> { error[0] ? <p>Error: { error[0] } </p> : <p></p> } </div>
|
|
||||||
<ul>
|
|
||||||
{submittedItems.map((item, index) => (
|
|
||||||
<li key={index}>
|
|
||||||
{item}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={inputValue}
|
|
||||||
onChange={(event) => setInputValue(event.target.value)}
|
|
||||||
placeholder="Enter login bonus ID"
|
|
||||||
/>
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
<p>You can find a list of available login bonus IDs <a href="https://git.ethanthesleepy.one/ethanaobrien/ew/src/branch/main/src/router/databases/json/login_bonus.json">here</a>. You should input the <code>id</code> field</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Home() {
|
|
||||||
const [user, userdata] = useState();
|
|
||||||
const [inputValue, setInputValue] = useState('');
|
|
||||||
const [serverTime, setServerTime] = useState('');
|
|
||||||
const error = useState("");
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
window.location.href = "/webui/logout";
|
|
||||||
}
|
|
||||||
const downloadFile = (contents, name) => {
|
|
||||||
let a = document.createElement("a");
|
|
||||||
a.href = URL.createObjectURL(new Blob([contents], {type: "application/json"}));
|
|
||||||
a.download = name;
|
|
||||||
a.click();
|
|
||||||
}
|
|
||||||
const expor = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
let resp = await Request("/api/webui/export");
|
|
||||||
if (resp.result !== "OK") {
|
|
||||||
error[1](resp.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
downloadFile(resp.data.userdata, "userdata.json");
|
|
||||||
downloadFile(resp.data.userhome, "userhome.json");
|
|
||||||
downloadFile(resp.data.missions, "missions.json");
|
|
||||||
downloadFile(resp.data.sifcards, "sifcards.json");
|
|
||||||
|
|
||||||
}
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
let time = Math.round(new Date(inputValue.trim()).getTime() / 1000);
|
|
||||||
if (inputValue.trim() === "-1") {
|
|
||||||
time = 1711741114;
|
|
||||||
} else if (inputValue.trim() === "0") {
|
|
||||||
time = 0;
|
|
||||||
}
|
|
||||||
if (time < 0 || isNaN(time)) return;
|
|
||||||
let resp = await Request("/api/webui/set_time", {
|
|
||||||
timestamp: time
|
|
||||||
});
|
|
||||||
if (resp.result !== "OK") {
|
|
||||||
error[1](resp.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error[1]("");
|
|
||||||
if (time === 0) {
|
|
||||||
setServerTime("now");
|
|
||||||
} else {
|
|
||||||
setServerTime((new Date(time * 1000)).toString());
|
|
||||||
}
|
|
||||||
setInputValue('');
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) return;
|
|
||||||
(async () => {
|
|
||||||
let resp = await Request("/api/webui/userInfo");
|
|
||||||
if (resp.result !== "OK") {
|
|
||||||
window.location.href = "/?message=" + encodeURIComponent(resp.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let user = resp.data.userdata;
|
|
||||||
bonusItems = resp.data.loginbonus.bonus_list;
|
|
||||||
/*
|
|
||||||
bonusItems = [{"master_login_bonus_id":1,"day_counts":[1,2],"event_bonus_list":[]}];
|
|
||||||
let user = {
|
|
||||||
user: {
|
|
||||||
id: 1,
|
|
||||||
rank: 3,
|
|
||||||
exp: 10,
|
|
||||||
last_login_time: 5
|
|
||||||
},
|
|
||||||
time: new Date()
|
|
||||||
}*/
|
|
||||||
if (resp.data.time === 0) {
|
|
||||||
setServerTime("now");
|
|
||||||
} else {
|
|
||||||
setServerTime((new Date(resp.data.time * 1000)).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
userdata(
|
|
||||||
<div>
|
|
||||||
<p>User id: { user.user.id } </p>
|
|
||||||
<p>Migration id: { getMigrationID(user.user.id) } </p>
|
|
||||||
<p>Rank: { user.user.rank } ({ user.user.exp } exp)</p>
|
|
||||||
<p>Last Login: { (new Date(user.user.last_login_time * 1000)).toString() } </p>
|
|
||||||
<Bonus />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="home">
|
|
||||||
<button id="logout" onClick={expor}>Export account</button><br/><br/><br/>
|
|
||||||
<button id="logout" onClick={logout}>Logout</button>
|
|
||||||
<h1>Home</h1>
|
|
||||||
|
|
||||||
{ user ? <div>
|
|
||||||
{ user }
|
|
||||||
<h2>Server time</h2>
|
|
||||||
<div id="error"> { error[0] ? <p>Error: { error[0] } </p> : <p></p> } </div>
|
|
||||||
<p>Currently set to { serverTime }. Setting to 0 will set it to now, and -1 will reset it. Time will still progress, based off of when you set this timestamp.</p>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={inputValue}
|
|
||||||
onChange={(event) => setInputValue(event.target.value)}
|
|
||||||
placeholder="Enter Server Time"
|
|
||||||
/>
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form></div>
|
|
||||||
: <p>Loading...</p> }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home;
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form {
|
|
||||||
width: 400px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 50px auto;
|
|
||||||
background-color: green;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form h1 {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px 0;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: black;
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="file"],
|
|
||||||
#login-form form input[type="password"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="submit"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: dodgerblue;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="submit"]:hover {
|
|
||||||
background-color: deepskyblue;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div p {
|
|
||||||
color: red;
|
|
||||||
grid-template-columns: auto auto auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div button {
|
|
||||||
width: 40%;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: blue;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div button:hover {
|
|
||||||
background-color: green;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import { useState } from 'react'
|
|
||||||
import './Import.css'
|
|
||||||
import Request from '../Request.jsx'
|
|
||||||
|
|
||||||
function Login() {
|
|
||||||
const error = useState(new URL(window.location).searchParams.get("message") || "");
|
|
||||||
const status = useState("");
|
|
||||||
const uid = useState((window.localStorage && window.localStorage.getItem("ew_uid")) || "");
|
|
||||||
let file=[], file1=[], file2=[], file3=[], password;
|
|
||||||
let has_imported = false;
|
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!file[0] || !file1[0] || has_imported || !password) return;
|
|
||||||
try {
|
|
||||||
has_imported = true;
|
|
||||||
let data = {
|
|
||||||
userdata: JSON.parse(await file[0].text()),
|
|
||||||
home: JSON.parse(await file1[0].text()),
|
|
||||||
missions: file2[0] ? JSON.parse(await file2[0].text()) : undefined,
|
|
||||||
sif_cards: file3[0] ? JSON.parse(await file3[0].text()) : undefined,
|
|
||||||
password: password,
|
|
||||||
jp: true
|
|
||||||
};
|
|
||||||
if (!data.userdata || !data.userdata.user || !data.userdata.user.id) {
|
|
||||||
error[1]("Incorrect user data file format");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!data.home || !data.home.home || !data.home.home.information_list) {
|
|
||||||
error[1]("Incorrect home data file format");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(data.missions) && data.missions) {
|
|
||||||
error[1]("Incorrect mission data file format");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(data.sif_cards) && data.sif_cards) {
|
|
||||||
error[1]("Incorrect sif card data file format");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let resp = await Request(
|
|
||||||
"/api/webui/import",
|
|
||||||
data
|
|
||||||
);
|
|
||||||
if (resp.result == "OK") {
|
|
||||||
status[1](<div><p>Account imported!</p><p>User id: {resp.uid}</p><p>Migration token: {resp.migration_token}</p></div>);
|
|
||||||
} else {
|
|
||||||
error[1](resp.message);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
error[1](e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="login-form">
|
|
||||||
<h1>Transfer</h1>
|
|
||||||
<form>
|
|
||||||
{ <p>{ status[0] } </p> }
|
|
||||||
<label htmlFor="id">User data file (required):</label>
|
|
||||||
<input type="file" id="id" name="id" onChange={(event) => {file = event.target.files}} accept="application/json"/>
|
|
||||||
|
|
||||||
<label htmlFor="file1">User Home data file (required):</label>
|
|
||||||
<input type="file" id="file1" name="file1" onChange={(event) => {file1 = event.target.files}} accept="application/json"/>
|
|
||||||
|
|
||||||
<label htmlFor="file2">User Missions data file (optional):</label>
|
|
||||||
<input type="file" id="file2" name="file2" onChange={(event) => {file2 = event.target.files}} accept="application/json"/>
|
|
||||||
|
|
||||||
<label htmlFor="file3">Sif cards data file (optional):</label>
|
|
||||||
<input type="file" id="file3" name="file3" onChange={(event) => {file3 = event.target.files}} accept="application/json"/>
|
|
||||||
|
|
||||||
<label htmlFor="password">Transfer passcode (game will not recognize special characters, only use letters and numbers or you will be locked out):</label>
|
|
||||||
<input type="password" id="password" name="password" onChange={(event) => {password = event.target.value}} />
|
|
||||||
|
|
||||||
<input type="submit" value="Submit" onClick={handleSubmit}/>
|
|
||||||
<div id="sub_div">
|
|
||||||
{ error[0] ? <p>Error: { error[0] }. Please reload the page and try again.</p> : <p></p> }
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Login;
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: #616161;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form {
|
|
||||||
width: 400px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: 50px auto;
|
|
||||||
background-color: green;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form h1 {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px 0;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form {
|
|
||||||
padding: 20px;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: black;
|
|
||||||
font-family: "Poppins", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="text"],
|
|
||||||
#login-form form input[type="password"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="submit"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: dodgerblue;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-form form input[type="submit"]:hover {
|
|
||||||
background-color: deepskyblue;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div p {
|
|
||||||
color: red;
|
|
||||||
grid-template-columns: auto auto auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div button {
|
|
||||||
width: 40%;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: blue;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sub_div button:hover {
|
|
||||||
background-color: green;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import { useState } from 'react'
|
|
||||||
import './Login.css'
|
|
||||||
import Request from '../Request.jsx'
|
|
||||||
|
|
||||||
function Login() {
|
|
||||||
const error = useState(new URL(window.location).searchParams.get("message") || "");
|
|
||||||
const uid = useState((window.localStorage && window.localStorage.getItem("ew_uid")) || "");
|
|
||||||
const password = useState("");
|
|
||||||
|
|
||||||
function showError(message) {
|
|
||||||
error[1](message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!uid[0] || !password[0]) {
|
|
||||||
showError("Missing userid/password");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isNaN(uid[0])) {
|
|
||||||
showError("UserID should be a number. (The \"Friend ID\" in your profile)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (window.localStorage) window.localStorage.setItem("ew_uid", uid[0]);
|
|
||||||
let resp = await Request(
|
|
||||||
"/api/webui/login",
|
|
||||||
{
|
|
||||||
uid: parseInt(uid[0]),
|
|
||||||
password: password[0]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (resp.result == "OK") {
|
|
||||||
window.location.href = "/home/";
|
|
||||||
} else {
|
|
||||||
showError(resp.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const import_user = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.location.href = "/import/";
|
|
||||||
}
|
|
||||||
|
|
||||||
const help = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.location.href = "/help/";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id="login-form">
|
|
||||||
<h1>Login</h1>
|
|
||||||
<form>
|
|
||||||
<label htmlFor="id">SIF2 ID:</label>
|
|
||||||
<input type="text" id="id" name="id" onChange={(event) => {uid[1](event.target.value)}} value={uid[0]} />
|
|
||||||
<label htmlFor="password">Transfer passcode:</label>
|
|
||||||
<input type="password" id="password" name="password" onChange={(event) => {password[1](event.target.value)}} />
|
|
||||||
<input type="submit" value="Submit" onClick={handleSubmit}/>
|
|
||||||
<div id="sub_div">
|
|
||||||
<button onClick={import_user}>Import User</button><br/><br/>
|
|
||||||
<button onClick={help}>Need help?</button><br/><br/>
|
|
||||||
{ error[0] ? <p>Error: { error[0] } </p> : <p></p> }
|
|
||||||
</div>
|
|
||||||
<p>EW Version 1.0.0 - <a href="https://git.ethanthesleepy.one/ethanaobrien/ew">View source</a> - <a href="https://git.ethanthesleepy.one/ethanaobrien/ew/src/branch/main/LICENSE">View license</a></p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Login;
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import Login from './login/Login.jsx'
|
|
||||||
import Home from './home/Home.jsx'
|
|
||||||
import Import from './import/Import.jsx'
|
|
||||||
import Help from './help/Help.jsx'
|
|
||||||
|
|
||||||
let Elem;
|
|
||||||
switch (window.location.pathname) {
|
|
||||||
case "/":
|
|
||||||
Elem = Login;
|
|
||||||
break;
|
|
||||||
case "/home/":
|
|
||||||
Elem = Home;
|
|
||||||
break;
|
|
||||||
case "/import/":
|
|
||||||
Elem = Import;
|
|
||||||
break;
|
|
||||||
case "/help/":
|
|
||||||
Elem = Help;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
window.location.pathname = "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Elem) {
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<Elem />
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
entryFileNames: 'index.js',
|
|
||||||
assetFileNames: 'index.css',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user