diff --git a/.gitignore b/.gitignore index d45de36..838a2f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target .idea -.env \ No newline at end of file +.env +.DS_Store diff --git a/.sqlx/query-02d51edf65163f311dfa215da26acb2bc8c02735e2f79782c612d5fe0cf01042.json b/.sqlx/query-02d51edf65163f311dfa215da26acb2bc8c02735e2f79782c612d5fe0cf01042.json new file mode 100644 index 0000000..a32e4bf --- /dev/null +++ b/.sqlx/query-02d51edf65163f311dfa215da26acb2bc8c02735e2f79782c612d5fe0cf01042.json @@ -0,0 +1,27 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (\n pid,\n username,\n password,\n birthdate,\n timezone,\n email,\n country,\n language,\n marketing_allowed,\n off_device_allowed,\n region,\n gender,\n mii_data,\n verification_code\n ) VALUES (\n $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Varchar", + "Varchar", + "Date", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Bool", + "Int4", + "Bpchar", + "Varchar", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "02d51edf65163f311dfa215da26acb2bc8c02735e2f79782c612d5fe0cf01042" +} diff --git a/.sqlx/query-08c4a5721982ecd267ebc515b866c61aaef9395170430a58dcd7ac78f8cc75b2.json b/.sqlx/query-08c4a5721982ecd267ebc515b866c61aaef9395170430a58dcd7ac78f8cc75b2.json new file mode 100644 index 0000000..529604f --- /dev/null +++ b/.sqlx/query-08c4a5721982ecd267ebc515b866c61aaef9395170430a58dcd7ac78f8cc75b2.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(SELECT 1 FROM users WHERE username = $1 ) as exists", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "08c4a5721982ecd267ebc515b866c61aaef9395170430a58dcd7ac78f8cc75b2" +} diff --git a/.sqlx/query-248fc3dbfadb793f1f380486d9c1c95230d4d5a7ee3cb66d9382b7f0522c5e82.json b/.sqlx/query-248fc3dbfadb793f1f380486d9c1c95230d4d5a7ee3cb66d9382b7f0522c5e82.json new file mode 100644 index 0000000..ec4df8e --- /dev/null +++ b/.sqlx/query-248fc3dbfadb793f1f380486d9c1c95230d4d5a7ee3cb66d9382b7f0522c5e82.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET email_verified_since = $1 WHERE pid = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamp", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "248fc3dbfadb793f1f380486d9c1c95230d4d5a7ee3cb66d9382b7f0522c5e82" +} diff --git a/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json b/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json new file mode 100644 index 0000000..99a26f0 --- /dev/null +++ b/.sqlx/query-3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT nextval('pid_counter') as pid", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "3c9b1695f8ae49e4308c048de98c1c262351465b6f102e98912f67442a1f54d9" +} diff --git a/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json b/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json new file mode 100644 index 0000000..f831fdc --- /dev/null +++ b/.sqlx/query-48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4.json @@ -0,0 +1,66 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from tokens where pid = $1 and token_id = $2 and random = $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "token_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "assigned_ip", + "type_info": "Inet" + }, + { + "ordinal": 3, + "name": "random", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "token_type", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "creation_time", + "type_info": "Timestamp" + }, + { + "ordinal": 6, + "name": "expires", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "title_id", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int4", + "Int8", + "Int4" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + true + ] + }, + "hash": "48710e0b87742cc3fef816b3c95604095f71324011e7093ec37af15da8c158f4" +} diff --git a/.sqlx/query-566221b869f293e6c721e2e8bbf3087943e5816a98cdce151302055c58bd1183.json b/.sqlx/query-566221b869f293e6c721e2e8bbf3087943e5816a98cdce151302055c58bd1183.json new file mode 100644 index 0000000..409a9b7 --- /dev/null +++ b/.sqlx/query-566221b869f293e6c721e2e8bbf3087943e5816a98cdce151302055c58bd1183.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "select pid, username from users where username = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "566221b869f293e6c721e2e8bbf3087943e5816a98cdce151302055c58bd1183" +} diff --git a/.sqlx/query-5bd26d4c9e701bde77ce598fea2ce0b98ea2c7de03e71ac704c3ba047162c0b2.json b/.sqlx/query-5bd26d4c9e701bde77ce598fea2ce0b98ea2c7de03e71ac704c3ba047162c0b2.json new file mode 100644 index 0000000..56b26e0 --- /dev/null +++ b/.sqlx/query-5bd26d4c9e701bde77ce598fea2ce0b98ea2c7de03e71ac704c3ba047162c0b2.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "select address, port from nex_servers where game_server_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "address", + "type_info": "Inet" + }, + { + "ordinal": 1, + "name": "port", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "5bd26d4c9e701bde77ce598fea2ce0b98ea2c7de03e71ac704c3ba047162c0b2" +} diff --git a/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json b/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json new file mode 100644 index 0000000..3c3dea3 --- /dev/null +++ b/.sqlx/query-606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100.json @@ -0,0 +1,130 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT * FROM users WHERE username = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "password", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "birthdate", + "type_info": "Date" + }, + { + "ordinal": 4, + "name": "timezone", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email_verified_since", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "country", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "language", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "gender", + "type_info": "Bpchar" + }, + { + "ordinal": 10, + "name": "marketing_allowed", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "off_device_allowed", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "region", + "type_info": "Int4" + }, + { + "ordinal": 13, + "name": "mii_data", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "account_level", + "type_info": "Int4" + }, + { + "ordinal": 15, + "name": "creation_date", + "type_info": "Timestamp" + }, + { + "ordinal": 16, + "name": "updated", + "type_info": "Timestamp" + }, + { + "ordinal": 17, + "name": "nex_password", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "verification_code", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true + ] + }, + "hash": "606364c79e0990deb07dfbe6c32b3d302d083ec5333f3a5ce04113c38a041100" +} diff --git a/.sqlx/query-6c1df0b05553305ba847f571a5859bf11353f28c25e4f81268e9379b5b2cb375.json b/.sqlx/query-6c1df0b05553305ba847f571a5859bf11353f28c25e4f81268e9379b5b2cb375.json new file mode 100644 index 0000000..f449706 --- /dev/null +++ b/.sqlx/query-6c1df0b05553305ba847f571a5859bf11353f28c25e4f81268e9379b5b2cb375.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "select nex_password from users where pid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "nex_password", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false + ] + }, + "hash": "6c1df0b05553305ba847f571a5859bf11353f28c25e4f81268e9379b5b2cb375" +} diff --git a/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json b/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json new file mode 100644 index 0000000..12f43d6 --- /dev/null +++ b/.sqlx/query-93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421.json @@ -0,0 +1,130 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT * FROM users WHERE pid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "password", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "birthdate", + "type_info": "Date" + }, + { + "ordinal": 4, + "name": "timezone", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email_verified_since", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "country", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "language", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "gender", + "type_info": "Bpchar" + }, + { + "ordinal": 10, + "name": "marketing_allowed", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "off_device_allowed", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "region", + "type_info": "Int4" + }, + { + "ordinal": 13, + "name": "mii_data", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "account_level", + "type_info": "Int4" + }, + { + "ordinal": 15, + "name": "creation_date", + "type_info": "Timestamp" + }, + { + "ordinal": 16, + "name": "updated", + "type_info": "Timestamp" + }, + { + "ordinal": 17, + "name": "nex_password", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "verification_code", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true + ] + }, + "hash": "93960465bbf8f670891d49b95fc52257c0ef596eee2d9a0e6a7d0aad03de4421" +} diff --git a/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json b/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json new file mode 100644 index 0000000..2da8161 --- /dev/null +++ b/.sqlx/query-9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1.json @@ -0,0 +1,66 @@ +{ + "db_name": "PostgreSQL", + "query": "select * from tokens where pid = $1 and token_id = $2 and random =$3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "token_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "assigned_ip", + "type_info": "Inet" + }, + { + "ordinal": 3, + "name": "random", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "token_type", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "creation_time", + "type_info": "Timestamp" + }, + { + "ordinal": 6, + "name": "expires", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "title_id", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int4", + "Int8", + "Int4" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + true + ] + }, + "hash": "9d3cee43a86cead9a6d078abc1266fc2a97ac6e25a9733d1d20faf555c67abe1" +} diff --git a/.sqlx/query-b16ba4b6c1b7d1c207e94515268c3bbd90d4bcf92d20203cba5afb2664f4bb8a.json b/.sqlx/query-b16ba4b6c1b7d1c207e94515268c3bbd90d4bcf92d20203cba5afb2664f4bb8a.json new file mode 100644 index 0000000..18d1e88 --- /dev/null +++ b/.sqlx/query-b16ba4b6c1b7d1c207e94515268c3bbd90d4bcf92d20203cba5afb2664f4bb8a.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT verification_code FROM users WHERE pid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "verification_code", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + true + ] + }, + "hash": "b16ba4b6c1b7d1c207e94515268c3bbd90d4bcf92d20203cba5afb2664f4bb8a" +} diff --git a/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json b/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json new file mode 100644 index 0000000..da6f233 --- /dev/null +++ b/.sqlx/query-c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(select 1 from users where pid = $1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + null + ] + }, + "hash": "c8d50662530cac49c4261fb321cd15f9e4bafdfca12d2130a873d44a88dd435b" +} diff --git a/.sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json b/.sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json new file mode 100644 index 0000000..7c1cae0 --- /dev/null +++ b/.sqlx/query-e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "insert into tokens (token_type, pid, title_id)\n values ($1, $2, $3) returning token_id, random", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "token_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "random", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Int4", + "Int4", + "Varchar" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "e5a2f7f28c3d7b9524d3dce48a9e47d6180ff634ebf59f3a1efd92b797170ac2" +} diff --git a/.sqlx/query-eafa97669e8ec04f1dc0a8e05d417f12ffeeb26a5eabca323abfed0e1d7bcbea.json b/.sqlx/query-eafa97669e8ec04f1dc0a8e05d417f12ffeeb26a5eabca323abfed0e1d7bcbea.json new file mode 100644 index 0000000..fe69d0a --- /dev/null +++ b/.sqlx/query-eafa97669e8ec04f1dc0a8e05d417f12ffeeb26a5eabca323abfed0e1d7bcbea.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "select pid, username from users where pid = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pid", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "eafa97669e8ec04f1dc0a8e05d417f12ffeeb26a5eabca323abfed0e1d7bcbea" +} diff --git a/Cargo.lock b/Cargo.lock index 9f3a89b..b4679e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "hmac", "juniper", "juniper_rocket", + "lettre", "log", "md-5", "mii", @@ -27,6 +28,7 @@ dependencies = [ "once_cell", "prost", "quick-xml", + "rand", "rocket", "serde", "serde_json", @@ -64,6 +66,18 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -470,6 +484,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.5", + "stacker", +] + [[package]] name = "cipher" version = "0.4.4" @@ -713,6 +737,22 @@ dependencies = [ "serde", ] +[[package]] +name = "email-encoding" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" +dependencies = [ + "base64", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -1072,6 +1112,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashbrown" @@ -1144,6 +1188,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + [[package]] name = "http" version = "0.2.12" @@ -1639,6 +1694,31 @@ dependencies = [ "spin", ] +[[package]] +name = "lettre" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759bc2b8eabb6a30b235d6f716f7f36479f4b38cbe65b8747aefee51f89e8437" +dependencies = [ + "base64", + "chumsky", + "email-encoding", + "email_address", + "fastrand", + "futures-util", + "hostname", + "httpdate", + "idna", + "mime", + "native-tls", + "nom", + "percent-encoding", + "quoted_printable", + "socket2", + "tokio", + "url", +] + [[package]] name = "libc" version = "0.2.170" @@ -1861,6 +1941,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2256,6 +2345,15 @@ dependencies = [ "prost", ] +[[package]] +name = "psm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +dependencies = [ + "cc", +] + [[package]] name = "quick-xml" version = "0.37.2" @@ -2275,6 +2373,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quoted_printable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" + [[package]] name = "rand" version = "0.8.5" @@ -3029,6 +3133,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "state" version = "0.6.0" @@ -3781,6 +3898,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + [[package]] name = "windows-registry" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 59de8b7..7252c2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,8 +44,10 @@ juniper_rocket = "0.9.0" tonic = "0.12.3" prost = "0.13.4" +lettre = "0.11.15" +rand = "0.8.5" [build-dependencies] -tonic-build = "0.12.3" \ No newline at end of file +tonic-build = "0.12.3" diff --git a/res/email/confirmationTemplate.html b/res/email/confirmationTemplate.html new file mode 100644 index 0000000..5387c5b --- /dev/null +++ b/res/email/confirmationTemplate.html @@ -0,0 +1,203 @@ + + + + + + + + +
Hello {{username}}! Your Splatfestival Network ID activation is almost complete. Please click the link in this email to confirm your e-mail address and complete the activation process.
+ + + + +
+ + + + +
+ + + + + + +
  + + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + +
+ + + +
 
+ + + + + + +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ Hello {{username}}! +
 
+ Your Splatfestival Network ID activation is almost complete. +
 
 
+ Enter the following 6-digit code on your console: +
 
+ {{confirmation-code}} +
 
+ We hope you have fun using our services! +
 
+ The SPFN team +
 
+
 
+
 
+ Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server or make a post on our Forum. +
 
+
+
 
+
+
+ + \ No newline at end of file diff --git a/src/account/account.rs b/src/account/account.rs index 6ceead2..e83a1be 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -54,7 +54,8 @@ pub struct User { pub mii_data: String, pub creation_date: NaiveDateTime, pub updated: NaiveDateTime, - pub nex_password: String + pub nex_password: String, + pub verification_code: Option } fn generate_nintendo_hash(pid: i32, text_password: &str) -> String{ diff --git a/src/email.rs b/src/email.rs new file mode 100644 index 0000000..255fe49 --- /dev/null +++ b/src/email.rs @@ -0,0 +1,38 @@ +use lettre::transport::smtp::authentication::Credentials; +use lettre::{Message, SmtpTransport, Transport}; +use std::env; +use std::fs; + +pub async fn send_verification_email(to: &str, code: i32, username: &str) -> Result<(), String> { + let smtp_user = env::var("SMTP_USER").map_err(|_| "SMTP_USER not set".to_string())?; + let smtp_pass = env::var("SMTP_PASS").map_err(|_| "SMTP_PASS not set".to_string())?; + let smtp_server = env::var("SMTP_SERVER").map_err(|_| "SMTP_SERVER not set".to_string())?; + + // Load template + let template = fs::read_to_string("res/email/confirmationTemplate.html") + .map_err(|e| format!("Failed to read email template: {}", e))?; + + // Replace placeholders + let body = template + .replace("{{username}}", username) + .replace("{{confirmation-code}}", &format!("{:06}", code)); + + let email = Message::builder() + .from(smtp_user.parse().unwrap()) + .to(to.parse().unwrap()) + .subject("Your Verification Code") + .header(lettre::message::header::ContentType::TEXT_HTML) + .body(body) + .map_err(|e| e.to_string())?; + + let creds = Credentials::new(smtp_user, smtp_pass); + + let mailer = SmtpTransport::relay(&smtp_server) + .map_err(|e| e.to_string())? + .credentials(creds) + .build(); + + mailer.send(&email).map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 26a766a..071c5e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ mod data_wrapper; #[deprecated] mod grpc; mod graphql; +mod email; type Pool = sqlx::Pool; @@ -109,7 +110,8 @@ async fn launch() -> _ { nnid::agreements::get_agreement, nnid::timezones::get_timezone, nnid::person_exists::person_exists, - nnid::email::validate, + nnid::support::validate, + nnid::support::verify_email, nnid::people::create_account, nnid::people::get_own_profile, nnid::oauth::generate_token::generate_token, diff --git a/src/nnid/email.rs b/src/nnid/email.rs deleted file mode 100644 index a229bd6..0000000 --- a/src/nnid/email.rs +++ /dev/null @@ -1,11 +0,0 @@ -use rocket::{post, FromForm}; -use rocket::form::Form; - -#[derive(FromForm)] -struct ValidateEmailInput{ - email: String, -} -#[post("/v1/api/support/validate/email", data="")] -pub fn validate(data: Form){ - -} \ No newline at end of file diff --git a/src/nnid/mod.rs b/src/nnid/mod.rs index e0c1f2f..a86c6f7 100644 --- a/src/nnid/mod.rs +++ b/src/nnid/mod.rs @@ -2,9 +2,9 @@ pub mod devices; pub mod agreements; pub mod timezones; pub mod person_exists; -pub mod email; pub mod oauth; mod pid_distribution; pub mod people; pub mod provider; pub mod mapped_ids; +pub mod support; diff --git a/src/nnid/people.rs b/src/nnid/people.rs index 5450d77..3ae5895 100644 --- a/src/nnid/people.rs +++ b/src/nnid/people.rs @@ -18,6 +18,8 @@ use crate::nnid::pid_distribution::next_pid; use crate::nnid::timezones::{OFFSET_FROM_TIMEZONE, ZONE_TO_TIMEZONES}; use crate::Pool; use crate::xml::{Xml, YesNoVal}; +use crate::email::send_verification_email; +use rand::Rng; static S3_URL_STRING: Lazy> = Lazy::new(|| env::var("S3_URL").expect("S3_URL not specified").into_boxed_str() @@ -114,6 +116,8 @@ pub async fn create_account(database: &State, data: Xml, data: Xml, data: Xml){ + if let Err(e) = send_verification_email(&*data.email, 123456, "Andrea Test Username").await { + println!("Failed to send verification email: {e}"); + } +} + +#[get("/v1/api/support/email_confirmation//")] +pub async fn verify_email(database: &State, pid: i32, code: i32) -> Result<(), Errors<'static>> { + let db = database.inner(); + + let result = sqlx::query!( + "SELECT verification_code FROM users WHERE pid = $1", + pid + ) + .fetch_optional(db) + .await; + + let Ok(Some(record)) = result else { + return Err(BAD_CODE_ERROR); + }; + + if let Some(stored_code) = record.verification_code { + if stored_code == code { + // Set email_verified_since to NOW + let now = Utc::now().naive_utc(); + let update_result = sqlx::query!( + "UPDATE users SET email_verified_since = $1 WHERE pid = $2", + now, + pid + ) + .execute(db) + .await; + + if update_result.is_err() { + return Err(BAD_CODE_ERROR); // fallback in case the update fails + } + + return Ok(()); // Success + } + } + + Err(BAD_CODE_ERROR) +}