[INIT] Initial Project Structure
This commit is contained in:
commit
0fc5f05b6a
105 changed files with 10448 additions and 0 deletions
60
lib/database/tables/KeyPairTableService.ts
Normal file
60
lib/database/tables/KeyPairTableService.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import config from "@lib/config";
|
||||
import TableService from "../TableService";
|
||||
import { CKeyPairContract } from "@lib/contracts/keypair.contracts";
|
||||
import { KeyPairErrors, ProjectErrors } from "@lib/vix/ClientErrors";
|
||||
import { encrypt, generateKeypair } from "@lib/svc/crypt.service";
|
||||
import { KeyPair, KeyPairType } from "@prisma/client";
|
||||
|
||||
declare type Custom = "Custom"; // Make sure this matches KeyPairType.Custom;
|
||||
|
||||
export default class KeyPairTableService extends TableService {
|
||||
protected table = "KeyPair";
|
||||
async byId(keypairId: string) {
|
||||
const keypair = this.pg.keyPair.findUnique({ where: { id: keypairId } });
|
||||
if (!keypair) throw KeyPairErrors.NotFoundKeypair;
|
||||
}
|
||||
|
||||
async byUsage(projectIdentity: string, usage: Custom): Promise<KeyPair[]>;
|
||||
async byUsage(projectIdentity: string, usage: Exclude<KeyPairType, Custom>): Promise<KeyPair | null>;
|
||||
async byUsage(projectIdentity: string, usage: KeyPairType): Promise<KeyPair[] | KeyPair | null> {
|
||||
const projectOr = { OR: [{ id: projectIdentity }, { slug: projectIdentity }] };
|
||||
const projectInclude = { keyPairs: { where: { usage: KeyPairType.UserToken } } };
|
||||
const project = await this.pg.project.findFirst({ where: projectOr, include: projectInclude });
|
||||
if (!project) throw ProjectErrors.BadRequestProjectIncomplete;
|
||||
const keypairs = project.keyPairs;
|
||||
if (usage !== KeyPairType.Custom && keypairs.length > 1)
|
||||
throw new Error(`Multiple keypairs found for project ${projectIdentity} and usage ${usage}`);
|
||||
if (usage !== KeyPairType.Custom && keypairs.length === 0) return null;
|
||||
if (usage !== KeyPairType.Custom) return keypairs[0];
|
||||
return keypairs;
|
||||
}
|
||||
|
||||
async $upsertDefaultKeyPairs() {
|
||||
const projectSlug = config.Server.projectSlug;
|
||||
const cairoProject = await this.pg.project.findUnique({ where: { slug: projectSlug } });
|
||||
if (!cairoProject) throw new Error("Cairo Project Not Found!");
|
||||
const projectId = cairoProject.id;
|
||||
await this.upsertProjecttDefaultKeyPairs(projectId);
|
||||
}
|
||||
|
||||
async upsertProjecttDefaultKeyPairs(projectId: string) {
|
||||
const storeKeypair = this.create.bind(this);
|
||||
const keyTypes = Object.values(KeyPairType).filter((kp) => kp !== KeyPairType.Custom);
|
||||
await Promise.all(
|
||||
keyTypes.map(async (kp) => {
|
||||
const existingKp = await this.byUsage(projectId, kp);
|
||||
if (!!existingKp) return;
|
||||
const { publicKey, privateKey } = await generateKeypair();
|
||||
const [encryptedPrivateKey, encryptedPublicKey] = await Promise.all([
|
||||
encrypt(privateKey, config.SigningOptions.Keys.KeyPair),
|
||||
encrypt(publicKey, config.SigningOptions.Keys.KeyPair),
|
||||
]);
|
||||
return storeKeypair({ encryptedPrivateKey, encryptedPublicKey, projectId, usage: kp });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async create(keypair: CKeyPairContract["Create"]) {
|
||||
return this.pg.keyPair.create({ data: keypair });
|
||||
}
|
||||
}
|
34
lib/database/tables/ProjectTableService.ts
Normal file
34
lib/database/tables/ProjectTableService.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import config from "@lib/config";
|
||||
import TableService from "../TableService";
|
||||
import { CProjectContract } from "@lib/types/ContractTypes";
|
||||
import { ProjectErrors } from "@lib/vix/ClientErrors";
|
||||
import { VERB } from "@dunemask/vix/logging";
|
||||
|
||||
export default class ProjectTableService extends TableService {
|
||||
protected table = "Project";
|
||||
async byId(projectId: string) {
|
||||
return this.pg.project.findUnique({ where: { id: projectId } });
|
||||
}
|
||||
|
||||
async bySlug(slug: string) {
|
||||
return this.pg.project.findUnique({ where: { slug } });
|
||||
}
|
||||
|
||||
async $upsertDefaultProject() {
|
||||
const { projectSlug: slug, projectName: name } = config.Server;
|
||||
const createOptions = { slug, name, parentProject: slug };
|
||||
const existingProject = await this.pg.project.findUnique({ where: { slug } });
|
||||
if (!!existingProject) return VERB("PROJECT", "Default project already exists!");
|
||||
VERB("PROJECT", "Default project not found! Creating now!");
|
||||
const proj = await this.pg.project.upsert({ where: { slug }, create: createOptions, update: createOptions });
|
||||
await this.pg.project.update({ where: { id: proj.id }, data: { parentProject: proj.id } }); // Use ProjectID instead of slug
|
||||
}
|
||||
|
||||
async create(project: CProjectContract["Create"] & { parentProject: string }) {
|
||||
const existingProject = await this.pg.project.findMany({ where: { id: project.slug } });
|
||||
if (existingProject.length > 1) throw ProjectErrors.BadRequestSlugInvalid;
|
||||
return this.pg.project.create({ data: project }).catch(() => {
|
||||
throw ProjectErrors.ConflictNonUnique;
|
||||
});
|
||||
}
|
||||
}
|
37
lib/database/tables/RolePolicyTableService.ts
Normal file
37
lib/database/tables/RolePolicyTableService.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import TableService from "../TableService";
|
||||
import { AuthorityType } from "@prisma/client";
|
||||
import { Policy, PolicyDefault } from "@lib/Policies";
|
||||
import config from "@lib/config";
|
||||
import { CRolePolicyContract } from "@lib/contracts/role-policy.contracts";
|
||||
|
||||
export default class RolePolicyTableService extends TableService {
|
||||
protected table = "RolePolicy";
|
||||
async byId(id: string) {
|
||||
return this.pg.rolePolicy.findUnique({ where: { id } });
|
||||
}
|
||||
|
||||
async $upsertDefaultAuthorities() {
|
||||
const projectSlug = config.Server.projectSlug;
|
||||
const cairoProject = await this.pg.project.findUnique({ where: { slug: projectSlug } });
|
||||
if (!cairoProject) throw new Error("Cairo Project Not Found!");
|
||||
const project = cairoProject.id;
|
||||
const $chk = ({ id, name, policies }: PolicyDefault) => this.$upsertDefaultsAuthority(project, name, id, policies);
|
||||
await Promise.all(Object.values(config.RolePolicy).map($chk));
|
||||
}
|
||||
|
||||
private async $upsertDefaultsAuthority(projectId: string, name: string, id: string, userPolicies: Policy[]) {
|
||||
const rootAuthority = config.RolePolicy.Root.id;
|
||||
const authorityType = id === rootAuthority ? AuthorityType.Root : AuthorityType.RolePolicy;
|
||||
const authority = id === rootAuthority ? name : rootAuthority; // Set Root Authority to root if root
|
||||
const policies = Policy.asStrings(userPolicies);
|
||||
return this.pg.rolePolicy.upsert({
|
||||
where: { id },
|
||||
create: { projectId, id, name, policies, authority, authorityType },
|
||||
update: { projectId, name, policies, authority, authorityType },
|
||||
});
|
||||
}
|
||||
|
||||
async create(rp: CRolePolicyContract["Create"]) {
|
||||
return this.pg.rolePolicy.create({ data: rp });
|
||||
}
|
||||
}
|
63
lib/database/tables/UsersTableService.ts
Normal file
63
lib/database/tables/UsersTableService.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import config from "@lib/config";
|
||||
import TableService from "../TableService";
|
||||
import { CUserContract } from "@lib/types/ContractTypes";
|
||||
import { hashText } from "@lib/modules/auth/auth.service";
|
||||
import { KeyPairType } from "@prisma/client";
|
||||
import { UserErrors } from "@lib/vix/ClientErrors";
|
||||
|
||||
// prettier-ignore
|
||||
const generateBase64Password = (length: number = 32): string => Array.from(crypto.getRandomValues(new Uint8Array(length)), byte => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.charAt(byte % 64)).join('');
|
||||
|
||||
export default class UsersTableService extends TableService {
|
||||
protected table = "User";
|
||||
async byId(userId: string) {
|
||||
return this.pg.user.findUnique({ where: { id: userId }, include: { rolePolicy: true, project: true } });
|
||||
}
|
||||
|
||||
async byUsername(username: string, projectId: string) {
|
||||
return this.pg.user.findUnique({ where: { projectId_username: { projectId, username } } });
|
||||
}
|
||||
|
||||
async byEmail(email: string, projectId: string) {
|
||||
return this.pg.user.findUnique({ where: { projectId_email: { projectId, email } } });
|
||||
}
|
||||
|
||||
async $upsertDefaultRootUser() {
|
||||
const project = await this.pg.project.findUnique({ where: { slug: config.Server.projectSlug } });
|
||||
if (!project) throw new Error("Cairo Project Not Found!");
|
||||
const rolePolicyId = config.RolePolicy.Root.id;
|
||||
return this.$upsertRootUser(project.id, rolePolicyId);
|
||||
}
|
||||
|
||||
async $upsertRootUser(projectId: string, rolePolicyId: string) {
|
||||
const root = await this.pg.user.findUnique({ where: { projectId_username: { username: "root", projectId } } });
|
||||
if (!!root) return;
|
||||
const password = config.Server.rootPassword ?? generateBase64Password();
|
||||
const hash = await hashText(password);
|
||||
const user = await this.pg.user.create({ data: { projectId, username: "root", email: "root", hash, rolePolicyId } });
|
||||
return { ...user, password };
|
||||
}
|
||||
|
||||
async create(options: CUserContract["Create"]) {
|
||||
const { hash, projectId, rolePolicyId } = options;
|
||||
const username = options.username?.toLowerCase();
|
||||
const email = options.email?.toLowerCase() ?? undefined;
|
||||
const [existingUsername, existingEmail] = await Promise.all([
|
||||
this.byUsername(username, projectId),
|
||||
!!email ? this.byEmail(email, projectId) : undefined,
|
||||
]);
|
||||
if (!existingUsername || !existingEmail) throw UserErrors.ConflictIdentityTaken;
|
||||
const userData = { projectId, username, email, hash, rolePolicyId };
|
||||
return this.pg.user.create({ data: userData, include: { rolePolicy: true } });
|
||||
}
|
||||
|
||||
async byIdentity(projectIdentity: string, identity: string) {
|
||||
const username = identity.toLowerCase();
|
||||
const email = identity.toLowerCase();
|
||||
const OrUser = { OR: [{ username }, { email }] };
|
||||
const OrProject = { project: { OR: [{ id: projectIdentity }, { slug: projectIdentity }] } };
|
||||
const projectInclude = { include: { keyPairs: { where: { usage: KeyPairType.UserToken } } } };
|
||||
const AND = [OrUser, OrProject];
|
||||
return this.pg.user.findFirst({ where: { AND }, include: { rolePolicy: true, project: projectInclude } });
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue