Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Shared modpacks routing #815

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft

Shared modpacks routing #815

wants to merge 29 commits into from

Conversation

thesuzerain
Copy link
Contributor

@thesuzerain thesuzerain commented Dec 29, 2023

NOTE: DO NOT MERGE until changes noted in the migrations file are reverted.

Fixes MOD-576

Notable deviations from spec:

  • Link generated is not immediately returned to the user, but attached to another route (so can be called multiple times for multiple share links to be generated as they run out of uses/expire)
  • Link generated is an API link to the join-profile route (with the appropriate url identifier). This will be changed to be a modrinth:// link as we move from testing this with cloudflare to having the theseus implementation.

@thesuzerain thesuzerain marked this pull request as ready for review January 3, 2024 02:01
Copy link
Member

@Geometrically Geometrically left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • make the routes internal also

updated timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
created timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,

maximum_users integer NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this field for?


CREATE TABLE shared_profiles_links (
id bigint PRIMARY KEY, -- id of the shared profile link (ignored in labrinth, for db use only)
link varchar(48) NOT NULL UNIQUE, -- extension of the url that identifies this (ie profiles/afgxxczsewq)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just make it based on the ID? Like discord's invites, it could be like: https://modrinth/invite/AAdwaDWD

maximum_users integer NOT NULL,

loader_id int NOT NULL REFERENCES loaders(id),
loader_version varchar(255) NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better for these to not be columns and rather their own column with strings as the id. So it would be like a jsonb metadata column like `{ "game_version": "1.19.2", "loader_version": "47.1.2" } (same with game versions) as games and loader versions can be specific to Minecraft

Comment on lines 40 to 52
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ClientProfileGame {
Minecraft {
game_id: GameId,
game_name: String,
game_version_id: LoaderFieldEnumValueId,
game_version: String,
},
Unknown {
game_id: GameId,
game_name: String,
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need an enum here? Since we already have games dynamically in the database. Couldn't we just add a jsonb metadata field with game metadata (and then remove game_id and game_name fields

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could build this in with the loader fields system... but I don't think it's the best idea bc stuff like loader versions aren't on labrinth

metadata jsonb NOT NULL DEFAULT '{}'::jsonb,

game_id int NOT NULL REFERENCES games(id),
game_version_id int NULL REFERENCES loader_field_enum_values(id) -- Minecraft java
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this exist if the game version is also in the metadata?

Comment on lines 117 to 146
#[derive(Serialize, Deserialize, Clone)]
pub struct ClientProfileShareLink {
pub id: ClientProfileLinkId, // The url identifier, encoded as base62
pub url: String, // Includes the url identifier, intentionally redundant
pub profile_id: ClientProfileId,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
}

impl From<database::models::client_profile_item::ClientProfileLink> for ClientProfileShareLink {
fn from(link: database::models::client_profile_item::ClientProfileLink) -> Self {
// Generate URL for easy access
let profile_id: ClientProfileId = link.shared_profile_id.into();
let link_id: ClientProfileLinkId = link.id.into();
let url = format!(
"{}/v3/client/profile/{}/accept/{}",
dotenvy::var("SELF_ADDR").unwrap(),
profile_id,
link_id
);

Self {
id: link_id,
url,
profile_id,
created: link.created,
expires: link.expires,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL shouldn't be returned / parsed here. The frontend will generate it based on the link ID

Comment on lines 51 to 55
/// Modrinth-associated versions
pub versions: Vec<VersionId>,
/// Overrides for this profile- only install paths are given,
/// hashes are looked up in the CDN by the client
pub override_install_paths: Vec<PathBuf>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean? IMO we shouldn't have the versions or any install info be returned here. It should be a separate route that is only called when the pack is being installed. And it can only be accessed once a user has accepted the invite.

Copy link
Contributor Author

@thesuzerain thesuzerain Jan 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im not sure I agree- I think it could be useful to be able to see the stuff youre going to install on your computer before accepting, or what's 'in/on' the shared modpack, like in the frontend as a snippet of what you're getting in the pack.

All this displays is what the versions are and the 'names' of the overrides. do you think its not worth returning this?

Comment on lines 495 to 502
// Delete a client profile
pub async fn profile_delete(
req: HttpRequest,
info: web::Path<(String,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should delete override files from the CDN as long as they are unused by other shared packs

version_id bigint NULL REFERENCES versions(id), -- for versions

-- for cdn links to files we host directly
file_hash varchar(255) NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should restructure this. Override files should be a separate table which this table refers too IMO. And then it should refer to this here (we could just re-use the files table TBH).

Like other API objects, this would allow us to have SHA1 and SHA512 hashes. Shared modpacks will also share files a ton (ex: if someone has the same exact config file between packs, we shouldn't haave to host it twice or have duplicate database entries)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just using the files table would probably work well enough. It would also allow it so if someone did ever upload their mod to Modrinth, we could "re-link" it to the project dynamically and change it from an override to a Modrinth version ID. If that makes sense

"{id}/override",
web::delete().to(client_profile_remove_overrides),
)
.route("{id}/share", web::get().to(profile_share))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this route is kind of pointless. We should just return share links in the main profile body. When would you get a profile and not get its share links?

Comment on lines 53 to 60
.route(
"{id}/share/{url_identifier}",
web::get().to(profile_link_get),
)
.route(
"{id}/accept/{url_identifier}",
web::post().to(accept_share_link),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These routes are structured wrong. A share link would be like https://modrinth.com/share/aaJJJwwa. You won't know the profile ID. There should be a route to just get a profile from share link and accept a share link

@thesuzerain thesuzerain marked this pull request as draft January 30, 2024 01:21
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants