diff --git a/.github/workflows/deploy-to-production-on-merge-to-main.yaml b/.github/workflows/deploy-to-production-on-merge-to-main.yaml index 977df301..26ffc207 100644 --- a/.github/workflows/deploy-to-production-on-merge-to-main.yaml +++ b/.github/workflows/deploy-to-production-on-merge-to-main.yaml @@ -23,6 +23,11 @@ jobs: name: build-musl - run: chmod +x target/x86_64-unknown-linux-musl/release/hot-or-not-web-leptos-ssr - uses: superfly/flyctl-actions/setup-flyctl@master + - name: Set cloudflare token + run: fly secrets set CF_TOKEN=$CF_TOKEN --app "hot-or-not-web-leptos-ssr" --stage + env: + CF_TOKEN: ${{ secrets.CLOUDFLARE_STREAM_IMAGES_ANALYTICS_READ_WRITE_SECRET }} + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} - name: Deploy a docker container to Fly.io run: flyctl deploy --remote-only env: diff --git a/Cargo.lock b/Cargo.lock index e1e0327e..29e2a037 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1494,6 +1494,7 @@ dependencies = [ "http", "ic-agent", "icondata", + "k256", "leptos", "leptos-use", "leptos_axum", @@ -2013,9 +2014,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", diff --git a/Cargo.toml b/Cargo.toml index 051e96ed..91347fb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ gloo = { version = "0.11.0", features = ["futures", "net", "net"] } once_cell = "1.19.0" async-trait = "0.1.77" web-time = "1.0.0" +k256 = { version = "0.13.3", default-features = false, features = ["std"] } [build-dependencies] candid_parser = "0.1.1" diff --git a/did/individual_user_template.did b/did/individual_user_template.did index 33cbd9fe..90b947f0 100644 --- a/did/individual_user_template.did +++ b/did/individual_user_template.did @@ -89,6 +89,7 @@ type HotOrNotOutcomePayoutEvent = variant { }; type IndividualUserTemplateInitArgs = record { known_principal_ids : opt vec record { KnownPrincipalType; principal }; + version : text; url_to_send_canister_metrics_to : opt text; profile_owner : opt principal; upgrade_version_number : opt nat64; @@ -102,6 +103,7 @@ type KnownPrincipalType = variant { CanisterIdDataBackup; CanisterIdPostCache; CanisterIdSNSController; + CanisterIdSnsGovernance; UserIdGlobalSuperAdmin; }; type MintEvent = variant { @@ -129,6 +131,7 @@ type PlacedBetDetail = record { }; type Post = record { id : nat64; + is_nsfw : bool; status : PostStatus; share_count : nat64; hashtags : vec text; @@ -143,6 +146,7 @@ type Post = record { }; type PostDetailsForFrontend = record { id : nat64; + is_nsfw : bool; status : PostStatus; home_feed_ranking_score : nat64; hashtags : vec text; @@ -151,6 +155,7 @@ type PostDetailsForFrontend = record { description : text; total_view_count : nat64; created_by_display_name : opt text; + created_at : SystemTime; created_by_unique_user_name : opt text; video_uid : text; created_by_user_principal_id : principal; @@ -159,6 +164,7 @@ type PostDetailsForFrontend = record { created_by_profile_photo_url : opt text; }; type PostDetailsFromFrontend = record { + is_nsfw : bool; hashtags : vec text; description : text; video_uid : text; @@ -267,7 +273,7 @@ type UserProfileUpdateDetailsFromFrontend = record { profile_picture_url : opt text; display_name : opt text; }; -service : (IndividualUserTemplateInitArgs) -> { +service : { add_post_v2 : (PostDetailsFromFrontend) -> (Result); backup_data_to_backup_canister : (principal, principal) -> (); bet_on_currently_viewing_post : (PlaceBetArg) -> (Result_1); @@ -293,12 +299,15 @@ service : (IndividualUserTemplateInitArgs) -> { get_profile_details : () -> (UserProfileDetailsForFrontend) query; get_rewarded_for_referral : (principal, principal) -> (); get_rewarded_for_signing_up : () -> (); + get_stable_memory_size : () -> (nat32) query; get_user_caniser_cycle_balance : () -> (nat) query; get_user_utility_token_transaction_history_with_pagination : ( nat64, nat64, ) -> (Result_5) query; get_utility_token_balance : () -> (nat64) query; + get_version : () -> (text) query; + get_version_number : () -> (nat64) query; get_well_known_principal_value : (KnownPrincipalType) -> ( opt principal, ) query; diff --git a/did/post_cache.did b/did/post_cache.did index f168a441..db5f346b 100644 --- a/did/post_cache.did +++ b/did/post_cache.did @@ -7,6 +7,7 @@ type KnownPrincipalType = variant { CanisterIdDataBackup; CanisterIdPostCache; CanisterIdSNSController; + CanisterIdSnsGovernance; UserIdGlobalSuperAdmin; }; type PostCacheInitArgs = record { @@ -23,7 +24,7 @@ type TopPostsFetchError = variant { InvalidBoundsPassed; ExceededMaxNumberOfItemsAllowedInOneRequest; }; -service : (PostCacheInitArgs) -> { +service : { get_top_posts_aggregated_from_canisters_on_this_network_for_home_feed : ( nat64, nat64, diff --git a/did/user_index.did b/did/user_index.did index fe0b58d0..8afd9e09 100644 --- a/did/user_index.did +++ b/did/user_index.did @@ -23,6 +23,7 @@ type KnownPrincipalType = variant { CanisterIdDataBackup; CanisterIdPostCache; CanisterIdSNSController; + CanisterIdSnsGovernance; UserIdGlobalSuperAdmin; }; type RejectionCode = variant { @@ -38,7 +39,9 @@ type Result = variant { Ok : record { CanisterStatusResponse }; Err : record { RejectionCode; text }; }; -type Result_1 = variant { Ok; Err : SetUniqueUsernameError }; +type Result_1 = variant { Ok : text; Err : text }; +type Result_2 = variant { Ok; Err : text }; +type Result_3 = variant { Ok; Err : SetUniqueUsernameError }; type SetUniqueUsernameError = variant { UsernameAlreadyTaken; SendingCanisterDoesNotMatchUserCanisterId; @@ -50,6 +53,7 @@ type SystemTime = record { }; type UpgradeStatus = record { version_number : nat64; + version : text; last_run_on : SystemTime; failed_canister_ids : vec record { principal; principal; text }; successful_upgrade_count : nat32; @@ -62,12 +66,18 @@ type UserAccessRole = variant { }; type UserIndexInitArgs = record { known_principal_ids : opt vec record { KnownPrincipalType; principal }; + version : text; access_control_map : opt vec record { principal; vec UserAccessRole }; }; -service : (UserIndexInitArgs) -> { +service : { + are_signups_enabled : () -> (bool) query; backup_all_individual_user_canisters : () -> (); + get_current_list_of_all_well_known_principal_values : () -> ( + vec record { KnownPrincipalType; principal }, + ) query; get_index_details_is_user_name_taken : (text) -> (bool) query; get_index_details_last_upgrade_status : () -> (UpgradeStatus) query; + get_list_of_available_canisters : () -> (vec principal) query; get_requester_principals_canister_id_create_if_not_exists_and_optionally_allow_referrer : ( opt principal, ) -> (principal); @@ -86,16 +96,20 @@ service : (UserIndexInitArgs) -> { principal, text, ) -> (); + reset_user_individual_canisters : (vec principal) -> (Result_1); set_permission_to_upgrade_individual_canisters : (bool) -> (text); start_upgrades_for_individual_canisters : () -> (text); + toggle_signups_enabled : () -> (Result_2); update_index_with_unique_user_name_corresponding_to_user_principal_id : ( text, principal, - ) -> (Result_1); + ) -> (Result_3); upgrade_specific_individual_user_canister_with_latest_wasm : ( principal, principal, opt CanisterInstallMode, - bool, ) -> (text); + validate_reset_user_individual_canisters : (vec principal) -> ( + Result_1, + ) query; } \ No newline at end of file diff --git a/fly.toml b/fly.toml index 0c6e13c7..843117d4 100644 --- a/fly.toml +++ b/fly.toml @@ -15,3 +15,6 @@ auto_stop_machines = true auto_start_machines = true min_machines_running = 0 processes = ["app"] + +[env] +CF_ACCOUNT_ID="a209c523d2d9646cc56227dbe6ce3ede" \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 8ba08799..4d742356 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,10 @@ use crate::{ err::ServerErrorPage, post_view::PostView, profile::ProfileView, root::RootPage, upload::UploadPostPage, }, - state::canisters::Canisters, + state::{ + auth::AuthClient, + canisters::{do_canister_auth, Canisters}, + }, }; use leptos::*; use leptos_meta::*; @@ -15,6 +18,10 @@ pub fn App() -> impl IntoView { // Provides context that manages stylesheets, titles, meta tags, etc. provide_meta_context(); provide_context(Canisters::default()); + provide_context(Resource::local( + || (), + |_| do_canister_auth(AuthClient::default()), + )); view! { diff --git a/src/canister/generated.rs b/src/canister/generated.rs new file mode 100644 index 00000000..5614f263 --- /dev/null +++ b/src/canister/generated.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/did/mod.rs")); diff --git a/src/canister/mod.rs b/src/canister/mod.rs index a5bbca56..69cfb6ae 100644 --- a/src/canister/mod.rs +++ b/src/canister/mod.rs @@ -1,8 +1,6 @@ //! Auto generated bindings for canisters #[allow(clippy::all)] -mod generated { - include!(concat!(env!("OUT_DIR"), "/did/mod.rs")); -} +mod generated; pub mod utils; pub use generated::*; diff --git a/src/consts.rs b/src/consts.rs index 5a51afef..720741c2 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -6,3 +6,5 @@ pub const FALLBACK_PROPIC_BASE: &str = "https://api.dicebear.com/7.x/big-smile/s pub const CF_WATERMARK_UID: &str = "28c721e45583a215d7b2ec1ae16e2679"; pub static CF_BASE_URL: Lazy = Lazy::new(|| Url::parse("https://api.cloudflare.com/client/v4/").unwrap()); +pub static AUTH_URL: Lazy = + Lazy::new(|| Url::parse("https://hot-or-not-auth.fly.dev/").unwrap()); diff --git a/src/page/post_view/mod.rs b/src/page/post_view/mod.rs index 639b847b..70e6e7ee 100644 --- a/src/page/post_view/mod.rs +++ b/src/page/post_view/mod.rs @@ -11,7 +11,7 @@ use leptos_router::*; use crate::{ component::spinner::FullScreenSpinner, - state::canisters::Canisters, + state::canisters::unauth_canisters, try_or_redirect, utils::route::{failure_redirect, go_to_root}, }; @@ -123,7 +123,7 @@ pub fn PostViewWithUpdates(initial_post: Option) -> impl IntoView { let current_idx = create_rw_signal(0); let fetch_video_uids = Resource::once(move || async move { - let canisters = expect_context::(); + let canisters = unauth_canisters(); let cursor = fetch_cursor.get_untracked(); let fetch_stream = VideoFetchStream::new(&canisters, cursor); let chunks = try_or_redirect!(fetch_stream.fetch_post_uids_chunked(8).await); diff --git a/src/page/post_view/video_iter.rs b/src/page/post_view/video_iter.rs index 8547153c..ff3a9210 100644 --- a/src/page/post_view/video_iter.rs +++ b/src/page/post_view/video_iter.rs @@ -10,7 +10,7 @@ use crate::{ use super::{error::PostViewError, FetchCursor}; pub async fn get_post_uid( - canisters: &Canisters, + canisters: &Canisters, user_canister: Principal, post_id: u64, ) -> Result, PostViewError> { @@ -51,12 +51,12 @@ pub struct PostDetails { } pub struct VideoFetchStream<'a> { - canisters: &'a Canisters, + canisters: &'a Canisters, cursor: FetchCursor, } impl<'a> VideoFetchStream<'a> { - pub fn new(canisters: &'a Canisters, cursor: FetchCursor) -> Self { + pub fn new(canisters: &'a Canisters, cursor: FetchCursor) -> Self { Self { canisters, cursor } } diff --git a/src/page/profile/ic.rs b/src/page/profile/ic.rs index 38601f0d..7654ca26 100644 --- a/src/page/profile/ic.rs +++ b/src/page/profile/ic.rs @@ -12,7 +12,7 @@ use crate::{ }, component::bullet_loader::BulletLoader, consts::FALLBACK_PROPIC_BASE, - state::canisters::Canisters, + state::canisters::unauth_canisters, }; #[derive(Serialize, Deserialize, Clone)] @@ -173,7 +173,7 @@ where I: 'static, C: Fn(R) -> Option> + 'static, { - let canisters = expect_context::(); + let canisters = unauth_canisters(); futures::stream::try_unfold( (getter, conv, canisters, 0usize, false), move |(getter, conv, canisters, mut cursor, mut ended)| async move { diff --git a/src/page/profile/mod.rs b/src/page/profile/mod.rs index 52d5af02..6a88877e 100644 --- a/src/page/profile/mod.rs +++ b/src/page/profile/mod.rs @@ -7,7 +7,7 @@ use leptos::*; use leptos_icons::*; use leptos_router::*; -use crate::{component::spinner::FullScreenSpinner, state::canisters::Canisters}; +use crate::{component::spinner::FullScreenSpinner, state::canisters::unauth_canisters}; use ic::ProfileDetails; use posts::ProfilePosts; @@ -117,7 +117,7 @@ pub fn ProfileView() -> impl IntoView { }; let user_details = create_resource(principal_or_username, |principal_or_username| async move { - let canisters = expect_context::(); + let canisters = unauth_canisters(); let user_index = canisters.user_index(); let user_canister = match principal_or_username? { Ok(p) => user_index diff --git a/src/page/profile/speculation.rs b/src/page/profile/speculation.rs index 8014b3b3..193d5f73 100644 --- a/src/page/profile/speculation.rs +++ b/src/page/profile/speculation.rs @@ -5,7 +5,7 @@ use leptos_icons::*; use super::ic::{ speculations_stream, BetDetails, BetOutcome, PostDetails, ProfileDetails, ProfileStream, }; -use crate::{canister::utils::bg_url, state::canisters::Canisters}; +use crate::{canister::utils::bg_url, state::canisters::unauth_canisters}; #[component] pub fn ExternalUser(user: Option) -> impl IntoView { @@ -104,7 +104,7 @@ pub fn Speculation(details: BetDetails) -> impl IntoView { let profile_details = create_resource( move || details.canister_id, move |canister_id| async move { - let canister = expect_context::(); + let canister = unauth_canisters(); let user = canister.individual_user(canister_id); let profile_details = user.get_profile_details().await.ok()?; Some(ProfileDetails::from(profile_details)) @@ -113,7 +113,7 @@ pub fn Speculation(details: BetDetails) -> impl IntoView { let post_details = create_resource( move || (details.canister_id, details.post_id), move |(canister_id, post_id)| async move { - let canister = expect_context::(); + let canister = unauth_canisters(); let user = canister.individual_user(canister_id); let post_details = user.get_individual_post_details_by_id(post_id).await.ok()?; Some(PostDetails::from(&post_details)) diff --git a/src/page/root.rs b/src/page/root.rs index dc0f08c5..91f951d3 100644 --- a/src/page/root.rs +++ b/src/page/root.rs @@ -4,11 +4,11 @@ use leptos::*; use leptos_router::*; #[cfg(feature = "ssr")] -use crate::{canister::post_cache, state::canisters::Canisters}; +use crate::{canister::post_cache, state::canisters::unauth_canisters}; #[server] async fn get_top_post_id() -> Result, ServerFnError> { - let canisters = expect_context::(); + let canisters = unauth_canisters(); let post_cache = canisters.post_cache(); let top_items = match post_cache diff --git a/src/page/upload/mod.rs b/src/page/upload/mod.rs index 30e7602c..b010de25 100644 --- a/src/page/upload/mod.rs +++ b/src/page/upload/mod.rs @@ -17,6 +17,8 @@ struct UploadParams { file_blob: FileWithUrl, hashtags: Vec, description: String, + enable_hot_or_not: bool, + is_nsfw: bool, } #[component] @@ -34,6 +36,8 @@ fn PreUploadView(trigger_upload: WriteSignal>) -> impl Into }); let desc = create_node_ref::