Setting protos support
This commit is contained in:
parent
bec4bfc743
commit
5e3080d72f
Binary file not shown.
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -62,6 +62,7 @@
|
|||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
||||||
"@typescript-eslint/parser": "^8.35.1",
|
"@typescript-eslint/parser": "^8.35.1",
|
||||||
|
"discord-protos": "^1.2.43",
|
||||||
"eslint": "^9.30.1",
|
"eslint": "^9.30.1",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
|
|||||||
@ -112,7 +112,29 @@ function main() {
|
|||||||
definitions = { ...definitions, [name]: { ...part } };
|
definitions = { ...definitions, [name]: { ...part } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteOneOfKindUndefinedRecursive(definitions, "$");
|
||||||
|
|
||||||
fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4));
|
fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteOneOfKindUndefinedRecursive(obj, path) {
|
||||||
|
if (
|
||||||
|
obj?.type === "object" &&
|
||||||
|
obj?.properties?.oneofKind?.type === "undefined"
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (const key in obj) {
|
||||||
|
if (
|
||||||
|
typeof obj[key] === "object" &&
|
||||||
|
deleteOneOfKindUndefinedRecursive(obj[key], path + "." + key)
|
||||||
|
) {
|
||||||
|
console.log("Deleting", path, key);
|
||||||
|
delete obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
187
src/api/routes/users/@me/settings-proto/1.ts
Normal file
187
src/api/routes/users/@me/settings-proto/1.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||||
|
Copyright (C) 2025 Spacebar and Spacebar Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { route } from "@spacebar/api";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
import {
|
||||||
|
OrmUtils,
|
||||||
|
SettingsProtoJsonResponse,
|
||||||
|
SettingsProtoResponse,
|
||||||
|
SettingsProtoUpdateJsonSchema,
|
||||||
|
SettingsProtoUpdateSchema,
|
||||||
|
UserSettingsProtos,
|
||||||
|
} from "@spacebar/util";
|
||||||
|
import { PreloadedUserSettings } from "discord-protos";
|
||||||
|
|
||||||
|
const router: Router = Router();
|
||||||
|
|
||||||
|
//#region Protobuf
|
||||||
|
router.get(
|
||||||
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
atomic: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Whether to try to apply the settings update atomically (default false)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(req.user_id);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: PreloadedUserSettings.toBase64(
|
||||||
|
userSettings.userSettings!,
|
||||||
|
),
|
||||||
|
} as SettingsProtoResponse);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/",
|
||||||
|
route({
|
||||||
|
requestBody: "SettingsProtoUpdateSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoUpdateResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { settings, required_data_version } =
|
||||||
|
req.body as SettingsProtoUpdateSchema;
|
||||||
|
const { atomic } = req.query;
|
||||||
|
const updatedSettings = PreloadedUserSettings.fromBase64(settings);
|
||||||
|
|
||||||
|
const resultObj = await patchUserSettings(
|
||||||
|
req.user_id,
|
||||||
|
updatedSettings,
|
||||||
|
required_data_version,
|
||||||
|
atomic == "true",
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: PreloadedUserSettings.toBase64(resultObj.settings),
|
||||||
|
out_of_date: resultObj.out_of_date,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region JSON
|
||||||
|
router.get(
|
||||||
|
"/json",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoJsonResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(req.user_id);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: PreloadedUserSettings.toJson(userSettings.userSettings!),
|
||||||
|
} as SettingsProtoJsonResponse);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/json",
|
||||||
|
route({
|
||||||
|
requestBody: "SettingsProtoUpdateJsonSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoUpdateJsonResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
atomic: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Whether to try to apply the settings update atomically (default false)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { settings, required_data_version } =
|
||||||
|
req.body as SettingsProtoUpdateJsonSchema;
|
||||||
|
const { atomic } = req.query;
|
||||||
|
const updatedSettings = PreloadedUserSettings.fromJson(settings);
|
||||||
|
|
||||||
|
const resultObj = await patchUserSettings(
|
||||||
|
req.user_id,
|
||||||
|
updatedSettings,
|
||||||
|
required_data_version,
|
||||||
|
atomic == "true",
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: PreloadedUserSettings.toJson(resultObj.settings),
|
||||||
|
out_of_date: resultObj.out_of_date,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
async function patchUserSettings(
|
||||||
|
userId: string,
|
||||||
|
updatedSettings: PreloadedUserSettings,
|
||||||
|
required_data_version: number | undefined,
|
||||||
|
atomic: boolean = false,
|
||||||
|
) {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(userId);
|
||||||
|
let settings = userSettings.userSettings!;
|
||||||
|
|
||||||
|
if (
|
||||||
|
required_data_version &&
|
||||||
|
settings.versions &&
|
||||||
|
settings.versions.dataVersion > required_data_version
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
settings: settings,
|
||||||
|
out_of_date: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Updating user settings for user ${userId} with atomic=${atomic}:`, updatedSettings);
|
||||||
|
|
||||||
|
if (!atomic) {
|
||||||
|
settings = Object.assign(settings, updatedSettings);
|
||||||
|
} else {
|
||||||
|
settings = OrmUtils.mergeDeep(settings, updatedSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
userSettings.userSettings = settings;
|
||||||
|
await userSettings.save();
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: settings,
|
||||||
|
out_of_date: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default router;
|
||||||
187
src/api/routes/users/@me/settings-proto/2.ts
Normal file
187
src/api/routes/users/@me/settings-proto/2.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||||
|
Copyright (C) 2025 Spacebar and Spacebar Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { route } from "@spacebar/api";
|
||||||
|
import { Request, Response, Router } from "express";
|
||||||
|
import {
|
||||||
|
OrmUtils,
|
||||||
|
SettingsProtoJsonResponse,
|
||||||
|
SettingsProtoResponse,
|
||||||
|
SettingsProtoUpdateJsonSchema,
|
||||||
|
SettingsProtoUpdateSchema,
|
||||||
|
UserSettingsProtos,
|
||||||
|
} from "@spacebar/util";
|
||||||
|
import { FrecencyUserSettings } from "discord-protos";
|
||||||
|
|
||||||
|
const router: Router = Router();
|
||||||
|
|
||||||
|
//#region Protobuf
|
||||||
|
router.get(
|
||||||
|
"/",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
atomic: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Whether to try to apply the settings update atomically (default false)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(req.user_id);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: FrecencyUserSettings.toBase64(
|
||||||
|
userSettings.frecencySettings!,
|
||||||
|
),
|
||||||
|
} as SettingsProtoResponse);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/",
|
||||||
|
route({
|
||||||
|
requestBody: "SettingsProtoUpdateSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoUpdateResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { settings, required_data_version } =
|
||||||
|
req.body as SettingsProtoUpdateSchema;
|
||||||
|
const { atomic } = req.query;
|
||||||
|
const updatedSettings = FrecencyUserSettings.fromBase64(settings);
|
||||||
|
|
||||||
|
const resultObj = await patchUserSettings(
|
||||||
|
req.user_id,
|
||||||
|
updatedSettings,
|
||||||
|
required_data_version,
|
||||||
|
atomic == "true",
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: FrecencyUserSettings.toBase64(resultObj.settings),
|
||||||
|
out_of_date: resultObj.out_of_date,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region JSON
|
||||||
|
router.get(
|
||||||
|
"/json",
|
||||||
|
route({
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoJsonResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(req.user_id);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: FrecencyUserSettings.toJson(userSettings.frecencySettings!),
|
||||||
|
} as SettingsProtoJsonResponse);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/json",
|
||||||
|
route({
|
||||||
|
requestBody: "SettingsProtoUpdateJsonSchema",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
body: "SettingsProtoUpdateJsonResponse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
atomic: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Whether to try to apply the settings update atomically (default false)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { settings, required_data_version } =
|
||||||
|
req.body as SettingsProtoUpdateJsonSchema;
|
||||||
|
const { atomic } = req.query;
|
||||||
|
const updatedSettings = FrecencyUserSettings.fromJson(settings);
|
||||||
|
|
||||||
|
const resultObj = await patchUserSettings(
|
||||||
|
req.user_id,
|
||||||
|
updatedSettings,
|
||||||
|
required_data_version,
|
||||||
|
atomic == "true",
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
settings: FrecencyUserSettings.toJson(resultObj.settings),
|
||||||
|
out_of_date: resultObj.out_of_date,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
async function patchUserSettings(
|
||||||
|
userId: string,
|
||||||
|
updatedSettings: FrecencyUserSettings,
|
||||||
|
required_data_version: number | undefined,
|
||||||
|
atomic: boolean = false,
|
||||||
|
) {
|
||||||
|
const userSettings = await UserSettingsProtos.getOrCreate(userId);
|
||||||
|
let settings = userSettings.frecencySettings!;
|
||||||
|
|
||||||
|
if (
|
||||||
|
required_data_version &&
|
||||||
|
settings.versions &&
|
||||||
|
settings.versions.dataVersion > required_data_version
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
settings: settings,
|
||||||
|
out_of_date: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Updating frecency settings for user ${userId} with atomic=${atomic}:`, updatedSettings);
|
||||||
|
|
||||||
|
if (!atomic) {
|
||||||
|
settings = Object.assign(settings, updatedSettings);
|
||||||
|
} else {
|
||||||
|
settings = OrmUtils.mergeDeep(settings, updatedSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
userSettings.frecencySettings = settings;
|
||||||
|
await userSettings.save();
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: settings,
|
||||||
|
out_of_date: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default router;
|
||||||
170
src/util/entities/UserSettingsProtos.ts
Normal file
170
src/util/entities/UserSettingsProtos.ts
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||||
|
Copyright (C) 2025 Spacebar and Spacebar Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Column, Entity, JoinColumn, OneToOne, RelationId } from "typeorm";
|
||||||
|
import { BaseClassWithoutId, PrimaryIdColumn } from "./BaseClass";
|
||||||
|
import { dbEngine } from "@spacebar/util";
|
||||||
|
import { User } from "./User";
|
||||||
|
import {
|
||||||
|
FrecencyUserSettings,
|
||||||
|
PreloadedUserSettings,
|
||||||
|
PreloadedUserSettings_LaunchPadMode,
|
||||||
|
PreloadedUserSettings_SwipeRightToLeftMode,
|
||||||
|
PreloadedUserSettings_Theme,
|
||||||
|
PreloadedUserSettings_TimestampHourCycle,
|
||||||
|
PreloadedUserSettings_UIDensity,
|
||||||
|
} from "discord-protos";
|
||||||
|
|
||||||
|
@Entity({
|
||||||
|
name: "user_settings_protos",
|
||||||
|
engine: dbEngine,
|
||||||
|
})
|
||||||
|
export class UserSettingsProtos extends BaseClassWithoutId {
|
||||||
|
@OneToOne(() => User, {
|
||||||
|
cascade: true,
|
||||||
|
orphanedRowAction: "delete",
|
||||||
|
eager: false,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: "user_id" })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@PrimaryIdColumn({ type: "text" })
|
||||||
|
user_id: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: String, name: "userSettings" })
|
||||||
|
_userSettings: string | undefined;
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: String, name: "frecencySettings" })
|
||||||
|
_frecencySettings: string | undefined;
|
||||||
|
|
||||||
|
// @Column({nullable: true, type: "simple-json"})
|
||||||
|
// testSettings: {};
|
||||||
|
|
||||||
|
bigintReplacer(_key: string, value: any): any {
|
||||||
|
if (typeof value === "bigint") {
|
||||||
|
return (value as bigint).toString();
|
||||||
|
} else if (value instanceof Uint8Array) {
|
||||||
|
return {
|
||||||
|
__type: "Uint8Array",
|
||||||
|
data: Array.from(value as Uint8Array)
|
||||||
|
.map((b) => b.toString(16).padStart(2, "0"))
|
||||||
|
.join(""),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bigintReviver(_key: string, value: any): any {
|
||||||
|
if (typeof value === "string" && /^\d+n$/.test(value)) {
|
||||||
|
return BigInt((value as string).slice(0, -1));
|
||||||
|
} else if (
|
||||||
|
typeof value === "object" &&
|
||||||
|
value !== null &&
|
||||||
|
"__type" in value
|
||||||
|
) {
|
||||||
|
if (value.__type === "Uint8Array" && "data" in value) {
|
||||||
|
return new Uint8Array(
|
||||||
|
value.data
|
||||||
|
.match(/.{1,2}/g)!
|
||||||
|
.map((byte: string) => parseInt(byte, 16)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get userSettings(): PreloadedUserSettings | undefined {
|
||||||
|
if (!this._userSettings) return undefined;
|
||||||
|
return PreloadedUserSettings.fromJson(
|
||||||
|
JSON.parse(this._userSettings, this.bigintReviver),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set userSettings(value: PreloadedUserSettings | undefined) {
|
||||||
|
if (value) {
|
||||||
|
// this._userSettings = JSON.stringify(value, this.bigintReplacer);
|
||||||
|
this._userSettings = PreloadedUserSettings.toJsonString(value);
|
||||||
|
} else {
|
||||||
|
this._userSettings = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get frecencySettings(): FrecencyUserSettings | undefined {
|
||||||
|
if (!this._frecencySettings) return undefined;
|
||||||
|
return FrecencyUserSettings.fromJson(
|
||||||
|
JSON.parse(this._frecencySettings, this.bigintReviver),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set frecencySettings(value: FrecencyUserSettings | undefined) {
|
||||||
|
if (value) {
|
||||||
|
this._frecencySettings = JSON.stringify(value, this.bigintReplacer);
|
||||||
|
} else {
|
||||||
|
this._frecencySettings = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getOrCreate(user_id: string): Promise<UserSettingsProtos> {
|
||||||
|
const user = await User.findOneOrFail({
|
||||||
|
where: { id: user_id },
|
||||||
|
select: { settings: true },
|
||||||
|
});
|
||||||
|
let userSettings = await UserSettingsProtos.findOne({
|
||||||
|
where: { user_id },
|
||||||
|
});
|
||||||
|
let modified = false;
|
||||||
|
if (!userSettings) {
|
||||||
|
userSettings = UserSettingsProtos.create({
|
||||||
|
user_id,
|
||||||
|
});
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userSettings.userSettings) {
|
||||||
|
userSettings.userSettings = PreloadedUserSettings.create({
|
||||||
|
ads: {
|
||||||
|
alwaysDeliver: false,
|
||||||
|
},
|
||||||
|
appearance: {
|
||||||
|
developerMode: user.settings?.developer_mode ?? true,
|
||||||
|
theme: PreloadedUserSettings_Theme.DARK,
|
||||||
|
mobileRedesignDisabled: true,
|
||||||
|
launchPadMode:
|
||||||
|
PreloadedUserSettings_LaunchPadMode.LAUNCH_PAD_DISABLED,
|
||||||
|
swipeRightToLeftMode:
|
||||||
|
PreloadedUserSettings_SwipeRightToLeftMode.SWIPE_RIGHT_TO_LEFT_REPLY,
|
||||||
|
timestampHourCycle:
|
||||||
|
PreloadedUserSettings_TimestampHourCycle.AUTO,
|
||||||
|
uiDensity:
|
||||||
|
PreloadedUserSettings_UIDensity.UI_DENSITY_COMPACT,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userSettings.frecencySettings) {
|
||||||
|
userSettings.frecencySettings = FrecencyUserSettings.create({});
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modified) userSettings = await userSettings.save();
|
||||||
|
|
||||||
|
return userSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -55,6 +55,7 @@ export * from "./TeamMember";
|
|||||||
export * from "./Template";
|
export * from "./Template";
|
||||||
export * from "./User";
|
export * from "./User";
|
||||||
export * from "./UserSettings";
|
export * from "./UserSettings";
|
||||||
|
export * from "./UserSettingsProtos";
|
||||||
export * from "./ValidRegistrationTokens";
|
export * from "./ValidRegistrationTokens";
|
||||||
export * from "./VoiceState";
|
export * from "./VoiceState";
|
||||||
export * from "./Webhook";
|
export * from "./Webhook";
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class UserSettingsProtos1752157979333 implements MigrationInterface {
|
||||||
|
name = 'UserSettingsProtos1752157979333'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE IF EXISTS "user_settings_protos"`);
|
||||||
|
await queryRunner.query(`CREATE TABLE "user_settings_protos" ("user_id" character varying NOT NULL, "userSettings" text, "frecencySettings" text, CONSTRAINT "PK_8ff3d1961a48b693810c9f99853" PRIMARY KEY ("user_id"))`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD CONSTRAINT "FK_8ff3d1961a48b693810c9f99853" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP CONSTRAINT "FK_8ff3d1961a48b693810c9f99853"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "user_settings_protos"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
src/util/schemas/SettingsProtoUpdateSchema.ts
Normal file
47
src/util/schemas/SettingsProtoUpdateSchema.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||||
|
Copyright (C) 2025 Spacebar and Spacebar Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { JsonValue } from "@protobuf-ts/runtime";
|
||||||
|
|
||||||
|
export interface SettingsProtoUpdateSchema {
|
||||||
|
settings: string;
|
||||||
|
required_data_version?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettingsProtoUpdateJsonSchema {
|
||||||
|
settings: JsonValue;
|
||||||
|
required_data_version?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these dont work with schema validation
|
||||||
|
// typed JSON schemas:
|
||||||
|
// export interface SettingsProtoUpdatePreloadedUserSettingsSchema {
|
||||||
|
// settings: PreloadedUserSettings;
|
||||||
|
// required_data_version?: number;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export interface SettingsProtoUpdateFrecencyUserSettingsSchema {
|
||||||
|
// settings: FrecencyUserSettings;
|
||||||
|
// required_data_version?: number;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: what is this?
|
||||||
|
// export interface SettingsProtoUpdateTestSettingsSchema {
|
||||||
|
// settings: {};
|
||||||
|
// required_data_version?: number;
|
||||||
|
// }
|
||||||
@ -69,6 +69,7 @@ export * from "./responses";
|
|||||||
export * from "./RoleModifySchema";
|
export * from "./RoleModifySchema";
|
||||||
export * from "./RolePositionUpdateSchema";
|
export * from "./RolePositionUpdateSchema";
|
||||||
export * from "./SelectProtocolSchema";
|
export * from "./SelectProtocolSchema";
|
||||||
|
export * from "./SettingsProtoUpdateSchema";
|
||||||
export * from "./StreamCreateSchema";
|
export * from "./StreamCreateSchema";
|
||||||
export * from "./StreamDeleteSchema";
|
export * from "./StreamDeleteSchema";
|
||||||
export * from "./StreamWatchSchema";
|
export * from "./StreamWatchSchema";
|
||||||
|
|||||||
53
src/util/schemas/responses/SettingsProtoUpdateResponse.ts
Normal file
53
src/util/schemas/responses/SettingsProtoUpdateResponse.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||||
|
Copyright (C) 2025 Spacebar and Spacebar Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { JsonValue } from "@protobuf-ts/runtime";
|
||||||
|
|
||||||
|
export interface SettingsProtoResponse {
|
||||||
|
settings: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettingsProtoUpdateResponse extends SettingsProtoResponse {
|
||||||
|
out_of_date?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettingsProtoJsonResponse {
|
||||||
|
settings: JsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettingsProtoUpdateJsonResponse extends SettingsProtoJsonResponse {
|
||||||
|
out_of_date?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these dont work with schemas validation
|
||||||
|
// Typed JSON schemas:
|
||||||
|
// export interface SettingsProtoUpdatePreloadedUserSettingsJsonResponse {
|
||||||
|
// settings: PreloadedUserSettings;
|
||||||
|
// out_of_date?: boolean;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export interface SettingsProtoUpdateFrecencyUserSettingsJsonResponse {
|
||||||
|
// settings: FrecencyUserSettings;
|
||||||
|
// out_of_date?: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: what is this?
|
||||||
|
// export interface SettingsProtoUpdateTestSettingsJsonResponse {
|
||||||
|
// settings: {};
|
||||||
|
// out_of_date?: boolean;
|
||||||
|
// }
|
||||||
@ -47,6 +47,7 @@ export * from "./LocationMetadataResponse";
|
|||||||
export * from "./MemberJoinGuildResponse";
|
export * from "./MemberJoinGuildResponse";
|
||||||
export * from "./OAuthAuthorizeResponse";
|
export * from "./OAuthAuthorizeResponse";
|
||||||
export * from "./RefreshUrlsResponse";
|
export * from "./RefreshUrlsResponse";
|
||||||
|
export * from "./SettingsProtoUpdateResponse";
|
||||||
export * from "./TeamListResponse";
|
export * from "./TeamListResponse";
|
||||||
export * from "./Tenor";
|
export * from "./Tenor";
|
||||||
export * from "./TokenResponse";
|
export * from "./TokenResponse";
|
||||||
|
|||||||
Reference in New Issue
Block a user