Merge branch 'api-refactor' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-06-24 19:44:22 +03:00
commit e2ee976a88
21 changed files with 259 additions and 257 deletions

View file

@ -175,7 +175,7 @@ e2e-pleroma:
NOTIFY_EMAIL: $E2E_ADMIN_EMAIL NOTIFY_EMAIL: $E2E_ADMIN_EMAIL
VITE_PROXY_TARGET: http://pleroma:4000 VITE_PROXY_TARGET: http://pleroma:4000
VITE_PROXY_ORIGIN: http://localhost:4000 VITE_PROXY_ORIGIN: http://localhost:4000
E2E_BASE_URL: http://localhost:8080 E2E_BASE_URL: http://localhost:8099
script: script:
- npm install -g yarn@1.22.22 - npm install -g yarn@1.22.22
- yarn --frozen-lockfile - yarn --frozen-lockfile

View file

@ -30,7 +30,7 @@ steps:
environment: environment:
APT_CACHE_DIR: apt-cache APT_CACHE_DIR: apt-cache
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
E2E_BASE_URL: http://localhost:8080 E2E_BASE_URL: http://localhost:8099
FF_NETWORK_PER_BUILD: "true" FF_NETWORK_PER_BUILD: "true"
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
VITE_PROXY_ORIGIN: "http://pleroma:4000" VITE_PROXY_ORIGIN: "http://pleroma:4000"

View file

@ -39,6 +39,8 @@ services:
interval: 5s interval: 5s
timeout: 3s timeout: 3s
retries: 60 retries: 60
ports:
- 4000:4000
e2e: e2e:
build: build:
@ -51,7 +53,7 @@ services:
CI: "1" CI: "1"
VITE_PROXY_TARGET: http://pleroma:4000 VITE_PROXY_TARGET: http://pleroma:4000
VITE_PROXY_ORIGIN: http://localhost:4000 VITE_PROXY_ORIGIN: http://localhost:4000
E2E_BASE_URL: http://localhost:8080 E2E_BASE_URL: http://localhost:8099
E2E_ADMIN_USERNAME: ${E2E_ADMIN_USERNAME:-admin} E2E_ADMIN_USERNAME: ${E2E_ADMIN_USERNAME:-admin}
E2E_ADMIN_PASSWORD: ${E2E_ADMIN_PASSWORD:-adminadmin} E2E_ADMIN_PASSWORD: ${E2E_ADMIN_PASSWORD:-adminadmin}
command: ["yarn", "e2e:pw"] command: ["yarn", "e2e:pw"]

View file

@ -99,7 +99,7 @@
"msw": "2.14.6", "msw": "2.14.6",
"nightwatch": "3.12.2", "nightwatch": "3.12.2",
"oxc": "^1.0.1", "oxc": "^1.0.1",
"playwright": "1.57.0", "playwright": "1.61.0",
"postcss": "8.5.6", "postcss": "8.5.6",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",

View file

@ -98,41 +98,31 @@ export const promisedRequest = async ({
} }
} }
let response = null const response = await fetch(url, options)
try { const data = await (async () => {
response = await fetch(url, options) const [contentType] = response.headers
const data = await (async () => { .get('content-type')
const [contentType] = response.headers .split(';')
.get('content-type') .map((x) => x.toLowerCase().trim())
.split(';') const contentLength = parseInt(response.headers.get('content-length'))
.map((x) => x.toLowerCase().trim()) if (contentLength === 0) return null
const contentLength = parseInt(response.headers.get('content-length'))
if (contentLength === 0) return null
switch (contentType) { switch (contentType) {
case 'text/plain': case 'text/plain':
return await response.text() return await response.text()
case 'application/json': case 'application/json':
return await response.json() return await response.json()
default: default:
return await response.bytes() return await response.bytes()
}
})()
const { ok, status } = response
if (ok) {
return { response, status, data }
} else {
throw new StatusCodeError(
response.status,
data,
{ url, options },
response,
)
} }
} catch (error) { })()
throw new Error(error, { url, options }, response)
const { ok, status } = response
if (ok) {
return { response, status, data }
} else {
throw new StatusCodeError(response.status, data, { url, options }, response)
} }
} }

View file

@ -2,8 +2,6 @@ import { reduce } from 'lodash'
import { paramsString, promisedRequest } from './helpers.js' import { paramsString, promisedRequest } from './helpers.js'
import { StatusCodeError } from 'src/services/errors/errors.js'
const REDIRECT_URI = `${window.location.origin}/oauth-callback` const REDIRECT_URI = `${window.location.origin}/oauth-callback`
export const MASTODON_APP_VERIFY_URL = '/api/v1/apps/verify_credentials' export const MASTODON_APP_VERIFY_URL = '/api/v1/apps/verify_credentials'

View file

@ -1,10 +1,6 @@
import { concat, each, last, map } from 'lodash'
import { paramsString, promisedRequest } from './helpers.js' import { paramsString, promisedRequest } from './helpers.js'
import { import {
parseAttachment,
parseChat,
parseLinkHeaderPagination, parseLinkHeaderPagination,
parseNotification, parseNotification,
parseSource, parseSource,
@ -143,8 +139,6 @@ const MASTODON_SEARCH_2 = ({
`/api/v2/search${paramsString({ q, resolve, limit, offset, following, type, withRelationships, accountId, excludeUnreviewed })}` `/api/v2/search${paramsString({ q, resolve, limit, offset, following, type, withRelationships, accountId, excludeUnreviewed })}`
const MASTODON_USER_SEARCH_URL = ({ q, resolve }) => const MASTODON_USER_SEARCH_URL = ({ q, resolve }) =>
`/api/v1/accounts/search${paramsString({ q, resolve })}` `/api/v1/accounts/search${paramsString({ q, resolve })}`
const MASTODON_STREAMING = ({ accessToken, stream }) =>
`/api/v1/streaming${paramsString({ accessToken, stream })}`
const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
const PLEROMA_EMOJI_REACTIONS_URL = (id) => const PLEROMA_EMOJI_REACTIONS_URL = (id) =>
`/api/v1/pleroma/statuses/${id}/reactions` `/api/v1/pleroma/statuses/${id}/reactions`
@ -487,168 +481,6 @@ export const search2 = ({
export const fetchKnownDomains = ({ credentials }) => export const fetchKnownDomains = ({ credentials }) =>
promisedRequest({ url: MASTODON_KNOWN_DOMAIN_LIST_URL, credentials }) promisedRequest({ url: MASTODON_KNOWN_DOMAIN_LIST_URL, credentials })
export const getMastodonSocketURI = ({ credentials, stream }, base) => {
return base + MASTODON_STREAMING({ accessToken: credentials, stream })
}
const MASTODON_STREAMING_EVENTS = new Set([
'update',
'notification',
'delete',
'filters_changed',
'status.update',
])
const PLEROMA_STREAMING_EVENTS = new Set([
'pleroma:chat_update',
'pleroma:respond',
])
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
// Uses EventTarget and a CustomEvent to proxy events
export const ProcessedWS = ({
url,
preprocessor = handleMastoWS,
id = 'Unknown',
credentials,
}) => {
const eventTarget = new EventTarget()
const socket = new WebSocket(url)
if (!socket) throw new Error(`Failed to create socket ${id}`)
const proxy = (original, eventName, processor = (a) => a) => {
original.addEventListener(eventName, (eventData) => {
eventTarget.dispatchEvent(
new CustomEvent(eventName, { detail: processor(eventData) }),
)
})
}
socket.addEventListener('open', (wsEvent) => {
console.debug(`[WS][${id}] Socket connected`, wsEvent)
if (credentials) {
socket.send(
JSON.stringify({
type: 'pleroma:authenticate',
token: credentials,
}),
)
}
})
socket.addEventListener('error', (wsEvent) => {
console.debug(`[WS][${id}] Socket errored`, wsEvent)
})
socket.addEventListener('close', (wsEvent) => {
console.debug(
`[WS][${id}] Socket disconnected with code ${wsEvent.code}`,
wsEvent,
)
})
// Commented code reason: very spammy, uncomment to enable message debug logging
/*
socket.addEventListener('message', (wsEvent) => {
console.debug(
`[WS][${id}] Message received`,
wsEvent
)
})
/**/
const onAuthenticated = () => {
eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated'))
}
proxy(socket, 'open')
proxy(socket, 'close')
proxy(socket, 'message', (event) => preprocessor(event, { onAuthenticated }))
proxy(socket, 'error')
// 1000 = Normal Closure
eventTarget.close = () => {
socket.close(1000, 'Shutting down socket')
}
eventTarget.getState = () => socket.readyState
eventTarget.subscribe = (stream, args = {}) => {
console.debug(`[WS][${id}] Subscribing to stream ${stream} with args`, args)
socket.send(
JSON.stringify({
type: 'subscribe',
stream,
...args,
}),
)
}
eventTarget.unsubscribe = (stream, args = {}) => {
console.debug(
`[WS][${id}] Unsubscribing from stream ${stream} with args`,
args,
)
socket.send(
JSON.stringify({
type: 'unsubscribe',
stream,
...args,
}),
)
}
return eventTarget
}
export const handleMastoWS = (
wsEvent,
{
onAuthenticated = () => {
/* no-op */
},
} = {},
) => {
const { data } = wsEvent
if (!data) return
const parsedEvent = JSON.parse(data)
const { event, payload } = parsedEvent
if (
MASTODON_STREAMING_EVENTS.has(event) ||
PLEROMA_STREAMING_EVENTS.has(event)
) {
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
if (event === 'delete') {
return { event, id: payload }
}
const data = payload ? JSON.parse(payload) : null
if (event === 'pleroma:respond') {
if (data.type === 'pleroma:authenticate') {
if (data.result === 'success') {
console.debug('[WS] Successfully authenticated')
onAuthenticated()
} else {
console.error('[WS] Unable to authenticate:', data.error)
wsEvent.target.close()
}
}
return null
} else if (event === 'update') {
return { event, status: parseStatus(data) }
} else if (event === 'status.update') {
return { event, status: parseStatus(data) }
} else if (event === 'notification') {
return { event, notification: parseNotification(data) }
} else if (event === 'pleroma:chat_update') {
return { event, chatUpdate: parseChat(data) }
}
} else {
console.warn('Unknown event', wsEvent)
return null
}
}
export const WSConnectionStatus = Object.freeze({
JOINED: 1,
CLOSED: 2,
ERROR: 3,
DISABLED: 4,
STARTING: 5,
STARTING_INITIAL: 6,
})
export const fetchScrobbles = ({ accountId, limit = 1 }) => export const fetchScrobbles = ({ accountId, limit = 1 }) =>
promisedRequest({ promisedRequest({
url: PLEROMA_SCROBBLES_URL(accountId, { limit }), url: PLEROMA_SCROBBLES_URL(accountId, { limit }),

View file

@ -1,4 +1,4 @@
import { concat, each, last, map } from 'lodash' import { concat, last } from 'lodash'
import { paramsString, promisedRequest } from './helpers.js' import { paramsString, promisedRequest } from './helpers.js'
import { fetchFriends, MASTODON_STATUS_URL } from './public.js' import { fetchFriends, MASTODON_STATUS_URL } from './public.js'

176
src/api/websocket.js Normal file
View file

@ -0,0 +1,176 @@
import { paramsString, promisedRequest } from './helpers.js'
import {
parseChat,
parseNotification,
parseStatus,
} from 'src/services/entity_normalizer/entity_normalizer.service.js'
const MASTODON_STREAMING = ({ accessToken, stream }) =>
`/api/v1/streaming${paramsString({ accessToken, stream })}`
export const getMastodonSocketURI = ({ credentials, stream }) => {
return MASTODON_STREAMING({ accessToken: credentials, stream })
}
const MASTODON_STREAMING_EVENTS = new Set([
'update',
'notification',
'delete',
'filters_changed',
'status.update',
])
const PLEROMA_STREAMING_EVENTS = new Set([
'pleroma:chat_update',
'pleroma:respond',
])
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
// Uses EventTarget and a CustomEvent to proxy events
export const ProcessedWS = ({
url,
preprocessor = handleMastoWS,
id = 'Unknown',
credentials,
}) => {
const eventTarget = new EventTarget()
const socket = new WebSocket(url)
if (!socket) throw new Error(`Failed to create socket ${id}`)
const proxy = (original, eventName, processor = (a) => a) => {
original.addEventListener(eventName, (eventData) => {
eventTarget.dispatchEvent(
new CustomEvent(eventName, { detail: processor(eventData) }),
)
})
}
socket.addEventListener('open', (wsEvent) => {
console.debug(`[WS][${id}] Socket connected`, wsEvent)
if (credentials) {
socket.send(
JSON.stringify({
type: 'pleroma:authenticate',
token: credentials,
}),
)
}
})
socket.addEventListener('error', (wsEvent) => {
console.debug(`[WS][${id}] Socket errored`, wsEvent)
})
socket.addEventListener('close', (wsEvent) => {
console.debug(
`[WS][${id}] Socket disconnected with code ${wsEvent.code}`,
wsEvent,
)
})
// Commented code reason: very spammy, uncomment to enable message debug logging
/*
socket.addEventListener('message', (wsEvent) => {
console.debug(
`[WS][${id}] Message received`,
wsEvent
)
})
/**/
const onAuthenticated = () => {
eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated'))
}
proxy(socket, 'open')
proxy(socket, 'close')
proxy(socket, 'message', (event) => preprocessor(event, { onAuthenticated }))
proxy(socket, 'error')
// 1000 = Normal Closure
eventTarget.close = () => {
socket.close(1000, 'Shutting down socket')
}
eventTarget.getState = () => socket.readyState
eventTarget.subscribe = (stream, args = {}) => {
console.debug(`[WS][${id}] Subscribing to stream ${stream} with args`, args)
socket.send(
JSON.stringify({
type: 'subscribe',
stream,
...args,
}),
)
}
eventTarget.unsubscribe = (stream, args = {}) => {
console.debug(
`[WS][${id}] Unsubscribing from stream ${stream} with args`,
args,
)
socket.send(
JSON.stringify({
type: 'unsubscribe',
stream,
...args,
}),
)
}
return eventTarget
}
export const handleMastoWS = (
wsEvent,
{
onAuthenticated = () => {
/* no-op */
},
} = {},
) => {
const { data } = wsEvent
if (!data) return
const parsedEvent = JSON.parse(data)
const { event, payload } = parsedEvent
if (
MASTODON_STREAMING_EVENTS.has(event) ||
PLEROMA_STREAMING_EVENTS.has(event)
) {
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
if (event === 'delete') {
return { event, id: payload }
}
const data = payload ? JSON.parse(payload) : null
if (event === 'pleroma:respond') {
if (data.type === 'pleroma:authenticate') {
if (data.result === 'success') {
console.debug('[WS] Successfully authenticated')
onAuthenticated()
} else {
if (data.error === 'already_authenticated') {
onAuthenticated()
} else {
console.error('[WS] Unable to authenticate:', data.error)
wsEvent.target.close()
}
}
}
return null
} else if (event === 'update') {
return { event, status: parseStatus(data) }
} else if (event === 'status.update') {
return { event, status: parseStatus(data) }
} else if (event === 'notification') {
return { event, notification: parseNotification(data) }
} else if (event === 'pleroma:chat_update') {
return { event, chatUpdate: parseChat(data) }
}
} else {
console.warn('Unknown event', wsEvent)
return null
}
}
export const WSConnectionStatus = Object.freeze({
JOINED: 1,
CLOSED: 2,
ERROR: 3,
DISABLED: 4,
STARTING: 5,
STARTING_INITIAL: 6,
})

View file

@ -23,7 +23,7 @@ import {
getOrCreateChat, getOrCreateChat,
sendChatMessage, sendChatMessage,
} from 'src/api/chats.js' } from 'src/api/chats.js'
import { WSConnectionStatus } from 'src/api/public.js' import { WSConnectionStatus } from 'src/api/websocket.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons' import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons'

View file

@ -10,11 +10,8 @@ import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useOAuthStore } from 'src/stores/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js'
import { import { fetchConversation, fetchStatus } from 'src/api/public.js'
fetchConversation, import { WSConnectionStatus } from 'src/api/websocket.js'
fetchStatus,
WSConnectionStatus,
} from 'src/api/public.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {

View file

@ -8,12 +8,12 @@ import { useInterfaceStore } from 'src/stores/interface.js'
import { useOAuthStore } from 'src/stores/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js'
import { useShoutStore } from 'src/stores/shout.js' import { useShoutStore } from 'src/stores/shout.js'
import { fetchTimeline } from 'src/api/public.js'
import { import {
fetchTimeline,
getMastodonSocketURI, getMastodonSocketURI,
ProcessedWS, ProcessedWS,
WSConnectionStatus, WSConnectionStatus,
} from 'src/api/public.js' } from 'src/api/websocket.js'
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service' import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js' import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js'
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js' import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
@ -97,9 +97,8 @@ const api = {
const { state, commit, dispatch, rootState } = store const { state, commit, dispatch, rootState } = store
const timelineData = rootState.statuses.timelines.friends const timelineData = rootState.statuses.timelines.friends
const serv = useInstanceStore().server.replace('http', 'ws')
const credentials = useOAuthStore().token const credentials = useOAuthStore().token
const url = getMastodonSocketURI({ credentials }, serv) const url = getMastodonSocketURI({ credentials })
state.mastoUserSocket = ProcessedWS({ state.mastoUserSocket = ProcessedWS({
url, url,

View file

@ -13,7 +13,7 @@ function humanizeErrors(errors) {
export function StatusCodeError(statusCode, body, options, response) { export function StatusCodeError(statusCode, body, options, response) {
this.name = 'StatusCodeError' this.name = 'StatusCodeError'
this.statusCode = statusCode this.statusCode = statusCode
this.statusText = body.error.error || body.error this.statusText = body.error || body
this.details = JSON && JSON.stringify ? JSON.stringify(body) : body this.details = JSON && JSON.stringify ? JSON.stringify(body) : body
this.errorData = body.error this.errorData = body.error
this.message = this.statusCode + ' - ' + this.statusText this.message = this.statusCode + ' - ' + this.statusText

View file

@ -87,22 +87,20 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
loadAdminStuff() { loadAdminStuff() {
getInstanceDBConfig({ getInstanceDBConfig({
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(({ data: backendDbConfig }) => { })
if (backendDbConfig.error) { .then(({ data: backendDbConfig }) =>
if (backendDbConfig.error.status === 400) {
backendDbConfig.error.json().then((errorJson) => {
if (/configurable_from_database/.test(errorJson.error)) {
this.setInstanceAdminNoDbConfig()
}
})
}
} else {
this.setInstanceAdminSettings({ this.setInstanceAdminSettings({
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
backendDbConfig, backendDbConfig,
}) }),
} )
}) .catch(({ statusCode, statusText }) => {
if (statusCode === 400) {
if (/configurable_from_database/.test(statusText)) {
this.setInstanceAdminNoDbConfig()
}
}
})
if (this.descriptions === null) { if (this.descriptions === null) {
getInstanceConfigDescriptions({ getInstanceConfigDescriptions({
credentials: useOAuthStore().token, credentials: useOAuthStore().token,

View file

@ -27,8 +27,8 @@ export const useBookmarkFoldersStore = defineStore('bookmarkFolders', {
}, },
actions: { actions: {
startFetching() { startFetching() {
promiseInterval(() => { this.fetcher = promiseInterval(() => {
this.fetcher = fetchBookmarkFolders({ fetchBookmarkFolders({
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}) })
.then(({ data: folders }) => this.setBookmarkFolders(folders)) .then(({ data: folders }) => this.setBookmarkFolders(folders))

View file

@ -34,8 +34,8 @@ export const useListsStore = defineStore('lists', {
}, },
actions: { actions: {
startFetching() { startFetching() {
promiseInterval(() => { this.fetcher = promiseInterval(() => {
this.fetcher = fetchLists({ fetchLists({
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}) })
.then(({ data: lists }) => this.setLists(lists)) .then(({ data: lists }) => this.setLists(lists))

View file

@ -1,7 +1,7 @@
/* global process */ /* global process */
import { defineConfig, devices } from 'playwright/test' import { defineConfig, devices } from 'playwright/test'
const baseURL = process.env.E2E_BASE_URL || 'http://localhost:8080' const baseURL = process.env.E2E_BASE_URL || 'http://localhost:8099'
export default defineConfig({ export default defineConfig({
testDir: './specs', testDir: './specs',
@ -25,12 +25,13 @@ export default defineConfig({
video: 'retain-on-failure', video: 'retain-on-failure',
}, },
webServer: { webServer: {
command: 'yarn dev -- --host 0.0.0.0 --port 8080 --strictPort', command: 'yarn dev -- --host 0.0.0.0 --port $PORT --strictPort',
url: baseURL, url: baseURL,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
timeout: 120_000, timeout: 120_000,
env: { env: {
...process.env, ...process.env,
PORT: process.env.PORT || '8099',
VITE_PROXY_TARGET: VITE_PROXY_TARGET:
process.env.VITE_PROXY_TARGET || 'http://localhost:4000', process.env.VITE_PROXY_TARGET || 'http://localhost:4000',
VITE_PROXY_ORIGIN: VITE_PROXY_ORIGIN:

View file

@ -4,7 +4,7 @@
module.exports = { module.exports = {
'default e2e tests': function (browser) { 'default e2e tests': function (browser) {
// automatically uses dev Server port from /config.index.js // automatically uses dev Server port from /config.index.js
// default: http://localhost:8080 // default: http://localhost:8099
// see nightwatch.conf.js // see nightwatch.conf.js
const devServer = browser.globals.devServerURL const devServer = browser.globals.devServerURL

View file

@ -69,6 +69,12 @@ describe('oauth store', () => {
it('should use create an app and record client id and secret', async ({ it('should use create an app and record client id and secret', async ({
worker, worker,
}) => { }) => {
worker.use(
http.post(MASTODON_APP_URL, () => {
return HttpResponse.text('Throttled', { status: 429 })
}),
)
const store = useOAuthStore() const store = useOAuthStore()
worker.use(...authApis()) worker.use(...authApis())
const app = await store.createApp() const app = await store.createApp()

View file

@ -73,6 +73,7 @@ export default defineConfig(async ({ mode, command }) => {
const settings = await getLocalDevSettings() const settings = await getLocalDevSettings()
const target = settings.target || 'http://localhost:4000/' const target = settings.target || 'http://localhost:4000/'
const origin = settings.origin || target const origin = settings.origin || target
const targetSW = target.replace(/^http/, 'ws')
const transformSW = getTransformSWSettings(settings) const transformSW = getTransformSWSettings(settings)
const proxy = { const proxy = {
'/api': { '/api': {
@ -80,6 +81,7 @@ export default defineConfig(async ({ mode, command }) => {
changeOrigin: true, changeOrigin: true,
cookieDomainRewrite: 'localhost', cookieDomainRewrite: 'localhost',
ws: true, ws: true,
rewriteWsOrigin: true,
}, },
'/auth': { '/auth': {
// Mastodon password reset lives here // Mastodon password reset lives here
@ -98,20 +100,21 @@ export default defineConfig(async ({ mode, command }) => {
changeOrigin: true, changeOrigin: true,
cookieDomainRewrite: 'localhost', cookieDomainRewrite: 'localhost',
}, },
'/socket': {
target,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
ws: true,
headers: {
Origin: origin,
},
},
'/oauth': { '/oauth': {
target, target,
changeOrigin: true, changeOrigin: true,
cookieDomainRewrite: 'localhost', cookieDomainRewrite: 'localhost',
}, },
'/socket': {
target: targetSW,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
rewriteWsOrigin: true,
ws: true,
headers: {
Origin: origin,
},
},
} }
const swSrc = 'src/sw.js' const swSrc = 'src/sw.js'

View file

@ -7289,17 +7289,17 @@ pkg-types@^1.3.1:
mlly "^1.7.4" mlly "^1.7.4"
pathe "^2.0.1" pathe "^2.0.1"
playwright-core@1.57.0: playwright-core@1.61.0:
version "1.57.0" version "1.61.0"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.57.0.tgz#3dcc9a865af256fa9f0af0d67fc8dd54eecaebf5" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.61.0.tgz#caf8078b2a39cd7738dc75ec11cb3b47f385c3f0"
integrity sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ== integrity sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==
playwright@1.57.0: playwright@1.61.0:
version "1.57.0" version "1.61.0"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.57.0.tgz#74d1dacff5048dc40bf4676940b1901e18ad0f46" resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.61.0.tgz#7082df3df08ffa82b11420ea5fae84a40bd16e3f"
integrity sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw== integrity sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==
dependencies: dependencies:
playwright-core "1.57.0" playwright-core "1.61.0"
optionalDependencies: optionalDependencies:
fsevents "2.3.2" fsevents "2.3.2"