mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-08 06:48:04 +03:00
Merge pull request #308 from zkldi:zkldi/issue-305-Add-global-user-settings
Add global user settings
This commit is contained in:
+1
-1
@@ -71,7 +71,7 @@
|
||||
"redis": "3.1.2",
|
||||
"rimraf": "3.0.2",
|
||||
"safe-json-stringify": "1.2.0",
|
||||
"tachi-common": "0.1.41",
|
||||
"tachi-common": "0.1.43",
|
||||
"typescript": "4.3.4",
|
||||
"winston": "3.3.3"
|
||||
},
|
||||
|
||||
Generated
+4
-4
@@ -52,7 +52,7 @@ specifiers:
|
||||
rimraf: 3.0.2
|
||||
safe-json-stringify: 1.2.0
|
||||
supertest: 6.1.3
|
||||
tachi-common: 0.1.41
|
||||
tachi-common: 0.1.43
|
||||
tap: 15.0.9
|
||||
ts-node: 10.0.0
|
||||
tsconfig-paths: 3.10.1
|
||||
@@ -85,7 +85,7 @@ dependencies:
|
||||
redis: 3.1.2
|
||||
rimraf: 3.0.2
|
||||
safe-json-stringify: 1.2.0
|
||||
tachi-common: 0.1.41_ts-node@10.0.0+typescript@4.3.4
|
||||
tachi-common: 0.1.43_ts-node@10.0.0+typescript@4.3.4
|
||||
typescript: 4.3.4
|
||||
winston: 3.3.3
|
||||
|
||||
@@ -3666,8 +3666,8 @@ packages:
|
||||
strip-ansi: 6.0.0
|
||||
dev: true
|
||||
|
||||
/tachi-common/0.1.41_ts-node@10.0.0+typescript@4.3.4:
|
||||
resolution: {integrity: sha512-3EdjrhDE8JNdxJg7Dsu9LfEeSet+0ghxdbIr9fBWDl++OtGJczL13NT3pLSc64ZZ5vkyFi6w6lO9NuSgpiZ5oQ==}
|
||||
/tachi-common/0.1.43_ts-node@10.0.0+typescript@4.3.4:
|
||||
resolution: {integrity: sha512-DBjICSN3lnQ0qmmo595CnWSJsZ/OfT+jsGDHsmzHEZc35KdTOdeNuYnDFU8zFNAWDBOAXtSiNQ/yAnraLhY+vQ==}
|
||||
dependencies:
|
||||
monk: 7.3.4
|
||||
tap: 15.0.9_ts-node@10.0.0+typescript@4.3.4
|
||||
|
||||
Vendored
+2
@@ -28,6 +28,7 @@ import {
|
||||
UGPTSettings,
|
||||
SessionViewDocument,
|
||||
ARCSavedProfileDocument,
|
||||
UserSettings,
|
||||
} from "tachi-common";
|
||||
import monk, { TMiddleware } from "monk";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
@@ -165,6 +166,7 @@ const db = {
|
||||
"game-stats-snapshots": monkDB.get<UserGameStatsSnapshot>("game-stats-snapshots"),
|
||||
"session-view-cache": monkDB.get<SessionViewDocument>("session-view-cache"),
|
||||
"arc-saved-profiles": monkDB.get<ARCSavedProfileDocument>("arc-saved-profiles"),
|
||||
"user-settings": monkDB.get<UserSettings>("user-settings"),
|
||||
};
|
||||
|
||||
export default db;
|
||||
|
||||
Vendored
+1
@@ -85,6 +85,7 @@ const staticIndexes: Partial<Record<ValidDatabases, Index[]>> = {
|
||||
index({ sessionID: 1, ip: 1 }, UNIQUE),
|
||||
index({ timestamp: 1 }, { expireAfterSeconds: ONE_DAY / 1000 }),
|
||||
],
|
||||
"user-settings": [index({ userID: 1 }, UNIQUE)],
|
||||
};
|
||||
|
||||
const indexes: Partial<Record<ValidDatabases, Index[]>> = staticIndexes;
|
||||
|
||||
@@ -99,7 +99,8 @@ t.test("#SetRequestPermissions", (t) => {
|
||||
const { req } = await expMiddlewareMock(SetRequestPermissions, {
|
||||
session: {
|
||||
tachi: {
|
||||
userID: 1,
|
||||
user: await db.users.findOne({ id: 1 }),
|
||||
settings: await db["user-settings"].findOne({ userID: 1 }),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,10 +37,10 @@ export const AllPermissions: Record<APIPermissions, true> = {
|
||||
* This is set on req[SYMBOL_TachiAPIAuth].
|
||||
*/
|
||||
export const SetRequestPermissions: RequestHandler = async (req, res, next) => {
|
||||
if (req.session?.tachi?.userID) {
|
||||
if (req.session?.tachi?.user.id) {
|
||||
req[SYMBOL_TachiAPIAuth] = {
|
||||
userID: req.session.tachi.userID,
|
||||
identifier: `Session-Key ${req.session.tachi.userID}`,
|
||||
userID: req.session.tachi.user.id,
|
||||
identifier: `Session-Key ${req.session.tachi.user.id}`,
|
||||
token: null,
|
||||
permissions: AllPermissions,
|
||||
};
|
||||
|
||||
@@ -2,13 +2,17 @@ import { RequestHandler } from "express";
|
||||
import db from "external/mongo/db";
|
||||
|
||||
export const UpdateLastSeen: RequestHandler = (req, res, next) => {
|
||||
if (!req.session.tachi?.userID) {
|
||||
if (!req.session.tachi?.user.id) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.session.tachi.settings.preferences.invisible) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// fire, but we have no reason to await it.
|
||||
db.users.update(
|
||||
{ id: req.session.tachi.userID },
|
||||
{ id: req.session.tachi.user.id },
|
||||
{
|
||||
$set: {
|
||||
lastSeen: Date.now(),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import bcrypt from "bcryptjs";
|
||||
import { integer, PrivateUserDocument, PublicUserDocument } from "tachi-common";
|
||||
import { InsertResult } from "monk";
|
||||
import { integer, PrivateUserDocument, PublicUserDocument, UserSettings } from "tachi-common";
|
||||
import db from "external/mongo/db";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { FormatUserDoc } from "utils/user";
|
||||
@@ -71,12 +70,17 @@ export async function AddNewInvite(user: PublicUserDocument) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const DEFAULT_USER_SETTINGS: UserSettings["preferences"] = {
|
||||
developerMode: false,
|
||||
invisible: false,
|
||||
};
|
||||
|
||||
export async function AddNewUser(
|
||||
username: string,
|
||||
password: string,
|
||||
email: string,
|
||||
userID: integer
|
||||
): Promise<InsertResult<PrivateUserDocument>> {
|
||||
) {
|
||||
const hashedPassword = await bcrypt.hash(password, BCRYPT_SALT_ROUNDS);
|
||||
|
||||
logger.verbose(`Hashed password for ${username}.`);
|
||||
@@ -99,7 +103,21 @@ export async function AddNewUser(
|
||||
badges: [],
|
||||
};
|
||||
|
||||
return db.users.insert(userDoc);
|
||||
const res = await db.users.insert(userDoc);
|
||||
|
||||
const settingsRes = await InsertDefaultUserSettings(userID);
|
||||
|
||||
return { newUser: res, newSettings: settingsRes };
|
||||
}
|
||||
|
||||
export function InsertDefaultUserSettings(userID: integer) {
|
||||
logger.verbose(`Inserting default settings for ${userID}.`);
|
||||
const userSettings: UserSettings = {
|
||||
userID,
|
||||
preferences: DEFAULT_USER_SETTINGS,
|
||||
};
|
||||
|
||||
return db["user-settings"].insert(userSettings);
|
||||
}
|
||||
|
||||
export async function ValidateCaptcha(
|
||||
@@ -122,3 +140,17 @@ export async function ValidateCaptcha(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function MountAuthCookie(
|
||||
req: Express.Request,
|
||||
user: PublicUserDocument,
|
||||
settings: UserSettings
|
||||
) {
|
||||
req.session.tachi = {
|
||||
user,
|
||||
settings,
|
||||
};
|
||||
|
||||
req.session.cookie.maxAge = 3.154e10;
|
||||
req.session.cookie.secure = process.env.NODE_ENV === "production";
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@ import {
|
||||
ReinstateInvite,
|
||||
ValidatePassword,
|
||||
ValidateCaptcha,
|
||||
MountAuthCookie,
|
||||
InsertDefaultUserSettings,
|
||||
} from "./auth";
|
||||
import {
|
||||
FormatUserDoc,
|
||||
GetSettingsForUser,
|
||||
GetUserCaseInsensitive,
|
||||
GetUserWithEmail,
|
||||
GetUserWithID,
|
||||
@@ -44,8 +47,8 @@ router.post(
|
||||
}
|
||||
),
|
||||
async (req, res) => {
|
||||
if (req.session.tachi?.userID) {
|
||||
logger.info(`Dual log-in attempted from ${req.session.tachi.userID}`);
|
||||
if (req.session.tachi?.user.id) {
|
||||
logger.info(`Dual log-in attempted from ${req.session.tachi.user.id}`);
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
description: `You are already logged in as someone.`,
|
||||
@@ -95,11 +98,24 @@ router.post(
|
||||
});
|
||||
}
|
||||
|
||||
req.session.tachi = {
|
||||
userID: requestedUser.id,
|
||||
};
|
||||
const user = await GetUserWithID(requestedUser.id);
|
||||
|
||||
req.session.cookie.maxAge = 3.154e10; // 1 year
|
||||
if (!user) {
|
||||
logger.severe(`User logged in as someone who does not exist?`, { requestedUser });
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
description: `An internal server error has occured.`,
|
||||
});
|
||||
}
|
||||
|
||||
let settings = await GetSettingsForUser(requestedUser.id);
|
||||
|
||||
if (!settings) {
|
||||
logger.warn(`User ${FormatUserDoc(user)} has no settings. Inserting default settings.`);
|
||||
settings = await InsertDefaultUserSettings(user.id);
|
||||
}
|
||||
|
||||
MountAuthCookie(req, user, settings);
|
||||
|
||||
logger.verbose(`${FormatUserDoc(requestedUser)} Logged in.`);
|
||||
|
||||
@@ -207,7 +223,7 @@ router.post(
|
||||
|
||||
// if we get to this point, We're good to create the user.
|
||||
|
||||
const newUser = await AddNewUser(
|
||||
const { newUser, newSettings } = await AddNewUser(
|
||||
req.body.username,
|
||||
req.body.password,
|
||||
req.body.email,
|
||||
@@ -218,17 +234,11 @@ router.post(
|
||||
throw new Error("AddNewUser failed to create a user.");
|
||||
}
|
||||
|
||||
// also set this as a cookie.
|
||||
req.session.tachi = {
|
||||
userID: newUser.id,
|
||||
};
|
||||
|
||||
req.session.cookie.maxAge = 3.154e10;
|
||||
req.session.cookie.secure = true;
|
||||
|
||||
// re-fetch the user like this so we guaranteeably omit the private fields.
|
||||
const user = await GetUserWithID(newUser.id);
|
||||
|
||||
MountAuthCookie(req, user!, newSettings);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Successfully created account ${req.body.username}!`,
|
||||
@@ -256,7 +266,7 @@ router.post(
|
||||
* @name POST /api/v1/auth/logout
|
||||
*/
|
||||
router.post("/logout", (req, res) => {
|
||||
if (!req.session?.tachi?.userID) {
|
||||
if (!req.session?.tachi?.user.id) {
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
description: `You are not logged in.`,
|
||||
|
||||
@@ -64,7 +64,7 @@ router.post(
|
||||
const inputParser = (logger: KtLogger) =>
|
||||
ResolveFileUploadData(importType, req.file!, req.body, logger);
|
||||
|
||||
const userDoc = await GetUserWithIDGuaranteed(req.session.tachi!.userID);
|
||||
const userDoc = await GetUserWithIDGuaranteed(req.session.tachi!.user.id);
|
||||
|
||||
// The <any, any> here is deliberate - TS picks the IIDX-CSV generic values
|
||||
// for this function call because it sees them first
|
||||
@@ -102,7 +102,7 @@ router.post(
|
||||
async (req, res) => {
|
||||
const importType = req.body.importType as APIImportTypes;
|
||||
|
||||
const userDoc = await GetUserWithIDGuaranteed(req.session.tachi!.userID);
|
||||
const userDoc = await GetUserWithIDGuaranteed(req.session.tachi!.user.id);
|
||||
|
||||
const inputParser = (logger: KtLogger) =>
|
||||
ResolveAPIImportParser(userDoc.id, importType, logger);
|
||||
|
||||
@@ -69,7 +69,7 @@ export const RequireAuthedAsUser: RequestHandler = (req, res, next) => {
|
||||
export const RequireSelfRequestFromUser: RequestHandler = (req, res, next) => {
|
||||
const user = req[SYMBOL_TachiData]!.requestedUser!;
|
||||
|
||||
if (!req.session.tachi?.userID || req[SYMBOL_TachiAPIAuth].userID !== user.id) {
|
||||
if (!req.session.tachi?.user.id || req[SYMBOL_TachiAPIAuth].userID !== user.id) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
description: `This request cannot be performed by an API key, and requires authentication.`,
|
||||
|
||||
@@ -6,6 +6,7 @@ import gamePTRouter from "./games/_game/_playtype/router";
|
||||
import bannerRouter from "./banner/router";
|
||||
import pfpRouter from "./pfp/router";
|
||||
import integrationsRouter from "./integrations/router";
|
||||
import settingsRouter from "./settings/router";
|
||||
import prValidate from "server/middleware/prudence-validate";
|
||||
import p from "prudence";
|
||||
import { optNull, optNullFluffStrField } from "utils/prudence";
|
||||
@@ -187,5 +188,6 @@ router.use("/games/:game/:playtype", gamePTRouter);
|
||||
router.use("/pfp", pfpRouter);
|
||||
router.use("/banner", bannerRouter);
|
||||
router.use("/integrations", integrationsRouter);
|
||||
router.use("/settings", settingsRouter);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import db from "external/mongo/db";
|
||||
import t from "tap";
|
||||
import mockApi from "test-utils/mock-api";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
|
||||
t.test("GET /api/v1/users/:userID/settings", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should return the users settings.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/users/1/settings");
|
||||
|
||||
t.strictSame(res.body.body, {
|
||||
userID: 1,
|
||||
preferences: {
|
||||
invisible: false,
|
||||
developerMode: true,
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("PATCH /api/v1/users/:userID/settings", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
t.beforeEach(async () => {
|
||||
await db["api-tokens"].insert({
|
||||
identifier: "foo",
|
||||
permissions: {
|
||||
customise_profile: true,
|
||||
},
|
||||
token: "foo",
|
||||
userID: 1,
|
||||
});
|
||||
});
|
||||
|
||||
t.test("Should mutate the users settings.", async (t) => {
|
||||
const res = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer foo")
|
||||
.send({
|
||||
developerMode: false,
|
||||
invisible: true,
|
||||
});
|
||||
|
||||
t.strictSame(res.body.body, {
|
||||
userID: 1,
|
||||
preferences: {
|
||||
invisible: true,
|
||||
developerMode: false,
|
||||
},
|
||||
});
|
||||
|
||||
const dbRes = await db["user-settings"].findOne({ userID: 1 });
|
||||
|
||||
t.strictSame(dbRes, {
|
||||
userID: 1,
|
||||
preferences: {
|
||||
invisible: true,
|
||||
developerMode: false,
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should 400 if body is empty.", async (t) => {
|
||||
const res = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer foo")
|
||||
.send({});
|
||||
|
||||
t.equal(res.statusCode, 400);
|
||||
|
||||
const dbRes = await db["user-settings"].findOne({ userID: 1 });
|
||||
|
||||
t.strictSame(
|
||||
dbRes,
|
||||
{
|
||||
userID: 1,
|
||||
preferences: {
|
||||
invisible: false,
|
||||
developerMode: true,
|
||||
},
|
||||
},
|
||||
"User Settings should be unmodified."
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should validate input.", async (t) => {
|
||||
const res = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer foo")
|
||||
.send({
|
||||
developerMode: "true",
|
||||
});
|
||||
|
||||
t.equal(res.statusCode, 400);
|
||||
|
||||
const res2 = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer foo")
|
||||
.send({
|
||||
invalid_prop: true,
|
||||
});
|
||||
|
||||
t.equal(res2.statusCode, 400);
|
||||
|
||||
const res3 = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer foo")
|
||||
.send({
|
||||
invisible: { $where: "alert(1)" },
|
||||
});
|
||||
|
||||
t.equal(res3.statusCode, 400);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Must be authenticated as that user.", async (t) => {
|
||||
await db["api-tokens"].insert({
|
||||
identifier: "not user1",
|
||||
permissions: {
|
||||
customise_profile: true,
|
||||
},
|
||||
token: "not_user1",
|
||||
userID: 2,
|
||||
});
|
||||
|
||||
const res = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer not_user1")
|
||||
.send({
|
||||
developerMode: false,
|
||||
});
|
||||
|
||||
t.equal(res.statusCode, 403);
|
||||
|
||||
const dbRes = await db["user-settings"].findOne({ userID: 1 });
|
||||
|
||||
t.equal(
|
||||
dbRes?.preferences.developerMode,
|
||||
true,
|
||||
"Settings should not be modified in the database."
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Must have the customise_profile permission.", async (t) => {
|
||||
await db["api-tokens"].insert({
|
||||
identifier: "no perm",
|
||||
permissions: {},
|
||||
token: "no_perm",
|
||||
userID: 1,
|
||||
});
|
||||
|
||||
const res = await mockApi
|
||||
.patch("/api/v1/users/1/settings")
|
||||
.set("Authorization", "Bearer no_perm")
|
||||
.send({
|
||||
developerMode: false,
|
||||
});
|
||||
|
||||
t.equal(res.statusCode, 403);
|
||||
|
||||
const dbRes = await db["user-settings"].findOne({ userID: 1 });
|
||||
|
||||
t.equal(
|
||||
dbRes?.preferences.developerMode,
|
||||
true,
|
||||
"Settings should not be modified in the database."
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Must be authenticated.", async (t) => {
|
||||
const res = await mockApi.patch("/api/v1/users/1/settings").send({
|
||||
developerMode: false,
|
||||
});
|
||||
|
||||
t.equal(res.statusCode, 401);
|
||||
|
||||
const dbRes = await db["user-settings"].findOne({ userID: 1 });
|
||||
|
||||
t.equal(
|
||||
dbRes?.preferences.developerMode,
|
||||
true,
|
||||
"Settings should not be modified in the database."
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Router } from "express";
|
||||
import db from "external/mongo/db";
|
||||
import { SYMBOL_TachiData } from "lib/constants/tachi";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { RequirePermissions } from "server/middleware/auth";
|
||||
import prValidate from "server/middleware/prudence-validate";
|
||||
import { DeleteUndefinedProps } from "utils/misc";
|
||||
import { FormatUserDoc, GetSettingsForUser } from "utils/user";
|
||||
import { RequireAuthedAsUser } from "../middleware";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
|
||||
/**
|
||||
* Retrieve this users settings. Note that these settings are NOT private.
|
||||
*
|
||||
* @name GET /api/v1/users/:userID/settings
|
||||
*/
|
||||
router.get("/", async (req, res) => {
|
||||
const user = req[SYMBOL_TachiData]!.requestedUser!;
|
||||
|
||||
const settings = await db["user-settings"].findOne({
|
||||
userID: user.id,
|
||||
});
|
||||
|
||||
if (!settings) {
|
||||
logger.severe(`User ${FormatUserDoc(user)} has no settings?`);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
description: `An internal server error has occured.`,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Retrieved settings.`,
|
||||
body: settings,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Update a user's settings.
|
||||
*
|
||||
* @param invisible - Whether to set the user to invisible or not.
|
||||
* @param developerMode - Whether to display developer specific information in the WebUI.
|
||||
*
|
||||
* @name PATCH /api/v1/users/:userID/settings
|
||||
*/
|
||||
router.patch(
|
||||
"/",
|
||||
RequirePermissions("customise_profile"),
|
||||
RequireAuthedAsUser,
|
||||
prValidate({
|
||||
invisible: "*boolean",
|
||||
developerMode: "*boolean",
|
||||
}),
|
||||
async (req, res) => {
|
||||
const user = req[SYMBOL_TachiData]!.requestedUser!;
|
||||
|
||||
const preferences = {
|
||||
invisible: req.body.invisible,
|
||||
developerMode: req.body.developerMode,
|
||||
};
|
||||
|
||||
DeleteUndefinedProps(preferences);
|
||||
|
||||
if (Object.keys(preferences).length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: `Nothing was provided to change!`,
|
||||
});
|
||||
}
|
||||
|
||||
const modifyObject: Record<string, boolean> = {};
|
||||
|
||||
for (const [k, v] of Object.entries(preferences)) {
|
||||
modifyObject[`preferences.${k}`] = v;
|
||||
}
|
||||
|
||||
await db["user-settings"].update(
|
||||
{
|
||||
userID: user.id,
|
||||
},
|
||||
{ $set: modifyObject }
|
||||
);
|
||||
|
||||
const settings = await GetSettingsForUser(user.id);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: `Updated settings.`,
|
||||
body: settings,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"userID": 1,
|
||||
"preferences": {
|
||||
"developerMode": true,
|
||||
"invisible": false
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -89,6 +89,7 @@ export function ResetCDN() {
|
||||
}
|
||||
|
||||
export async function SetIndexesForDB() {
|
||||
await ResetDBState();
|
||||
const url = `${ServerConfig.MONGO_CONNECTION_URL}/testingdb`;
|
||||
|
||||
logger.info(`Setting indexes for ${url}`);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
FolderDocument,
|
||||
TableDocument,
|
||||
integer,
|
||||
SessionDocument,
|
||||
ScoreDocument,
|
||||
ChartDocument,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
Playtypes,
|
||||
TierlistParent,
|
||||
SongDocument,
|
||||
UserSettings,
|
||||
} from "tachi-common";
|
||||
|
||||
declare module "express-session" {
|
||||
@@ -21,7 +21,8 @@ declare module "express-session" {
|
||||
}
|
||||
|
||||
export interface TachiSessionData {
|
||||
userID: integer;
|
||||
user: PublicUserDocument;
|
||||
settings: UserSettings;
|
||||
}
|
||||
|
||||
export interface TachiAPIFailResponse {
|
||||
|
||||
@@ -95,6 +95,12 @@ export function GetUserWithID(userID: integer): Promise<FindOneResult<PublicUser
|
||||
) as Promise<FindOneResult<PublicUserDocument>>;
|
||||
}
|
||||
|
||||
export function GetSettingsForUser(userID: integer) {
|
||||
return db["user-settings"].findOne({
|
||||
userID: userID,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the users for these user IDs.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user