Compare commits

...

4 Commits

Author SHA1 Message Date
syuilo b5782fbacd
Merge 8cce6dff51 into 65e51463c8 2026-02-02 00:19:37 +09:00
かっこかり 65e51463c8
fix(frontend): CSSの指定が誤っている問題を修正 (#17135) 2026-01-31 22:38:16 +09:00
Ken_Cir 39362f78a6
fix(backend): inconsistent permissions for /admin/get-user-ips (#17136)
* fix(backend): inconsistent permissions for /admin/get-user-ips

* Update Changelog
2026-01-31 22:37:48 +09:00
syuilo 8cce6dff51 wip 2026-01-06 11:38:10 +09:00
4 changed files with 69 additions and 9 deletions

View File

@ -31,6 +31,7 @@
- JSONによるClient Information Discoveryを行うには、レスポンスの`Content-Type`ヘッダーが`application/json`である必要があります
- 従来の実装12 February 2022版・HTML Microformat形式も引き続きサポートされます
- Enhance: メモリ使用量を削減
- Fix: `/admin/get-user-ips` エンドポイントのアクセス権限を管理者のみに修正
## 2025.12.2

View File

@ -1,7 +1,7 @@
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { build } from 'esbuild';
import * as esbuild from 'esbuild';
import { swcPlugin } from 'esbuild-plugin-swc';
const _filename = fileURLToPath(import.meta.url);
@ -103,12 +103,16 @@ if (!args.includes('--no-clean')) {
fs.rmSync('./built', { recursive: true, force: true });
}
//await minifyJsFiles(join(_dirname, 'node_modules'));
await minifyJsFiles(join(_dirname, '../../node_modules'));
await buildSrc();
async function buildSrc() {
console.log(`[${_package.name}] start building...`);
await build(options)
await esbuild.build(options)
.then(() => {
console.log(`[${_package.name}] build succeeded.`);
})
@ -119,3 +123,58 @@ async function buildSrc() {
console.log(`[${_package.name}] finish building.`);
}
async function minifyJsFile(fullPath) {
if (!fullPath.includes('node_modules') || fullPath.includes('storybook') || fullPath.includes('tensorflow') || fullPath.includes('vite') || fullPath.includes('vue') || fullPath.includes('esbuild') || fullPath.includes('typescript') || fullPath.includes('css') || fullPath.includes('lint') || fullPath.includes('roll') || fullPath.includes('sass')) {
console.log(`Skipped: ${fullPath}`);
return;
}
try {
const data = fs.readFileSync(fullPath, 'utf-8');
if (data.includes('0 && (module.exports')) {
console.log(`Skipped: ${fullPath}`);
return;
}
//await esbuild.build({
// entryPoints: [fullPath],
// minifyWhitespace: true,
// outdir: dirname(fullPath),
// allowOverwrite: true,
//});
const result = await esbuild.transform(data, {
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: false, // nestjsが壊れる
treeShaking: false,
});
fs.writeFileSync(fullPath, result.code, 'utf-8');
console.log(`Minified: ${fullPath}`);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (err) {
console.log(`Skipped (error): ${fullPath}`);
}
}
async function minifyJsFiles(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
await minifyJsFiles(fullPath);
} else if (entry.isFile() && entry.name.endsWith('.js')) {
await minifyJsFile(fullPath);
} else {
// resolve symbolic link
const stats = fs.lstatSync(fullPath);
if (stats.isSymbolicLink()) {
const realPath = fs.realpathSync(fullPath);
const realStats = fs.statSync(realPath);
if (realStats.isDirectory()) {
await minifyJsFiles(realPath);
} else if (realStats.isFile() && realPath.endsWith('.js')) {
await minifyJsFile(realPath);
}
}
}
}
}

View File

@ -13,7 +13,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
requireAdmin: true,
kind: 'read:admin:user-ips',
res: {
type: 'array',

View File

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.body">
<div :class="$style.top">
<button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu">
<img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
<img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="view-transition-name: navbar-serverIcon;"/>
</button>
<button v-if="!iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
<i v-if="store.r.realtimeMode.value" class="ti ti-bolt ti-fw"></i>
@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.middle">
<MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
<i :class="$style.itemIcon" class="ti ti-home ti-fw" style="viewTransitionName: navbar-homeIcon;"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
<i :class="$style.itemIcon" class="ti ti-home ti-fw" style="view-transition-name: navbar-homeIcon;"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
</MkA>
<template v-for="item in prefer.r.menu.value">
<div v-if="item === '-'" :class="$style.divider"></div>
@ -43,14 +43,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div :class="$style.divider"></div>
<MkA v-if="$i != null && ($i.isAdmin || $i.isModerator)" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" :activeClass="$style.active" to="/admin">
<i :class="$style.itemIcon" class="ti ti-dashboard ti-fw" style="viewTransitionName: navbar-controlPanel;"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span>
<i :class="$style.itemIcon" class="ti ti-dashboard ti-fw" style="view-transition-name: navbar-controlPanel;"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span>
</MkA>
<button class="_button" :class="$style.item" @click="more">
<i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw" style="viewTransitionName: navbar-more;"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
<i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw" style="view-transition-name: navbar-more;"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
<span v-if="otherMenuItemIndicated" :class="$style.itemIndicator" class="_blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings">
<i :class="$style.itemIcon" class="ti ti-settings ti-fw" style="viewTransitionName: navbar-settings;"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
<i :class="$style.itemIcon" class="ti ti-settings ti-fw" style="view-transition-name: navbar-settings;"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
</MkA>
</div>
<div :class="$style.bottom">
@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span>
</button>
<button v-if="$i != null" v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu">
<MkAvatar :user="$i" :class="$style.avatar" style="viewTransitionName: navbar-avatar;"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/>
<MkAvatar :user="$i" :class="$style.avatar" style="view-transition-name: navbar-avatar;"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/>
</button>
</div>
</div>