mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
1f097b0fdd
* chore: resolve no-explicit-any lint warnings in plugins Replaces uses of `any` in the plugins directory with concrete types, `unknown`, or structured type assertions, addressing the remaining typescript-eslint(no-explicit-any) warnings flagged by oxlint. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: address review feedback in GitLabIssueProvider Drop trailing semicolon from log string and add early return in `destroyNamespace` when neither `user_id` nor `full_path` is present to avoid an unnecessary full-scan transaction. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
import Router from "koa-router";
|
|
import { Op } from "sequelize";
|
|
import { Sequelize } from "sequelize-typescript";
|
|
import { StatusFilter } from "@shared/types";
|
|
import auth from "@server/middlewares/authentication";
|
|
import validate from "@server/middlewares/validate";
|
|
import { Group, User } from "@server/models";
|
|
import SearchProviderManager from "@server/utils/SearchProviderManager";
|
|
import { can } from "@server/policies";
|
|
import {
|
|
presentDocuments,
|
|
presentGroup,
|
|
presentUser,
|
|
} from "@server/presenters";
|
|
import type { APIContext } from "@server/types";
|
|
import pagination from "../middlewares/pagination";
|
|
import * as T from "./schema";
|
|
|
|
const router = new Router();
|
|
|
|
router.post(
|
|
"suggestions.mention",
|
|
auth(),
|
|
pagination(),
|
|
validate(T.SuggestionsListSchema),
|
|
async (ctx: APIContext<T.SuggestionsListReq>) => {
|
|
const { query } = ctx.input.body;
|
|
const { offset, limit } = ctx.state.pagination;
|
|
const actor = ctx.state.auth.user;
|
|
|
|
const [documents, users, groups, collections] = await Promise.all([
|
|
SearchProviderManager.getProvider().searchTitlesForUser(actor, {
|
|
query,
|
|
offset,
|
|
limit,
|
|
statusFilter: [StatusFilter.Published],
|
|
}),
|
|
User.findAll({
|
|
where: {
|
|
teamId: actor.teamId,
|
|
suspendedAt: {
|
|
[Op.eq]: null,
|
|
},
|
|
[Op.and]: query
|
|
? {
|
|
[Op.or]: [
|
|
Sequelize.literal(
|
|
`unaccent(LOWER(email)) like unaccent(LOWER(:query))`
|
|
),
|
|
Sequelize.literal(
|
|
`unaccent(LOWER(name)) like unaccent(LOWER(:query))`
|
|
),
|
|
],
|
|
}
|
|
: {},
|
|
},
|
|
order: [["name", "ASC"]],
|
|
replacements: { query: `%${query}%` },
|
|
offset,
|
|
limit,
|
|
}),
|
|
Group.findAll({
|
|
where: {
|
|
teamId: actor.teamId,
|
|
disableMentions: false,
|
|
[Op.and]: query
|
|
? Sequelize.literal(
|
|
`unaccent(LOWER(name)) like unaccent(LOWER(:query))`
|
|
)
|
|
: {},
|
|
},
|
|
order: [["name", "ASC"]],
|
|
replacements: { query: `%${query}%` },
|
|
offset,
|
|
limit,
|
|
}),
|
|
SearchProviderManager.getProvider().searchCollectionsForUser(actor, {
|
|
query,
|
|
offset,
|
|
limit,
|
|
}),
|
|
]);
|
|
|
|
ctx.body = {
|
|
pagination: ctx.state.pagination,
|
|
data: {
|
|
documents: await presentDocuments(ctx, documents),
|
|
users: users.map((user) =>
|
|
presentUser(user, {
|
|
includeEmail: !!can(actor, "readEmail", user),
|
|
includeDetails: !!can(actor, "readDetails", user),
|
|
})
|
|
),
|
|
groups: await Promise.all(groups.map((group) => presentGroup(group))),
|
|
collections,
|
|
},
|
|
};
|
|
}
|
|
);
|
|
|
|
export default router;
|