Merge branch 'feat/isIndexable' into 'develop'

feat:  `indexable` User property

Co-authored-by: Namekuji <nmkj@waah.day>

Closes #10610

See merge request firefish/firefish!10585
This commit is contained in:
Kainoa Kanter 2023-09-04 02:34:04 +00:00
commit d9f7e2bede
19 changed files with 138 additions and 11 deletions

View File

@ -762,8 +762,7 @@ no: "No"
driveFilesCount: "Number of Drive files" driveFilesCount: "Number of Drive files"
driveUsage: "Drive space usage" driveUsage: "Drive space usage"
noCrawle: "Reject crawler indexing" noCrawle: "Reject crawler indexing"
noCrawleDescription: "Ask search engines to not index your profile page, posts, Pages, noCrawleDescription: "Ask external search engines to not index your content."
etc."
lockedAccountInfo: "Unless you set your post visiblity to \"Followers only\", your lockedAccountInfo: "Unless you set your post visiblity to \"Followers only\", your
posts will be visible to anyone, even if you require followers to be manually approved." posts will be visible to anyone, even if you require followers to be manually approved."
alwaysMarkSensitive: "Mark as NSFW by default" alwaysMarkSensitive: "Mark as NSFW by default"
@ -1139,6 +1138,8 @@ confirm: "Confirm"
importZip: "Import ZIP" importZip: "Import ZIP"
exportZip: "Export ZIP" exportZip: "Export ZIP"
emojiPackCreator: "Emoji pack creator" emojiPackCreator: "Emoji pack creator"
indexable: "Indexable"
indexableDescription: "Allow built-in search to show your public posts"
languageForTranslation: "Post translation language" languageForTranslation: "Post translation language"
detectPostLanguage: "Automatically detect the language and show a translate button for posts in foreign languages" detectPostLanguage: "Automatically detect the language and show a translate button for posts in foreign languages"

View File

@ -6,6 +6,7 @@ mod m20230531_180824_drop_reversi;
mod m20230627_185451_index_note_url; mod m20230627_185451_index_note_url;
mod m20230709_000510_move_antenna_to_cache; mod m20230709_000510_move_antenna_to_cache;
mod m20230806_170616_fix_antenna_stream_ids; mod m20230806_170616_fix_antenna_stream_ids;
mod m20230904_013244_is_indexable;
pub struct Migrator; pub struct Migrator;
@ -17,6 +18,7 @@ impl MigratorTrait for Migrator {
Box::new(m20230627_185451_index_note_url::Migration), Box::new(m20230627_185451_index_note_url::Migration),
Box::new(m20230709_000510_move_antenna_to_cache::Migration), Box::new(m20230709_000510_move_antenna_to_cache::Migration),
Box::new(m20230806_170616_fix_antenna_stream_ids::Migration), Box::new(m20230806_170616_fix_antenna_stream_ids::Migration),
Box::new(m20230904_013244_is_indexable::Migration),
] ]
} }
} }

View File

@ -0,0 +1,74 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(User::Table)
.add_column(
ColumnDef::new(User::IsIndexable)
.boolean()
.not_null()
.default(true),
)
.to_owned(),
)
.await?;
manager
.alter_table(
Table::alter()
.table(UserProfile::Table)
.add_column(
ColumnDef::new(UserProfile::IsIndexable)
.boolean()
.not_null()
.default(true),
)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(User::Table)
.drop_column(User::IsIndexable)
.to_owned(),
)
.await?;
manager
.alter_table(
Table::alter()
.table(UserProfile::Table)
.drop_column(UserProfile::IsIndexable)
.to_owned(),
)
.await?;
Ok(())
}
}
/// Learn more at https://docs.rs/sea-query#iden
#[derive(Iden)]
enum User {
Table,
#[iden = "isIndexable"]
IsIndexable,
}
#[derive(Iden)]
enum UserProfile {
Table,
#[iden = "isIndexable"]
IsIndexable,
}

View File

@ -71,6 +71,8 @@ pub struct Model {
pub also_known_as: Option<String>, pub also_known_as: Option<String>,
#[sea_orm(column_name = "speakAsCat")] #[sea_orm(column_name = "speakAsCat")]
pub speak_as_cat: bool, pub speak_as_cat: bool,
#[sea_orm(column_name = "isIndexable")]
pub is_indexable: bool,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -75,6 +75,8 @@ pub struct Model {
pub moderation_note: String, pub moderation_note: String,
#[sea_orm(column_name = "preventAiLearning")] #[sea_orm(column_name = "preventAiLearning")]
pub prevent_ai_learning: bool, pub prevent_ai_learning: bool,
#[sea_orm(column_name = "isIndexable")]
pub is_indexable: bool,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -167,6 +167,12 @@ export class UserProfile {
}) })
public noCrawle: boolean; public noCrawle: boolean;
@Column("boolean", {
default: true,
comment: "Whether User is indexable.",
})
public isIndexable: boolean;
@Column("boolean", { @Column("boolean", {
default: true, default: true,
}) })

View File

@ -265,6 +265,13 @@ export class User {
}) })
public driveCapacityOverrideMb: number | null; public driveCapacityOverrideMb: number | null;
@Index()
@Column("boolean", {
default: true,
comment: "Whether the User is indexable.",
})
public isIndexable: boolean;
constructor(data: Partial<User>) { constructor(data: Partial<User>) {
if (data == null) return; if (data == null) return;

View File

@ -454,6 +454,7 @@ export const UserRepository = db.getRepository(User).extend({
isModerator: user.isModerator || falsy, isModerator: user.isModerator || falsy,
isBot: user.isBot || falsy, isBot: user.isBot || falsy,
isLocked: user.isLocked, isLocked: user.isLocked,
isIndexable: user.isIndexable,
isCat: user.isCat || falsy, isCat: user.isCat || falsy,
speakAsCat: user.speakAsCat || falsy, speakAsCat: user.speakAsCat || falsy,
instance: user.host instance: user.host

View File

@ -66,6 +66,11 @@ export const packedUserLiteSchema = {
nullable: false, nullable: false,
optional: true, optional: true,
}, },
isIndexable: {
type: "boolean",
nullable: false,
optional: true,
},
speakAsCat: { speakAsCat: {
type: "boolean", type: "boolean",
nullable: false, nullable: false,

View File

@ -205,10 +205,10 @@ export async function createPerson(
if (typeof person.followers === "string") { if (typeof person.followers === "string") {
try { try {
let data = await fetch(person.followers, { const data = await fetch(person.followers, {
headers: { Accept: "application/json" }, headers: { Accept: "application/json" },
}); });
let json_data = JSON.parse(await data.text()); const json_data = JSON.parse(await data.text());
followersCount = json_data.totalItems; followersCount = json_data.totalItems;
} catch { } catch {
@ -220,10 +220,10 @@ export async function createPerson(
if (typeof person.following === "string") { if (typeof person.following === "string") {
try { try {
let data = await fetch(person.following, { const data = await fetch(person.following, {
headers: { Accept: "application/json" }, headers: { Accept: "application/json" },
}); });
let json_data = JSON.parse(await data.text()); const json_data = JSON.parse(await data.text());
followingCount = json_data.totalItems; followingCount = json_data.totalItems;
} catch (e) { } catch (e) {
@ -235,10 +235,10 @@ export async function createPerson(
if (typeof person.outbox === "string") { if (typeof person.outbox === "string") {
try { try {
let data = await fetch(person.outbox, { const data = await fetch(person.outbox, {
headers: { Accept: "application/json" }, headers: { Accept: "application/json" },
}); });
let json_data = JSON.parse(await data.text()); const json_data = JSON.parse(await data.text());
notesCount = json_data.totalItems; notesCount = json_data.totalItems;
} catch (e) { } catch (e) {
@ -302,6 +302,7 @@ export async function createPerson(
tags, tags,
isBot, isBot,
isCat: (person as any).isCat === true, isCat: (person as any).isCat === true,
isIndexable: person.indexable,
}), }),
)) as IRemoteUser; )) as IRemoteUser;
@ -547,6 +548,7 @@ export async function updatePerson(
tags, tags,
isBot: getApType(object) !== "Person", isBot: getApType(object) !== "Person",
isCat: (person as any).isCat === true, isCat: (person as any).isCat === true,
isIndexable: person.indexable,
isLocked: !!person.manuallyApprovesFollowers, isLocked: !!person.manuallyApprovesFollowers,
movedToUri: person.movedTo || null, movedToUri: person.movedTo || null,
alsoKnownAs: person.alsoKnownAs || null, alsoKnownAs: person.alsoKnownAs || null,

View File

@ -30,6 +30,7 @@ export const renderActivity = (x: any): IActivity | null => {
Emoji: "toot:Emoji", Emoji: "toot:Emoji",
featured: "toot:featured", featured: "toot:featured",
discoverable: "toot:discoverable", discoverable: "toot:discoverable",
indexable: "toot:indexable",
// schema // schema
schema: "http://schema.org#", schema: "http://schema.org#",
PropertyValue: "schema:PropertyValue", PropertyValue: "schema:PropertyValue",

View File

@ -81,6 +81,7 @@ export async function renderPerson(user: ILocalUser) {
discoverable: !!user.isExplorable, discoverable: !!user.isExplorable,
publicKey: renderKey(user, keypair, "#main-key"), publicKey: renderKey(user, keypair, "#main-key"),
isCat: user.isCat, isCat: user.isCat,
indexable: user.isIndexable,
attachment: attachment.length ? attachment : undefined, attachment: attachment.length ? attachment : undefined,
} as any; } as any;

View File

@ -190,8 +190,9 @@ export interface IActor extends IObject {
movedTo?: string; movedTo?: string;
alsoKnownAs?: string[]; alsoKnownAs?: string[];
discoverable?: boolean; discoverable?: boolean;
indexable?: boolean;
inbox: string; inbox: string;
sharedInbox?: string; // backward compatibility.. ig sharedInbox?: string; // Backwards compatibility
publicKey?: { publicKey?: {
id: string; id: string;
publicKeyPem: string; publicKeyPem: string;

View File

@ -60,6 +60,7 @@ export default define(meta, paramDef, async (ps, me) => {
emailVerified: profile.emailVerified, emailVerified: profile.emailVerified,
autoAcceptFollowed: profile.autoAcceptFollowed, autoAcceptFollowed: profile.autoAcceptFollowed,
noCrawle: profile.noCrawle, noCrawle: profile.noCrawle,
isIndexable: profile.isIndexable,
preventAiLearning: profile.preventAiLearning, preventAiLearning: profile.preventAiLearning,
alwaysMarkNsfw: profile.alwaysMarkNsfw, alwaysMarkNsfw: profile.alwaysMarkNsfw,
autoSensitive: profile.autoSensitive, autoSensitive: profile.autoSensitive,

View File

@ -120,6 +120,7 @@ export const paramDef = {
isBot: { type: "boolean" }, isBot: { type: "boolean" },
isCat: { type: "boolean" }, isCat: { type: "boolean" },
speakAsCat: { type: "boolean" }, speakAsCat: { type: "boolean" },
isIndexable: { type: "boolean" },
injectFeaturedNote: { type: "boolean" }, injectFeaturedNote: { type: "boolean" },
receiveAnnouncementEmail: { type: "boolean" }, receiveAnnouncementEmail: { type: "boolean" },
alwaysMarkNsfw: { type: "boolean" }, alwaysMarkNsfw: { type: "boolean" },
@ -206,6 +207,10 @@ export default define(meta, paramDef, async (ps, _user, token) => {
if (typeof ps.preventAiLearning === "boolean") if (typeof ps.preventAiLearning === "boolean")
profileUpdates.preventAiLearning = ps.preventAiLearning; profileUpdates.preventAiLearning = ps.preventAiLearning;
if (typeof ps.isCat === "boolean") updates.isCat = ps.isCat; if (typeof ps.isCat === "boolean") updates.isCat = ps.isCat;
if (typeof ps.isIndexable === "boolean") {
updates.isIndexable = ps.isIndexable;
profileUpdates.isIndexable = ps.isIndexable;
}
if (typeof ps.speakAsCat === "boolean") updates.speakAsCat = ps.speakAsCat; if (typeof ps.speakAsCat === "boolean") updates.speakAsCat = ps.speakAsCat;
if (typeof ps.injectFeaturedNote === "boolean") if (typeof ps.injectFeaturedNote === "boolean")
profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;

View File

@ -608,7 +608,7 @@ export default define(meta, paramDef, async (ps, user) => {
throw new ApiError(meta.errors.noSuchNote); throw new ApiError(meta.errors.noSuchNote);
} }
if (publishing) { if (publishing && user.isIndexable) {
index(note, true); index(note, true);
// Publish update event for the updated note details // Publish update event for the updated note details

View File

@ -165,6 +165,7 @@ export default async (
createdAt: User["createdAt"]; createdAt: User["createdAt"];
isBot: User["isBot"]; isBot: User["isBot"];
inbox?: User["inbox"]; inbox?: User["inbox"];
isIndexable?: User["isIndexable"];
}, },
data: Option, data: Option,
silent = false, silent = false,
@ -652,7 +653,9 @@ export default async (
} }
// Register to search database // Register to search database
await index(note, false); if (user.isIndexable) {
await index(note, false);
}
}); });
async function renderNoteOrRenoteActivity(data: Option, note: Note) { async function renderNoteOrRenoteActivity(data: Option, note: Note) {

View File

@ -151,6 +151,7 @@ describe("ユーザー", () => {
carefulBot: user.carefulBot, carefulBot: user.carefulBot,
autoAcceptFollowed: user.autoAcceptFollowed, autoAcceptFollowed: user.autoAcceptFollowed,
noCrawle: user.noCrawle, noCrawle: user.noCrawle,
isIndexable: user.isIndexable,
preventAiLearning: user.preventAiLearning, preventAiLearning: user.preventAiLearning,
isExplorable: user.isExplorable, isExplorable: user.isExplorable,
isDeleted: user.isDeleted, isDeleted: user.isDeleted,
@ -529,6 +530,8 @@ describe("ユーザー", () => {
{ parameters: (): object => ({ autoAcceptFollowed: false }) }, { parameters: (): object => ({ autoAcceptFollowed: false }) },
{ parameters: (): object => ({ noCrawle: true }) }, { parameters: (): object => ({ noCrawle: true }) },
{ parameters: (): object => ({ noCrawle: false }) }, { parameters: (): object => ({ noCrawle: false }) },
{ parameters: (): object => ({ isIndexable: true }) },
{ parameters: (): object => ({ isIndexable: false }) },
{ parameters: (): object => ({ preventAiLearning: false }) }, { parameters: (): object => ({ preventAiLearning: false }) },
{ parameters: (): object => ({ preventAiLearning: true }) }, { parameters: (): object => ({ preventAiLearning: true }) },
{ parameters: (): object => ({ isBot: true }) }, { parameters: (): object => ({ isBot: true }) },

View File

@ -52,6 +52,14 @@
i18n.ts.hideOnlineStatusDescription i18n.ts.hideOnlineStatusDescription
}}</template> }}</template>
</FormSwitch> </FormSwitch>
<FormSwitch
v-model="isIndexable"
class="_formBlock"
@update:modelValue="save()"
>
{{ i18n.ts.indexable }}
<template #caption>{{ i18n.ts.indexableDescription }}</template>
</FormSwitch>
<FormSwitch <FormSwitch
v-model="noCrawle" v-model="noCrawle"
class="_formBlock" class="_formBlock"
@ -155,6 +163,7 @@ import { definePageMetadata } from "@/scripts/page-metadata";
const isLocked = ref($i.isLocked); const isLocked = ref($i.isLocked);
const autoAcceptFollowed = ref($i.autoAcceptFollowed); const autoAcceptFollowed = ref($i.autoAcceptFollowed);
const noCrawle = ref($i.noCrawle); const noCrawle = ref($i.noCrawle);
const isIndexable = ref($i.isIndexable);
const isExplorable = ref($i.isExplorable); const isExplorable = ref($i.isExplorable);
const hideOnlineStatus = ref($i.hideOnlineStatus); const hideOnlineStatus = ref($i.hideOnlineStatus);
const publicReactions = ref($i.publicReactions); const publicReactions = ref($i.publicReactions);
@ -178,6 +187,7 @@ function save() {
isLocked: !!isLocked.value, isLocked: !!isLocked.value,
autoAcceptFollowed: !!autoAcceptFollowed.value, autoAcceptFollowed: !!autoAcceptFollowed.value,
noCrawle: !!noCrawle.value, noCrawle: !!noCrawle.value,
isIndexable: !!isIndexable.value,
isExplorable: !!isExplorable.value, isExplorable: !!isExplorable.value,
hideOnlineStatus: !!hideOnlineStatus.value, hideOnlineStatus: !!hideOnlineStatus.value,
publicReactions: !!publicReactions.value, publicReactions: !!publicReactions.value,