Files
outline/server/routes/api/suggestions/suggestions.ts
T
Tom Moor 1f097b0fdd chore: resolve no-explicit-any lint warnings in plugins (#12237)
* 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>
2026-05-01 08:29:58 -04:00

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;