Merge branch 'develop' into pari-20241009

This commit is contained in:
fly_mc 2024-10-23 00:27:42 +08:00
commit 9832482660
23 changed files with 365 additions and 48 deletions

View file

@ -1,7 +1,8 @@
## Unreleased ## 2024.10.2
### General ### General
- Feat: コンテンツの表示にログインを必須にできるように - Feat: コンテンツの表示にログインを必須にできるように
- Feat: 過去のノートを非公開化/フォロワーのみ表示可能にできるように
### Client ### Client
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように - Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
@ -14,8 +15,8 @@
- Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正 - Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正
### Server ### Server
- - Fix: Nested proxy requestsを検出した際にブロックするように
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)
## 2024.10.1 ## 2024.10.1

View file

@ -947,6 +947,9 @@ oneHour: "One hour"
oneDay: "One day" oneDay: "One day"
oneWeek: "One week" oneWeek: "One week"
oneMonth: "One month" oneMonth: "One month"
threeMonths: "3 months"
oneYear: "1 year"
threeDays: "3 days"
reflectMayTakeTime: "It may take some time for this to be reflected." reflectMayTakeTime: "It may take some time for this to be reflected."
failedToFetchAccountInformation: "Could not fetch account information" failedToFetchAccountInformation: "Could not fetch account information"
rateLimitExceeded: "Rate limit exceeded" rateLimitExceeded: "Rate limit exceeded"
@ -1295,6 +1298,27 @@ passkeyVerificationFailed: "Passkey verification has failed."
passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification has succeeded but password-less login is disabled." passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification has succeeded but password-less login is disabled."
messageToFollower: "Message to followers" messageToFollower: "Message to followers"
target: "Target" target: "Target"
testCaptchaWarning: "This is a feature for CAPTCHA testing. **Do not use in production environment.**"
prohibitedWordsForNameOfUser: "Prohibited words (for user names)"
prohibitedWordsForNameOfUserDescription: "If a user's name contains any string in this list, the name change will be rejected. Users with moderator privileges are not affected by this restriction."
yourNameContainsProhibitedWords: "The name you attempted to change contains prohibited strings"
yourNameContainsProhibitedWordsDescription: "Your name contains prohibited strings. If you want to use this name, please contact the server administrator."
thisContentsAreMarkedAsSigninRequiredByAuthor: "The author has set this content to require login to view"
lockdown: "Lockdown"
_accountSettings:
requireSigninToViewContents: "Require login to view contents"
requireSigninToViewContentsDescription1: "Requires login to view all content you create, such as notes. This can help prevent crawlers from collecting information."
requireSigninToViewContentsDescription2: "URL previews (OGP), web page embeds, and note quotes from servers that don't support these features will also be unavailable."
requireSigninToViewContentsDescription3: "These restrictions may not apply to content federated to remote servers."
makeNotesFollowersOnlyBefore: "Make past notes visible to followers only"
makeNotesFollowersOnlyBeforeDescription: "While this feature is enabled, notes that are older than the specified date/time or have passed the specified time period will be visible to followers only. When disabled, notes will return to their original visibility settings."
makeNotesHiddenBefore: "Make past notes private"
makeNotesHiddenBeforeDescription: "While this feature is enabled, notes that are older than the specified date/time or have passed the specified time period will be visible only to yourself (private). When disabled, notes will return to their original visibility settings."
mayNotEffectForFederatedNotes: "This may not affect notes that have been federated to remote servers."
notesHavePassedSpecifiedPeriod: "Notes that have passed the specified time period"
notesOlderThanSpecifiedDateAndTime: "Notes older than the specified date and time"
_mfm: _mfm:
intro: "MFM is a markup language used on Misskey, Sharkey, Firefish, Akkoma, and more that can be used in many places. Here you can view a list of all available MFM syntax." intro: "MFM is a markup language used on Misskey, Sharkey, Firefish, Akkoma, and more that can be used in many places. Here you can view a list of all available MFM syntax."
dummy: "Welcome to Pari Cafe" dummy: "Welcome to Pari Cafe"

42
locales/index.d.ts vendored
View file

@ -3806,6 +3806,18 @@ export interface Locale extends ILocale {
* 1 * 1
*/ */
"oneMonth": string; "oneMonth": string;
/**
* 3
*/
"threeMonths": string;
/**
* 1
*/
"oneYear": string;
/**
* 3
*/
"threeDays": string;
/** /**
* *
*/ */
@ -5208,7 +5220,7 @@ export interface Locale extends ILocale {
*/ */
"requireSigninToViewContents": string; "requireSigninToViewContents": string;
/** /**
* *
*/ */
"requireSigninToViewContentsDescription1": string; "requireSigninToViewContentsDescription1": string;
/** /**
@ -5219,6 +5231,34 @@ export interface Locale extends ILocale {
* *
*/ */
"requireSigninToViewContentsDescription3": string; "requireSigninToViewContentsDescription3": string;
/**
*
*/
"makeNotesFollowersOnlyBefore": string;
/**
*
*/
"makeNotesFollowersOnlyBeforeDescription": string;
/**
*
*/
"makeNotesHiddenBefore": string;
/**
* ()
*/
"makeNotesHiddenBeforeDescription": string;
/**
*
*/
"mayNotEffectForFederatedNotes": string;
/**
*
*/
"notesHavePassedSpecifiedPeriod": string;
/**
*
*/
"notesOlderThanSpecifiedDateAndTime": string;
}; };
"_abuseUserReport": { "_abuseUserReport": {
/** /**

View file

@ -947,6 +947,9 @@ oneHour: "1時間"
oneDay: "1日" oneDay: "1日"
oneWeek: "1週間" oneWeek: "1週間"
oneMonth: "1ヶ月" oneMonth: "1ヶ月"
threeMonths: "3ヶ月"
oneYear: "1年"
threeDays: "3日"
reflectMayTakeTime: "反映されるまで時間がかかる場合があります。" reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
failedToFetchAccountInformation: "アカウント情報の取得に失敗しました" failedToFetchAccountInformation: "アカウント情報の取得に失敗しました"
rateLimitExceeded: "レート制限を超えました" rateLimitExceeded: "レート制限を超えました"
@ -1299,9 +1302,16 @@ lockdown: "ロックダウン"
_accountSettings: _accountSettings:
requireSigninToViewContents: "コンテンツの表示にログインを必須にする" requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
requireSigninToViewContentsDescription1: "あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーから情報を収集されるのを防ぐ効果が期待できます。" requireSigninToViewContentsDescription1: "あなたが作成した全てのノートなどのコンテンツを表示するのにログインを必須にします。クローラーに情報が収集されるのを防ぐ効果が期待できます。"
requireSigninToViewContentsDescription2: "URLプレビュー(OGP)、Webページへの埋め込み、ートの引用に対応していないサーバーからの表示も不可になります。" requireSigninToViewContentsDescription2: "URLプレビュー(OGP)、Webページへの埋め込み、ートの引用に対応していないサーバーからの表示も不可になります。"
requireSigninToViewContentsDescription3: "リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。" requireSigninToViewContentsDescription3: "リモートサーバーに連合されたコンテンツでは、これらの制限が適用されない場合があります。"
makeNotesFollowersOnlyBefore: "過去のノートをフォロワーのみ表示可能にする"
makeNotesFollowersOnlyBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートがフォロワーのみ表示可能になります。無効に戻すと、ノートの公開状態も元に戻ります。"
makeNotesHiddenBefore: "過去のノートを非公開化する"
makeNotesHiddenBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートが自分のみ表示可能(非公開化)になります。無効に戻すと、ノートの公開状態も元に戻ります。"
mayNotEffectForFederatedNotes: "リモートサーバーに連合されたノートには効果が及ばない場合があります。"
notesHavePassedSpecifiedPeriod: "指定した時間を経過しているノート"
notesOlderThanSpecifiedDateAndTime: "指定した日時より前のノート"
_abuseUserReport: _abuseUserReport:
forward: "転送" forward: "転送"

View file

@ -947,6 +947,9 @@ oneHour: "1 小时"
oneDay: "1 天" oneDay: "1 天"
oneWeek: "1 周" oneWeek: "1 周"
oneMonth: "1 个月" oneMonth: "1 个月"
threeMonths: "3 个月"
oneYear: "1 年"
threeDays: "3 天"
reflectMayTakeTime: "可能需要一些时间才能体现出效果。" reflectMayTakeTime: "可能需要一些时间才能体现出效果。"
failedToFetchAccountInformation: "获取账户信息失败" failedToFetchAccountInformation: "获取账户信息失败"
rateLimitExceeded: "已超过速率限制" rateLimitExceeded: "已超过速率限制"
@ -1301,6 +1304,27 @@ prohibitedWordsForNameOfUser: "用户名中禁止的词"
prohibitedWordsForNameOfUserDescription: "更改用户名时,如果用户名中包含此列表里的词汇,用户的改名请求将被拒绝。持有管理员权限的用户不受此限制。" prohibitedWordsForNameOfUserDescription: "更改用户名时,如果用户名中包含此列表里的词汇,用户的改名请求将被拒绝。持有管理员权限的用户不受此限制。"
yourNameContainsProhibitedWords: "目标用户名包含违禁词" yourNameContainsProhibitedWords: "目标用户名包含违禁词"
yourNameContainsProhibitedWordsDescription: "用户名内含有违禁词。若想使用此用户名,请联系服务器管理员。" yourNameContainsProhibitedWordsDescription: "用户名内含有违禁词。若想使用此用户名,请联系服务器管理员。"
testCaptchaWarning: "这是用于验证码测试的功能。**请勿在生产环境中使用。**"
prohibitedWordsForNameOfUser: "禁用词(用户名)"
prohibitedWordsForNameOfUserDescription: "如果用户名中包含此列表中的任何字符串,将拒绝用户名更改。具有版主权限的用户不受此限制影响。"
yourNameContainsProhibitedWords: "您尝试更改的名称包含禁用字符串"
yourNameContainsProhibitedWordsDescription: "您的名称包含禁用字符串。如果您想使用此名称,请联系服务器管理员。"
thisContentsAreMarkedAsSigninRequiredByAuthor: "作者已设置此内容需要登录才能查看"
lockdown: "封锁"
_accountSettings:
requireSigninToViewContents: "需要登录才能查看内容"
requireSigninToViewContentsDescription1: "要求登录才能查看您创建的所有内容,如笔记等。这可以防止爬虫收集信息。"
requireSigninToViewContentsDescription2: "URL预览OGP、网页嵌入和不支持这些功能的服务器的笔记引用也将无法显示。"
requireSigninToViewContentsDescription3: "这些限制可能不适用于已联邦到远程服务器的内容。"
makeNotesFollowersOnlyBefore: "将过去的笔记设为仅关注者可见"
makeNotesFollowersOnlyBeforeDescription: "启用此功能期间,早于指定日期时间或已超过指定时间段的笔记将仅对关注者可见。禁用后,笔记将恢复到原始可见性设置。"
makeNotesHiddenBefore: "将过去的笔记设为私密"
makeNotesHiddenBeforeDescription: "启用此功能期间,早于指定日期时间或已超过指定时间段的笔记将仅对自己可见(私密)。禁用后,笔记将恢复到原始可见性设置。"
mayNotEffectForFederatedNotes: "这可能不会影响已联邦到远程服务器的笔记。"
notesHavePassedSpecifiedPeriod: "已超过指定时间段的笔记"
notesOlderThanSpecifiedDateAndTime: "早于指定日期和时间的笔记"
_mfm: _mfm:
intro: "MFM是一种在Misskey、Sharkey、Firefish、Akkoma等平台上使用的标记语言可以在多处使用。在这里你可以查看所有可用的MFM语法。" intro: "MFM是一种在Misskey、Sharkey、Firefish、Akkoma等平台上使用的标记语言可以在多处使用。在这里你可以查看所有可用的MFM语法。"
dummy: "Welcome to Pari Cafe" dummy: "Welcome to Pari Cafe"

View file

@ -947,6 +947,9 @@ oneHour: "一小時"
oneDay: "一天" oneDay: "一天"
oneWeek: "一週" oneWeek: "一週"
oneMonth: "一個月" oneMonth: "一個月"
threeMonths: "3 個月"
oneYear: "1 年"
threeDays: "3 天"
reflectMayTakeTime: "可能需要一些時間才會出現效果。" reflectMayTakeTime: "可能需要一些時間才會出現效果。"
failedToFetchAccountInformation: "取得帳戶資訊失敗" failedToFetchAccountInformation: "取得帳戶資訊失敗"
rateLimitExceeded: "已超過速率限制" rateLimitExceeded: "已超過速率限制"
@ -1301,6 +1304,27 @@ prohibitedWordsForNameOfUser: "禁止使用的字詞(使用者名稱)"
prohibitedWordsForNameOfUserDescription: "如果使用者名稱包含此清單中的任何字串,則拒絕重新命名使用者。 具有審查員權限的使用者不受此限制的影響。" prohibitedWordsForNameOfUserDescription: "如果使用者名稱包含此清單中的任何字串,則拒絕重新命名使用者。 具有審查員權限的使用者不受此限制的影響。"
yourNameContainsProhibitedWords: "您嘗試更改的名稱包含禁止的字串" yourNameContainsProhibitedWords: "您嘗試更改的名稱包含禁止的字串"
yourNameContainsProhibitedWordsDescription: "名稱中包含禁止使用的字串。 如果您想使用此名稱,請聯絡您的伺服器管理員。" yourNameContainsProhibitedWordsDescription: "名稱中包含禁止使用的字串。 如果您想使用此名稱,請聯絡您的伺服器管理員。"
testCaptchaWarning: "這是用於驗證碼測試的功能。**請勿在生產環境中使用。**"
prohibitedWordsForNameOfUser: "禁用詞(用戶名)"
prohibitedWordsForNameOfUserDescription: "如果用戶名中包含此列表中的任何字符串,將拒絕用戶名更改。具有版主權限的用戶不受此限制影響。"
yourNameContainsProhibitedWords: "您嘗試更改的名稱包含禁用字符串"
yourNameContainsProhibitedWordsDescription: "您的名稱包含禁用字符串。如果您想使用此名稱,請聯繫伺服器管理員。"
thisContentsAreMarkedAsSigninRequiredByAuthor: "作者已設置此內容需要登入才能查看"
lockdown: "封鎖"
_accountSettings:
requireSigninToViewContents: "需要登入才能檢視內容"
requireSigninToViewContentsDescription1: "要求登入才能檢視您建立的所有內容,如筆記等。這可以防止爬蟲收集資訊。"
requireSigninToViewContentsDescription2: "URL預覽OGP、網頁嵌入和不支援這些功能的伺服器的筆記引用也將無法顯示。"
requireSigninToViewContentsDescription3: "這些限制可能不適用於已聯邦到遠端伺服器的內容。"
makeNotesFollowersOnlyBefore: "將過去的筆記設為僅關注者可見"
makeNotesFollowersOnlyBeforeDescription: "啟用此功能期間,早於指定日期時間或已超過指定時間段的筆記將僅對關注者可見。停用後,筆記將恢復到原始可見性設定。"
makeNotesHiddenBefore: "將過去的筆記設為私密"
makeNotesHiddenBeforeDescription: "啟用此功能期間,早於指定日期時間或已超過指定時間段的筆記將僅對自己可見(私密)。停用後,筆記將恢復到原始可見性設定。"
mayNotEffectForFederatedNotes: "這可能不會影響已聯邦到遠端伺服器的筆記。"
notesHavePassedSpecifiedPeriod: "已超過指定時間段的筆記"
notesOlderThanSpecifiedDateAndTime: "早於指定日期和時間的筆記"
_mfm: _mfm:
intro: "MFM是一種在Misskey、Sharkey、Firefish、Akkoma等平臺上使用的標記語言可以在多處使用。在這裡你可以查看所有可用的MFM語法。" intro: "MFM是一種在Misskey、Sharkey、Firefish、Akkoma等平臺上使用的標記語言可以在多處使用。在這裡你可以查看所有可用的MFM語法。"
dummy: "Welcome to Pari Cafe" dummy: "Welcome to Pari Cafe"

View file

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2024.10.1-pari-Macaron", "version": "2024.10.2-pari-Affogato",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class MakeNotesHiddenBefore1729486255072 {
name = 'MakeNotesHiddenBefore1729486255072'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" ADD "makeNotesFollowersOnlyBefore" integer`);
await queryRunner.query(`ALTER TABLE "user" ADD "makeNotesHiddenBefore" integer`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "makeNotesHiddenBefore"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "makeNotesFollowersOnlyBefore"`);
}
}

View file

@ -84,6 +84,8 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser {
isHibernated: false, isHibernated: false,
isDeleted: false, isDeleted: false,
requireSigninToViewContents: false, requireSigninToViewContents: false,
makeNotesFollowersOnlyBefore: null,
makeNotesHiddenBefore: null,
emojis: [], emojis: [],
score: 0, score: 0,
host: null, host: null,

View file

@ -497,6 +497,8 @@ export class ApRendererService {
_misskey_summary: profile.description, _misskey_summary: profile.description,
_misskey_followedMessage: profile.followedMessage, _misskey_followedMessage: profile.followedMessage,
_misskey_requireSigninToViewContents: user.requireSigninToViewContents, _misskey_requireSigninToViewContents: user.requireSigninToViewContents,
_misskey_makeNotesFollowersOnlyBefore: user.makeNotesFollowersOnlyBefore,
_misskey_makeNotesHiddenBefore: user.makeNotesHiddenBefore,
icon: avatar ? this.renderImage(avatar) : null, icon: avatar ? this.renderImage(avatar) : null,
image: banner ? this.renderImage(banner) : null, image: banner ? this.renderImage(banner) : null,
tag, tag,

View file

@ -556,6 +556,8 @@ const extension_context_definition = {
'_misskey_summary': 'misskey:_misskey_summary', '_misskey_summary': 'misskey:_misskey_summary',
'_misskey_followedMessage': 'misskey:_misskey_followedMessage', '_misskey_followedMessage': 'misskey:_misskey_followedMessage',
'_misskey_requireSigninToViewContents': 'misskey:_misskey_requireSigninToViewContents', '_misskey_requireSigninToViewContents': 'misskey:_misskey_requireSigninToViewContents',
'_misskey_makeNotesFollowersOnlyBefore': 'misskey:_misskey_makeNotesFollowersOnlyBefore',
'_misskey_makeNotesHiddenBefore': 'misskey:_misskey_makeNotesHiddenBefore',
'isCat': 'misskey:isCat', 'isCat': 'misskey:isCat',
// vcard // vcard
vcard: 'http://www.w3.org/2006/vcard/ns#', vcard: 'http://www.w3.org/2006/vcard/ns#',

View file

@ -357,6 +357,8 @@ export class ApPersonService implements OnModuleInit {
isBot, isBot,
isCat: (person as any).isCat === true, isCat: (person as any).isCat === true,
requireSigninToViewContents: (person as any).requireSigninToViewContents === true, requireSigninToViewContents: (person as any).requireSigninToViewContents === true,
makeNotesFollowersOnlyBefore: (person as any).makeNotesFollowersOnlyBefore ?? null,
makeNotesHiddenBefore: (person as any).makeNotesHiddenBefore ?? null,
emojis, emojis,
})) as MiRemoteUser; })) as MiRemoteUser;

View file

@ -15,6 +15,8 @@ export interface IObject {
_misskey_summary?: string; _misskey_summary?: string;
_misskey_followedMessage?: string | null; _misskey_followedMessage?: string | null;
_misskey_requireSigninToViewContents?: boolean; _misskey_requireSigninToViewContents?: boolean;
_misskey_makeNotesFollowersOnlyBefore?: number | null;
_misskey_makeNotesHiddenBefore?: number | null;
published?: string; published?: string;
cc?: ApObject; cc?: ApObject;
to?: ApObject; to?: ApObject;

View file

@ -102,57 +102,83 @@ export class NoteEntityService implements OnModuleInit {
} }
@bindThis @bindThis
private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null) { private async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null): Promise<void> {
// FIXME: このvisibility変更処理が当関数にあるのは若干不自然かもしれない(関数名を treatVisibility とかに変える手もある)
if (packedNote.visibility === 'public' || packedNote.visibility === 'home') {
const followersOnlyBefore = packedNote.user.makeNotesFollowersOnlyBefore;
if ((followersOnlyBefore != null)
&& (
(followersOnlyBefore <= 0 && (Date.now() - new Date(packedNote.createdAt).getTime() > 0 - (followersOnlyBefore * 1000)))
|| (followersOnlyBefore > 0 && (new Date(packedNote.createdAt).getTime() < followersOnlyBefore * 1000))
)
) {
packedNote.visibility = 'followers';
}
}
if (meId === packedNote.userId) return;
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false; let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示 if (packedNote.user.requireSigninToViewContents && meId == null) {
if (packedNote.visibility === 'specified') { hide = true;
if (meId == null) { }
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (specified) { if (!hide) {
hide = false; const hiddenBefore = packedNote.user.makeNotesHiddenBefore;
} else { if ((hiddenBefore != null)
&& (
(hiddenBefore <= 0 && (Date.now() - new Date(packedNote.createdAt).getTime() > 0 - (hiddenBefore * 1000)))
|| (hiddenBefore > 0 && (new Date(packedNote.createdAt).getTime() < hiddenBefore * 1000))
)
) {
hide = true;
}
}
// visibility が specified かつ自分が指定されていなかったら非表示
if (!hide) {
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true; hide = true;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some(id => meId === id);
if (!specified) {
hide = true;
}
} }
} }
} }
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示 // visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (packedNote.visibility === 'followers') { if (!hide) {
if (meId == null) { if (packedNote.visibility === 'followers') {
hide = true; if (meId == null) {
} else if (meId === packedNote.userId) { hide = true;
hide = false; } else if (packedNote.reply && (meId === packedNote.reply.userId)) {
} else if (packedNote.reply && (meId === packedNote.reply.userId)) { // 自分の投稿に対するリプライ
// 自分の投稿に対するリプライ hide = false;
hide = false; } else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) { // 自分へのメンション
// 自分へのメンション hide = false;
hide = false; } else {
} else { // フォロワーかどうか
// フォロワーかどうか // TODO: 当関数呼び出しごとにクエリが走るのは重そうだからなんとかする
const isFollowing = await this.followingsRepository.exists({ const isFollowing = await this.followingsRepository.exists({
where: { where: {
followeeId: packedNote.userId, followeeId: packedNote.userId,
followerId: meId, followerId: meId,
}, },
}); });
hide = !isFollowing; hide = !isFollowing;
}
} }
} }
if (packedNote.user.requireSigninToViewContents && meId == null) {
hide = true;
}
if (hide) { if (hide) {
packedNote.visibleUserIds = undefined; packedNote.visibleUserIds = undefined;
packedNote.fileIds = []; packedNote.fileIds = [];
@ -161,6 +187,7 @@ export class NoteEntityService implements OnModuleInit {
packedNote.poll = undefined; packedNote.poll = undefined;
packedNote.cw = null; packedNote.cw = null;
packedNote.isHidden = true; packedNote.isHidden = true;
// TODO: hiddenReason みたいなのを提供しても良さそう
} }
} }

View file

@ -518,6 +518,8 @@ export class UserEntityService implements OnModuleInit {
isBot: user.isBot, isBot: user.isBot,
isCat: user.isCat, isCat: user.isCat,
requireSigninToViewContents: user.requireSigninToViewContents === false ? undefined : true, requireSigninToViewContents: user.requireSigninToViewContents === false ? undefined : true,
makeNotesFollowersOnlyBefore: user.makeNotesFollowersOnlyBefore ?? undefined,
makeNotesHiddenBefore: user.makeNotesHiddenBefore ?? undefined,
instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? { instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? {
name: instance.name, name: instance.name,
softwareName: instance.softwareName, softwareName: instance.softwareName,

View file

@ -208,6 +208,18 @@ export class MiUser {
}) })
public requireSigninToViewContents: boolean; public requireSigninToViewContents: boolean;
// in sec, マイナスで相対時間
@Column('integer', {
nullable: true,
})
public makeNotesFollowersOnlyBefore: number | null;
// in sec, マイナスで相対時間
@Column('integer', {
nullable: true,
})
public makeNotesHiddenBefore: number | null;
// アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ
@Column('boolean', { @Column('boolean', {
default: false, default: false,

View file

@ -119,6 +119,14 @@ export const packedUserLiteSchema = {
type: 'boolean', type: 'boolean',
nullable: false, optional: true, nullable: false, optional: true,
}, },
makeNotesFollowersOnlyBefore: {
type: 'number',
nullable: true, optional: true,
},
makeNotesHiddenBefore: {
type: 'number',
nullable: true, optional: true,
},
instance: { instance: {
type: 'object', type: 'object',
nullable: false, optional: true, nullable: false, optional: true,

View file

@ -319,6 +319,12 @@ export class FileServerService {
); );
} }
if (!request.headers['user-agent']) {
throw new StatusError('User-Agent is required', 400, 'User-Agent is required');
} else if (request.headers['user-agent'].toLowerCase().indexOf('misskey/') !== -1) {
throw new StatusError('Refusing to proxy a request from another proxy', 403, 'Proxy is recursive');
}
// Create temp file // Create temp file
const file = await this.getStreamAndTypeFromUrl(url); const file = await this.getStreamAndTypeFromUrl(url);
if (file === '404') { if (file === '404') {

View file

@ -180,6 +180,8 @@ export const paramDef = {
noCrawle: { type: 'boolean' }, noCrawle: { type: 'boolean' },
preventAiLearning: { type: 'boolean' }, preventAiLearning: { type: 'boolean' },
requireSigninToViewContents: { type: 'boolean' }, requireSigninToViewContents: { type: 'boolean' },
makeNotesFollowersOnlyBefore: { type: 'integer', nullable: true },
makeNotesHiddenBefore: { type: 'integer', nullable: true },
isBot: { type: 'boolean' }, isBot: { type: 'boolean' },
isCat: { type: 'boolean' }, isCat: { type: 'boolean' },
injectFeaturedNote: { type: 'boolean' }, injectFeaturedNote: { type: 'boolean' },
@ -336,6 +338,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle;
if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning; if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning;
if (typeof ps.requireSigninToViewContents === 'boolean') updates.requireSigninToViewContents = ps.requireSigninToViewContents; if (typeof ps.requireSigninToViewContents === 'boolean') updates.requireSigninToViewContents = ps.requireSigninToViewContents;
if ((typeof ps.makeNotesFollowersOnlyBefore === 'number') || (ps.makeNotesFollowersOnlyBefore === null)) updates.makeNotesFollowersOnlyBefore = ps.makeNotesFollowersOnlyBefore;
if ((typeof ps.makeNotesHiddenBefore === 'number') || (ps.makeNotesHiddenBefore === null)) updates.makeNotesHiddenBefore = ps.makeNotesHiddenBefore;
if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat;
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;

View file

@ -46,7 +46,7 @@ import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
const props = defineProps<{ const props = defineProps<{
modelValue: string | null; modelValue: string | number | null;
required?: boolean; required?: boolean;
readonly?: boolean; readonly?: boolean;
disabled?: boolean; disabled?: boolean;

View file

@ -45,17 +45,89 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
<FormSection> <FormSection>
<template #label>{{ i18n.ts.lockdown }}</template> <template #label>{{ i18n.ts.lockdown }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
<div class="_gaps_m"> <div class="_gaps_m">
<MkSwitch v-model="requireSigninToViewContents" @update:modelValue="save()"> <MkSwitch v-model="requireSigninToViewContents" @update:modelValue="save()">
{{ i18n.ts._accountSettings.requireSigninToViewContents }}<span class="_beta">{{ i18n.ts.beta }}</span> {{ i18n.ts._accountSettings.requireSigninToViewContents }}
<template #caption> <template #caption>
<div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div> <div>{{ i18n.ts._accountSettings.requireSigninToViewContentsDescription1 }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div> <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription2 }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div> <div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.requireSigninToViewContentsDescription3 }}</div>
</template> </template>
</MkSwitch> </MkSwitch>
<FormSlot>
<template #label>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</template>
<div class="_gaps_s">
<MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
<option :value="null">{{ i18n.ts.none }}</option>
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
</MkSelect>
<MkSelect v-if="makeNotesFollowersOnlyBefore_type === 'relative'" v-model="makeNotesFollowersOnlyBefore">
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
</MkSelect>
<MkInput
v-if="makeNotesFollowersOnlyBefore_type === 'absolute'"
:modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')"
type="date"
:manualSave="true"
@update:modelValue="makeNotesFollowersOnlyBefore = Math.floor(new Date($event).getTime() / 1000)"
>
</MkInput>
</div>
<template #caption>
<div>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div>
</template>
</FormSlot>
<FormSlot>
<template #label>{{ i18n.ts._accountSettings.makeNotesHiddenBefore }}</template>
<div class="_gaps_s">
<MkSelect :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
<option :value="null">{{ i18n.ts.none }}</option>
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
</MkSelect>
<MkSelect v-if="makeNotesHiddenBefore_type === 'relative'" v-model="makeNotesHiddenBefore">
<option :value="-3600">{{ i18n.ts.oneHour }}</option>
<option :value="-86400">{{ i18n.ts.oneDay }}</option>
<option :value="-259200">{{ i18n.ts.threeDays }}</option>
<option :value="-604800">{{ i18n.ts.oneWeek }}</option>
<option :value="-2592000">{{ i18n.ts.oneMonth }}</option>
<option :value="-7776000">{{ i18n.ts.threeMonths }}</option>
<option :value="-31104000">{{ i18n.ts.oneYear }}</option>
</MkSelect>
<MkInput
v-if="makeNotesHiddenBefore_type === 'absolute'"
:modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')"
type="date"
:manualSave="true"
@update:modelValue="makeNotesHiddenBefore = Math.floor(new Date($event).getTime() / 1000)"
>
</MkInput>
</div>
<template #caption>
<div>{{ i18n.ts._accountSettings.makeNotesHiddenBeforeDescription }}</div>
<div><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i> {{ i18n.ts._accountSettings.mayNotEffectForFederatedNotes }}</div>
</template>
</FormSlot>
</div> </div>
</FormSection> </FormSection>
@ -87,7 +159,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue'; import { ref, computed, watch } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import FormSection from '@/components/form/section.vue'; import FormSection from '@/components/form/section.vue';
@ -97,6 +169,9 @@ import { defaultStore } from '@/store.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { signinRequired } from '@/account.js'; import { signinRequired } from '@/account.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import FormSlot from '@/components/form/slot.vue';
import { formatDateTimeString } from '@/scripts/format-time-string.js';
import MkInput from '@/components/MkInput.vue';
const $i = signinRequired(); const $i = signinRequired();
@ -106,6 +181,8 @@ const noCrawle = ref($i.noCrawle);
const preventAiLearning = ref($i.preventAiLearning); const preventAiLearning = ref($i.preventAiLearning);
const isExplorable = ref($i.isExplorable); const isExplorable = ref($i.isExplorable);
const requireSigninToViewContents = ref($i.requireSigninToViewContents ?? false); const requireSigninToViewContents = ref($i.requireSigninToViewContents ?? false);
const makeNotesFollowersOnlyBefore = ref($i.makeNotesFollowersOnlyBefore ?? null);
const makeNotesHiddenBefore = ref($i.makeNotesHiddenBefore ?? null);
const hideOnlineStatus = ref($i.hideOnlineStatus); const hideOnlineStatus = ref($i.hideOnlineStatus);
const publicReactions = ref($i.publicReactions); const publicReactions = ref($i.publicReactions);
const followingVisibility = ref($i.followingVisibility); const followingVisibility = ref($i.followingVisibility);
@ -116,6 +193,30 @@ const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNote
const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility')); const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
const keepCw = computed(defaultStore.makeGetterSetter('keepCw')); const keepCw = computed(defaultStore.makeGetterSetter('keepCw'));
const makeNotesFollowersOnlyBefore_type = computed(() => {
if (makeNotesFollowersOnlyBefore.value == null) {
return null;
} else if (makeNotesFollowersOnlyBefore.value >= 0) {
return 'absolute';
} else {
return 'relative';
}
});
const makeNotesHiddenBefore_type = computed(() => {
if (makeNotesHiddenBefore.value == null) {
return null;
} else if (makeNotesHiddenBefore.value >= 0) {
return 'absolute';
} else {
return 'relative';
}
});
watch([makeNotesFollowersOnlyBefore, makeNotesHiddenBefore], () => {
save();
});
function save() { function save() {
misskeyApi('i/update', { misskeyApi('i/update', {
isLocked: !!isLocked.value, isLocked: !!isLocked.value,
@ -124,6 +225,8 @@ function save() {
preventAiLearning: !!preventAiLearning.value, preventAiLearning: !!preventAiLearning.value,
isExplorable: !!isExplorable.value, isExplorable: !!isExplorable.value,
requireSigninToViewContents: !!requireSigninToViewContents.value, requireSigninToViewContents: !!requireSigninToViewContents.value,
makeNotesFollowersOnlyBefore: makeNotesFollowersOnlyBefore.value,
makeNotesHiddenBefore: makeNotesHiddenBefore.value,
hideOnlineStatus: !!hideOnlineStatus.value, hideOnlineStatus: !!hideOnlineStatus.value,
publicReactions: !!publicReactions.value, publicReactions: !!publicReactions.value,
followingVisibility: followingVisibility.value, followingVisibility: followingVisibility.value,

View file

@ -1,7 +1,7 @@
{ {
"type": "module", "type": "module",
"name": "misskey-js", "name": "misskey-js",
"version": "2024.10.1", "version": "2024.10.2-alpha.0",
"description": "Misskey SDK for JavaScript", "description": "Misskey SDK for JavaScript",
"license": "MIT", "license": "MIT",
"main": "./built/index.js", "main": "./built/index.js",

View file

@ -3746,6 +3746,8 @@ export type components = {
isBot?: boolean; isBot?: boolean;
isCat?: boolean; isCat?: boolean;
requireSigninToViewContents?: boolean; requireSigninToViewContents?: boolean;
makeNotesFollowersOnlyBefore?: number | null;
makeNotesHiddenBefore?: number | null;
instance?: { instance?: {
name: string | null; name: string | null;
softwareName: string | null; softwareName: string | null;
@ -19864,6 +19866,8 @@ export type operations = {
noCrawle?: boolean; noCrawle?: boolean;
preventAiLearning?: boolean; preventAiLearning?: boolean;
requireSigninToViewContents?: boolean; requireSigninToViewContents?: boolean;
makeNotesFollowersOnlyBefore?: number | null;
makeNotesHiddenBefore?: number | null;
isBot?: boolean; isBot?: boolean;
isCat?: boolean; isCat?: boolean;
injectFeaturedNote?: boolean; injectFeaturedNote?: boolean;