Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

proposal.rs 3.0 KiB

7 månader sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. use crate::db::db::get_connection;
  2. use crate::ipfs::ipfs::IpfsService;
  3. use crate::schema::proposals;
  4. use crate::types::proposal::{Proposal, ProposalError, ProposalFile};
  5. use crate::utils::content::extract_summary;
  6. use crate::utils::ipfs::{upload_json_and_get_hash, read_json_via_cat as util_read_json_via_cat, DEFAULT_MAX_JSON_SIZE};
  7. use diesel::{RunQueryDsl, QueryDsl, ExpressionMethods};
  8. use ipfs_api_backend_actix::IpfsClient;
  9. use crate::types::ipfs::IpfsResult;
  10. const STORAGE_DIR: &str = "/puffpastry/proposals";
  11. const FILE_EXTENSION: &str = "json";
  12. pub struct ProposalService {
  13. client: IpfsClient,
  14. }
  15. impl IpfsService<ProposalFile> for ProposalService {
  16. type Err = ProposalError;
  17. async fn save(&mut self, proposal: ProposalFile) -> IpfsResult<String, Self::Err> {
  18. self.store_proposal_to_ipfs(proposal).await
  19. }
  20. async fn read(&mut self, hash: String) -> IpfsResult<ProposalFile, Self::Err> {
  21. self.read_proposal_file(hash).await
  22. }
  23. }
  24. impl ProposalService {
  25. pub fn new(client: IpfsClient) -> Self {
  26. Self { client }
  27. }
  28. async fn store_proposal_to_ipfs(&self, proposal: ProposalFile) -> IpfsResult<String, ProposalError> {
  29. let proposal_record = Proposal::new(
  30. proposal.name.clone(),
  31. Some(extract_summary(&proposal.content)),
  32. proposal.creator.clone(),
  33. );
  34. let hash = self.upload_to_ipfs(&proposal).await?;
  35. self.save_to_database(proposal_record.with_cid(hash.clone()).mark_as_current()).await?;
  36. // After saving the new proposal, set is_current = false for any proposal
  37. // that has previous_cid equal to this new hash
  38. {
  39. use crate::schema::proposals::dsl::{proposals as proposals_table, previous_cid as previous_cid_col, is_current as is_current_col};
  40. let mut conn = get_connection()
  41. .map_err(|e| ProposalError::DatabaseError(e))?;
  42. // Ignore the count result; if no rows match, that's fine
  43. let _ = diesel::update(proposals_table.filter(previous_cid_col.eq(hash.clone())))
  44. .set(is_current_col.eq(false))
  45. .execute(&mut conn)
  46. .map_err(|e| ProposalError::DatabaseError(Box::new(e)))?;
  47. }
  48. Ok(hash)
  49. }
  50. async fn upload_to_ipfs(&self, data: &ProposalFile) -> IpfsResult<String, ProposalError> {
  51. upload_json_and_get_hash::<ProposalFile, ProposalError>(&self.client, STORAGE_DIR, FILE_EXTENSION, data).await
  52. }
  53. async fn save_to_database(&self, proposal: Proposal) -> IpfsResult<(), ProposalError> {
  54. let mut conn = get_connection()
  55. .map_err(|e| ProposalError::DatabaseError(e))?;
  56. diesel::insert_into(proposals::table)
  57. .values(&proposal)
  58. .execute(&mut conn)
  59. .map_err(|e| ProposalError::DatabaseError(Box::new(e)))?;
  60. Ok(())
  61. }
  62. async fn read_proposal_file(&self, hash: String) -> IpfsResult<ProposalFile, ProposalError> {
  63. util_read_json_via_cat::<ProposalFile, ProposalError>(&self.client, &hash, DEFAULT_MAX_JSON_SIZE).await
  64. }
  65. }