|
- use crate::db::db::get_connection;
- use crate::ipfs::ipfs::IpfsService;
- use crate::schema::proposals;
- use crate::types::proposal::{Proposal, ProposalError, ProposalFile};
- use crate::utils::content::extract_summary;
- use crate::utils::ipfs::{upload_json_and_get_hash, read_json_via_cat as util_read_json_via_cat, DEFAULT_MAX_JSON_SIZE};
- use diesel::{RunQueryDsl, QueryDsl, ExpressionMethods};
- use ipfs_api_backend_actix::IpfsClient;
- use crate::types::ipfs::IpfsResult;
-
- const STORAGE_DIR: &str = "/puffpastry/proposals";
- const FILE_EXTENSION: &str = "json";
-
- pub struct ProposalService {
- client: IpfsClient,
- }
-
- impl IpfsService<ProposalFile> for ProposalService {
- type Err = ProposalError;
-
- async fn save(&mut self, proposal: ProposalFile) -> IpfsResult<String, Self::Err> {
- self.store_proposal_to_ipfs(proposal).await
- }
-
- async fn read(&mut self, hash: String) -> IpfsResult<ProposalFile, Self::Err> {
- self.read_proposal_file(hash).await
- }
- }
-
- impl ProposalService {
- pub fn new(client: IpfsClient) -> Self {
- Self { client }
- }
-
- async fn store_proposal_to_ipfs(&self, proposal: ProposalFile) -> IpfsResult<String, ProposalError> {
- let proposal_record = Proposal::new(
- proposal.name.clone(),
- Some(extract_summary(&proposal.content)),
- proposal.creator.clone(),
- );
-
- let hash = self.upload_to_ipfs(&proposal).await?;
- self.save_to_database(proposal_record.with_cid(hash.clone()).mark_as_current()).await?;
-
- // After saving the new proposal, set is_current = false for any proposal
- // that has previous_cid equal to this new hash
- {
- use crate::schema::proposals::dsl::{proposals as proposals_table, previous_cid as previous_cid_col, is_current as is_current_col};
- let mut conn = get_connection()
- .map_err(|e| ProposalError::DatabaseError(e))?;
-
- // Ignore the count result; if no rows match, that's fine
- let _ = diesel::update(proposals_table.filter(previous_cid_col.eq(hash.clone())))
- .set(is_current_col.eq(false))
- .execute(&mut conn)
- .map_err(|e| ProposalError::DatabaseError(Box::new(e)))?;
- }
-
- Ok(hash)
- }
-
- async fn upload_to_ipfs(&self, data: &ProposalFile) -> IpfsResult<String, ProposalError> {
- upload_json_and_get_hash::<ProposalFile, ProposalError>(&self.client, STORAGE_DIR, FILE_EXTENSION, data).await
- }
-
- async fn save_to_database(&self, proposal: Proposal) -> IpfsResult<(), ProposalError> {
- let mut conn = get_connection()
- .map_err(|e| ProposalError::DatabaseError(e))?;
-
- diesel::insert_into(proposals::table)
- .values(&proposal)
- .execute(&mut conn)
- .map_err(|e| ProposalError::DatabaseError(Box::new(e)))?;
-
- Ok(())
- }
-
- async fn read_proposal_file(&self, hash: String) -> IpfsResult<ProposalFile, ProposalError> {
- util_read_json_via_cat::<ProposalFile, ProposalError>(&self.client, &hash, DEFAULT_MAX_JSON_SIZE).await
- }
- }
|