summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authorAcid Chicken (硫酸鶏) <root@acid-chicken.com>2024-02-17 13:34:50 +0900
committerGitHub <noreply@github.com>2024-02-17 13:34:50 +0900
commitacba96c1d34572ed7bd454462c2462d2a32369f4 (patch)
tree9960987a3d6b39847bcf252514eca58650f9d59c /packages/frontend/src
parentfeat: add link to local note in initial comment of abuse note (#13347) (diff)
downloadmisskey-acba96c1d34572ed7bd454462c2462d2a32369f4.tar.gz
misskey-acba96c1d34572ed7bd454462c2462d2a32369f4.tar.bz2
misskey-acba96c1d34572ed7bd454462c2462d2a32369f4.zip
feat: license violation protection (#13285)
* spec(frontend): aboutページにリポジトリ・フィードバックのURLを表示させる Cherry-picked from MisskeyIO#441 Cherry-picked from MisskeyIO#438 * feat: license violation protection * build: fix typo * build: fix typo * fix: farewell to the static type land * fix: key typo * fix: import typo * fix: properly interpret `prominently` * docs: add disclaimer * docs: update CHANGELOG * chore: add gap --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/boot/main-boot.ts8
-rw-r--r--packages/frontend/src/components/MkSourceCodeAvailablePopup.vue112
-rw-r--r--packages/frontend/src/local-storage.ts1
-rw-r--r--packages/frontend/src/pages/about-misskey.vue24
-rw-r--r--packages/frontend/src/pages/about.vue40
-rw-r--r--packages/frontend/src/pages/admin/branding.vue16
-rw-r--r--packages/frontend/src/pages/admin/settings.vue15
7 files changed, 206 insertions, 10 deletions
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index afe8e2ac1b..b19d45a35e 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -11,6 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, signout, updateAccount } from '@/account.js';
+import { fetchInstance, instance } from '@/instance.js';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { makeHotkey } from '@/scripts/hotkey.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
@@ -234,6 +235,13 @@ export async function mainBoot() {
}
}
+ fetchInstance().then(() => {
+ const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
+ if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
+ popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
+ }
+ });
+
if ('Notification' in window) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
diff --git a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
new file mode 100644
index 0000000000..80f3a6709c
--- /dev/null
+++ b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue
@@ -0,0 +1,112 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_panel _shadow" :class="$style.root">
+ <div :class="$style.icon">
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-open-source" width="40" height="40" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
+ <path d="M12 3a9 9 0 0 1 3.618 17.243l-2.193 -5.602a3 3 0 1 0 -2.849 0l-2.193 5.603a9 9 0 0 1 3.617 -17.244z"/>
+ </svg>
+ </div>
+ <div :class="$style.main">
+ <div :class="$style.title">
+ <I18n :src="i18n.ts.aboutX" tag="span">
+ <template #x>
+ {{ instance.name ?? host }}
+ </template>
+ </I18n>
+ </div>
+ <div :class="$style.text">
+ <I18n :src="i18n.ts._aboutMisskey.thisIsModifiedVersion" tag="span">
+ <template #name>
+ {{ instance.name ?? host }}
+ </template>
+ </I18n>
+ <I18n :src="i18n.ts.correspondingSourceIsAvailable" tag="span">
+ <template #anchor>
+ <MkA to="/about-misskey" class="_link">{{ i18n.ts.aboutMisskey }}</MkA>
+ </template>
+ </I18n>
+ </div>
+ <div class="_buttons">
+ <MkButton @click="close">{{ i18n.ts.gotIt }}</MkButton>
+ </div>
+ </div>
+ <button class="_button" :class="$style.close" @click="close"><i class="ti ti-x"></i></button>
+</div>
+</template>
+
+<script lang="ts" setup>
+import MkButton from '@/components/MkButton.vue';
+import { host } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { miLocalStorage } from '@/local-storage.js';
+import * as os from '@/os.js';
+
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
+const zIndex = os.claimZIndex('low');
+
+function close() {
+ miLocalStorage.setItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read', 'true');
+ emit('closed');
+}
+</script>
+
+<style lang="scss" module>
+.root {
+ position: fixed;
+ z-index: v-bind(zIndex);
+ bottom: var(--margin);
+ left: 0;
+ right: 0;
+ margin: auto;
+ box-sizing: border-box;
+ width: calc(100% - (var(--margin) * 2));
+ max-width: 500px;
+ display: flex;
+}
+
+.icon {
+ text-align: center;
+ padding-top: 25px;
+ width: 100px;
+ color: var(--accent);
+}
+@media (max-width: 500px) {
+ .icon {
+ width: 80px;
+ }
+}
+@media (max-width: 450px) {
+ .icon {
+ width: 70px;
+ }
+}
+
+.main {
+ padding: 25px 25px 25px 0;
+ flex: 1;
+}
+
+.close {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ padding: 8px;
+}
+
+.title {
+ font-weight: bold;
+}
+
+.text {
+ margin: 0.7em 0 1em 0;
+}
+</style>
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 355715bcc6..3de81c9bb9 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -12,6 +12,7 @@ type Keys =
'latestDonationInfoShownAt' |
'neverShowDonationInfo' |
'neverShowLocalOnlyInfo' |
+ 'modifiedVersionMustProminentlyOfferInAgplV3Section13Read' |
'lastUsed' |
'lang' |
'drafts' |
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index a2ecae3b80..fd97ab97b9 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<FormLink to="https://github.com/misskey-dev/misskey" external>
<template #icon><i class="ti ti-code"></i></template>
- {{ i18n.ts._aboutMisskey.source }}
+ {{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original }})
<template #suffix>GitHub</template>
</FormLink>
<FormLink to="https://crowdin.com/project/misskey" external>
@@ -46,6 +46,25 @@ SPDX-License-Identifier: AGPL-3.0-only
</FormLink>
</div>
</FormSection>
+ <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'">
+ <div class="_gaps_s">
+ <MkInfo>
+ {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }}
+ </MkInfo>
+ <FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external>
+ <template #icon><i class="ti ti-code"></i></template>
+ {{ i18n.ts._aboutMisskey.source }}
+ </FormLink>
+ <FormLink v-if="instance.providesTarball" :to="`/tarball/misskey-${version}.tar.gz`" external>
+ <template #icon><i class="ti ti-download"></i></template>
+ {{ i18n.ts._aboutMisskey.source }}
+ <template #suffix>Tarball</template>
+ </FormLink>
+ <MkInfo v-if="!instance.repositoryUrl && !instance.providesTarball" warn>
+ {{ i18n.ts.sourceCodeIsNotYetProvided }}
+ </MkInfo>
+ </div>
+ </FormSection>
<FormSection>
<template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template>
<div :class="$style.contributors">
@@ -118,9 +137,10 @@ import { version } from '@/config.js';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import MkButton from '@/components/MkButton.vue';
-import MkLink from '@/components/MkLink.vue';
+import MkInfo from '@/components/MkInfo.vue';
import { physics } from '@/scripts/physics.js';
import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index f99eb9c577..324d1c11de 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -31,7 +31,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue>
<div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })">
</div>
- <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink>
+ <FormLink to="/about-misskey">
+ <template #icon><i class="ti ti-info-circle"></i></template>
+ {{ i18n.ts.aboutMisskey }}
+ </FormLink>
+ <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external>
+ <template #icon><i class="ti ti-code"></i></template>
+ {{ i18n.ts.sourceCode }}
+ </FormLink>
+ <MkInfo v-else warn>
+ {{ i18n.ts.sourceCodeIsNotYetProvided }}
+ </MkInfo>
</div>
</FormSection>
@@ -47,17 +57,33 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #value>{{ instance.maintainerEmail }}</template>
</MkKeyValue>
</FormSplit>
- <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>{{ i18n.ts.impressum }}</FormLink>
+ <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external>
+ <template #icon><i class="ti ti-user-shield"></i></template>
+ {{ i18n.ts.impressum }}
+ </FormLink>
<div class="_gaps_s">
<MkFolder v-if="instance.serverRules.length > 0">
- <template #label>{{ i18n.ts.serverRules }}</template>
+ <template #label>
+ <i class="ti ti-checkup-list"></i>
+ {{ i18n.ts.serverRules }}
+ </template>
<ol class="_gaps_s" :class="$style.rules">
- <li v-for="item, index in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
+ <li v-for="(item, index) in instance.serverRules" :key="index" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li>
</ol>
</MkFolder>
- <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>{{ i18n.ts.termsOfService }}</FormLink>
- <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>{{ i18n.ts.privacyPolicy }}</FormLink>
+ <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external>
+ <template #icon><i class="ti ti-license"></i></template>
+ {{ i18n.ts.termsOfService }}
+ </FormLink>
+ <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external>
+ <template #icon><i class="ti ti-shield-lock"></i></template>
+ {{ i18n.ts.privacyPolicy }}
+ </FormLink>
+ <FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external>
+ <template #icon><i class="ti ti-message"></i></template>
+ {{ i18n.ts.feedback }}
+ </FormLink>
</div>
</div>
</FormSection>
@@ -86,7 +112,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink>
<FormLink :to="`/robots.txt`" external>robots.txt</FormLink>
<FormLink :to="`/manifest.json`" external>manifest.json</FormLink>
- <FormLink :to="`/tarball/misskey-${version}.tar.gz`" external>source code</FormLink>
</div>
</FormSection>
</div>
@@ -116,6 +141,7 @@ import FormSuspense from '@/components/form/suspense.vue';
import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkInfo from '@/components/MkInfo.vue';
import MkInstanceStats from '@/components/MkInstanceStats.vue';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 4ac2011aaf..2b559f92c9 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -76,6 +76,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
</MkTextarea>
+ <MkInput v-model="repositoryUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.repositoryUrl }}</template>
+ </MkInput>
+
+ <MkInput v-model="feedbackUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.feedbackUrl }}</template>
+ </MkInput>
+
<MkTextarea v-model="manifestJsonOverride">
<template #label>{{ i18n.ts._serverSettings.manifestJsonOverride }}</template>
</MkTextarea>
@@ -120,6 +130,8 @@ const defaultDarkTheme = ref<string | null>(null);
const serverErrorImageUrl = ref<string | null>(null);
const infoImageUrl = ref<string | null>(null);
const notFoundImageUrl = ref<string | null>(null);
+const repositoryUrl = ref<string | null>(null);
+const feedbackUrl = ref<string | null>(null);
const manifestJsonOverride = ref<string>('{}');
async function init() {
@@ -135,6 +147,8 @@ async function init() {
serverErrorImageUrl.value = meta.serverErrorImageUrl;
infoImageUrl.value = meta.infoImageUrl;
notFoundImageUrl.value = meta.notFoundImageUrl;
+ repositoryUrl.value = meta.repositoryUrl;
+ feedbackUrl.value = meta.feedbackUrl;
manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
}
@@ -151,6 +165,8 @@ function save() {
infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value,
notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value,
serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value,
+ repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value,
+ feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,
manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
}).then(() => {
fetchInstance();
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 8af9deae62..c505d70aa9 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -34,6 +34,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</FormSplit>
+ <MkInput v-model="repositoryUrl" type="url">
+ <template #label>{{ i18n.ts.repositoryUrl }}</template>
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template>
+ </MkInput>
+
+ <MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn>
+ {{ i18n.ts.repositoryUrlOrTarballRequired }}
+ </MkInfo>
+
<MkInput v-model="impressumUrl" type="url">
<template #label>{{ i18n.ts.impressumUrl }}</template>
<template #prefix><i class="ti ti-link"></i></template>
@@ -159,7 +169,7 @@ import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
-import { fetchInstance } from '@/instance.js';
+import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkButton from '@/components/MkButton.vue';
@@ -169,6 +179,7 @@ const shortName = ref<string | null>(null);
const description = ref<string | null>(null);
const maintainerName = ref<string | null>(null);
const maintainerEmail = ref<string | null>(null);
+const repositoryUrl = ref<string | null>(null);
const impressumUrl = ref<string | null>(null);
const pinnedUsers = ref<string>('');
const cacheRemoteFiles = ref<boolean>(false);
@@ -191,6 +202,7 @@ async function init(): Promise<void> {
description.value = meta.description;
maintainerName.value = meta.maintainerName;
maintainerEmail.value = meta.maintainerEmail;
+ repositoryUrl.value = meta.repositoryUrl;
impressumUrl.value = meta.impressumUrl;
pinnedUsers.value = meta.pinnedUsers.join('\n');
cacheRemoteFiles.value = meta.cacheRemoteFiles;
@@ -214,6 +226,7 @@ async function save(): void {
description: description.value,
maintainerName: maintainerName.value,
maintainerEmail: maintainerEmail.value,
+ repositoryUrl: repositoryUrl.value,
impressumUrl: impressumUrl.value,
pinnedUsers: pinnedUsers.value.split('\n'),
cacheRemoteFiles: cacheRemoteFiles.value,