summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/web
diff options
context:
space:
mode:
authormisskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com>2025-08-31 08:42:43 +0000
committerGitHub <noreply@github.com>2025-08-31 08:42:43 +0000
commitec21336d45e6e3bb26a0225fc0aa57ac98d5be4b (patch)
tree2c7aef5ba1626009377faf96941a57411dd619e5 /packages/backend/src/server/web
parentMerge pull request #16244 from misskey-dev/develop (diff)
parentRelease: 2025.8.0 (diff)
downloadmisskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.tar.gz
misskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.tar.bz2
misskey-ec21336d45e6e3bb26a0225fc0aa57ac98d5be4b.zip
Merge pull request #16335 from misskey-dev/develop
Release: 2025.8.0
Diffstat (limited to 'packages/backend/src/server/web')
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts34
-rw-r--r--packages/backend/src/server/web/boot.embed.js85
-rw-r--r--packages/backend/src/server/web/boot.js147
-rw-r--r--packages/backend/src/server/web/manifest.json8
-rw-r--r--packages/backend/src/server/web/views/base-embed.pug3
-rw-r--r--packages/backend/src/server/web/views/base.pug3
6 files changed, 133 insertions, 147 deletions
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 8ca61a497d..b515a0c0c8 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -20,17 +20,6 @@ import type { Config } from '@/config.js';
import { getNoteSummary } from '@/misc/get-note-summary.js';
import { DI } from '@/di-symbols.js';
import * as Acct from '@/misc/acct.js';
-import type {
- DbQueue,
- DeliverQueue,
- EndedPollNotificationQueue,
- InboxQueue,
- ObjectStorageQueue,
- RelationshipQueue,
- SystemQueue,
- UserWebhookDeliverQueue,
- SystemWebhookDeliverQueue,
-} from '@/core/QueueModule.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
@@ -129,16 +118,6 @@ export class ClientServerService {
private feedService: FeedService,
private roleService: RoleService,
private clientLoggerService: ClientLoggerService,
-
- @Inject('queue:system') public systemQueue: SystemQueue,
- @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue,
- @Inject('queue:deliver') public deliverQueue: DeliverQueue,
- @Inject('queue:inbox') public inboxQueue: InboxQueue,
- @Inject('queue:db') public dbQueue: DbQueue,
- @Inject('queue:relationship') public relationshipQueue: RelationshipQueue,
- @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
- @Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
- @Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
) {
//this.createServer = this.createServer.bind(this);
}
@@ -188,6 +167,10 @@ export class ClientServerService {
'url': 'url',
},
},
+ 'shortcuts': [{
+ 'name': 'Safemode',
+ 'url': '/?safemode=true',
+ }],
};
manifest = {
@@ -580,7 +563,7 @@ export class ClientServerService {
id: request.params.note,
visibility: In(['public', 'home']),
},
- relations: ['user'],
+ relations: ['user', 'reply', 'renote'],
});
if (
@@ -821,8 +804,11 @@ export class ClientServerService {
fastify.get<{ Params: { note: string; } }>('/embed/notes/:note', async (request, reply) => {
reply.removeHeader('X-Frame-Options');
- const note = await this.notesRepository.findOneBy({
- id: request.params.note,
+ const note = await this.notesRepository.findOne({
+ where: {
+ id: request.params.note,
+ },
+ relations: ['user', 'reply', 'renote'],
});
if (note == null) return;
diff --git a/packages/backend/src/server/web/boot.embed.js b/packages/backend/src/server/web/boot.embed.js
index 9de1275380..022ff064ad 100644
--- a/packages/backend/src/server/web/boot.embed.js
+++ b/packages/backend/src/server/web/boot.embed.js
@@ -32,61 +32,30 @@
}
//#region Detect language & fetch translations
- if (!localStorage.hasOwnProperty('locale')) {
- const supportedLangs = LANGS;
- let lang = localStorage.getItem('lang');
- if (lang == null || !supportedLangs.includes(lang)) {
- if (supportedLangs.includes(navigator.language)) {
- lang = navigator.language;
- } else {
- lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
-
- // Fallback
- if (lang == null) lang = 'en-US';
- }
- }
-
- const metaRes = await window.fetch('/api/meta', {
- method: 'POST',
- body: JSON.stringify({}),
- credentials: 'omit',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- if (metaRes.status !== 200) {
- renderError('META_FETCH');
- return;
- }
- const meta = await metaRes.json();
- const v = meta.version;
- if (v == null) {
- renderError('META_FETCH_V');
- return;
- }
+ const supportedLangs = LANGS;
+ /** @type { string } */
+ let lang = localStorage.getItem('lang');
+ if (lang == null || !supportedLangs.includes(lang)) {
+ if (supportedLangs.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
- // for https://github.com/misskey-dev/misskey/issues/10202
- if (lang == null || lang.toString == null || lang.toString() === 'null') {
- console.error('invalid lang value detected!!!', typeof lang, lang);
- lang = 'en-US';
+ // Fallback
+ if (lang == null) lang = 'en-US';
}
+ }
- const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
- if (localRes.status === 200) {
- localStorage.setItem('lang', lang);
- localStorage.setItem('locale', await localRes.text());
- localStorage.setItem('localeVersion', v);
- } else {
- renderError('LOCALE_FETCH');
- return;
- }
+ // for https://github.com/misskey-dev/misskey/issues/10202
+ if (lang == null || lang.toString == null || lang.toString() === 'null') {
+ console.error('invalid lang value detected!!!', typeof lang, lang);
+ lang = 'en-US';
}
//#endregion
//#region Script
async function importAppScript() {
- await import(`/embed_vite/${CLIENT_ENTRY}`)
+ await import(CLIENT_ENTRY ? `/embed_vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/embed_vite/src/_boot_.ts')
.catch(async e => {
console.error(e);
renderError('APP_IMPORT');
@@ -115,10 +84,26 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
- const locale = JSON.parse(localStorage.getItem('locale') || '{}');
+ let messages = null;
+ const bootloaderLocales = localStorage.getItem('bootloaderLocales');
+ if (bootloaderLocales) {
+ messages = JSON.parse(bootloaderLocales);
+ }
+ if (!messages) {
+ // older version of misskey does not store bootloaderLocales, stores locale as a whole
+ const legacyLocale = localStorage.getItem('locale');
+ if (legacyLocale) {
+ const parsed = JSON.parse(legacyLocale);
+ messages = {
+ ...(parsed._bootErrors ?? {}),
+ reload: parsed.reload,
+ };
+ }
+ }
+ if (!messages) messages = {};
- const title = locale?._bootErrors?.title || 'Failed to initialize Misskey';
- const reload = locale?.reload || 'Reload';
+ const title = messages?.title || 'Failed to initialize Misskey';
+ const reload = messages?.reload || 'Reload';
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
<div class="message">${title}</div>
diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 24794cbf2a..0c0b46f82b 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -22,62 +22,31 @@
return;
}
- //#region Detect language & fetch translations
- if (!localStorage.hasOwnProperty('locale')) {
- const supportedLangs = LANGS;
- let lang = localStorage.getItem('lang');
- if (lang == null || !supportedLangs.includes(lang)) {
- if (supportedLangs.includes(navigator.language)) {
- lang = navigator.language;
- } else {
- lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
-
- // Fallback
- if (lang == null) lang = 'en-US';
- }
- }
-
- const metaRes = await window.fetch('/api/meta', {
- method: 'POST',
- body: JSON.stringify({}),
- credentials: 'omit',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- if (metaRes.status !== 200) {
- renderError('META_FETCH');
- return;
- }
- const meta = await metaRes.json();
- const v = meta.version;
- if (v == null) {
- renderError('META_FETCH_V');
- return;
- }
+ //#region Detect language
+ const supportedLangs = LANGS;
+ /** @type { string } */
+ let lang = localStorage.getItem('lang');
+ if (lang == null || !supportedLangs.includes(lang)) {
+ if (supportedLangs.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
- // for https://github.com/misskey-dev/misskey/issues/10202
- if (lang == null || lang.toString == null || lang.toString() === 'null') {
- console.error('invalid lang value detected!!!', typeof lang, lang);
- lang = 'en-US';
+ // Fallback
+ if (lang == null) lang = 'en-US';
}
+ }
- const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
- if (localRes.status === 200) {
- localStorage.setItem('lang', lang);
- localStorage.setItem('locale', await localRes.text());
- localStorage.setItem('localeVersion', v);
- } else {
- renderError('LOCALE_FETCH');
- return;
- }
+ // for https://github.com/misskey-dev/misskey/issues/10202
+ if (lang == null || lang.toString == null || lang.toString() === 'null') {
+ console.error('invalid lang value detected!!!', typeof lang, lang);
+ lang = 'en-US';
}
//#endregion
//#region Script
async function importAppScript() {
- await import(`/vite/${CLIENT_ENTRY}`)
+ await import(CLIENT_ENTRY ? `/vite/${CLIENT_ENTRY.replace('scripts', lang)}` : '/vite/src/_boot_.ts')
.catch(async e => {
console.error(e);
renderError('APP_IMPORT', e);
@@ -94,23 +63,37 @@
}
//#endregion
+ let isSafeMode = (localStorage.getItem('isSafeMode') === 'true');
+
+ if (!isSafeMode) {
+ const urlParams = new URLSearchParams(window.location.search);
+
+ if (urlParams.has('safemode') && urlParams.get('safemode') === 'true') {
+ localStorage.setItem('isSafeMode', 'true');
+ isSafeMode = true;
+ }
+ }
+
//#region Theme
- const theme = localStorage.getItem('theme');
- if (theme) {
- for (const [k, v] of Object.entries(JSON.parse(theme))) {
- document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
+ if (!isSafeMode) {
+ const theme = localStorage.getItem('theme');
+ if (theme) {
+ for (const [k, v] of Object.entries(JSON.parse(theme))) {
+ document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
- // HTMLの theme-color 適用
- if (k === 'htmlThemeColor') {
- for (const tag of document.head.children) {
- if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
- tag.setAttribute('content', v);
- break;
+ // HTMLの theme-color 適用
+ if (k === 'htmlThemeColor') {
+ for (const tag of document.head.children) {
+ if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+ tag.setAttribute('content', v);
+ break;
+ }
}
}
}
}
}
+
const colorScheme = localStorage.getItem('colorScheme');
if (colorScheme) {
document.documentElement.style.setProperty('color-scheme', colorScheme);
@@ -127,11 +110,13 @@
document.documentElement.classList.add('useSystemFont');
}
- const customCss = localStorage.getItem('customCss');
- if (customCss && customCss.length > 0) {
- const style = document.createElement('style');
- style.innerHTML = customCss;
- document.head.appendChild(style);
+ if (!isSafeMode) {
+ const customCss = localStorage.getItem('customCss');
+ if (customCss && customCss.length > 0) {
+ const style = document.createElement('style');
+ style.innerHTML = customCss;
+ document.head.appendChild(style);
+ }
}
async function addStyle(styleText) {
@@ -146,9 +131,25 @@
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}
- const locale = JSON.parse(localStorage.getItem('locale') || '{}');
+ let messages = null;
+ const bootloaderLocales = localStorage.getItem('bootloaderLocales');
+ if (bootloaderLocales) {
+ messages = JSON.parse(bootloaderLocales);
+ }
+ if (!messages) {
+ // older version of misskey does not store bootloaderLocales, stores locale as a whole
+ const legacyLocale = localStorage.getItem('locale');
+ if (legacyLocale) {
+ const parsed = JSON.parse(legacyLocale);
+ messages = {
+ ...(parsed._bootErrors ?? {}),
+ reload: parsed.reload,
+ };
+ }
+ }
+ if (!messages) messages = {};
- const messages = Object.assign({
+ messages = Object.assign({
title: 'Failed to initialize Misskey',
solution: 'The following actions may solve the problem.',
solution1: 'Update your os and browser',
@@ -159,8 +160,12 @@
otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool',
- }, locale?._bootErrors || {});
- const reload = locale?.reload || 'Reload';
+ otherOption4: 'Start Misskey in safe mode',
+ reload: 'Reload',
+ }, messages);
+
+ const safeModeUrl = new URL(window.location.href);
+ safeModeUrl.searchParams.set('safemode', 'true');
let errorsElement = document.getElementById('errors');
@@ -173,7 +178,7 @@
</svg>
<h1>${messages.title}</h1>
<button class="button-big" onclick="location.reload(true);">
- <span class="button-label-big">${reload}</span>
+ <span class="button-label-big">${messages?.reload}</span>
</button>
<p><b>${messages.solution}</b></p>
<p>${messages.solution1}</p>
@@ -182,6 +187,12 @@
<p>${messages.solution4}</p>
<details style="color: #86b300;">
<summary>${messages.otherOption}</summary>
+ <a href="${safeModeUrl}">
+ <button class="button-small">
+ <span class="button-label-small">${messages.otherOption4}</span>
+ </button>
+ </a>
+ <br>
<a href="/flush">
<button class="button-small">
<span class="button-label-small">${messages.otherOption1}</span>
diff --git a/packages/backend/src/server/web/manifest.json b/packages/backend/src/server/web/manifest.json
index 41171d62a1..90d4530857 100644
--- a/packages/backend/src/server/web/manifest.json
+++ b/packages/backend/src/server/web/manifest.json
@@ -34,5 +34,11 @@
"text": "text",
"url": "url"
}
- }
+ },
+ "shortcuts": [
+ {
+ "name": "Safemode",
+ "url": "/?safemode=true"
+ }
+ ]
}
diff --git a/packages/backend/src/server/web/views/base-embed.pug b/packages/backend/src/server/web/views/base-embed.pug
index baa0909676..29de86b8b6 100644
--- a/packages/backend/src/server/web/views/base-embed.pug
+++ b/packages/backend/src/server/web/views/base-embed.pug
@@ -19,7 +19,6 @@ html(class='embed')
meta(name='format-detection' content='telephone=no,date=no,address=no,email=no,url=no')
link(rel='icon' href= icon || '/favicon.ico')
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
- link(rel='modulepreload' href=`/embed_vite/${entry.file}`)
if !config.frontendEmbedManifestExists
script(type="module" src="/embed_vite/@vite/client")
@@ -40,7 +39,7 @@ html(class='embed')
script.
var VERSION = "#{version}";
- var CLIENT_ENTRY = "#{entry.file}";
+ var CLIENT_ENTRY = !{JSON.stringify(entry.file)};
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 3883b5e5ab..a76c75fe5c 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -37,7 +37,6 @@ html
link(rel='prefetch' href=serverErrorImageUrl)
link(rel='prefetch' href=infoImageUrl)
link(rel='prefetch' href=notFoundImageUrl)
- link(rel='modulepreload' href=`/vite/${entry.file}`)
if !config.frontendManifestExists
script(type="module" src="/vite/@vite/client")
@@ -69,7 +68,7 @@ html
script.
var VERSION = "#{version}";
- var CLIENT_ENTRY = "#{entry.file}";
+ var CLIENT_ENTRY = !{JSON.stringify(entry.file)};
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson