Merge commit '7fa442fb28a2b9156690ff147206275163e7aec8' into beta

This commit is contained in:
2025-10-19 06:50:50 +03:00
1007 changed files with 143497 additions and 11362 deletions

View File

@@ -109,7 +109,7 @@ export const getAuthUrl = (provider, redirect = '/dashboard') => {
const route = useNativeRoute()
const fullURL = route.query.launcher
? 'https://launcher-files.modrinth.com'
? getLauncherRedirectUrl(route)
: `${config.public.siteUrl}/auth/sign-in?redirect=${redirect}`
return `${config.public.apiBaseUrl}auth/init?provider=${provider}&url=${encodeURIComponent(fullURL)}`
@@ -131,3 +131,12 @@ export const removeAuthProvider = async (provider) => {
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`
}

View File

@@ -0,0 +1,187 @@
import type { Ref } from 'vue'
import { computed, ref } from 'vue'
export interface FormRequestAttributes {
form_type: 'W-9' | 'W-8BEN' | 'W-8BEN-E' | string
company_id: number
company_name: string
company_email: string
reference_id: string | null
form_id: string | null
signed_at: string | null
tin_match_status: string | null
expires_at: string | null
}
export interface FormRequestLinks {
action_validate?: string
action_complete?: string
[key: string]: unknown
}
export interface FormRequestData {
id: string
type: 'form_request' | string
attributes: FormRequestAttributes
links?: FormRequestLinks
[key: string]: unknown
}
export interface FormRequestResponse {
data: FormRequestData
[key: string]: unknown
}
export interface UseAvalara1099Options {
prefill?: Record<string, unknown>
// Optional override for the origin (defaults to vendor CDN domain)
origin?: string
// Optional hook to further style the injected dialog/iframe
styleDialog?: (dialog: HTMLDialogElement, iframe: HTMLIFrameElement | null) => void
// Poll interval while waiting for global to appear
pollIntervalMs?: number
// Max time to wait for script before rejecting (ms); 0/undefined => no timeout
timeoutMs?: number
}
interface AvalaraGlobal {
requestW9: (
formRequest: FormRequestResponse | FormRequestData,
opts?: { prefill?: Record<string, unknown> },
) => Promise<FormRequestResponse> | any
requestW8BEN: (
formRequest: FormRequestResponse | FormRequestData,
opts?: { prefill?: Record<string, unknown> },
) => Promise<FormRequestResponse> | any
requestW8BENE: (
formRequest: FormRequestResponse | FormRequestData,
opts?: { prefill?: Record<string, unknown> },
) => Promise<FormRequestResponse> | any
origin?: string
[key: string]: unknown
}
declare global {
interface Window {
Avalara1099?: AvalaraGlobal
}
}
const injectedKey = '__avalara1099_script_injected__'
function ensureScriptInjected(origin: string) {
if (import.meta.server) return
const w = window as any
if (w[injectedKey]) return
w[injectedKey] = true
useHead({
script: [
{
src: `${origin.replace(/\/$/, '')}/api/request_form.js`,
crossorigin: 'anonymous',
type: 'module',
},
],
})
}
async function waitForAvalara(opts: {
pollIntervalMs: number
timeoutMs?: number
origin: string
}): Promise<AvalaraGlobal> {
if (import.meta.server) throw new Error('Avalara 1099 is client-side only')
ensureScriptInjected(opts.origin)
const start = Date.now()
return await new Promise((resolve, reject) => {
const poll = () => {
const g = window.Avalara1099
if (g) return resolve(g)
if (opts.timeoutMs && opts.timeoutMs > 0 && Date.now() - start > opts.timeoutMs) {
return reject(new Error('Timed out waiting for Avalara1099 script to load'))
}
setTimeout(poll, opts.pollIntervalMs)
}
poll()
})
}
export function useAvalara1099(
initial: FormRequestResponse | FormRequestData,
options: UseAvalara1099Options = {},
) {
const origin = options.origin || 'https://www.track1099.com'
const pollIntervalMs = options.pollIntervalMs ?? 250
const timeoutMs = options.timeoutMs
const request: Ref<FormRequestResponse | FormRequestData> = ref(initial)
const loading = ref(false)
const error: Ref<unknown> = ref(null)
const signedAt = computed(() => {
const data = (request.value as FormRequestResponse).data || request.value
return data.attributes?.signed_at ? new Date(data.attributes.signed_at) : null
})
const status = computed(() => (signedAt.value ? 'signed' : 'incomplete'))
async function start(): Promise<FormRequestResponse | FormRequestData> {
loading.value = true
error.value = null
try {
const g = await waitForAvalara({ pollIntervalMs, timeoutMs, origin })
const data = (request.value as FormRequestResponse).data || request.value
const formType = data.attributes?.form_type
// Defensive deep clone to strip proxies / non-cloneable refs before postMessage
// (DataCloneError guard)
let safeRequest: any
try {
safeRequest = JSON.parse(JSON.stringify(request.value))
} catch {
// Fallback shallow copy
safeRequest = Array.isArray(request.value)
? [...(request.value as any)]
: { ...(request.value as any) }
}
let safePrefill: any = undefined
if (options.prefill) {
try {
safePrefill = JSON.parse(JSON.stringify(options.prefill))
} catch {
safePrefill = { ...options.prefill }
}
}
let promise: any
if (formType === 'W-8BEN') {
promise = g.requestW8BEN(safeRequest, { prefill: safePrefill })
} else if (formType === 'W-9') {
promise = g.requestW9(safeRequest, { prefill: safePrefill })
} else if (formType === 'W-8BEN-E' || formType === 'W-8BEN E') {
promise = g.requestW8BENE(safeRequest, { prefill: safePrefill })
} else {
throw new Error(`Unsupported form_type: ${formType}`)
}
// The vendor promise resolves with an updated form request (signed state)
const newReq = await promise
request.value = newReq
return newReq
} catch (e) {
error.value = e
throw e
} finally {
loading.value = false
}
}
return {
start,
request,
signedAt,
status, // 'signed' | 'incomplete'
loading,
error,
}
}

View File

@@ -22,9 +22,11 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
showVersionFilesInTable: false,
// showAdsWithPlus: false,
alwaysShowChecklistAsPopup: true,
testTaxForm: false,
// Feature toggles
projectTypesPrimaryNav: false,
enableMedalPromotion: true,
hidePlusPromoInUserMenu: false,
oldProjectCards: true,
newProjectCards: false,
@@ -34,6 +36,9 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
showProjectPageDownloadModalServersPromo: false,
showProjectPageCreateServersTooltip: true,
showProjectPageQuickServerButton: false,
newProjectGeneralSettings: false,
newProjectEnvironmentSettings: true,
hideRussiaCensorshipBanner: false,
// advancedRendering: true,
// externalLinksNewTab: true,
// notUsingBlockers: false,

View File

@@ -41,12 +41,6 @@ export class BackupsModule extends ServerModule {
await this.fetch() // Refresh this module
}
async prepare(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/prepare-download`, {
method: 'POST',
})
}
async lock(backupId: string): Promise<void> {
await useServersFetch(`servers/${this.serverId}/backups/${backupId}/lock`, {
method: 'POST',

View File

@@ -34,6 +34,8 @@ export class GeneralModule extends ServerModule implements ServerGeneral {
node!: { token: string; instance: string }
flows?: { intro?: boolean }
is_medal?: boolean
async fetch(): Promise<void> {
const data = await useServersFetch<ServerGeneral>(`servers/${this.serverId}`, {}, 'general')

View File

@@ -77,6 +77,7 @@ export const initUserProjects = async () => {
if (auth.user && auth.user.id) {
try {
user.projects = await useBaseFetch(`user/${auth.user.id}/projects`)
user.projectsV3 = await useBaseFetch(`user/${auth.user.id}/projects`, { apiVersion: 3 })
} catch (err) {
console.error(err)
}