diff --git a/.github/instructions/i18n-convert.instructions.md b/.github/instructions/i18n-convert.instructions.md index b8ce2218c1..91786f4fe3 100644 --- a/.github/instructions/i18n-convert.instructions.md +++ b/.github/instructions/i18n-convert.instructions.md @@ -81,6 +81,6 @@ Please follow these rules precisely: Use existing patterns from our codebase: - Variables/plurals: see `apps/frontend/src/pages/frog.vue` -- Rich-text link tags: see `apps/frontend/src/pages/auth/welcome.vue` and `apps/frontend/src/error.vue` +- Rich-text link tags: see `apps/frontend/src/error.vue` When you finish, there should be no hard-coded English strings left in the template—everything comes from `formatMessage` or ``. diff --git a/apps/frontend/src/app.vue b/apps/frontend/src/app.vue index 396d7a0356..0396906fd0 100644 --- a/apps/frontend/src/app.vue +++ b/apps/frontend/src/app.vue @@ -12,6 +12,8 @@ import { I18nDebugPanel, LoadingBar, NotificationPanel } from '@modrinth/ui' import { setupProviders } from '~/providers/setup.ts' +import { useAuth } from './composables/auth' + const auth = await useAuth() setupProviders(auth) diff --git a/apps/frontend/src/components/ui/auth/CreateAccount.vue b/apps/frontend/src/components/ui/auth/CreateAccount.vue new file mode 100644 index 0000000000..7d43a90b91 --- /dev/null +++ b/apps/frontend/src/components/ui/auth/CreateAccount.vue @@ -0,0 +1,267 @@ + + + diff --git a/apps/frontend/src/components/ui/HCaptcha.vue b/apps/frontend/src/components/ui/auth/HCaptcha.vue similarity index 76% rename from apps/frontend/src/components/ui/HCaptcha.vue rename to apps/frontend/src/components/ui/auth/HCaptcha.vue index ebbec75f8f..c07a47a449 100644 --- a/apps/frontend/src/components/ui/HCaptcha.vue +++ b/apps/frontend/src/components/ui/auth/HCaptcha.vue @@ -47,17 +47,34 @@ defineExpose({ > - diff --git a/apps/frontend/src/components/ui/auth/SignIn.vue b/apps/frontend/src/components/ui/auth/SignIn.vue new file mode 100644 index 0000000000..3b806a6d29 --- /dev/null +++ b/apps/frontend/src/components/ui/auth/SignIn.vue @@ -0,0 +1,376 @@ + + + + + diff --git a/apps/frontend/src/components/ui/auth/SignUp.vue b/apps/frontend/src/components/ui/auth/SignUp.vue new file mode 100644 index 0000000000..6fedd7d7fc --- /dev/null +++ b/apps/frontend/src/components/ui/auth/SignUp.vue @@ -0,0 +1,236 @@ + + + diff --git a/apps/frontend/src/components/ui/create/ProjectCreateModal.vue b/apps/frontend/src/components/ui/create/ProjectCreateModal.vue index 77f51ac7b2..3f3aef438a 100644 --- a/apps/frontend/src/components/ui/create/ProjectCreateModal.vue +++ b/apps/frontend/src/components/ui/create/ProjectCreateModal.vue @@ -164,9 +164,7 @@ defineExpose({ show, }) -const auth = (await useAuth()) as Ref<{ - user: { id: string; username: string; avatar_url: string } | null -}> +const auth = await useAuth() const messages = defineMessages({ title: { diff --git a/apps/frontend/src/components/ui/dashboard/withdraw-stages/LegacyPaypalDetailsStage.vue b/apps/frontend/src/components/ui/dashboard/withdraw-stages/LegacyPaypalDetailsStage.vue index 6278f19a2f..24974b3bba 100644 --- a/apps/frontend/src/components/ui/dashboard/withdraw-stages/LegacyPaypalDetailsStage.vue +++ b/apps/frontend/src/components/ui/dashboard/withdraw-stages/LegacyPaypalDetailsStage.vue @@ -120,7 +120,7 @@ import { computed, onMounted, ref, watch } from 'vue' import RevenueInputField from '@/components/ui/dashboard/RevenueInputField.vue' import WithdrawFeeBreakdown from '@/components/ui/dashboard/WithdrawFeeBreakdown.vue' -import { getAuthUrl, removeAuthProvider, useAuth } from '@/composables/auth.js' +import { getAuthUrl, removeAuthProvider, useAuth } from '@/composables/auth.ts' import { useWithdrawContext } from '@/providers/creator-withdraw.ts' const { withdrawData, maxWithdrawAmount, availableMethods, calculateFees, saveStateToStorage } = @@ -193,7 +193,6 @@ async function saveVenmoHandle() { }, }) - // @ts-expect-error auth.js is not typed await useAuth(auth.value.token) initialVenmoHandle.value = venmoHandle.value.trim() diff --git a/apps/frontend/src/components/ui/dashboard/withdraw-stages/TremendousDetailsStage.vue b/apps/frontend/src/components/ui/dashboard/withdraw-stages/TremendousDetailsStage.vue index a9dfa3e7e7..fdd371148c 100644 --- a/apps/frontend/src/components/ui/dashboard/withdraw-stages/TremendousDetailsStage.vue +++ b/apps/frontend/src/components/ui/dashboard/withdraw-stages/TremendousDetailsStage.vue @@ -358,8 +358,8 @@ import { computed, onMounted, ref, watch } from 'vue' import RevenueInputField from '@/components/ui/dashboard/RevenueInputField.vue' import WithdrawFeeBreakdown from '@/components/ui/dashboard/WithdrawFeeBreakdown.vue' -import { useAuth } from '@/composables/auth.js' import { useWithdrawContext } from '@/providers/creator-withdraw.ts' +import { useAuth } from '~/composables/auth.ts' const debug = useDebugLogger('TremendousDetailsStage') const { withdrawData, maxWithdrawAmount, availableMethods, paymentOptions, calculateFees } = diff --git a/apps/frontend/src/components/ui/moderation/ModerationReportCard.vue b/apps/frontend/src/components/ui/moderation/ModerationReportCard.vue index ef77f3146f..e1a23d1c5b 100644 --- a/apps/frontend/src/components/ui/moderation/ModerationReportCard.vue +++ b/apps/frontend/src/components/ui/moderation/ModerationReportCard.vue @@ -190,7 +190,6 @@ import { LinkIcon, } from '@modrinth/assets' import { type ExtendedReport, reportQuickReplies } from '@modrinth/moderation' -import { type OverflowMenuOption, useFormatDateTime } from '@modrinth/ui' import { Avatar, ButtonStyled, @@ -198,6 +197,8 @@ import { getProjectTypeIcon, injectNotificationManager, OverflowMenu, + type OverflowMenuOption, + useFormatDateTime, useRelativeTime, } from '@modrinth/ui' import { formatProjectType } from '@modrinth/utils' diff --git a/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue b/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue index 97e83c06ff..bb309d7c07 100644 --- a/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue +++ b/apps/frontend/src/components/ui/moderation/ModerationTechRevCard.vue @@ -26,11 +26,11 @@ import { getProjectTypeIcon, injectModrinthClient, injectNotificationManager, + NavTabs, OverflowMenu, type OverflowMenuOption, useFormatDateTime, } from '@modrinth/ui' -import { NavTabs } from '@modrinth/ui' import { capitalizeString, formatProjectType, diff --git a/apps/frontend/src/components/ui/project-settings/ServerCompatibilityModal/stages/SelectPublishedModpack.vue b/apps/frontend/src/components/ui/project-settings/ServerCompatibilityModal/stages/SelectPublishedModpack.vue index 16fd0fa9b6..1346fe17dd 100644 --- a/apps/frontend/src/components/ui/project-settings/ServerCompatibilityModal/stages/SelectPublishedModpack.vue +++ b/apps/frontend/src/components/ui/project-settings/ServerCompatibilityModal/stages/SelectPublishedModpack.vue @@ -87,7 +87,7 @@ const currentProjectId = computed(() => projectV3.value?.id) const { selectedProjectId, selectedVersionId } = injectServerCompatibilityContext() const { labrinth } = injectModrinthClient() const { addNotification } = injectNotificationManager() -const auth = (await useAuth()) as { user?: { id: string } } +const auth = await useAuth() interface VersionInfo { id: string diff --git a/apps/frontend/src/composables/auth.js b/apps/frontend/src/composables/auth.js deleted file mode 100644 index e69d6a4d52..0000000000 --- a/apps/frontend/src/composables/auth.js +++ /dev/null @@ -1,157 +0,0 @@ -export const useAuth = async (oldToken = null) => { - const auth = useState('auth', () => ({ - user: null, - token: '', - headers: {}, - })) - - if (!auth.value.user || oldToken) { - auth.value = await initAuth(oldToken) - } - - return auth -} - -export const initAuth = async (oldToken = null) => { - const auth = { - user: null, - token: '', - } - - if (oldToken === 'none') { - return auth - } - - const route = useRoute() - const authCookie = useCookie('auth-token', { - maxAge: 60 * 60 * 24 * 365 * 10, - sameSite: 'lax', - secure: true, - httpOnly: false, - path: '/', - }) - - if (oldToken) { - authCookie.value = oldToken - } - - if (route.query.code && !route.fullPath.includes('new_account=true')) { - authCookie.value = route.query.code - } - - if (route.fullPath.includes('new_account=true') && route.path !== '/auth/welcome') { - const redirect = route.path.startsWith('/auth/') ? null : route.fullPath - - await navigateTo( - `/auth/welcome?authToken=${route.query.code}${ - redirect ? `&redirect=${encodeURIComponent(redirect)}` : '' - }`, - ) - } - - if (authCookie.value) { - auth.token = authCookie.value - - if (!auth.token || !auth.token.startsWith('mra_')) { - return auth - } - - try { - auth.user = await useBaseFetch( - 'user', - { - headers: { - Authorization: auth.token, - }, - }, - true, - ) - } catch { - /* empty */ - } - } - - if (!auth.user && auth.token) { - try { - const session = await useBaseFetch( - 'session/refresh', - { - method: 'POST', - headers: { - Authorization: auth.token, - }, - }, - true, - ) - - auth.token = session.session - authCookie.value = auth.token - - auth.user = await useBaseFetch( - 'user', - { - headers: { - Authorization: auth.token, - }, - }, - true, - ) - } catch { - authCookie.value = null - } - } - - return auth -} - -export const getSignInRedirectPath = (route) => { - const fullPath = route.fullPath - if (fullPath === '/auth' || fullPath.startsWith('/auth/')) { - return '/dashboard' - } - return fullPath -} - -export const getSignInRouteObj = (route, redirectOverride) => ({ - path: '/auth/sign-in', - query: { - redirect: redirectOverride ?? getSignInRedirectPath(route), - }, -}) - -export const getAuthUrl = (provider, redirect = '/dashboard') => { - const config = useRuntimeConfig() - const route = useNativeRoute() - - const fullURL = route.query.launcher - ? getLauncherRedirectUrl(route) - : `${config.public.siteUrl}/auth/sign-in?redirect=${encodeURIComponent(redirect)}` - - return `${config.public.apiBaseUrl}auth/init?provider=${provider}&url=${encodeURIComponent(fullURL)}` -} - -export const removeAuthProvider = async (provider) => { - startLoading() - - const auth = await useAuth() - - await useBaseFetch('auth/provider', { - method: 'DELETE', - body: { - provider, - }, - }) - - await useAuth(auth.value.token) - - stopLoading() -} - -export const getLauncherRedirectUrl = (route) => { - const usesLocalhostRedirectionScheme = - ['4', '6'].includes(route.query.ipver) && Number(route.query.port) < 65536 - - return usesLocalhostRedirectionScheme - ? `http://${route.query.ipver === '4' ? '127.0.0.1' : '[::1]'}:${route.query.port}` - : `https://launcher-files.modrinth.com` -} diff --git a/apps/frontend/src/composables/auth.ts b/apps/frontend/src/composables/auth.ts new file mode 100644 index 0000000000..2519d8232a --- /dev/null +++ b/apps/frontend/src/composables/auth.ts @@ -0,0 +1,208 @@ +import type { Labrinth } from '@modrinth/api-client' +import { useStorage } from '@vueuse/core' +import type { LocationQueryValue, RouteLocationNormalizedLoaded } from 'vue-router' + +import type { CookieOptions } from '#app' + +type AuthState = { + user: Labrinth.Users.v2.User | null + token: string +} + +type QueryValue = LocationQueryValue | LocationQueryValue[] | undefined +type FullPathRoute = Pick +type LauncherRoute = Pick + +export const LAST_SIGN_IN_OAUTH_PROVIDER_STORAGE_KEY = 'auth-last-sign-in-oauth-provider' +export const PENDING_SIGN_IN_OAUTH_PROVIDER_STORAGE_KEY = 'auth-pending-sign-in-oauth-provider' + +const AUTH_COOKIE_OPTIONS = { + maxAge: 60 * 60 * 24 * 365 * 10, + sameSite: 'lax', + secure: true, + httpOnly: false, + path: '/', +} satisfies CookieOptions + +const getQueryString = (value: QueryValue) => { + if (Array.isArray(value)) { + return value[0] ?? null + } + return value ?? null +} + +export const useAuth = async (oldToken: string | null | undefined = null) => { + const auth = useState('auth', () => ({ + user: null, + token: '', + })) + + if (!auth.value.user || oldToken) { + auth.value = await initAuth(oldToken) + } + + return auth +} + +export const initAuth = async (oldToken: string | null | undefined = null) => { + const auth: AuthState = { + user: null, + token: '', + } + + if (oldToken === 'none') { + return auth + } + + const route = useRoute() + const authCookie = useCookie('auth-token', AUTH_COOKIE_OPTIONS) + const authCode = getQueryString(route.query.code) + + if (oldToken) { + authCookie.value = oldToken + } + + if (authCode) { + authCookie.value = authCode + } + + if (authCookie.value) { + auth.token = authCookie.value + + if (!auth.token || !auth.token.startsWith('mra_')) { + return auth + } + + try { + auth.user = (await useBaseFetch( + 'user', + { + headers: { + Authorization: auth.token, + }, + }, + true, + )) as Labrinth.Users.v2.User + } catch { + /* empty */ + } + } + + if (!auth.user && auth.token) { + try { + const session = (await useBaseFetch( + 'session/refresh', + { + method: 'POST', + headers: { + Authorization: auth.token, + }, + }, + true, + )) as { session: string } + + auth.token = session.session + authCookie.value = auth.token + + auth.user = (await useBaseFetch( + 'user', + { + headers: { + Authorization: auth.token, + }, + }, + true, + )) as Labrinth.Users.v2.User + } catch { + authCookie.value = null + } + } + + return auth +} + +export const getSignInRedirectPath = (route: FullPathRoute) => { + const fullPath = route.fullPath + if (fullPath === '/auth' || fullPath.startsWith('/auth/')) { + return '/dashboard' + } + return fullPath +} + +export const getSignInRouteObj = (route: FullPathRoute, redirectOverride?: string | null) => ({ + path: '/auth/sign-in', + query: { + redirect: redirectOverride ?? getSignInRedirectPath(route), + }, +}) + +export const getAuthUrl = (provider: string, redirect = '/dashboard') => { + const config = useRuntimeConfig() + const route = useNativeRoute() + const launcher = getQueryString(route.query.launcher) + + const fullURL = launcher + ? (() => { + const callbackUrl = new URL('/auth/sign-in', config.public.siteUrl) + callbackUrl.searchParams.set('launcher', launcher) + + const ipver = getQueryString(route.query.ipver) + const port = getQueryString(route.query.port) + + if (ipver) { + callbackUrl.searchParams.set('ipver', ipver) + } + + if (port) { + callbackUrl.searchParams.set('port', port) + } + + return callbackUrl.toString() + })() + : `${config.public.siteUrl}/auth/sign-in?redirect=${encodeURIComponent(redirect)}` + + return `${config.public.apiBaseUrl}auth/init?provider=${provider}&url=${encodeURIComponent(fullURL)}` +} + +export const promotePendingSignInOAuthProvider = () => { + if (!import.meta.client) return + const pending = useStorage( + PENDING_SIGN_IN_OAUTH_PROVIDER_STORAGE_KEY, + null, + undefined, + { initOnMounted: true }, + ) + if (!pending.value) return + const last = useStorage(LAST_SIGN_IN_OAUTH_PROVIDER_STORAGE_KEY, null, undefined, { + initOnMounted: true, + }) + last.value = pending.value + pending.value = null +} + +export const removeAuthProvider = async (provider: string) => { + startLoading() + + const auth = await useAuth() + + await useBaseFetch('auth/provider', { + method: 'DELETE', + body: { + provider, + }, + }) + + await useAuth(auth.value.token) + + stopLoading() +} + +export const getLauncherRedirectUrl = (route: LauncherRoute) => { + const ipver = getQueryString(route.query.ipver) + const port = Number(getQueryString(route.query.port)) + const usesLocalhostRedirectionScheme = ['4', '6'].includes(ipver ?? '') && port < 65536 + + return usesLocalhostRedirectionScheme + ? `http://${ipver === '4' ? '127.0.0.1' : '[::1]'}:${port}` + : 'https://launcher-files.modrinth.com' +} diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue index f0bf302991..ec03c4ab7c 100644 --- a/apps/frontend/src/layouts/default.vue +++ b/apps/frontend/src/layouts/default.vue @@ -762,7 +762,7 @@ import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal. import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue' import ProjectCreateModal from '~/components/ui/create/ProjectCreateModal.vue' import ModrinthFooter from '~/components/ui/ModrinthFooter.vue' -import { getSignInRouteObj } from '~/composables/auth.js' +import { getSignInRouteObj } from '~/composables/auth.ts' import { errors as generatedStateErrors } from '~/generated/state.json' import { getProjectTypeMessage } from '~/utils/i18n-project-type.ts' diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index 3ff2360a0f..670a0047f3 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -218,12 +218,75 @@ "auth.authorize.authorize-app-name": { "message": "Authorize {appName}" }, + "auth.authorize.error.missing-parameters": { + "message": "Missing required OAuth query parameters." + }, "auth.authorize.error.no-redirect-url": { "message": "No redirect location found in response" }, + "auth.authorize.errro-title": { + "message": "An Error Occured" + }, "auth.authorize.redirect-url": { "message": "You will be redirected to {url}" }, + "auth.continue-with-provider": { + "message": "Continue with {provider}" + }, + "auth.create-account.age-requirement.warning-title": { + "message": "Age requirement" + }, + "auth.create-account.complete-sign-up": { + "message": "Complete sign up" + }, + "auth.create-account.date-of-birth.invalid.text": { + "message": "Please enter a valid date of birth. Year cannot be 0000." + }, + "auth.create-account.date-of-birth.invalid.title": { + "message": "Invalid date of birth" + }, + "auth.create-account.date-of-birth.label": { + "message": "Date of birth" + }, + "auth.create-account.date-of-birth.over13-helper": { + "message": "You must be over 13 years old to use Modrinth." + }, + "auth.create-account.date-of-birth.placeholder": { + "message": "Select your date of birth" + }, + "auth.create-account.date-of-birth.required.text": { + "message": "Please enter your date of birth before continuing." + }, + "auth.create-account.date-of-birth.required.title": { + "message": "Date of birth required" + }, + "auth.create-account.date-of-birth.under13-helper": { + "message": "You cannot create an account at Modrinth unless you are over 13 years old." + }, + "auth.create-account.info-panel.source-code-link": { + "message": "Relevant source code" + }, + "auth.create-account.info-panel.text": { + "message": "We do not store your date of birth, it is only used to confirm your age at sign up." + }, + "auth.create-account.page-title": { + "message": "Create Account" + }, + "auth.create-account.security-check.label": { + "message": "Security check" + }, + "auth.create-account.subscribe.label": { + "message": "Keep me updated on the cool things Modrinth is working on via email" + }, + "auth.create-account.title": { + "message": "Create an Account" + }, + "auth.create-account.username.optional-label": { + "message": "Username" + }, + "auth.create-account.username.placeholder": { + "message": "Enter username" + }, "auth.reset-password.method-choice.action": { "message": "Send recovery email" }, @@ -266,45 +329,57 @@ "auth.sign-in.2fa.placeholder": { "message": "Enter code..." }, - "auth.sign-in.additional-options": { - "message": "Forgot password?Create an account" + "auth.sign-in.continue-with-email": { + "message": "Continue with Email" + }, + "auth.sign-in.create-account": { + "message": "Sign up" + }, + "auth.sign-in.forgot-password": { + "message": "Forgot password" + }, + "auth.sign-in.last-sign-in": { + "message": "Last sign in" + }, + "auth.sign-in.no-account": { + "message": "Don't have an account?" }, "auth.sign-in.sign-in-with": { - "message": "Sign in with" + "message": "Sign into Modrinth" }, "auth.sign-in.title": { "message": "Sign In" }, - "auth.sign-in.use-password": { - "message": "Or use a password" + "auth.sign-up.age-requirement.warning-title": { + "message": "Age requirement" }, - "auth.sign-up.action.create-account": { - "message": "Create account" + "auth.sign-up.continue-with-email": { + "message": "Continue with Email" }, "auth.sign-up.legal-dislaimer": { "message": "By creating an account, you agree to Modrinth's Terms and Privacy Policy." }, - "auth.sign-up.notification.password-mismatch.text": { - "message": "Passwords do not match!" + "auth.sign-up.show-fewer-options": { + "message": "Show fewer options" + }, + "auth.sign-up.show-other-options": { + "message": "Show other options" }, "auth.sign-up.sign-in-option.title": { "message": "Already have an account?" }, - "auth.sign-up.subscribe.label": { - "message": "Subscribe to updates about Modrinth" - }, "auth.sign-up.title": { "message": "Sign Up" }, - "auth.sign-up.title.create-account": { - "message": "Or create an account yourself" - }, "auth.sign-up.title.sign-up-with": { - "message": "Sign up with" + "message": "Create an Account" }, "auth.verify-email.action.account-settings": { "message": "Account settings" }, + "auth.verify-email.action.discover-mods": { + "message": "Discover mods" + }, "auth.verify-email.already-verified.description": { "message": "Your email is already verified!" }, @@ -323,6 +398,15 @@ "auth.verify-email.failed-verification.title": { "message": "Email verification failed" }, + "auth.verify-email.notification.email-sent.description": { + "message": "An email with a link to verify your account has been sent to {email}." + }, + "auth.verify-email.notification.email-sent.title": { + "message": "Email sent" + }, + "auth.verify-email.notification.error-occurred.title": { + "message": "An error occurred" + }, "auth.verify-email.post-verification.description": { "message": "Your email address has been successfully verified!" }, @@ -332,21 +416,6 @@ "auth.verify-email.title": { "message": "Verify Email" }, - "auth.welcome.checkbox.subscribe": { - "message": "Subscribe to updates about Modrinth" - }, - "auth.welcome.description": { - "message": "You’re now part of the awesome community of creators & explorers already building, downloading, and staying up-to-date with amazing mods." - }, - "auth.welcome.label.tos": { - "message": "By creating an account, you have agreed to Modrinth's Terms and Privacy Policy." - }, - "auth.welcome.long-title": { - "message": "Welcome to Modrinth!" - }, - "auth.welcome.title": { - "message": "Welcome" - }, "collection.button.edit-icon": { "message": "Edit icon" }, diff --git a/apps/frontend/src/pages/[type]/[id].vue b/apps/frontend/src/pages/[type]/[id].vue index ea80b82c85..a9ca77217d 100644 --- a/apps/frontend/src/pages/[type]/[id].vue +++ b/apps/frontend/src/pages/[type]/[id].vue @@ -1140,7 +1140,7 @@ import MessageBanner from '~/components/ui/MessageBanner.vue' import ModerationChecklist from '~/components/ui/moderation/checklist/ModerationChecklist.vue' import ModerationProjectNags from '~/components/ui/moderation/ModerationProjectNags.vue' import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue' -import { getSignInRouteObj } from '~/composables/auth.js' +import { getSignInRouteObj } from '~/composables/auth.ts' import { saveFeatureFlags } from '~/composables/featureFlags.ts' import { STALE_TIME, STALE_TIME_LONG } from '~/composables/queries/project' import { versionQueryOptions } from '~/composables/queries/version' diff --git a/apps/frontend/src/pages/[type]/[id]/settings/versions.vue b/apps/frontend/src/pages/[type]/[id]/settings/versions.vue index 20b7d65943..1c3094a64f 100644 --- a/apps/frontend/src/pages/[type]/[id]/settings/versions.vue +++ b/apps/frontend/src/pages/[type]/[id]/settings/versions.vue @@ -308,7 +308,7 @@ import { import { useTemplateRef } from 'vue' import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue' -import { getSignInRouteObj } from '~/composables/auth.js' +import { getSignInRouteObj } from '~/composables/auth.ts' import { reportVersion } from '~/utils/report-helpers.ts' const route = useRoute() diff --git a/apps/frontend/src/pages/[type]/[id]/version/[version].vue b/apps/frontend/src/pages/[type]/[id]/version/[version].vue index ff6decdfc8..a0478f9373 100644 --- a/apps/frontend/src/pages/[type]/[id]/version/[version].vue +++ b/apps/frontend/src/pages/[type]/[id]/version/[version].vue @@ -443,7 +443,7 @@ import { formatBytes, renderHighlightedString } from '@modrinth/utils' import Breadcrumbs from '~/components/ui/Breadcrumbs.vue' import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue' import Modal from '~/components/ui/Modal.vue' -import { getSignInRouteObj } from '~/composables/auth.js' +import { getSignInRouteObj } from '~/composables/auth.ts' import { useImageUpload } from '~/composables/image-upload.ts' import { inferVersionInfo } from '~/helpers/infer' import { createDataPackVersion } from '~/helpers/package.js' diff --git a/apps/frontend/src/pages/[type]/[id]/versions.vue b/apps/frontend/src/pages/[type]/[id]/versions.vue index 3d67f1ac88..0866511808 100644 --- a/apps/frontend/src/pages/[type]/[id]/versions.vue +++ b/apps/frontend/src/pages/[type]/[id]/versions.vue @@ -269,7 +269,7 @@ import { import { onMounted, useTemplateRef } from 'vue' import CreateProjectVersionModal from '~/components/ui/create-project-version/CreateProjectVersionModal.vue' -import { getSignInRouteObj } from '~/composables/auth.js' +import { getSignInRouteObj } from '~/composables/auth.ts' import { reportVersion } from '~/utils/report-helpers.ts' const route = useRoute() diff --git a/apps/frontend/src/pages/auth.vue b/apps/frontend/src/pages/auth.vue index 205866bcee..4b2bade85b 100644 --- a/apps/frontend/src/pages/auth.vue +++ b/apps/frontend/src/pages/auth.vue @@ -8,99 +8,7 @@ useSeoMeta({ }) - - diff --git a/apps/frontend/src/pages/auth/authorize.vue b/apps/frontend/src/pages/auth/authorize.vue index c411819eb7..d26d4782a3 100644 --- a/apps/frontend/src/pages/auth/authorize.vue +++ b/apps/frontend/src/pages/auth/authorize.vue @@ -1,29 +1,33 @@ - - - diff --git a/apps/frontend/src/pages/auth/create/oauth.vue b/apps/frontend/src/pages/auth/create/oauth.vue new file mode 100644 index 0000000000..d2bde7b193 --- /dev/null +++ b/apps/frontend/src/pages/auth/create/oauth.vue @@ -0,0 +1,196 @@ + + + diff --git a/apps/frontend/src/pages/auth/reset-password.vue b/apps/frontend/src/pages/auth/reset-password.vue index 365a9063f5..caaaab6bca 100644 --- a/apps/frontend/src/pages/auth/reset-password.vue +++ b/apps/frontend/src/pages/auth/reset-password.vue @@ -1,14 +1,24 @@ - - - diff --git a/apps/frontend/src/pages/dashboard/organizations.vue b/apps/frontend/src/pages/dashboard/organizations.vue index d2d1cc755d..3f934dcbee 100644 --- a/apps/frontend/src/pages/dashboard/organizations.vue +++ b/apps/frontend/src/pages/dashboard/organizations.vue @@ -55,7 +55,6 @@ import { Avatar, defineMessages, injectModrinthClient, useVIntl } from '@modrint import { useQuery } from '@tanstack/vue-query' import OrganizationCreateModal from '~/components/ui/create/OrganizationCreateModal.vue' -import { useAuth } from '~/composables/auth.js' const { formatMessage } = useVIntl() diff --git a/apps/frontend/src/pages/settings/account.vue b/apps/frontend/src/pages/settings/account.vue index 44c767bf70..5496057e68 100644 --- a/apps/frontend/src/pages/settings/account.vue +++ b/apps/frontend/src/pages/settings/account.vue @@ -9,23 +9,30 @@ :has-to-type="true" @proceed="deleteAccount" /> - -
-

{{ formatMessage(messages.emailNotPublicNotice) }}

- - +
+ + {{ formatMessage(messages.emailNotPublicNotice) }} + +
+ + +
- - + -
-
    -
  • {{ formatMessage(messages.passwordsDoNotMatchError) }}
  • -
- - - -
-
- + -
+