forked from NextGraph/nextgraph-rs
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
10 KiB
280 lines
10 KiB
2 years ago
|
/*
|
||
12 months ago
|
* Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||
2 years ago
|
* All rights reserved.
|
||
|
* Licensed under the Apache License, Version 2.0
|
||
|
* <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||
|
* or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||
|
* at your option. All files in the project carrying such
|
||
|
* notice may not be copied, modified, or distributed except
|
||
|
* according to those terms.
|
||
|
*/
|
||
|
|
||
10 months ago
|
use std::collections::HashMap;
|
||
|
use std::fs::{read, File, OpenOptions};
|
||
|
use std::io::Write;
|
||
|
use std::path::{Path, PathBuf};
|
||
|
use std::sync::Mutex;
|
||
2 years ago
|
|
||
9 months ago
|
use crate::server_storage::admin::account::Account;
|
||
|
use crate::server_storage::admin::invitation::Invitation;
|
||
|
use crate::server_storage::admin::wallet::Wallet;
|
||
2 years ago
|
use crate::types::*;
|
||
9 months ago
|
use ng_net::server_broker::*;
|
||
10 months ago
|
use ng_net::types::*;
|
||
10 months ago
|
use ng_repo::errors::{ProtocolError, ServerError, StorageError};
|
||
10 months ago
|
use ng_repo::kcv_storage::KCVStorage;
|
||
10 months ago
|
use ng_repo::log::*;
|
||
10 months ago
|
use ng_repo::types::*;
|
||
9 months ago
|
use ng_storage_rocksdb::block_storage::RocksDbBlockStorage;
|
||
|
use ng_storage_rocksdb::kcv_storage::RocksDbKCVStorage;
|
||
2 years ago
|
|
||
9 months ago
|
pub(crate) struct RocksDbServerStorage {
|
||
9 months ago
|
wallet_storage: RocksDbKCVStorage,
|
||
|
accounts_storage: RocksDbKCVStorage,
|
||
9 months ago
|
//peers_storage: RocksDbKCVStorage,
|
||
10 months ago
|
peers_last_seq_path: PathBuf,
|
||
|
peers_last_seq: Mutex<HashMap<PeerId, u64>>,
|
||
9 months ago
|
block_storage: RocksDbBlockStorage,
|
||
|
core_storage: RocksDbKCVStorage,
|
||
2 years ago
|
}
|
||
|
|
||
9 months ago
|
impl RocksDbServerStorage {
|
||
9 months ago
|
pub(crate) fn open(
|
||
2 years ago
|
path: &mut PathBuf,
|
||
|
master_key: SymKey,
|
||
|
admin_invite: Option<BootstrapContentV0>,
|
||
|
) -> Result<Self, StorageError> {
|
||
|
// create/open the WALLET
|
||
|
let mut wallet_path = path.clone();
|
||
|
wallet_path.push("wallet");
|
||
|
std::fs::create_dir_all(wallet_path.clone()).unwrap();
|
||
2 years ago
|
log_debug!("opening wallet DB");
|
||
2 years ago
|
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
|
||
9 months ago
|
let wallet_storage = RocksDbKCVStorage::open(&wallet_path, master_key.slice().clone())?;
|
||
2 years ago
|
let wallet = Wallet::open(&wallet_storage);
|
||
|
|
||
|
// create/open the ACCOUNTS storage
|
||
|
let mut accounts_path = path.clone();
|
||
|
let accounts_key;
|
||
|
accounts_path.push("accounts");
|
||
|
|
||
|
if admin_invite.is_some() && !accounts_path.exists() && !wallet.exists_accounts_key() {
|
||
|
accounts_key = wallet.create_accounts_key()?;
|
||
|
std::fs::create_dir_all(accounts_path.clone()).unwrap();
|
||
2 years ago
|
let accounts_storage =
|
||
9 months ago
|
RocksDbKCVStorage::open(&accounts_path, accounts_key.slice().clone())?;
|
||
2 years ago
|
let symkey = SymKey::random();
|
||
|
let invite_code = InvitationCode::Admin(symkey.clone());
|
||
2 years ago
|
let _ = Invitation::create(
|
||
|
&invite_code,
|
||
|
0,
|
||
|
&Some("admin user automatically invited at first startup".to_string()),
|
||
|
&accounts_storage,
|
||
|
)?;
|
||
10 months ago
|
let invitation = ng_net::types::Invitation::V0(InvitationV0 {
|
||
2 years ago
|
code: Some(symkey),
|
||
|
name: Some("your NG Box, as admin".into()),
|
||
|
url: None,
|
||
|
bootstrap: admin_invite.unwrap(),
|
||
|
});
|
||
|
for link in invitation.get_urls() {
|
||
|
println!("The admin invitation link is: {}", link)
|
||
|
}
|
||
|
} else {
|
||
|
if admin_invite.is_some() {
|
||
|
log_warn!("Cannot add an admin invitation anymore, as it is not the first start of the server.");
|
||
|
}
|
||
|
accounts_key = wallet.get_or_create_accounts_key()?;
|
||
|
}
|
||
2 years ago
|
log_debug!("opening accounts DB");
|
||
2 years ago
|
std::fs::create_dir_all(accounts_path.clone()).unwrap();
|
||
|
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
|
||
10 months ago
|
let accounts_storage =
|
||
9 months ago
|
RocksDbKCVStorage::open(&accounts_path, accounts_key.slice().clone())?;
|
||
2 years ago
|
|
||
|
// create/open the PEERS storage
|
||
9 months ago
|
// log_debug!("opening peers DB");
|
||
|
// let peers_key = wallet.get_or_create_peers_key()?;
|
||
|
// let mut peers_path = path.clone();
|
||
|
// peers_path.push("peers");
|
||
|
// std::fs::create_dir_all(peers_path.clone()).unwrap();
|
||
|
// //TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
|
||
|
// let peers_storage = RocksDbKCVStorage::open(&peers_path, peers_key.slice().clone())?;
|
||
2 years ago
|
|
||
10 months ago
|
// creates the path for peers_last_seq
|
||
|
let mut peers_last_seq_path = path.clone();
|
||
|
peers_last_seq_path.push("peers_last_seq");
|
||
|
std::fs::create_dir_all(peers_last_seq_path.clone()).unwrap();
|
||
|
|
||
9 months ago
|
// opening block_storage
|
||
|
let mut blocks_path = path.clone();
|
||
|
blocks_path.push("blocks");
|
||
|
std::fs::create_dir_all(blocks_path.clone()).unwrap();
|
||
|
let blocks_key = wallet.get_or_create_blocks_key()?;
|
||
|
let block_storage = RocksDbBlockStorage::open(&blocks_path, *blocks_key.slice())?;
|
||
|
|
||
|
// create/open the PEERS storage
|
||
|
log_debug!("opening core DB");
|
||
|
let core_key = wallet.get_or_create_core_key()?;
|
||
|
let mut core_path = path.clone();
|
||
|
core_path.push("core");
|
||
|
std::fs::create_dir_all(core_path.clone()).unwrap();
|
||
|
//TODO redo the whole key passing mechanism in RKV so it uses zeroize all the way
|
||
|
let core_storage = RocksDbKCVStorage::open(&core_path, core_key.slice().clone())?;
|
||
|
|
||
|
Ok(RocksDbServerStorage {
|
||
2 years ago
|
wallet_storage,
|
||
|
accounts_storage,
|
||
9 months ago
|
//peers_storage,
|
||
10 months ago
|
peers_last_seq_path,
|
||
|
peers_last_seq: Mutex::new(HashMap::new()),
|
||
9 months ago
|
block_storage,
|
||
|
core_storage,
|
||
2 years ago
|
})
|
||
2 years ago
|
}
|
||
|
|
||
9 months ago
|
pub(crate) fn next_seq_for_peer(&self, peer: &PeerId, seq: u64) -> Result<(), ServerError> {
|
||
10 months ago
|
// for now we don't use the hashmap.
|
||
|
// TODO: let's see if the lock is even needed
|
||
|
let _ = self.peers_last_seq.lock();
|
||
|
|
||
|
let mut filename = self.peers_last_seq_path.clone();
|
||
|
filename.push(format!("{}", peer));
|
||
|
let file = read(filename.clone());
|
||
|
let mut file_save = match file {
|
||
|
Ok(ser) => {
|
||
|
let last: u64 = serde_bare::from_slice(&ser).map_err(|e| ServerError::FileError)?;
|
||
|
if last >= seq {
|
||
|
return Err(ServerError::SequenceMismatch);
|
||
|
}
|
||
|
OpenOptions::new()
|
||
|
.write(true)
|
||
|
.open(filename)
|
||
|
.map_err(|e| ServerError::FileError)?
|
||
|
}
|
||
|
Err(_) => File::create(filename).map_err(|e| ServerError::FileError)?,
|
||
|
};
|
||
|
let ser = serde_bare::to_vec(&seq).unwrap();
|
||
|
file_save
|
||
|
.write_all(&ser)
|
||
|
.map_err(|e| ServerError::FileError)?;
|
||
|
|
||
|
file_save.sync_data().map_err(|e| ServerError::FileError)?;
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
9 months ago
|
pub(crate) fn get_user(&self, user_id: PubKey) -> Result<bool, ProtocolError> {
|
||
2 years ago
|
log_debug!("get_user {user_id}");
|
||
|
Ok(Account::open(&user_id, &self.accounts_storage)?.is_admin()?)
|
||
|
}
|
||
9 months ago
|
pub(crate) fn add_user(&self, user_id: PubKey, is_admin: bool) -> Result<(), ProtocolError> {
|
||
2 years ago
|
log_debug!("add_user {user_id} is admin {is_admin}");
|
||
|
Account::create(&user_id, is_admin, &self.accounts_storage)?;
|
||
|
Ok(())
|
||
|
}
|
||
9 months ago
|
pub(crate) fn del_user(&self, user_id: PubKey) -> Result<(), ProtocolError> {
|
||
2 years ago
|
log_debug!("del_user {user_id}");
|
||
|
let acc = Account::open(&user_id, &self.accounts_storage)?;
|
||
|
acc.del()?;
|
||
|
Ok(())
|
||
|
}
|
||
9 months ago
|
pub(crate) fn list_users(&self, admins: bool) -> Result<Vec<PubKey>, ProtocolError> {
|
||
2 years ago
|
log_debug!("list_users that are admin == {admins}");
|
||
|
Ok(Account::get_all_users(admins, &self.accounts_storage)?)
|
||
|
}
|
||
9 months ago
|
pub(crate) fn list_invitations(
|
||
2 years ago
|
&self,
|
||
|
admin: bool,
|
||
|
unique: bool,
|
||
|
multi: bool,
|
||
|
) -> Result<Vec<(InvitationCode, u32, Option<String>)>, ProtocolError> {
|
||
|
log_debug!("list_invitations admin={admin} unique={unique} multi={multi}");
|
||
|
Ok(Invitation::get_all_invitations(
|
||
|
&self.accounts_storage,
|
||
|
admin,
|
||
|
unique,
|
||
|
multi,
|
||
|
)?)
|
||
|
}
|
||
9 months ago
|
pub(crate) fn add_invitation(
|
||
2 years ago
|
&self,
|
||
|
invite_code: &InvitationCode,
|
||
|
expiry: u32,
|
||
|
memo: &Option<String>,
|
||
|
) -> Result<(), ProtocolError> {
|
||
|
log_debug!("add_invitation {invite_code} expiry {expiry}");
|
||
|
Invitation::create(invite_code, expiry, memo, &self.accounts_storage)?;
|
||
|
Ok(())
|
||
|
}
|
||
9 months ago
|
pub(crate) fn get_invitation_type(&self, invite_code: [u8; 32]) -> Result<u8, ProtocolError> {
|
||
2 years ago
|
log_debug!("get_invitation_type {:?}", invite_code);
|
||
|
let inv = Invitation::open(&invite_code, &self.accounts_storage)?;
|
||
|
inv.get_type()
|
||
|
}
|
||
9 months ago
|
pub(crate) fn remove_invitation(&self, invite_code: [u8; 32]) -> Result<(), ProtocolError> {
|
||
2 years ago
|
log_debug!("remove_invitation {:?}", invite_code);
|
||
|
let inv = Invitation::open(&invite_code, &self.accounts_storage)?;
|
||
|
inv.del()?;
|
||
|
Ok(())
|
||
|
}
|
||
9 months ago
|
pub(crate) fn get_repo_pin_status(
|
||
10 months ago
|
&self,
|
||
|
overlay: &OverlayId,
|
||
|
repo: &RepoHash,
|
||
10 months ago
|
) -> Result<RepoPinStatus, ServerError> {
|
||
|
Err(ServerError::False)
|
||
10 months ago
|
//TODO: implement correctly !
|
||
10 months ago
|
// Ok(RepoPinStatus::V0(RepoPinStatusV0 {
|
||
|
// hash: repo.clone(),
|
||
10 months ago
|
|
||
10 months ago
|
// // only possible for RW overlays
|
||
|
// expose_outer: false,
|
||
10 months ago
|
|
||
10 months ago
|
// // list of topics that are subscribed to
|
||
|
// topics: vec![],
|
||
|
// }))
|
||
10 months ago
|
}
|
||
|
|
||
9 months ago
|
pub(crate) fn pin_repo(
|
||
10 months ago
|
&self,
|
||
|
overlay: &OverlayId,
|
||
|
repo: &RepoHash,
|
||
|
ro_topics: &Vec<TopicId>,
|
||
|
rw_topics: &Vec<PublisherAdvert>,
|
||
10 months ago
|
) -> Result<RepoOpened, ServerError> {
|
||
10 months ago
|
//TODO: implement correctly !
|
||
|
let mut opened = Vec::with_capacity(ro_topics.len() + rw_topics.len());
|
||
|
for topic in ro_topics {
|
||
|
opened.push((*topic).into());
|
||
|
}
|
||
|
for topic in rw_topics {
|
||
|
opened.push((*topic).into());
|
||
|
}
|
||
|
Ok(opened)
|
||
|
}
|
||
|
|
||
9 months ago
|
pub(crate) fn topic_sub(
|
||
10 months ago
|
&self,
|
||
|
overlay: &OverlayId,
|
||
|
repo: &RepoHash,
|
||
|
topic: &TopicId,
|
||
|
publisher: Option<&PublisherAdvert>,
|
||
10 months ago
|
) -> Result<TopicSubRes, ServerError> {
|
||
10 months ago
|
//TODO: implement correctly !
|
||
|
Ok(TopicSubRes::V0(TopicSubResV0 {
|
||
|
topic: topic.clone(),
|
||
|
known_heads: vec![],
|
||
|
publisher: publisher.is_some(),
|
||
|
}))
|
||
|
}
|
||
9 months ago
|
|
||
9 months ago
|
pub(crate) fn get_commit(
|
||
|
&self,
|
||
|
overlay: &OverlayId,
|
||
|
id: &ObjectId,
|
||
|
) -> Result<Vec<Block>, ServerError> {
|
||
9 months ago
|
//TODO: implement correctly !
|
||
|
Ok(vec![Block::dummy()])
|
||
|
}
|
||
2 years ago
|
}
|