Merge branch 'master' of ssh://ssh.spbr.net:51122/spacebar/nex-account

This commit is contained in:
Maple Nebel 2026-07-05 21:00:57 +02:00
commit de7f525814
15 changed files with 278 additions and 45 deletions

10
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nex-account.iml" filepath="$PROJECT_DIR$/.idea/nex-account.iml" />
</modules>
</component>
</project>

11
.idea/nex-account.iml generated Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -0,0 +1,28 @@
{
"db_name": "PostgreSQL",
"query": "SELECT nex_key FROM nex_accounts WHERE pid = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "nex_key",
"type_info": "Bytea",
"origin": {
"Table": {
"table": "nex_accounts",
"name": "nex_key"
}
}
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
false
]
},
"hash": "1a808ced9b9a9c16ac33f512f654f610a6a9abba836191b99504d4c9ee2f7393"
}

View file

@ -1,17 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "insert into nex_accounts (\n pid, principal_name, email, nex_key\n ) values (\n $1, $2, $3, $4\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Varchar",
"Varchar",
"Bytea"
]
},
"nullable": []
},
"hash": "34f1dcfd8c6253ba5594808b8c37b3f4ad3d398edd233221b5983522b37f3579"
}

View file

@ -0,0 +1,14 @@
{
"db_name": "PostgreSQL",
"query": "delete from staged_nex_accounts where pid = $1",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": []
},
"hash": "52d2ac25e2c21058e059c39d1a071f9f505fdf2c2ece4023842704f97f60b0ca"
}

View file

@ -0,0 +1,40 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT pid, nex_key FROM staged_nex_accounts WHERE pid = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "pid",
"type_info": "Int4",
"origin": {
"Table": {
"table": "staged_nex_accounts",
"name": "pid"
}
}
},
{
"ordinal": 1,
"name": "nex_key",
"type_info": "Bytea",
"origin": {
"Table": {
"table": "staged_nex_accounts",
"name": "nex_key"
}
}
}
],
"parameters": {
"Left": [
"Int4"
]
},
"nullable": [
false,
false
]
},
"hash": "b7c02e50d638b6eb590a8587bf210fb7a48bb6bad15dd8a2cba6e2ea2413d40e"
}

View file

@ -0,0 +1,17 @@
{
"db_name": "PostgreSQL",
"query": "insert into nex_accounts (\n pid, principal_name, email, nex_key\n ) values (\n $1, $2, $3, $4\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Varchar",
"Varchar",
"Bytea"
]
},
"nullable": []
},
"hash": "d26bde57980b537b6529856653f38ccbbc965c030cd1fb957e8d9c1a8df10bdd"
}

View file

@ -0,0 +1,15 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO staged_nex_accounts (\n pid, nex_key\n ) values (\n $1, $2\n )",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int4",
"Bytea"
]
},
"nullable": []
},
"hash": "f58463b6ad7efbbd69d7ba4849bafd38d6db52734517874358585dc613fc56a6"
}

2
Cargo.lock generated
View file

@ -959,7 +959,7 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
[[package]]
name = "nex-account"
version = "0.1.3"
version = "0.2.1"
dependencies = [
"base64",
"bytemuck",

View file

@ -1,6 +1,6 @@
[package]
name = "nex-account"
version = "0.1.3"
version = "0.2.1"
edition = "2024"
publish = ["spbr"]

View file

@ -2,10 +2,20 @@ syntax = "proto3";
package nex_account;
message ActCreateInfoNoPid {
message ActCreateInfo {
string principal_name = 1;
bytes key = 2;
string email = 3;
int32 pid = 4;
}
message ActStageInfo {
bytes password = 1;
}
message ActStageReturn {
bytes nex_key = 1;
int32 pid = 2;
}
message PID {
@ -17,6 +27,7 @@ message NexKey {
}
service NexAccountService {
rpc CreateNewSequentialOrUpdateAndGetAccount(ActCreateInfoNoPid) returns (PID);
rpc CreateNewSequentialOrUpdateAndGetAccount(ActCreateInfo) returns (NexKey);
rpc GetNexKeyByPid(PID) returns (NexKey);
rpc StageNewAccount(ActStageInfo) returns (ActStageReturn);
}

View file

@ -7,11 +7,11 @@ use log::error;
use simplelog::{Config, TerminalMode};
use sqlx::{PgPool, query};
use tonic::{Response, transport::Server};
use crate::grpc::{
ActCreateInfoNoPid, NexKey, Pid,
ActCreateInfo, NexKey, Pid, ActStageInfo, ActStageReturn,
nex_account_service_server::{NexAccountService, NexAccountServiceServer},
};
use md5::{Digest, Md5};
pub mod grpc;
@ -45,47 +45,102 @@ pub async fn next_pid(pool: &PgPool) -> i32 {
}
}
/// use this on the sqlx error to print the error message and return a status indicating its not availible currently to the caller
/// use this on the sqlx error to print the error message and return a status indicating it's not available currently to the caller
fn db_neverfail(sql_err: sqlx::Error) -> tonic::Status {
error!("sqlx error occurred: {}", sql_err);
tonic::Status::aborted("error in database")
}
pub fn derive_key(pid: i32, password: &[u8]) -> [u8; 16] {
let iteration_count = 65000 + pid % 1024;
// we do one iteration out here to ensure the key is always 16 bytes
let mut key: [u8; 16] = {
let mut md5 = Md5::new();
md5.update(password);
md5.finalize().try_into().unwrap()
};
for _ in 1..iteration_count {
let mut md5 = Md5::new();
md5.update(key);
key = md5.finalize().try_into().unwrap();
}
key
}
#[tonic::async_trait]
impl NexAccountService for NexAccountServer {
async fn create_new_sequential_or_update_and_get_account(
&self,
request: tonic::Request<ActCreateInfoNoPid>,
) -> std::result::Result<tonic::Response<Pid>, tonic::Status> {
request: tonic::Request<ActCreateInfo>,
) -> Result<Response<NexKey>, tonic::Status> {
let request = request.into_inner();
let next_pid = next_pid(&self.pool).await;
let staged = query!(
r#"
SELECT pid, nex_key FROM staged_nex_accounts WHERE pid = $1
"#,
request.pid
)
.fetch_optional(&self.pool)
.await;
if request.key.len() != 16 {
return Err(tonic::Status::invalid_argument("invalid key length"));
match staged {
Ok(Some(row)) => {
println!("staged");
let pid = row.pid;
query!(
"insert into nex_accounts (
pid, principal_name, email, nex_key
) values (
$1, $2, $3, $4
)",
pid,
request.principal_name,
request.email,
row.nex_key
)
.execute(&self.pool)
.await
.map_err(db_neverfail)?;
// deletion query
query!(
"delete from staged_nex_accounts where pid = $1",
pid
)
.execute(&self.pool)
.await
.map_err(db_neverfail)?;
return Ok(Response::new(NexKey { key: row.nex_key }));
}
Ok(None) => {
println!("account is NOT a staged account, continuing...");
}
Err(e) => {
return Err(db_neverfail(e));
}
}
query!(
"insert into nex_accounts (
pid, principal_name, email, nex_key
) values (
$1, $2, $3, $4
)",
next_pid,
request.principal_name,
request.email,
&request.key[..]
let row = query!(
"SELECT nex_key FROM nex_accounts WHERE pid = $1",
request.pid
)
.execute(&self.pool)
.await
.map_err(db_neverfail)?;
Ok(Response::new(Pid { pid: next_pid }))
.fetch_one(&self.pool)
.await
.map_err(db_neverfail)?;
Ok(Response::new(NexKey { key: row.nex_key}))
}
async fn get_nex_key_by_pid(
&self,
request: tonic::Request<Pid>,
) -> std::result::Result<tonic::Response<NexKey>, tonic::Status> {
) -> Result<Response<NexKey>, tonic::Status> {
let pid = request.into_inner().pid;
let sql_res = query!("select nex_key from nex_accounts where pid = $1", pid)
@ -101,6 +156,35 @@ impl NexAccountService for NexAccountServer {
))
}
}
async fn stage_new_account(
&self,
request: tonic::Request<ActStageInfo>,
) -> Result<tonic::Response<ActStageReturn>, tonic::Status> {
let request = request.into_inner();
let next_pid = next_pid(&self.pool).await;
let nex_key = derive_key(next_pid, &request.password);
if nex_key.len() != 16 {
return Err(tonic::Status::invalid_argument("invalid key length"));
}
query!(
"INSERT INTO staged_nex_accounts (
pid, nex_key
) values (
$1, $2
)",
next_pid,
&nex_key
)
.execute(&self.pool)
.await
.map_err(db_neverfail)?;
Ok(Response::new(ActStageReturn{ pid: next_pid, nex_key: nex_key.into() }))
}
}
#[tokio::main]