enhance(backend): improve server icon setting
Resolve #11481 Resolve #10901
This commit is contained in:
parent
d2831c612f
commit
814e28459e
11 changed files with 175 additions and 50 deletions
|
@ -26,6 +26,8 @@
|
||||||
- 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121
|
- 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121
|
||||||
- 二要素認証でパスキーをサポートするようになりました
|
- 二要素認証でパスキーをサポートするようになりました
|
||||||
- 通知をテストできるようになりました
|
- 通知をテストできるようになりました
|
||||||
|
- PWAのアイコンが設定できるようになりました
|
||||||
|
- manifest.jsonをオーバーライド可能に
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- プロフィールにその人が作ったPlayの一覧出せるように
|
- プロフィールにその人が作ったPlayの一覧出せるように
|
||||||
|
|
9
locales/index.d.ts
vendored
9
locales/index.d.ts
vendored
|
@ -359,7 +359,6 @@ export interface Locale {
|
||||||
"driveCapacityPerLocalAccount": string;
|
"driveCapacityPerLocalAccount": string;
|
||||||
"driveCapacityPerRemoteAccount": string;
|
"driveCapacityPerRemoteAccount": string;
|
||||||
"inMb": string;
|
"inMb": string;
|
||||||
"iconUrl": string;
|
|
||||||
"bannerUrl": string;
|
"bannerUrl": string;
|
||||||
"backgroundImageUrl": string;
|
"backgroundImageUrl": string;
|
||||||
"basicInfo": string;
|
"basicInfo": string;
|
||||||
|
@ -1140,6 +1139,14 @@ export interface Locale {
|
||||||
"_serverRules": {
|
"_serverRules": {
|
||||||
"description": string;
|
"description": string;
|
||||||
};
|
};
|
||||||
|
"_serverSettings": {
|
||||||
|
"iconUrl": string;
|
||||||
|
"appIconDescription": string;
|
||||||
|
"appIconUsageExample": string;
|
||||||
|
"appIconStyleRecommendation": string;
|
||||||
|
"appIconResolutionMustBe": string;
|
||||||
|
"manifestJsonOverride": string;
|
||||||
|
};
|
||||||
"_accountMigration": {
|
"_accountMigration": {
|
||||||
"moveFrom": string;
|
"moveFrom": string;
|
||||||
"moveFromSub": string;
|
"moveFromSub": string;
|
||||||
|
|
|
@ -356,7 +356,6 @@ invite: "招待"
|
||||||
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
|
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
|
||||||
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
|
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
|
||||||
inMb: "メガバイト単位"
|
inMb: "メガバイト単位"
|
||||||
iconUrl: "アイコン画像のURL (faviconなど)"
|
|
||||||
bannerUrl: "バナー画像のURL"
|
bannerUrl: "バナー画像のURL"
|
||||||
backgroundImageUrl: "背景画像のURL"
|
backgroundImageUrl: "背景画像のURL"
|
||||||
basicInfo: "基本情報"
|
basicInfo: "基本情報"
|
||||||
|
@ -1138,6 +1137,14 @@ _initialAccountSetting:
|
||||||
_serverRules:
|
_serverRules:
|
||||||
description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。"
|
description: "新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。"
|
||||||
|
|
||||||
|
_serverSettings:
|
||||||
|
iconUrl: "アイコン画像のURL"
|
||||||
|
appIconDescription: "{host}がアプリとして表示される際のアイコンを指定します。"
|
||||||
|
appIconUsageExample: "例: PWAや、スマートフォンのホーム画面にブックマークとして追加された時など"
|
||||||
|
appIconStyleRecommendation: "画像は透過部分が無く、塗りつぶされた余白がある背景を持つことが推奨されます。"
|
||||||
|
appIconResolutionMustBe: "解像度は必ず{resolution}である必要があります。"
|
||||||
|
manifestJsonOverride: "manifest.jsonのオーバーライド"
|
||||||
|
|
||||||
_accountMigration:
|
_accountMigration:
|
||||||
moveFrom: "別のアカウントからこのアカウントに移行"
|
moveFrom: "別のアカウントからこのアカウントに移行"
|
||||||
moveFromSub: "別のアカウントへエイリアスを作成"
|
moveFromSub: "別のアカウントへエイリアスを作成"
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
export class ServerIconsAndManifest1694850832075 {
|
||||||
|
name = 'ServerIconsAndManifest1694850832075'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "app192IconUrl" character varying(1024)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "app512IconUrl" character varying(1024)`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "manifestJsonOverride" character varying(8192) NOT NULL DEFAULT '{}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "manifestJsonOverride"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "app512IconUrl"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "app192IconUrl"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,6 +107,18 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public iconUrl: string | null;
|
public iconUrl: string | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 1024,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public app192IconUrl: string | null;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 1024,
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public app512IconUrl: string | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024,
|
length: 1024,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -444,6 +456,12 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public serverRules: string[];
|
public serverRules: string[];
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 8192,
|
||||||
|
default: '{}',
|
||||||
|
})
|
||||||
|
public manifestJsonOverride: string;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
|
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
|
||||||
})
|
})
|
||||||
|
|
|
@ -85,6 +85,14 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
app192IconUrl: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
app512IconUrl: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
enableEmail: {
|
enableEmail: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -278,6 +286,10 @@ export const meta = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
manifestJsonOverride: {
|
||||||
|
type: 'string',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
policies: {
|
policies: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -331,6 +343,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
notFoundImageUrl: instance.notFoundImageUrl,
|
notFoundImageUrl: instance.notFoundImageUrl,
|
||||||
infoImageUrl: instance.infoImageUrl,
|
infoImageUrl: instance.infoImageUrl,
|
||||||
iconUrl: instance.iconUrl,
|
iconUrl: instance.iconUrl,
|
||||||
|
app192IconUrl: instance.app192IconUrl,
|
||||||
|
app512IconUrl: instance.app512IconUrl,
|
||||||
backgroundImageUrl: instance.backgroundImageUrl,
|
backgroundImageUrl: instance.backgroundImageUrl,
|
||||||
logoImageUrl: instance.logoImageUrl,
|
logoImageUrl: instance.logoImageUrl,
|
||||||
defaultLightTheme: instance.defaultLightTheme,
|
defaultLightTheme: instance.defaultLightTheme,
|
||||||
|
@ -383,6 +397,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
enableServerMachineStats: instance.enableServerMachineStats,
|
enableServerMachineStats: instance.enableServerMachineStats,
|
||||||
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
enableIdenticonGeneration: instance.enableIdenticonGeneration,
|
||||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||||
|
manifestJsonOverride: instance.manifestJsonOverride,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ export const paramDef = {
|
||||||
infoImageUrl: { type: 'string', nullable: true },
|
infoImageUrl: { type: 'string', nullable: true },
|
||||||
notFoundImageUrl: { type: 'string', nullable: true },
|
notFoundImageUrl: { type: 'string', nullable: true },
|
||||||
iconUrl: { type: 'string', nullable: true },
|
iconUrl: { type: 'string', nullable: true },
|
||||||
|
app192IconUrl: { type: 'string', nullable: true },
|
||||||
|
app512IconUrl: { type: 'string', nullable: true },
|
||||||
backgroundImageUrl: { type: 'string', nullable: true },
|
backgroundImageUrl: { type: 'string', nullable: true },
|
||||||
logoImageUrl: { type: 'string', nullable: true },
|
logoImageUrl: { type: 'string', nullable: true },
|
||||||
name: { type: 'string', nullable: true },
|
name: { type: 'string', nullable: true },
|
||||||
|
@ -104,6 +106,7 @@ export const paramDef = {
|
||||||
enableIdenticonGeneration: { type: 'boolean' },
|
enableIdenticonGeneration: { type: 'boolean' },
|
||||||
serverRules: { type: 'array', items: { type: 'string' } },
|
serverRules: { type: 'array', items: { type: 'string' } },
|
||||||
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
preservedUsernames: { type: 'array', items: { type: 'string' } },
|
||||||
|
manifestJsonOverride: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -153,6 +156,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.iconUrl = ps.iconUrl;
|
set.iconUrl = ps.iconUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.app192IconUrl !== undefined) {
|
||||||
|
set.app192IconUrl = ps.app192IconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps.app512IconUrl !== undefined) {
|
||||||
|
set.app512IconUrl = ps.app512IconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.serverErrorImageUrl !== undefined) {
|
if (ps.serverErrorImageUrl !== undefined) {
|
||||||
set.serverErrorImageUrl = ps.serverErrorImageUrl;
|
set.serverErrorImageUrl = ps.serverErrorImageUrl;
|
||||||
}
|
}
|
||||||
|
@ -421,6 +432,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.preservedUsernames = ps.preservedUsernames;
|
set.preservedUsernames = ps.preservedUsernames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.manifestJsonOverride !== undefined) {
|
||||||
|
set.manifestJsonOverride = ps.manifestJsonOverride;
|
||||||
|
}
|
||||||
|
|
||||||
await this.metaService.update(set);
|
await this.metaService.update(set);
|
||||||
this.moderationLogService.insertModerationLog(me, 'updateMeta');
|
this.moderationLogService.insertModerationLog(me, 'updateMeta');
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,45 +51,6 @@ const assets = `${_dirname}/../../../../../built/_frontend_dist_/`;
|
||||||
const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`;
|
const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`;
|
||||||
const viteOut = `${_dirname}/../../../../../built/_vite_/`;
|
const viteOut = `${_dirname}/../../../../../built/_vite_/`;
|
||||||
|
|
||||||
const manifest = {
|
|
||||||
'short_name': 'Misskey',
|
|
||||||
'name': 'Misskey',
|
|
||||||
'start_url': '/',
|
|
||||||
'display': 'standalone',
|
|
||||||
'background_color': '#313a42',
|
|
||||||
'theme_color': '#86b300',
|
|
||||||
'icons': [
|
|
||||||
{
|
|
||||||
'src': '/static-assets/icons/192.png',
|
|
||||||
'sizes': '192x192',
|
|
||||||
'type': 'image/png',
|
|
||||||
'purpose': 'maskable',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'src': '/static-assets/icons/512.png',
|
|
||||||
'sizes': '512x512',
|
|
||||||
'type': 'image/png',
|
|
||||||
'purpose': 'maskable',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'src': '/static-assets/splash.png',
|
|
||||||
'sizes': '300x300',
|
|
||||||
'type': 'image/png',
|
|
||||||
'purpose': 'any',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'share_target': {
|
|
||||||
'action': '/share/',
|
|
||||||
'method': 'GET',
|
|
||||||
'enctype': 'application/x-www-form-urlencoded',
|
|
||||||
'params': {
|
|
||||||
'title': 'title',
|
|
||||||
'text': 'text',
|
|
||||||
'url': 'url',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ClientServerService {
|
export class ClientServerService {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
@ -148,16 +109,60 @@ export class ClientServerService {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async manifestHandler(reply: FastifyReply) {
|
private async manifestHandler(reply: FastifyReply) {
|
||||||
const res = deepClone(manifest);
|
|
||||||
|
|
||||||
const instance = await this.metaService.fetch(true);
|
const instance = await this.metaService.fetch(true);
|
||||||
|
|
||||||
res.short_name = instance.name ?? 'Misskey';
|
let manifest = {
|
||||||
res.name = instance.name ?? 'Misskey';
|
// 空文字列の場合右辺を使いたいため
|
||||||
if (instance.themeColor) res.theme_color = instance.themeColor;
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
|
'short_name': instance.name || 'Misskey',
|
||||||
|
// 空文字列の場合右辺を使いたいため
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
|
'name': instance.name || 'Misskey',
|
||||||
|
'start_url': '/',
|
||||||
|
'display': 'standalone',
|
||||||
|
'background_color': '#313a42',
|
||||||
|
// 空文字列の場合右辺を使いたいため
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
|
'theme_color': instance.themeColor || '#86b300',
|
||||||
|
'icons': [{
|
||||||
|
// 空文字列の場合右辺を使いたいため
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
|
'src': instance.app192IconUrl || '/static-assets/icons/192.png',
|
||||||
|
'sizes': '192x192',
|
||||||
|
'type': 'image/png',
|
||||||
|
'purpose': 'maskable',
|
||||||
|
}, {
|
||||||
|
// 空文字列の場合右辺を使いたいため
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||||
|
'src': instance.app512IconUrl || '/static-assets/icons/512.png',
|
||||||
|
'sizes': '512x512',
|
||||||
|
'type': 'image/png',
|
||||||
|
'purpose': 'maskable',
|
||||||
|
}, {
|
||||||
|
'src': '/static-assets/splash.png',
|
||||||
|
'sizes': '300x300',
|
||||||
|
'type': 'image/png',
|
||||||
|
'purpose': 'any',
|
||||||
|
}],
|
||||||
|
'share_target': {
|
||||||
|
'action': '/share/',
|
||||||
|
'method': 'GET',
|
||||||
|
'enctype': 'application/x-www-form-urlencoded',
|
||||||
|
'params': {
|
||||||
|
'title': 'title',
|
||||||
|
'text': 'text',
|
||||||
|
'url': 'url',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
manifest = {
|
||||||
|
...manifest,
|
||||||
|
...JSON.parse(instance.manifestJsonOverride === '' ? '{}' : instance.manifestJsonOverride),
|
||||||
|
};
|
||||||
|
|
||||||
reply.header('Cache-Control', 'max-age=300');
|
reply.header('Cache-Control', 'max-age=300');
|
||||||
return (res);
|
return (manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -165,6 +170,7 @@ export class ClientServerService {
|
||||||
return {
|
return {
|
||||||
instanceName: meta.name ?? 'Misskey',
|
instanceName: meta.name ?? 'Misskey',
|
||||||
icon: meta.iconUrl,
|
icon: meta.iconUrl,
|
||||||
|
appleTouchIcon: meta.app512IconUrl,
|
||||||
themeColor: meta.themeColor,
|
themeColor: meta.themeColor,
|
||||||
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
|
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
|
||||||
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
|
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
|
||||||
|
|
|
@ -28,7 +28,7 @@ html
|
||||||
meta(property='og:site_name' content= instanceName || 'Misskey')
|
meta(property='og:site_name' content= instanceName || 'Misskey')
|
||||||
meta(name='viewport' content='width=device-width, initial-scale=1')
|
meta(name='viewport' content='width=device-width, initial-scale=1')
|
||||||
link(rel='icon' href= icon || '/favicon.ico')
|
link(rel='icon' href= icon || '/favicon.ico')
|
||||||
link(rel='apple-touch-icon' href= icon || '/apple-touch-icon.png')
|
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
|
||||||
link(rel='manifest' href='/manifest.json')
|
link(rel='manifest' href='/manifest.json')
|
||||||
link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`)
|
link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`)
|
||||||
link(rel='prefetch' href=serverErrorImageUrl)
|
link(rel='prefetch' href=serverErrorImageUrl)
|
||||||
|
|
|
@ -12,7 +12,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkInput v-model="iconUrl">
|
<MkInput v-model="iconUrl">
|
||||||
<template #prefix><i class="ti ti-link"></i></template>
|
<template #prefix><i class="ti ti-link"></i></template>
|
||||||
<template #label>{{ i18n.ts.iconUrl }}</template>
|
<template #label>{{ i18n.ts._serverSettings.iconUrl }}</template>
|
||||||
|
</MkInput>
|
||||||
|
|
||||||
|
<MkInput v-model="app192IconUrl">
|
||||||
|
<template #prefix><i class="ti ti-link"></i></template>
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/192px)</template>
|
||||||
|
<template #caption>
|
||||||
|
<div>{{ i18n.t('_serverSettings.appIconDescription', { host: instance.name ?? host }) }}</div>
|
||||||
|
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||||
|
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||||
|
<div><strong>{{ i18n.t('_serverSettings.appIconResolutionMustBe', { resolution: '192x192px' }) }}</strong></div>
|
||||||
|
</template>
|
||||||
|
</MkInput>
|
||||||
|
|
||||||
|
<MkInput v-model="app512IconUrl">
|
||||||
|
<template #prefix><i class="ti ti-link"></i></template>
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.iconUrl }} (App/512px)</template>
|
||||||
|
<template #caption>
|
||||||
|
<div>{{ i18n.t('_serverSettings.appIconDescription', { host: instance.name ?? host }) }}</div>
|
||||||
|
<div>({{ i18n.ts._serverSettings.appIconUsageExample }})</div>
|
||||||
|
<div>{{ i18n.ts._serverSettings.appIconStyleRecommendation }}</div>
|
||||||
|
<div><strong>{{ i18n.t('_serverSettings.appIconResolutionMustBe', { resolution: '512x512px' }) }}</strong></div>
|
||||||
|
</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
<MkInput v-model="bannerUrl">
|
<MkInput v-model="bannerUrl">
|
||||||
|
@ -53,6 +75,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
|
<template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
|
||||||
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
|
||||||
|
<MkTextarea v-model="manifestJsonOverride">
|
||||||
|
<template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template>
|
||||||
|
</MkTextarea>
|
||||||
</div>
|
</div>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
@ -69,6 +95,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
import { } from 'vue';
|
||||||
|
import JSON5 from 'json5';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
@ -77,13 +104,16 @@ import FormSection from '@/components/form/section.vue';
|
||||||
import FormSplit from '@/components/form/split.vue';
|
import FormSplit from '@/components/form/split.vue';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { fetchInstance } from '@/instance';
|
import { instance, fetchInstance } from '@/instance';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkColorInput from '@/components/MkColorInput.vue';
|
import MkColorInput from '@/components/MkColorInput.vue';
|
||||||
|
import { host } from '@/config';
|
||||||
|
|
||||||
let iconUrl: string | null = $ref(null);
|
let iconUrl: string | null = $ref(null);
|
||||||
|
let app192IconUrl: string | null = $ref(null);
|
||||||
|
let app512IconUrl: string | null = $ref(null);
|
||||||
let bannerUrl: string | null = $ref(null);
|
let bannerUrl: string | null = $ref(null);
|
||||||
let backgroundImageUrl: string | null = $ref(null);
|
let backgroundImageUrl: string | null = $ref(null);
|
||||||
let themeColor: any = $ref(null);
|
let themeColor: any = $ref(null);
|
||||||
|
@ -92,10 +122,13 @@ let defaultDarkTheme: any = $ref(null);
|
||||||
let serverErrorImageUrl: string | null = $ref(null);
|
let serverErrorImageUrl: string | null = $ref(null);
|
||||||
let infoImageUrl: string | null = $ref(null);
|
let infoImageUrl: string | null = $ref(null);
|
||||||
let notFoundImageUrl: string | null = $ref(null);
|
let notFoundImageUrl: string | null = $ref(null);
|
||||||
|
let manifestJsonOverride: string = $ref('{}');
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api('admin/meta');
|
const meta = await os.api('admin/meta');
|
||||||
iconUrl = meta.iconUrl;
|
iconUrl = meta.iconUrl;
|
||||||
|
app192IconUrl = meta.app192IconUrl;
|
||||||
|
app512IconUrl = meta.app512IconUrl;
|
||||||
bannerUrl = meta.bannerUrl;
|
bannerUrl = meta.bannerUrl;
|
||||||
backgroundImageUrl = meta.backgroundImageUrl;
|
backgroundImageUrl = meta.backgroundImageUrl;
|
||||||
themeColor = meta.themeColor;
|
themeColor = meta.themeColor;
|
||||||
|
@ -104,11 +137,14 @@ async function init() {
|
||||||
serverErrorImageUrl = meta.serverErrorImageUrl;
|
serverErrorImageUrl = meta.serverErrorImageUrl;
|
||||||
infoImageUrl = meta.infoImageUrl;
|
infoImageUrl = meta.infoImageUrl;
|
||||||
notFoundImageUrl = meta.notFoundImageUrl;
|
notFoundImageUrl = meta.notFoundImageUrl;
|
||||||
|
manifestJsonOverride = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog('admin/update-meta', {
|
os.apiWithDialog('admin/update-meta', {
|
||||||
iconUrl,
|
iconUrl,
|
||||||
|
app192IconUrl,
|
||||||
|
app512IconUrl,
|
||||||
bannerUrl,
|
bannerUrl,
|
||||||
backgroundImageUrl,
|
backgroundImageUrl,
|
||||||
themeColor: themeColor === '' ? null : themeColor,
|
themeColor: themeColor === '' ? null : themeColor,
|
||||||
|
@ -117,6 +153,7 @@ function save() {
|
||||||
infoImageUrl,
|
infoImageUrl,
|
||||||
notFoundImageUrl,
|
notFoundImageUrl,
|
||||||
serverErrorImageUrl,
|
serverErrorImageUrl,
|
||||||
|
manifestJsonOverride: manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride)),
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
|
|
@ -353,6 +353,9 @@ export type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata;
|
||||||
export type AdminInstanceMetadata = DetailedInstanceMetadata & {
|
export type AdminInstanceMetadata = DetailedInstanceMetadata & {
|
||||||
// TODO: There are more fields.
|
// TODO: There are more fields.
|
||||||
blockedHosts: string[];
|
blockedHosts: string[];
|
||||||
|
app192IconUrl: string | null;
|
||||||
|
app512IconUrl: string | null;
|
||||||
|
manifestJsonOverride: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ServerInfo = {
|
export type ServerInfo = {
|
||||||
|
|
Loading…
Reference in a new issue