Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .github/instructions/i18n-convert.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,24 @@ Please follow these rules precisely:
"By creating an account, you agree to our <terms-link>Terms</terms-link> and <privacy-link>Privacy Policy</privacy-link>."
- Render rich-text messages with `<IntlFormatted>` from `@modrinth/ui` using named slots:
<IntlFormatted :message-id="messages.tosLabel">
<template #terms-link="{ children }">
<NuxtLink to="/terms">
<component :is="() => children" />
</NuxtLink>
</template>
<template #privacy-link="{ children }">
<NuxtLink to="/privacy">
<component :is="() => children" />
</NuxtLink>
</template>
<template #terms-link="{ children }">
<NuxtLink to="/terms">
<component :is="() => children" />
</NuxtLink>
</template>
<template #privacy-link="{ children }">
<NuxtLink to="/privacy">
<component :is="() => children" />
</NuxtLink>
</template>
</IntlFormatted>
- For simple emphasis: `'Welcome to <strong>Modrinth</strong>!'` with a slot:
<template #strong="{ children }">
<strong><component :is="() => children" /></strong>
<strong><component :is="() => children" /></strong>
</template>
- For more complex child handling, use `normalizeChildren` from `@modrinth/ui`:
<template #bold="{ children }">
<strong><component :is="() => normalizeChildren(children)" /></strong>
<strong><component :is="() => normalizeChildren(children)" /></strong>
</template>

5. Formatting in templates
Expand Down
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,22 @@ Each project may have its own file with detailed instructions:
## Code Guidelines

### Comments

- DO NOT use "heading" comments like: `=== Helper methods ===`.
- Use doc comments, but avoid inline comments unless ABSOLUTELY necessary for clarity. Code should aim to be self documenting!

## Bash Guidelines

### Output handling

- DO NOT pipe output through `head`, `tail`, `less`, or `more`
- NEVER use `| head -n X` or `| tail -n X` to truncate output
- IMPORTANT: Run commands directly without pipes when possible
- IMPORTANT: If you need to limit output, use command-specific flags (e.g. `git log -n 10` instead of `git log | head -10`)
- ALWAYS read the full output — never pipe through filters

### General

- Do not create new non-source code files (e.g. Bash scripts, SQL scripts) unless explicitly prompted to
- For Frontend, when doing lint checks, only use the `prepr` commands, do not use `typecheck` or `tsc` etc.
- Types in `@modrinth/utils` are considered highly outdated, if a component needs them, check if you can switch said component to use types from `packages/api-client`
Expand Down
4 changes: 1 addition & 3 deletions apps/app/capabilities/plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"identifier": "plugins",
"description": "",
"local": true,
"windows": [
"main"
],
"windows": ["main"],
"permissions": [
"dialog:allow-open",
"dialog:allow-confirm",
Expand Down
6 changes: 1 addition & 5 deletions apps/daedalus_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ futures = { workspace = true }
indexmap = { workspace = true, features = ["serde"] }
itertools = { workspace = true }
quick-xml = { workspace = true }
reqwest = { workspace = true, features = [
"json",
"rustls-tls-native-roots",
"stream",
] }
reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots", "stream"] }
rust-s3 = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
Expand Down
5 changes: 3 additions & 2 deletions apps/docs/src/styles/modrinth.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
::backdrop,
:root[data-theme='light'],
[data-theme='light'] ::backdrop {
--sl-font-system: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto,
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
--sl-font-system:
Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell,
Fira Sans, Droid Sans, Helvetica Neue, sans-serif;

--sl-color-white: var(--color-contrast); /* “white” */
--sl-color-gray-1: var(--color-base);
Expand Down
39 changes: 21 additions & 18 deletions apps/frontend/src/pages/settings/billing/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@
v-bind="subscription.serverInfo"
:pending-change="getPendingChange(subscription)"
:cancellation-date="getCancellationDate(subscription)"
:on-download-backup="getBackupDownloadForServer(subscription.serverInfo)"
:on-download-world="
getWorldDownload(subscription.serverInfo.server_id, serverFullList)
"
/>
<div v-else class="w-fit">
<p>
Expand Down Expand Up @@ -508,11 +510,7 @@
{{ formatMessage(messages.upgrade) }}
</Button>
<Button
v-else-if="
getPyroCharge(subscription) &&
(getPyroCharge(subscription).status === 'cancelled' ||
getPyroCharge(subscription).status === 'failed')
"
v-else-if="canResubscribe(subscription)"
type="colored"
color="green"
size="xl"
Expand Down Expand Up @@ -703,8 +701,10 @@ import {
CopyCode,
defineMessages,
getPaymentMethodIcon,
hasAvailableWorldDownload,
injectModrinthClient,
injectNotificationManager,
isWithinServerResubscribeWindow,
paymentMethodMessages,
PurchaseModal,
ResubscribeModal,
Expand All @@ -713,7 +713,7 @@ import {
useDebugLogger,
useFormatDateTime,
useFormatPrice,
useServerBackupDownload,
useServerWorldDownload,
useVIntl,
} from '@modrinth/ui'
import { calculateSavings, getCurrency } from '@modrinth/utils'
Expand All @@ -727,7 +727,7 @@ import { products } from '~/generated/state.json'

const { addNotification, handleError } = injectNotificationManager()
const client = injectModrinthClient()
const { getLatestBackupDownload } = useServerBackupDownload()
const { getWorldDownload } = useServerWorldDownload()
const debug = useDebugLogger('Billing')
definePageMeta({
middleware: 'auth',
Expand Down Expand Up @@ -1102,17 +1102,17 @@ const pyroSubscriptions = computed(() => {
}
})
.filter((subscription) => {
// files expire 30 days after cancellation
const cancellationDate = getCancellationDate(subscription)
if (
!cancellationDate ||
subscription.serverInfo?.status !== 'suspended' ||
subscription.serverInfo?.suspension_reason !== 'cancelled'
)
return true
const cancellation = new Date(cancellationDate)
const thirtyDaysLater = new Date(cancellation.getTime() + 30 * 24 * 60 * 60 * 1000)
return new Date() <= thirtyDaysLater
const cancellationDate = getCancellationDate(subscription)
if (!cancellationDate || !serverFullList.value) return true
return (
isWithinServerResubscribeWindow(cancellationDate) ||
hasAvailableWorldDownload(subscription.serverInfo.server_id, serverFullList.value)
)
})
})

Expand Down Expand Up @@ -1274,6 +1274,13 @@ function getCancellationDate(subscription) {
return null
}

function canResubscribe(subscription) {
const charge = getPyroCharge(subscription)
if (!charge) return false
if (charge.status === 'failed') return true
return charge.status === 'cancelled' && isWithinServerResubscribeWindow(charge.due)
}

const getProductSize = (product) => {
if (!product || !product.metadata) return formatMessage(commonMessages.planUnknownLabel)
const ramSize = product.metadata.ram
Expand Down Expand Up @@ -1388,10 +1395,6 @@ function handlePyroResubscribeConfirm({ subscriptionId, wasSuspended }) {
return resubscribePyro(subscriptionId, wasSuspended)
}

function getBackupDownloadForServer(serverInfo) {
return getLatestBackupDownload(serverInfo.server_id, serverFullList.value)
}

const refresh = async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: ['billing'] }),
Expand Down
10 changes: 5 additions & 5 deletions apps/labrinth/.idea/copilot.data.migration.ask2agent.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions apps/labrinth/.idea/labrinth.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions apps/labrinth/.idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions apps/labrinth/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion apps/labrinth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ urlencoding = { workspace = true }
utoipa = { workspace = true, features = ["url"] }
uuid = { workspace = true, features = ["fast-rng", "serde", "v4", "v7"] }
validator = { workspace = true, features = ["derive"] }
webauthn-rs = { workspace = true, features = ["conditional-ui", "danger-allow-state-serialisation"] }
webauthn-rs = { workspace = true, features = [
"conditional-ui",
"danger-allow-state-serialisation",
] }
webauthn-rs-proto = { workspace = true }
webp = { workspace = true }
woothee = { workspace = true }
Expand Down
19 changes: 16 additions & 3 deletions apps/labrinth/src/api_v2_description.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
> API v2 is Modrinth's current, official API. It is the recommended API for integrations and applications and will receive long-term support.

## Authentication

This API has two options for authentication: personal access tokens and [OAuth2](https://en.wikipedia.org/wiki/OAuth).
All tokens are tied to a Modrinth user and use the `Authorization` header of the request.

Example:

```
Authorization: mrp_RNtLRSPmGj2pd1v1ubi52nX7TJJM9sznrmwhAuj511oe4t1jAqAQ3D6Wc8Ic
```

> [!tip] You do not need a token for most requests.
>
> Generally speaking, only the following types of requests require a token:
>
> - those which create data (such as version creation)
> - those which modify data (such as editing a project)
> - those which access private data (such as draft projects, notifications, emails, and payout data)
Expand All @@ -24,18 +27,22 @@ Please note that certain scopes and requests cannot be completed with a personal
For example, deleting a user account can only be done through Modrinth's frontend.

### Personal access tokens

Personal access tokens (PATs) can be generated from [the user settings](https://modrinth.com/settings/account).

### GitHub tokens

For backwards compatibility purposes, some types of GitHub tokens also work for authenticating a user with Modrinth's API, granting all scopes.
**We urge any application still using GitHub tokens to start using personal access tokens for security and reliability purposes.**

## Cross-Origin Resource Sharing

This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with the [W3C spec](https://www.w3.org/TR/cors/).
This allows for cross-domain communication from the browser.
All responses have a wildcard same-origin which makes them completely public and accessible to everyone, including any code on any site.

## Identifiers

The majority of items you can interact with in the API have a unique eight-digit base62 ID.
Projects, versions, users, threads, teams, and reports all use this same way of identifying themselves.
Version files use the sha1 or sha512 file hashes as identifiers.
Expand All @@ -45,7 +52,9 @@ While unique IDs are constant, slugs and usernames can change at any moment.
If you want to store something in the long term, it is recommended to use the unique ID.

## Ratelimits

The API has a ratelimit defined per IP. Limits and remaining amounts are given in the response headers.

- `X-Ratelimit-Limit`: the maximum number of requests that can be made in a minute
- `X-Ratelimit-Remaining`: the number of requests remaining in the current ratelimit window
- `X-Ratelimit-Reset`: the time in seconds until the ratelimit window resets
Expand All @@ -57,20 +66,24 @@ The ratelimit is currently 300 requests per minute per IP. If your application i
Higher rate limits are granted only in very rare cases. If you believe your use case requires an exception, please [contact us](mailto:support@modrinth.com).

## User Agents

To access the Modrinth API, you **must** use provide a uniquely-identifying `User-Agent` header.
Providing a user agent that only identifies your HTTP client library (such as "okhttp/4.9.3") increases the likelihood that we will block your traffic.
It is recommended, but not required, to include contact information in your user agent.
This allows us to contact you if we would like a change in your application's behavior without having to block your traffic.

- Bad: `User-Agent: okhttp/4.9.3`
- Good: `User-Agent: project_name`
- Better: `User-Agent: github_username/project_name/1.56.0`
- Best: `User-Agent: github_username/project_name/1.56.0 (launcher.com)` or `User-Agent: github_username/project_name/1.56.0 (contact@launcher.com)`

## Versioning

Modrinth follows a simple pattern for its API versioning.
In the event of a breaking API change, the API version in the URL path is bumped, and migration steps will be published below.

### Migrations

Inside the following spoiler, you will be able to find all changes between versions of the Modrinth API, accompanied by tips and a guide to migrate applications to newer versions.

<details><summary>API v1 to API v2</summary>
Expand All @@ -82,12 +95,12 @@ These bullet points cover most changes in the v2 API, but please note that field
- New search facet: `project_type`
- Alphabetical sort removed (it didn't work and is not possible due to limits in MeiliSearch)
- New search fields: `project_type`, `gallery`
- The gallery field is an array of URLs to images that are part of the project's gallery
- The gallery field is an array of URLs to images that are part of the project's gallery
- The gallery is a new feature which allows the user to upload images showcasing their mod to the CDN which will be displayed on their mod page
- Internal change: Any project file uploaded to Modrinth is now validated to make sure it's a valid Minecraft mod, Modpack, etc.
- For example, a Forge 1.17 mod with a JAR not containing a mods.toml will not be allowed to be uploaded to Modrinth
- For example, a Forge 1.17 mod with a JAR not containing a mods.toml will not be allowed to be uploaded to Modrinth
- In project creation, projects may not upload a mod with no versions to review, however they can be saved as a draft
- Similarly, for version creation, a version may not be uploaded without any files
- Similarly, for version creation, a version may not be uploaded without any files
- Donation URLs have been enabled
- New project status: `archived`. Projects with this status do not appear in search
- Tags (such as categories, loaders) now have icons (SVGs) and specific project types attached
Expand Down
1 change: 0 additions & 1 deletion apps/labrinth/src/api_v3_description.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
>
> **Please do not use this API in production. It will be deprecated in the future.**


> [!tip]
> You're probably looking for [Modrinth's public API v2](/docs?api=v2)
Loading
Loading