mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: initial commit
This commit is contained in:
55
api/src/cms/schemas/content-type.schema.ts
Normal file
55
api/src/cms/schemas/content-type.schema.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class ContentType extends BaseSchema {
|
||||
/**
|
||||
* The name the content type.
|
||||
*/
|
||||
@Prop({ type: String, required: true, unique: true })
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* The way this type is defined and is presented.
|
||||
*/
|
||||
|
||||
@Prop({
|
||||
type: mongoose.Schema.Types.Mixed,
|
||||
default: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: 'Status',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
})
|
||||
fields?: {
|
||||
name: string;
|
||||
label: string;
|
||||
type: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const ContentTypeModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
name: ContentType.name,
|
||||
schema: SchemaFactory.createForClass(ContentType),
|
||||
});
|
||||
|
||||
export default ContentTypeModel.schema;
|
||||
106
api/src/cms/schemas/content.schema.ts
Normal file
106
api/src/cms/schemas/content.schema.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import mongoose, { Document } from 'mongoose';
|
||||
|
||||
import { config } from '@/config';
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
|
||||
import { ContentType } from './content-type.schema';
|
||||
|
||||
export type ContentDocument = Document<Content>;
|
||||
|
||||
@Schema({ timestamps: true, strict: false })
|
||||
export class ContentStub extends BaseSchema {
|
||||
/**
|
||||
* The content type of this content.
|
||||
*/
|
||||
@Prop({
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'ContentType',
|
||||
})
|
||||
entity: unknown;
|
||||
|
||||
/**
|
||||
* The title of the content.
|
||||
*/
|
||||
@Prop({ type: String, required: true })
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Either of not this content is active.
|
||||
*/
|
||||
@Prop({ type: Boolean, default: true })
|
||||
status?: boolean;
|
||||
|
||||
@Prop({ type: mongoose.Schema.Types.Mixed })
|
||||
dynamicFields?: Record<string, any>;
|
||||
|
||||
@Prop({ type: String })
|
||||
rag?: string;
|
||||
|
||||
/**
|
||||
* Helper to return the internal url of this content.
|
||||
*/
|
||||
static getUrl(item: Content): string {
|
||||
return new URL('/content/view/' + item.id, config.apiPath).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that returns the relative chatbot payload for this content.
|
||||
*/
|
||||
static getPayload(item: Content): string {
|
||||
return 'postback' in item ? (item.postback as string) : item.title;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Content extends ContentStub {
|
||||
@Transform(({ obj }) => obj.entity.toString())
|
||||
entity: string;
|
||||
|
||||
static flatDynamicFields(element: Content) {
|
||||
Object.entries(element.dynamicFields).forEach(([key, value]) => {
|
||||
element[key] = value;
|
||||
});
|
||||
element.dynamicFields = undefined;
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class ContentFull extends ContentStub {
|
||||
@Type(() => ContentType)
|
||||
entity: ContentType;
|
||||
}
|
||||
|
||||
const ContentSchema = SchemaFactory.createForClass(ContentStub);
|
||||
ContentSchema.index(
|
||||
{
|
||||
title: 'text',
|
||||
rag: 'text',
|
||||
},
|
||||
{
|
||||
weights: {
|
||||
title: 2,
|
||||
rag: 1,
|
||||
},
|
||||
background: false,
|
||||
},
|
||||
);
|
||||
|
||||
export const ContentModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
name: Content.name,
|
||||
schema: ContentSchema,
|
||||
});
|
||||
|
||||
export default ContentModel.schema;
|
||||
80
api/src/cms/schemas/menu.schema.ts
Normal file
80
api/src/cms/schemas/menu.schema.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
|
||||
import { MenuType } from './types/menu';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class MenuStub extends BaseSchema {
|
||||
/**
|
||||
* The displayed title of the menu.
|
||||
*/
|
||||
@Prop({ isRequired: true, type: String })
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* If this menu item is part of an other nested menu (parent), this will indicate that parent.
|
||||
*/
|
||||
@Prop({
|
||||
type: MongooseSchema.Types.ObjectId,
|
||||
ref: 'Menu',
|
||||
isRequired: false,
|
||||
})
|
||||
parent?: unknown;
|
||||
|
||||
/**
|
||||
* Type of the menu item, one of: web_url, postback, nested.
|
||||
*/
|
||||
@Prop({ type: String, enum: Object.values(MenuType), required: true })
|
||||
type: MenuType;
|
||||
|
||||
/**
|
||||
* The content of the payload, if the menu item type is postback.
|
||||
*/
|
||||
@Prop({ type: String })
|
||||
payload?: string;
|
||||
|
||||
/**
|
||||
* The url if the menu item is web_url.
|
||||
*/
|
||||
@Prop({ type: String, validate: (url: string | URL) => !!new URL(url) })
|
||||
url?: string;
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Menu extends MenuStub {
|
||||
@Transform(({ obj }) => obj.parent?.toString())
|
||||
parent?: string;
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class MenuFull extends MenuStub {
|
||||
@Type(() => Menu)
|
||||
parent: Menu;
|
||||
}
|
||||
|
||||
export type MenuDocument = THydratedDocument<Menu>;
|
||||
|
||||
// This is an optional additional validation step to enforce the data structure
|
||||
// this function relies on two assumptions,
|
||||
// 1. if a path is changed during an update or creation, the new value is already validated
|
||||
// 2. if a document is created, it is already validated: the properties are already set
|
||||
|
||||
export const MenuModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
name: Menu.name,
|
||||
schema: SchemaFactory.createForClass(MenuStub),
|
||||
});
|
||||
|
||||
export default MenuModel.schema;
|
||||
48
api/src/cms/schemas/types/menu.ts
Normal file
48
api/src/cms/schemas/types/menu.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { MenuStub, Menu } from '../menu.schema';
|
||||
|
||||
export enum MenuType {
|
||||
web_url = 'web_url',
|
||||
postback = 'postback',
|
||||
nested = 'nested',
|
||||
}
|
||||
interface MenuAttrs {
|
||||
type: MenuType;
|
||||
payload?: unknown;
|
||||
url?: unknown;
|
||||
}
|
||||
|
||||
export interface NestedMenuAttrs {
|
||||
type: MenuType.nested;
|
||||
payload?: never;
|
||||
url?: never;
|
||||
}
|
||||
|
||||
export interface PostbackMenuAttrs {
|
||||
type: MenuType.postback;
|
||||
payload: string;
|
||||
url?: never;
|
||||
}
|
||||
|
||||
export interface WebUrlMenuAttrs {
|
||||
type: MenuType.web_url;
|
||||
payload?: never;
|
||||
url: string;
|
||||
}
|
||||
|
||||
type AnyMenuAttrs = NestedMenuAttrs | PostbackMenuAttrs | WebUrlMenuAttrs;
|
||||
|
||||
export type AnyMenu<T extends MenuStub = Menu> = Omit<T, keyof MenuAttrs> &
|
||||
AnyMenuAttrs;
|
||||
|
||||
export type MenuTree = (AnyMenu<Menu> & {
|
||||
call_to_actions?: MenuTree;
|
||||
})[];
|
||||
Reference in New Issue
Block a user