168 lines
3.9 KiB
TypeScript
168 lines
3.9 KiB
TypeScript
// services/project.ts
|
|
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
|
|
// All Rights Reserved
|
|
|
|
import slug from "slug";
|
|
|
|
import { MongooseBaseQueryOptions, PopulateOptions } from "mongoose";
|
|
|
|
import { IProject, IUser, ProjectStatus } from "@gadget/api";
|
|
import Project from "@/models/project.js";
|
|
|
|
import { DtpService } from "../lib/service.js";
|
|
|
|
export interface IProjectDefinition {
|
|
name: string;
|
|
slug: string;
|
|
gitUrl?: string;
|
|
status?: ProjectStatus;
|
|
}
|
|
|
|
class ProjectService extends DtpService {
|
|
private populateProject: PopulateOptions[];
|
|
|
|
get name(): string {
|
|
return "ProjectService";
|
|
}
|
|
|
|
get slug(): string {
|
|
return "svc:project";
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this.populateProject = [
|
|
{
|
|
path: "user",
|
|
select: "-passwordSalt -password",
|
|
},
|
|
];
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
this.log.info("service started");
|
|
}
|
|
|
|
async stop(): Promise<void> {
|
|
this.log.info("service stopped");
|
|
}
|
|
|
|
async create(user: IUser, definition: IProjectDefinition): Promise<IProject> {
|
|
const NOW = new Date();
|
|
|
|
const project = new Project();
|
|
project.createdAt = NOW;
|
|
project.user = user._id;
|
|
project.status = ProjectStatus.Active;
|
|
project.name = definition.name;
|
|
project.slug = slug(definition.slug.trim().toLowerCase());
|
|
|
|
if (definition.gitUrl) {
|
|
project.gitUrl = definition.gitUrl;
|
|
}
|
|
|
|
this.log.info("creating project", { name: project.name });
|
|
await project.save();
|
|
|
|
return project.populate(this.populateProject);
|
|
}
|
|
|
|
async update(
|
|
project: IProject,
|
|
definition: IProjectDefinition,
|
|
): Promise<IProject> {
|
|
const update: MongooseBaseQueryOptions = { $set: {}, $unset: {} };
|
|
|
|
if (definition.status) {
|
|
if (definition.status !== project.status) {
|
|
update.$set.status = definition.status;
|
|
}
|
|
}
|
|
|
|
if (definition.name) {
|
|
if (definition.name !== project.name) {
|
|
update.$set.name = definition.name;
|
|
}
|
|
}
|
|
|
|
if (definition.slug) {
|
|
if (definition.slug !== project.slug) {
|
|
update.$set.slug = slug(definition.slug.trim().toLowerCase());
|
|
}
|
|
}
|
|
|
|
if (definition.gitUrl) {
|
|
if (definition.gitUrl !== project.gitUrl) {
|
|
update.$set.gitUrl = definition.gitUrl;
|
|
}
|
|
} else {
|
|
update.$unset.gitUrl = 1;
|
|
}
|
|
|
|
const newProject = await Project.findOneAndUpdate(
|
|
{ _id: project._id },
|
|
update,
|
|
{ new: true, populate: this.populateProject },
|
|
);
|
|
if (!newProject) {
|
|
const error = new Error("project has been removed");
|
|
error.statusCode = 404;
|
|
throw error;
|
|
}
|
|
|
|
this.log.info("project updated", {
|
|
old: project,
|
|
new: newProject.toObject(),
|
|
});
|
|
|
|
return newProject;
|
|
}
|
|
|
|
async setStatus(project: IProject, status: ProjectStatus): Promise<IProject> {
|
|
const newProject = await Project.findOneAndUpdate(
|
|
{ _id: project._id },
|
|
{ $set: { status } },
|
|
{ new: true, populate: this.populateProject },
|
|
);
|
|
if (!newProject) {
|
|
const error = new Error("project has been removed");
|
|
error.statusCode = 404;
|
|
throw error;
|
|
}
|
|
|
|
this.log.info("project status updated", {
|
|
project: {
|
|
_id: project._id,
|
|
slug: project.slug,
|
|
},
|
|
old: project.status,
|
|
new: newProject.status,
|
|
});
|
|
|
|
return newProject;
|
|
}
|
|
|
|
async getForUser(user: IUser): Promise<IProject[]> {
|
|
const projects = await Project.find({ user: user._id })
|
|
.sort({ name: 1 })
|
|
.populate(this.populateProject);
|
|
return projects;
|
|
}
|
|
|
|
async findById(id: string): Promise<IProject | null> {
|
|
return Project.findById(id).populate(this.populateProject);
|
|
}
|
|
|
|
async findBySlug(slug: string, user: IUser): Promise<IProject | null> {
|
|
return Project.findOne({ slug, user: user._id }).populate(
|
|
this.populateProject,
|
|
);
|
|
}
|
|
|
|
async delete(project: IProject): Promise<void> {
|
|
await Project.deleteOne({ _id: project._id });
|
|
}
|
|
}
|
|
|
|
export default new ProjectService();
|