Compare commits
8 commits
dc9531322a
...
3aa455b43c
Author | SHA1 | Date | |
---|---|---|---|
3aa455b43c | |||
846e69fe7b | |||
b02bba83d3 | |||
cd18bf0c48 | |||
|
4bf63a73e0 | ||
e4bcd23f9d | |||
949084e218 | |||
90a30f82be |
32 changed files with 137 additions and 68 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -58,6 +58,8 @@ packages/backend/assets/LICENSE
|
||||||
packages/megalodon/lib
|
packages/megalodon/lib
|
||||||
packages/megalodon/.idea
|
packages/megalodon/.idea
|
||||||
|
|
||||||
|
*.traineddata
|
||||||
|
|
||||||
# blender backups
|
# blender backups
|
||||||
*.blend1
|
*.blend1
|
||||||
*.blend2
|
*.blend2
|
||||||
|
|
|
@ -8,7 +8,6 @@ const replace = require("gulp-replace");
|
||||||
const terser = require("gulp-terser");
|
const terser = require("gulp-terser");
|
||||||
const cssnano = require("gulp-cssnano");
|
const cssnano = require("gulp-cssnano");
|
||||||
|
|
||||||
const locales = require("./locales");
|
|
||||||
const meta = require("./package.json");
|
const meta = require("./package.json");
|
||||||
|
|
||||||
gulp.task("copy:backend:views", () =>
|
gulp.task("copy:backend:views", () =>
|
||||||
|
@ -29,8 +28,9 @@ gulp.task("copy:client:fonts", () =>
|
||||||
.pipe(gulp.dest("./built/_client_dist_/fonts/")),
|
.pipe(gulp.dest("./built/_client_dist_/fonts/")),
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("copy:client:locales", (cb) => {
|
gulp.task("copy:client:locales", async (cb) => {
|
||||||
fs.mkdirSync("./built/_client_dist_/locales", { recursive: true });
|
fs.mkdirSync("./built/_client_dist_/locales", { recursive: true });
|
||||||
|
const { default: locales } = await import("./locales/index.mjs");
|
||||||
|
|
||||||
const v = { _version_: meta.version };
|
const v = { _version_: meta.version };
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ gulp.task("copy:client:locales", (cb) => {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("build:backend:script", () => {
|
gulp.task("build:backend:script", async () => {
|
||||||
|
const { default: locales } = await import("./locales/index.mjs");
|
||||||
|
|
||||||
return gulp
|
return gulp
|
||||||
.src([
|
.src([
|
||||||
"./packages/backend/src/server/web/boot.js",
|
"./packages/backend/src/server/web/boot.js",
|
||||||
|
|
|
@ -1178,6 +1178,7 @@ useCdn: "Get assets from CDN"
|
||||||
useCdnDescription: "Load some static assets like Twemoji from the JSDelivr CDN instead of this Firefish server."
|
useCdnDescription: "Load some static assets like Twemoji from the JSDelivr CDN instead of this Firefish server."
|
||||||
suggested: "Suggested"
|
suggested: "Suggested"
|
||||||
noLanguage: "No language"
|
noLanguage: "No language"
|
||||||
|
searchCwAndAlt: "Include content warnings and alt texts"
|
||||||
|
|
||||||
_sensitiveMediaDetection:
|
_sensitiveMediaDetection:
|
||||||
description: "Reduces the effort of server moderation through automatically recognizing
|
description: "Reduces the effort of server moderation through automatically recognizing
|
||||||
|
|
|
@ -2,8 +2,14 @@
|
||||||
* Languages Loader
|
* Languages Loader
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require("fs");
|
import fs from "node:fs";
|
||||||
const yaml = require("js-yaml");
|
import yaml from "js-yaml";
|
||||||
|
|
||||||
|
import { dirname } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const languages = [];
|
const languages = [];
|
||||||
const languages_custom = [];
|
const languages_custom = [];
|
||||||
|
|
||||||
|
@ -66,16 +72,17 @@ const locales_custom = languages_custom.reduce(
|
||||||
);
|
);
|
||||||
Object.assign(locales, locales_custom);
|
Object.assign(locales, locales_custom);
|
||||||
|
|
||||||
module.exports = Object.entries(locales).reduce(
|
export default Object.entries(locales).reduce(
|
||||||
(a, [k, v]) => (
|
(a, [k, v]) => (
|
||||||
(a[k] = (() => {
|
(a[k] = (() => {
|
||||||
const [lang] = k.split("-");
|
const [lang] = k.split("-");
|
||||||
return k === "en-US" ? v :
|
return k === "en-US"
|
||||||
merge(
|
? v
|
||||||
locales["en-US"],
|
: merge(
|
||||||
locales[`${lang}-${primaries[lang]}`] || {},
|
locales["en-US"],
|
||||||
v,
|
locales[`${lang}-${primaries[lang]}`] || {},
|
||||||
);
|
v,
|
||||||
|
);
|
||||||
})()),
|
})()),
|
||||||
a
|
a
|
||||||
),
|
),
|
|
@ -2057,3 +2057,4 @@ searchUsersDescription: "投稿検索で投稿者を絞りたい場合、@user@e
|
||||||
searchRange: "投稿期間(オプション)"
|
searchRange: "投稿期間(オプション)"
|
||||||
searchRangeDescription: "投稿検索で投稿期間を絞りたい場合、20220615-20231031 のような形式で投稿期間を入力してください。今年の日付を指定する場合には年の指定を省略できます(0105-0106 や 20231105-0110 のように)。\n\n開始日と終了日のどちらか一方は省略可能です。例えば -0102 とすると今年1月2日までの投稿のみを、20231026- とすると2023年10月26日以降の投稿のみを検索します。"
|
searchRangeDescription: "投稿検索で投稿期間を絞りたい場合、20220615-20231031 のような形式で投稿期間を入力してください。今年の日付を指定する場合には年の指定を省略できます(0105-0106 や 20231105-0110 のように)。\n\n開始日と終了日のどちらか一方は省略可能です。例えば -0102 とすると今年1月2日までの投稿のみを、20231026- とすると2023年10月26日以降の投稿のみを検索します。"
|
||||||
searchPostsWithFiles: "添付ファイルのある投稿のみ"
|
searchPostsWithFiles: "添付ファイルのある投稿のみ"
|
||||||
|
searchCwAndAlt: "閲覧注意の注釈と添付ファイルの代替テキストも検索する"
|
||||||
|
|
|
@ -2050,3 +2050,4 @@ searchUsersDescription: "如欲搜尋特定使用者的貼文,請以「@user@e
|
||||||
searchWords: "搜尋關鍵字 / 查詢ID或URL"
|
searchWords: "搜尋關鍵字 / 查詢ID或URL"
|
||||||
searchWordsDescription: "如欲搜尋貼文,請在此欄位輸入欲搜尋的關鍵字。以空格分隔關鍵字以進行AND搜尋、在關鍵字之間插入「OR」以進行OR搜尋。\n舉例來說,輸入「早上 晚上」會搜尋包含「早上」和「晚上」的貼文,「早上 OR 晚上」會搜尋包含「早上」或「晚上」(或兩者皆包含)的貼文。\n\n如欲前往特定使用者或貼文的頁面,請在此欄位輸入使用者ID(@user@example.com)或貼文的URL,並點擊「查詢」按鈕。點擊「搜尋」按鈕則會搜尋字面上包含輸入的ID或URL的貼文。"
|
searchWordsDescription: "如欲搜尋貼文,請在此欄位輸入欲搜尋的關鍵字。以空格分隔關鍵字以進行AND搜尋、在關鍵字之間插入「OR」以進行OR搜尋。\n舉例來說,輸入「早上 晚上」會搜尋包含「早上」和「晚上」的貼文,「早上 OR 晚上」會搜尋包含「早上」或「晚上」(或兩者皆包含)的貼文。\n\n如欲前往特定使用者或貼文的頁面,請在此欄位輸入使用者ID(@user@example.com)或貼文的URL,並點擊「查詢」按鈕。點擊「搜尋」按鈕則會搜尋字面上包含輸入的ID或URL的貼文。"
|
||||||
suggested: "建議"
|
suggested: "建議"
|
||||||
|
searchCwAndAlt: "包含內容警告及替代文字"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
||||||
DELETE FROM "migrations" WHERE name IN (
|
DELETE FROM "migrations" WHERE name IN (
|
||||||
|
'IndexAltTextAndCw1708872574733',
|
||||||
'SeparateHardMuteWordsAndPatterns1706413792769',
|
'SeparateHardMuteWordsAndPatterns1706413792769',
|
||||||
'RenameMetaColumns1705944717480',
|
'RenameMetaColumns1705944717480',
|
||||||
'RemoveNativeUtilsMigration1705877093218',
|
'RemoveNativeUtilsMigration1705877093218',
|
||||||
|
@ -13,6 +14,10 @@ DELETE FROM "migrations" WHERE name IN (
|
||||||
'EmojiModerator1692825433698'
|
'EmojiModerator1692825433698'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- index-alt-text-and-cw
|
||||||
|
DROP INDEX "IDX_f4f7b93d05958527300d79ac82";
|
||||||
|
DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f";
|
||||||
|
|
||||||
-- separate-hard-mute-words-and-patterns
|
-- separate-hard-mute-words-and-patterns
|
||||||
UPDATE "user_profile" SET "mutedWords" = "mutedWords" || array_to_json("mutedPatterns")::jsonb;
|
UPDATE "user_profile" SET "mutedWords" = "mutedWords" || array_to_json("mutedPatterns")::jsonb;
|
||||||
ALTER TABLE "user_profile" DROP "mutedPatterns";
|
ALTER TABLE "user_profile" DROP "mutedPatterns";
|
||||||
|
|
|
@ -79,7 +79,7 @@ fn check_word_mute_impl(
|
||||||
let text_lower = text.to_lowercase();
|
let text_lower = text.to_lowercase();
|
||||||
muted_word_list
|
muted_word_list
|
||||||
.iter()
|
.iter()
|
||||||
.all(|muted_word| text_lower.contains(muted_word))
|
.all(|muted_word| text_lower.contains(&muted_word.to_lowercase()))
|
||||||
})
|
})
|
||||||
}) || muted_patterns.iter().any(|muted_pattern| {
|
}) || muted_patterns.iter().any(|muted_pattern| {
|
||||||
Regex::new(convert_regex(muted_pattern).as_str())
|
Regex::new(convert_regex(muted_pattern).as_str())
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
export class IndexAltTextAndCw1708872574733 {
|
||||||
|
name = "IndexAltTextAndCw1708872574733";
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2)`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IDX_f4f7b93d05958527300d79ac82" ON "drive_file" USING "pgroonga" ("comment" pgroonga_varchar_full_text_search_ops_v2)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_f4f7b93d05958527300d79ac82"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,6 +70,7 @@ export class DriveFile {
|
||||||
})
|
})
|
||||||
public size: number;
|
public size: number;
|
||||||
|
|
||||||
|
@Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: DB_MAX_IMAGE_COMMENT_LENGTH,
|
length: DB_MAX_IMAGE_COMMENT_LENGTH,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -79,6 +79,7 @@ export class Note {
|
||||||
})
|
})
|
||||||
public name: string | null;
|
public name: string | null;
|
||||||
|
|
||||||
|
@Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2
|
||||||
@Column("varchar", {
|
@Column("varchar", {
|
||||||
length: 512,
|
length: 512,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { insertModerationLog } from "@/services/insert-moderation-log.js";
|
import { insertModerationLog } from "@/services/insert-moderation-log.js";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import define from "@/server/api/define.js";
|
import define from "@/server/api/define.js";
|
||||||
|
import { ApiError } from "@/server/api/error.js";
|
||||||
import { Emojis } from "@/models/index.js";
|
import { Emojis } from "@/models/index.js";
|
||||||
import { In } from "typeorm";
|
import { In } from "typeorm";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
|
|
|
@ -14,18 +14,7 @@ export const paramDef = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default define(meta, paramDef, async () => {
|
export default define(meta, paramDef, async () => {
|
||||||
let tag_name: string;
|
|
||||||
const pattern = /"version": "(.*)",$/m;
|
|
||||||
|
|
||||||
await fetch(
|
|
||||||
"https://code.naskya.net/naskya/firefish/source-by/main/package.json",
|
|
||||||
)
|
|
||||||
.then((response) => response.text())
|
|
||||||
.then((text) => {
|
|
||||||
tag_name = pattern.exec(text)[1];
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tag_name,
|
tag_name: "neko",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-
|
||||||
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
|
||||||
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
|
||||||
import { generateMutedNoteThreadQuery } from "@/server/api/common/generate-muted-note-thread-query.js";
|
import { generateMutedNoteThreadQuery } from "@/server/api/common/generate-muted-note-thread-query.js";
|
||||||
|
import { generateMutedNoteQuery } from "@/server/api/common/generate-muted-note-query.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ["notes"],
|
tags: ["notes"],
|
||||||
|
@ -69,6 +70,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
|
generateMutedNoteQuery(query, user);
|
||||||
generateMutedNoteThreadQuery(query, user);
|
generateMutedNoteThreadQuery(query, user);
|
||||||
generateBlockedUserQuery(query, user);
|
generateBlockedUserQuery(query, user);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// import { FindManyOptions, In } from "typeorm";
|
// import { FindManyOptions, In } from "typeorm";
|
||||||
|
import { Brackets } from "typeorm";
|
||||||
import { Notes } from "@/models/index.js";
|
import { Notes } from "@/models/index.js";
|
||||||
import { Note } from "@/models/entities/note.js";
|
import { Note } from "@/models/entities/note.js";
|
||||||
// import config from "@/config/index.js";
|
// import config from "@/config/index.js";
|
||||||
|
@ -56,6 +57,7 @@ export const paramDef = {
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
withFiles: { type: "boolean", nullable: true },
|
withFiles: { type: "boolean", nullable: true },
|
||||||
|
searchCwAndAlt: { type: "boolean", nullable: true },
|
||||||
channelId: {
|
channelId: {
|
||||||
type: "string",
|
type: "string",
|
||||||
format: "misskey:id",
|
format: "misskey:id",
|
||||||
|
@ -92,7 +94,28 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.query != null) {
|
if (ps.query != null) {
|
||||||
query.andWhere("note.text &@~ :q", { q: `${sqlLikeEscape(ps.query)}` });
|
const q = sqlLikeEscape(ps.query);
|
||||||
|
|
||||||
|
if (ps.searchCwAndAlt) {
|
||||||
|
query.andWhere(
|
||||||
|
new Brackets((qb) => {
|
||||||
|
qb.where("note.text &@~ :q", { q })
|
||||||
|
.orWhere("note.cw &@~ :q", { q })
|
||||||
|
.orWhere(
|
||||||
|
`EXISTS (
|
||||||
|
SELECT FROM "drive_file"
|
||||||
|
WHERE
|
||||||
|
comment &@~ :q
|
||||||
|
AND
|
||||||
|
drive_file."id" = ANY(note."fileIds")
|
||||||
|
)`,
|
||||||
|
{ q },
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
query.andWhere("note.text &@~ :q", { q });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query.innerJoinAndSelect("note.user", "user");
|
query.innerJoinAndSelect("note.user", "user");
|
||||||
|
|
|
@ -54,7 +54,9 @@ const bullBoardPath = "/queue";
|
||||||
|
|
||||||
// Authenticate
|
// Authenticate
|
||||||
app.use(async (ctx, next) => {
|
app.use(async (ctx, next) => {
|
||||||
if (ctx.path === bullBoardPath || ctx.path.startsWith(`${bullBoardPath}/`)) {
|
const url = decodeURI(ctx.path);
|
||||||
|
|
||||||
|
if (url === bullBoardPath || url.startsWith(`${bullBoardPath}/`)) {
|
||||||
const token = ctx.cookies.get("token");
|
const token = ctx.cookies.get("token");
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
ctx.status = 401;
|
ctx.status = 401;
|
||||||
|
|
|
@ -887,14 +887,17 @@ async function createMentionedEvents(
|
||||||
nm: NotificationManager,
|
nm: NotificationManager,
|
||||||
) {
|
) {
|
||||||
for (const u of mentionedUsers.filter((u) => Users.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter((u) => Users.isLocalUser(u))) {
|
||||||
const threadMuted = await NoteThreadMutings.findOneBy({
|
const isWordMuted = await MutedNotes.existsBy({
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
threadId: note.threadId || note.id,
|
noteId: note.id,
|
||||||
});
|
});
|
||||||
|
if (isWordMuted) continue;
|
||||||
|
|
||||||
if (threadMuted) {
|
const isThreadMuted = await NoteThreadMutings.existsBy({
|
||||||
continue;
|
userId: u.id,
|
||||||
}
|
threadId: note.threadId ?? note.id,
|
||||||
|
});
|
||||||
|
if (isThreadMuted) continue;
|
||||||
|
|
||||||
// note with "specified" visibility might not be visible to mentioned users
|
// note with "specified" visibility might not be visible to mentioned users
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "client",
|
"name": "client",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "pnpm vite build --watch --mode development",
|
"watch": "pnpm vite build --watch --mode development",
|
||||||
"build": "pnpm vite build",
|
"build": "pnpm vite build",
|
||||||
|
|
|
@ -68,6 +68,12 @@
|
||||||
:class="$style.input"
|
:class="$style.input"
|
||||||
>{{ i18n.ts.searchPostsWithFiles }}</FormSwitch
|
>{{ i18n.ts.searchPostsWithFiles }}</FormSwitch
|
||||||
>
|
>
|
||||||
|
<FormSwitch
|
||||||
|
v-model="searchCwAndAlt"
|
||||||
|
class="form-switch"
|
||||||
|
:class="$style.input"
|
||||||
|
>{{ i18n.ts.searchCwAndAlt }}</FormSwitch
|
||||||
|
>
|
||||||
<div :class="$style.buttons">
|
<div :class="$style.buttons">
|
||||||
<MkButton inline primary @click="search"
|
<MkButton inline primary @click="search"
|
||||||
>{{ i18n.ts.search }}
|
>{{ i18n.ts.search }}
|
||||||
|
@ -128,7 +134,8 @@ const searchRange = ref(
|
||||||
}`
|
}`
|
||||||
: "",
|
: "",
|
||||||
);
|
);
|
||||||
const searchPostsWithFiles = ref(searchParams.get("withFiles") === "true");
|
const searchPostsWithFiles = ref(searchParams.get("withFiles") === "1");
|
||||||
|
const searchCwAndAlt = ref(searchParams.get("detailed") === "1");
|
||||||
|
|
||||||
function done(canceled: boolean, result?: searchQuery) {
|
function done(canceled: boolean, result?: searchQuery) {
|
||||||
emit("done", { canceled, result });
|
emit("done", { canceled, result });
|
||||||
|
@ -149,6 +156,7 @@ function search() {
|
||||||
from: searchUsers.value === "" ? undefined : searchUsers.value,
|
from: searchUsers.value === "" ? undefined : searchUsers.value,
|
||||||
range: searchRange.value === "" ? undefined : searchRange.value,
|
range: searchRange.value === "" ? undefined : searchRange.value,
|
||||||
withFiles: searchPostsWithFiles.value,
|
withFiles: searchPostsWithFiles.value,
|
||||||
|
searchCwAndAlt: searchCwAndAlt.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,15 +42,6 @@
|
||||||
i18n.ts.configure
|
i18n.ts.configure
|
||||||
}}</MkA></MkInfo
|
}}</MkA></MkInfo
|
||||||
>
|
>
|
||||||
<MkInfo v-if="updateAvailable" warn class="info"
|
|
||||||
>{{ i18n.ts.updateAvailable }}
|
|
||||||
<a
|
|
||||||
href="https://code.naskya.net/naskya/firefish"
|
|
||||||
target="_bank"
|
|
||||||
class="_link"
|
|
||||||
>{{ i18n.ts.check }}</a
|
|
||||||
></MkInfo
|
|
||||||
>
|
|
||||||
|
|
||||||
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
<MkSuperMenu :def="menuDef" :grid="narrow"></MkSuperMenu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,7 +104,6 @@ const noBotProtection =
|
||||||
!instance.enableRecaptcha;
|
!instance.enableRecaptcha;
|
||||||
const noEmailServer = !instance.enableEmail;
|
const noEmailServer = !instance.enableEmail;
|
||||||
const thereIsUnresolvedAbuseReport = ref(false);
|
const thereIsUnresolvedAbuseReport = ref(false);
|
||||||
const updateAvailable = ref(false);
|
|
||||||
const currentPage = computed(() => router.currentRef.value.child);
|
const currentPage = computed(() => router.currentRef.value.child);
|
||||||
|
|
||||||
os.api("admin/abuse-user-reports", {
|
os.api("admin/abuse-user-reports", {
|
||||||
|
@ -123,13 +113,6 @@ os.api("admin/abuse-user-reports", {
|
||||||
if (reports?.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
if (reports?.length > 0) thereIsUnresolvedAbuseReport.value = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (defaultStore.state.showAdminUpdates) {
|
|
||||||
os.api("latest-version").then((res) => {
|
|
||||||
if (res == null || res.tag_name == null) return;
|
|
||||||
updateAvailable.value = !version.startsWith(res.tag_name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const NARROW_THRESHOLD = 600;
|
const NARROW_THRESHOLD = 600;
|
||||||
const ro = new ResizeObserver((entries, observer) => {
|
const ro = new ResizeObserver((entries, observer) => {
|
||||||
if (entries.length === 0) return;
|
if (entries.length === 0) return;
|
||||||
|
|
|
@ -63,7 +63,8 @@ const props = defineProps<{
|
||||||
since?: string;
|
since?: string;
|
||||||
until?: string;
|
until?: string;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
withFiles: "false" | "true";
|
withFiles: "0" | "1";
|
||||||
|
searchCwAndAlt: "0" | "1";
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const userId = props.user == null ? undefined : await getUserId(props.user);
|
const userId = props.user == null ? undefined : await getUserId(props.user);
|
||||||
|
@ -79,7 +80,8 @@ const notesPagination = {
|
||||||
props.since == null ? undefined : getUnixTime(props.since, false),
|
props.since == null ? undefined : getUnixTime(props.since, false),
|
||||||
untilDate:
|
untilDate:
|
||||||
props.until == null ? undefined : getUnixTime(props.until, true),
|
props.until == null ? undefined : getUnixTime(props.until, true),
|
||||||
withFiles: props.withFiles === "true",
|
withFiles: props.withFiles === "1",
|
||||||
|
searchCwAndAlt: props.searchCwAndAlt === "1",
|
||||||
channelId: props.channel,
|
channelId: props.channel,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
|
@ -310,6 +310,7 @@ export const routes = [
|
||||||
until: "until",
|
until: "until",
|
||||||
withFiles: "withFiles",
|
withFiles: "withFiles",
|
||||||
channel: "channel",
|
channel: "channel",
|
||||||
|
detailed: "searchCwAndAlt",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ export async function search() {
|
||||||
from?: string;
|
from?: string;
|
||||||
range?: string;
|
range?: string;
|
||||||
withFiles: boolean;
|
withFiles: boolean;
|
||||||
|
searchCwAndAlt: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
>((resolve, _) => {
|
>((resolve, _) => {
|
||||||
|
@ -69,24 +70,35 @@ export async function search() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.action === "search") {
|
if (result.action === "search") {
|
||||||
let paramString = `withFiles=${result.withFiles ? "true" : "false"}`;
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
params.append("withFiles", result.withFiles ? "1" : "0");
|
||||||
|
|
||||||
if (result.query != null) {
|
if (result.query != null) {
|
||||||
paramString += `&q=${encodeURIComponent(result.query)}`;
|
params.append("q", result.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.from != null) {
|
if (result.from != null) {
|
||||||
if (result.from === "me" || result.from.includes("@"))
|
if (result.from === "me" || result.from.includes("@"))
|
||||||
paramString += `&user=${encodeURIComponent(result.from)}`;
|
params.append("user", result.from);
|
||||||
else paramString += `&host=${encodeURIComponent(result.from)}`;
|
else params.append("host", result.from);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.range != null) {
|
if (result.range != null) {
|
||||||
const split = result.range.split("-");
|
const split = result.range.split("-");
|
||||||
if (split[0] !== "") paramString += `&since=${split[0]}`;
|
if (split.length === 1) {
|
||||||
if (split[1] !== "") paramString += `&until=${split[1]}`;
|
params.append("since", result.range);
|
||||||
|
params.append("until", result.range);
|
||||||
|
} else {
|
||||||
|
if (split[0] !== "") params.append("since", split[0]);
|
||||||
|
if (split[1] !== "") params.append("until", split[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mainRouter.push(`/search?${paramString}`);
|
if (result.searchCwAndAlt) {
|
||||||
|
params.append("detailed", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
mainRouter.push(`/search?${params.toString()}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@ import * as fs from "fs";
|
||||||
import pluginVue from "@vitejs/plugin-vue";
|
import pluginVue from "@vitejs/plugin-vue";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
import viteCompression from "vite-plugin-compression";
|
import locales from "../../locales/index.mjs";
|
||||||
import locales from "../../locales";
|
import meta from "../../package.json" assert { type: "json" };
|
||||||
import meta from "../../package.json";
|
|
||||||
import pluginJson5 from "./vite.json5";
|
import pluginJson5 from "./vite.json5";
|
||||||
|
import viteCompression from "vite-plugin-compression";
|
||||||
|
|
||||||
const extensions = [
|
const extensions = [
|
||||||
".ts",
|
".ts",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "sw",
|
"name": "sw",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm vite build --emptyOutDir",
|
"build": "pnpm vite build --emptyOutDir",
|
||||||
"build:debug": "pnpm run build",
|
"build:debug": "pnpm run build",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"noLib": false,
|
"noLib": false,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
|
@ -23,10 +24,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"typeRoots": [
|
"typeRoots": ["node_modules/@types", "@types"],
|
||||||
"node_modules/@types",
|
|
||||||
"@types",
|
|
||||||
],
|
|
||||||
"lib": ["esnext", "webworker"]
|
"lib": ["esnext", "webworker"]
|
||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
const locales = require("../../locales");
|
import locales from "../../locales/index.mjs";
|
||||||
const meta = require("../../package.json");
|
import meta from "../../package.json" assert { type: "json" };
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === "production";
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
import viteCompression from "vite-plugin-compression";
|
import viteCompression from "vite-plugin-compression";
|
||||||
|
|
Loading…
Reference in a new issue