Merge pull request #3165 from ajhollid/fix/team-ts

fix/team ts
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-01-21 11:57:21 -08:00
commit b0da1f4259
12 files changed
+111 -120

No files matched your search

View File
Whitespace-only changes.
+7 -7
View File
@@ -1,4 +1,3 @@
import TranslationService from "../service/system/translationService.js";
import MongoDB from "../db/MongoDB.js";
import NetworkService from "../service/infrastructure/networkService.js";
import EmailService from "../service/infrastructure/emailService.js";
@@ -81,6 +80,7 @@ import {
MongoSettingsRepository,
MongoNotificationsRepository,
MongoIncidentRepository,
MongoTeamsRepository,
IMonitorsRepository,
IChecksRepository,
IMonitorStatsRepository,
@@ -91,14 +91,13 @@ import {
ISettingsRepository,
INotificationsRepository,
IIncidentsRepository,
ITeamsRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
export type InitializedServices = {
//v1
settingsService: any;
translationService: any;
db: any;
networkService: any;
emailService: any;
@@ -127,6 +126,7 @@ export type InitializedServices = {
settingsRepository: ISettingsRepository;
notificationsRepository: INotificationsRepository;
incidentsRepository: IIncidentsRepository;
teamsRepository: ITeamsRepository;
};
export const initializeServices = async ({
@@ -138,9 +138,6 @@ export const initializeServices = async ({
envSettings: EnvConfig;
settingsService: any;
}): Promise<InitializedServices> => {
const translationService = new TranslationService(logger);
await translationService.initialize();
// Create DB
const inviteModule = new InviteModule({ InviteToken, crypto });
const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, AppSettings });
@@ -177,6 +174,8 @@ export const initializeServices = async ({
const settingsRepository = new MongoSettingsRepository();
const notificationsRepository = new MongoNotificationsRepository();
const incidentsRepository = new MongoIncidentRepository();
const teamsRepository = new MongoTeamsRepository();
const networkService = new NetworkService({
axios,
got,
@@ -260,6 +259,7 @@ export const initializeServices = async ({
invitesRepository,
recoveryTokensRepository,
settingsRepository,
teamsRepository,
});
const diagnosticService = new DiagnosticService();
@@ -289,7 +289,6 @@ export const initializeServices = async ({
const services = {
//v1
settingsService,
translationService,
db,
networkService,
emailService,
@@ -318,6 +317,7 @@ export const initializeServices = async ({
settingsRepository,
notificationsRepository,
incidentsRepository,
teamsRepository,
};
return services;
-14
View File
@@ -1,14 +0,0 @@
import mongoose from "mongoose";
const TeamSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
},
{
timestamps: true,
}
);
export default mongoose.model("Team", TeamSchema);
+28
View File
@@ -0,0 +1,28 @@
import { Schema, model, type Types } from "mongoose";
import type { Team } from "@/types/index.js";
type TeamDocumentBase = Omit<Team, "id" | "createdAt" | "updatedAt"> & {};
interface TeamDocument extends TeamDocumentBase {
_id: Types.ObjectId;
createdAt: Date;
updatedAt: Date;
}
const TeamSchema = new Schema<TeamDocument>(
{
email: {
type: String,
required: true,
unique: true,
},
},
{
timestamps: true,
}
);
const TeamModel = model<TeamDocument>("Team", TeamSchema);
export type { TeamDocument };
export { TeamModel };
export default TeamModel;
+3
View File
@@ -27,3 +27,6 @@ export { default as NotificationModel } from "@/db/models/Notification.js";
export * from "@/db/models/Incident.js";
export { default as IncidentModel } from "@/db/models/Incident.js";
export * from "@/db/models/Team.js";
export { default as TeamModel } from "@/db/models/Team.js";
+3
View File
@@ -27,3 +27,6 @@ export { default as MongoNotificationsRepository } from "@/repositories/notifica
export * from "@/repositories/incidents/IIncidentsRepository.js";
export { default as MongoIncidentRepository } from "@/repositories/incidents/MongoIncidentRepository.js";
export * from "@/repositories/teams/ITeamsRepository.js";
export { default as MongoTeamsRepository } from "@/repositories/teams/MongoTeamsRepository.js";
@@ -0,0 +1,9 @@
import type { Team } from "@/types/index.js";
export interface ITeamsRepository {
// create
create(email: string): Promise<Team>;
// fetch
// update
// delete
// other
}
@@ -0,0 +1,36 @@
import { Team } from "@/types/index.js";
import { TeamDocument, TeamModel } from "@/db/models/index.js";
import { ITeamsRepository } from "@/repositories/index.js";
import mongoose from "mongoose";
class MongoTeamsRepository implements ITeamsRepository {
private toStringId = (value?: mongoose.Types.ObjectId | string | null): string => {
if (!value) {
return "";
}
return value instanceof mongoose.Types.ObjectId ? value.toString() : String(value);
};
private toDateString = (value?: Date | string | null): string => {
if (!value) {
return new Date(0).toISOString();
}
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
};
private toEntity = (doc: TeamDocument): Team => {
return {
id: this.toStringId(doc._id),
email: doc.email,
createdAt: this.toDateString(doc.createdAt),
updatedAt: this.toDateString(doc.updatedAt),
};
};
create = async (email: string) => {
const team = await TeamModel.create({ email });
return this.toEntity(team);
};
}
export default MongoTeamsRepository;
+18 -7
View File
@@ -1,9 +1,15 @@
import { IInvitesRepository, IMonitorsRepository, IRecoveryTokensRepository, IUsersRepository, ISettingsRepository } from "@/repositories/index.js";
import Team from "@/db/models/Team.js";
import {
IInvitesRepository,
IMonitorsRepository,
IRecoveryTokensRepository,
IUsersRepository,
ISettingsRepository,
ITeamsRepository,
} from "@/repositories/index.js";
import type { User } from "@/types/index.js";
import bcrypt from "bcryptjs";
import { AppError } from "@/utils/AppError.js";
import { ISuperSimpleQueue } from "../infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
import { ISuperSimpleQueue } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
const SERVICE_NAME = "userService";
@@ -21,6 +27,7 @@ class UserService {
private invitesRepository: IInvitesRepository;
private recoveryTokensRepository: IRecoveryTokensRepository;
private settingsRepository: ISettingsRepository;
private teamsRepository: ITeamsRepository;
constructor({
crypto,
@@ -34,6 +41,7 @@ class UserService {
invitesRepository,
recoveryTokensRepository,
settingsRepository,
teamsRepository,
}: {
crypto: any;
emailService: any;
@@ -47,6 +55,7 @@ class UserService {
invitesRepository: IInvitesRepository;
recoveryTokensRepository: IRecoveryTokensRepository;
settingsRepository: ISettingsRepository;
teamsRepository: ITeamsRepository;
}) {
this.emailService = emailService;
this.settingsService = settingsService;
@@ -59,6 +68,7 @@ class UserService {
this.invitesRepository = invitesRepository;
this.recoveryTokensRepository = recoveryTokensRepository;
this.settingsRepository = settingsRepository;
this.teamsRepository = teamsRepository;
}
get serviceName() {
@@ -85,10 +95,11 @@ class UserService {
const jwtSecret = this.crypto.randomBytes(64).toString("hex");
await this.settingsRepository.update({ jwtSecret });
// Create a new team
const team = new Team({
email: user.email,
});
user.teamId = team._id;
if (!user.email) {
throw new AppError({ message: "Email is required for first user", service: SERVICE_NAME, method: "registerUser", status: 400 });
}
const team = await this.teamsRepository.create(user.email);
user.teamId = team.id;
}
const newUser = await this.usersRepository.create({ ...user }, file);
@@ -1,92 +0,0 @@
import fs from "fs";
import path from "path";
class TranslationService {
static SERVICE_NAME = "TranslationService";
constructor(logger) {
this.logger = logger;
this.translations = {};
this._language = "en";
this.localesDir = path.join(process.cwd(), "src/locales");
}
get serviceName() {
return TranslationService.SERVICE_NAME;
}
setLanguage(language) {
this._language = language;
}
get language() {
return this._language;
}
async initialize() {
try {
await this.loadFromFiles();
} catch (error) {
this.logger.error({
message: error.message,
service: "TranslationService",
method: "initialize",
stack: error.stack,
});
}
}
async loadFromFiles() {
try {
if (!fs.existsSync(this.localesDir)) {
return false;
}
const files = fs.readdirSync(this.localesDir).filter((file) => file.endsWith(".json"));
if (files.length === 0) {
return false;
}
for (const file of files) {
const language = file.replace(".json", "");
const filePath = path.join(this.localesDir, file);
const content = fs.readFileSync(filePath, "utf8");
this.translations[language] = JSON.parse(content);
}
this.logger.info({
message: "Translations loaded from files successfully",
service: "TranslationService",
method: "loadFromFiles",
});
return true;
} catch (error) {
this.logger.error({
message: error.message,
service: "TranslationService",
method: "loadFromFiles",
stack: error.stack,
});
return false;
}
}
getTranslation(key) {
let language = this._language;
try {
return this.translations[language]?.[key] || this.translations["en"]?.[key] || key;
} catch (error) {
this.logger.error({
message: error.message,
service: "TranslationService",
method: "getTranslation",
stack: error.stack,
});
return key;
}
}
}
export default TranslationService;
+1
View File
@@ -11,3 +11,4 @@ export * from "@/types/notification.js";
export * from "@/types/alert.js";
export * from "@/types/incident.js";
export * from "@/types/email.js";
export * from "@/types/team.js";
+6
View File
@@ -0,0 +1,6 @@
export interface Team {
id: string;
email: string;
createdAt: string;
updatedAt: string;
}