mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-07 14:28:09 +03:00
Seems good?
This commit is contained in:
@@ -9,10 +9,11 @@ import {
|
||||
Playtypes,
|
||||
SongDocument,
|
||||
AnyChartDocument,
|
||||
PrivateUserDocument,
|
||||
integer,
|
||||
} from "tachi-common";
|
||||
import { EscapeStringRegexp } from "../../utils/misc";
|
||||
import { OMIT_PRIVATE_USER_RETURNS } from "../../utils/user";
|
||||
import { GetOnlineCutoff, OMIT_PRIVATE_USER_RETURNS } from "../../utils/user";
|
||||
import { CONF_INFO } from "../setup/config";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
@@ -125,18 +126,21 @@ export function SearchSessions(
|
||||
* aren't allowed spaces in their name. In short, $text is very
|
||||
* poor at actually matching usernames.
|
||||
*/
|
||||
export function SearchUsersRegExp(search: string) {
|
||||
export function SearchUsersRegExp(search: string, matchOnline = false) {
|
||||
const regexEsc = EscapeStringRegexp(search.toLowerCase());
|
||||
|
||||
return db.users.find(
|
||||
{
|
||||
usernameLowercase: { $regex: new RegExp(regexEsc, "u") },
|
||||
},
|
||||
{
|
||||
limit: 25,
|
||||
projection: OMIT_PRIVATE_USER_RETURNS,
|
||||
}
|
||||
);
|
||||
const matchQuery: FilterQuery<PrivateUserDocument> = {
|
||||
usernameLowercase: { $regex: new RegExp(regexEsc, "u") },
|
||||
};
|
||||
|
||||
if (matchOnline) {
|
||||
matchQuery.lastSeen = { $gt: GetOnlineCutoff() };
|
||||
}
|
||||
|
||||
return db.users.find(matchQuery, {
|
||||
limit: 25,
|
||||
projection: OMIT_PRIVATE_USER_RETURNS,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import t from "tap";
|
||||
import db from "../../../../../external/mongo/db";
|
||||
import mockApi from "../../../../../test-utils/mock-api";
|
||||
import ResetDBState from "../../../../../test-utils/resets";
|
||||
import { PrivateUserDocument, PublicUserDocument } from "tachi-common";
|
||||
import { CloseAllConnections } from "../../../../../test-utils/close-connections";
|
||||
|
||||
t.test("GET /api/v1/users", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should search users if search param is set.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/users?search=zkldi");
|
||||
|
||||
t.equal(res.body.body.length, 1);
|
||||
t.equal(res.body.body[0].username, "test_zkldi");
|
||||
|
||||
const res2 = await mockApi.get("/api/v1/users?search=nothing");
|
||||
|
||||
t.equal(res2.body.body.length, 0);
|
||||
|
||||
const res3 = await mockApi.get("/api/v1/users?search=ZklDI");
|
||||
|
||||
t.equal(res3.body.body.length, 1);
|
||||
t.equal(res3.body.body[0].username, "test_zkldi");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should sanitise input for regex stuff.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/users?search=.*");
|
||||
|
||||
t.equal(res.body.body.length, 0);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should restrict returns to only online users if online is set.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/users?online=true");
|
||||
|
||||
t.equal(res.body.body.length, 0);
|
||||
|
||||
await db.users.insert({
|
||||
usernameLowercase: "online_dude",
|
||||
lastSeen: Date.now(),
|
||||
id: 2,
|
||||
} as PrivateUserDocument);
|
||||
|
||||
const res2 = await mockApi.get("/api/v1/users?online=true");
|
||||
|
||||
t.equal(res2.body.body.length, 1);
|
||||
t.equal(res2.body.body[0].usernameLowercase, "online_dude");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should return users sorted by lastSeen if no arguments are passed.", async (t) => {
|
||||
await db.users.insert({
|
||||
usernameLowercase: "online_dude",
|
||||
lastSeen: Date.now(),
|
||||
id: 2,
|
||||
} as PrivateUserDocument);
|
||||
|
||||
const res = await mockApi.get("/api/v1/users");
|
||||
|
||||
t.equal(res.body.body.length, 2);
|
||||
t.strictSame(
|
||||
res.body.body.map((e: PublicUserDocument) => e.usernameLowercase),
|
||||
["online_dude", "test_zkldi"]
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.teardown(CloseAllConnections);
|
||||
@@ -1,17 +1,49 @@
|
||||
import { Router } from "express";
|
||||
import db from "../../../../../external/mongo/db";
|
||||
import { SearchUsersRegExp } from "../../../../../lib/search/search";
|
||||
import { IsString } from "../../../../../utils/misc";
|
||||
import { GetOnlineCutoff, OMIT_PRIVATE_USER_RETURNS } from "../../../../../utils/user";
|
||||
import userIDRouter from "./_userID/router";
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
|
||||
/**
|
||||
* List Users.
|
||||
* @param online - Only return online users.
|
||||
* @param username - Return users similar to this username.
|
||||
* Search users.
|
||||
*
|
||||
* @param online - Restrict returned users to those who are online.
|
||||
* @param search - Search for users where their name contains this string. If not present, returns
|
||||
* users sorted by last appearance.
|
||||
*
|
||||
* @name GET /api/v1/users
|
||||
*/
|
||||
router.get("/", async (req, res) => {
|
||||
// i dont know yet.
|
||||
const onlyOnline = !!req.query.online;
|
||||
|
||||
let users;
|
||||
|
||||
if (req.query.search) {
|
||||
if (!IsString(req.query.search)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `Search parameter was invalid.`,
|
||||
});
|
||||
}
|
||||
users = await SearchUsersRegExp(req.query.search!, onlyOnline);
|
||||
} else {
|
||||
const query = onlyOnline ? { lastSeen: { $gt: GetOnlineCutoff() } } : {};
|
||||
|
||||
users = await db.users.find(query, {
|
||||
sort: { lastSeen: -1 },
|
||||
limit: 100,
|
||||
projection: OMIT_PRIVATE_USER_RETURNS,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Returned ${users.length} users.`,
|
||||
body: users,
|
||||
});
|
||||
});
|
||||
|
||||
router.use("/:userID", userIDRouter);
|
||||
|
||||
@@ -176,3 +176,13 @@ export async function GetUsersRanking(stats: UserGameStats) {
|
||||
outOf: aggRes[0].outOf as integer,
|
||||
};
|
||||
}
|
||||
|
||||
const FIVE_MINUTES = 1000 * 60 * 5;
|
||||
|
||||
/**
|
||||
* Returns the cutoff point for "being online" in tachi. This means the user
|
||||
* has made any page request in the past 5 minutes.
|
||||
*/
|
||||
export function GetOnlineCutoff() {
|
||||
return Date.now() - FIVE_MINUTES;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user