templating api

This commit is contained in:
2026-06-19 22:20:43 -07:00
parent 08d7a80f56
commit 84f894c356
110 changed files with 12432 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
import { CreationAttributes } from "@sequelize/core"
import { isNil, random } from "lodash"
import { User } from "@/models"
import BaseService from "@/services/base-service"
export type UserCreationAttributes = Partial<CreationAttributes<User>>
export class CreateService extends BaseService {
constructor(private attributes: UserCreationAttributes) {
super()
}
async perform(): Promise<User> {
const { email, auth0Subject, roles, ...optionalAttributes } = this.attributes
if (isNil(email)) {
throw new Error("Email is required")
}
if (isNil(auth0Subject)) {
throw new Error("Auth0 Subject is required")
}
const [emailLocalPart] = email.split("@")
/**
* Yep, if we don't have enough data, your name becomes your email split randomly.
* This way we can at least have a first name and last name,
* and the first and last name are likely to be distinct.
*/
const randomSplit = random(1, emailLocalPart.length - 2)
const [firstNameFallback, lastNameFallback] = emailLocalPart.includes(".")
? emailLocalPart.split(".")
: [emailLocalPart.slice(0, randomSplit), emailLocalPart.slice(randomSplit)]
const { firstName, lastName } = optionalAttributes
const firstNameOrFallback = firstName || firstNameFallback
const lastNameOrFallback = lastName || lastNameFallback
const user = await User.create({
...optionalAttributes,
email,
auth0Subject: auth0Subject,
firstName: firstNameOrFallback,
lastName: lastNameOrFallback,
displayName: `${firstNameOrFallback} ${lastNameOrFallback}`,
roles: roles ?? [User.Roles.USER],
})
return user
}
}
export default CreateService
+14
View File
@@ -0,0 +1,14 @@
import { User } from "@/models"
import BaseService from "@/services/base-service"
export class DestroyService extends BaseService {
constructor(private user: User) {
super()
}
async perform() {
throw new Error("Not implemented")
}
}
export default DestroyService
@@ -0,0 +1,35 @@
import { auth0Integration } from "@/integrations"
import { User } from "@/models"
import { Op } from "@sequelize/core"
import BaseService from "@/services/base-service"
export class FindFromAuth0TokenService extends BaseService {
constructor(private token: string) {
super()
}
async perform(): Promise<User> {
const { auth0Subject, email } = await auth0Integration.getUserInfo(this.token)
const existingUser = await User.withScope(["asCurrentUser"]).findOne({
where: { auth0Subject },
})
if (existingUser) {
return existingUser
}
const firstTimeUser = await User.withScope(["asCurrentUser"]).findOne({
where: { [Op.or]: [{ auth0Subject: email }, { email: email }] },
})
if (firstTimeUser) {
await firstTimeUser.update({ auth0Subject })
return firstTimeUser
}
throw new Error("No user found for this token.")
}
}
export default FindFromAuth0TokenService
+6
View File
@@ -0,0 +1,6 @@
export { CreateService } from "./create-service"
export { UpdateService } from "./update-service"
export { DestroyService } from "./destroy-service"
// Special Services
export { FindFromAuth0TokenService } from "./find-from-auth0-token-service"
+21
View File
@@ -0,0 +1,21 @@
import { Attributes } from "@sequelize/core"
import { User } from "@/models"
import BaseService from "@/services/base-service"
export type UserUpdateAttributes = Partial<Attributes<User>>
export class UpdateService extends BaseService {
constructor(
private user: User,
private attributes: UserUpdateAttributes
) {
super()
}
async perform(): Promise<User> {
return this.user.update(this.attributes)
}
}
export default UpdateService