Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
branches-ignore:
- main

concurrency:
group: docker-build-pr-${{ github.event.number }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
Expand Down
12 changes: 4 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
branches:
- 'main'

concurrency:
group: registry-push-${{ github.ref }}
cancel-in-progress: true

jobs:
docker:
runs-on: ubuntu-latest
Expand All @@ -23,7 +27,6 @@ jobs:
with:
images: |
ghcr.io/import-ai/omnibox-backend
registry.cn-hangzhou.aliyuncs.com/import-ai/omnibox-backend
tags: |
type=ref,event=branch
type=ref,event=pr
Expand All @@ -44,13 +47,6 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Login to AliYun Registry
uses: docker/login-action@v3
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALIYUN_DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.ALIYUN_DOCKER_REGISTRY_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v6
with:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
+ Watch & Debug mode

```shell
docker compose -f compose.yaml -f dev.yaml up -d
docker compose -f base.yaml -f dev.yaml up -d
```

+ Build mode

```shell
docker compose -f compose.yaml -f build.yaml up -d --build
docker compose -f base.yaml -f build.yaml up -d --build
```

+ Run with persistence postgres and minio data
Expand Down
3 changes: 2 additions & 1 deletion compose.yaml → base.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: omnibox-backend-dev
name: omnibox-local

services:
backend:
Expand All @@ -9,6 +9,7 @@ services:
environment:
OBB_DB_HOST: postgres
OBB_MINIO_ENDPOINT: http://username:password@minio:9000
OBB_WIZARD_BASE_URL: http://wizard:8000
depends_on:
postgres:
condition: service_healthy
Expand Down
2 changes: 1 addition & 1 deletion build.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker compose -f compose.yaml -f build.yaml up --build
# docker compose -f base.yaml -f build.yaml up --build
services:
backend:
build: .
2 changes: 1 addition & 1 deletion dev.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# docker compose -f compose.yaml -f dev.yaml up
# docker compose -f base.yaml -f dev.yaml up
services:
backend:
image: node:22
Expand Down
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
// import { CacheModule } from '@nestjs/cache-manager';
import { AuthModule } from 'src/auth/auth.module';
import { UserModule } from 'src/user/user.module';
import { AppController } from './app.controller';
import { AppController } from 'src/app/app.controller';
import { MailModule } from 'src/mail/mail.module';
import { TasksModule } from 'src/tasks/tasks.module';
import { WizardModule } from 'src/wizard/wizard.module';
Expand All @@ -15,6 +15,8 @@ import { SnakeCaseInterceptor } from 'src/interceptor/snake-case';
import { NamespacesModule } from 'src/namespaces/namespaces.module';
import { PermissionsModule } from 'src/permissions/permissions.module';
import { GroupsModule } from 'src/groups/groups.module';
import { ConversationsModule } from 'src/conversations/conversations.module';
import { MessagesModule } from 'src/messages/messages.module';

@Module({
controllers: [AppController],
Expand All @@ -39,6 +41,8 @@ import { GroupsModule } from 'src/groups/groups.module';
WizardModule,
GroupsModule,
PermissionsModule,
ConversationsModule,
MessagesModule,
// CacheModule.registerAsync({
// imports: [ConfigModule],
// inject: [ConfigService],
Expand Down
53 changes: 53 additions & 0 deletions src/conversations/conversations.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Controller,
Delete,
Get,
Param,
Post,
Query,
Req,
} from '@nestjs/common';
import { ConversationsService } from './conversations.service';

@Controller('api/v1/namespaces/:namespaceId/conversations')
export class ConversationsController {
constructor(private readonly conversationsService: ConversationsService) {}

@Get()
async list(
@Req() req,
@Param('namespaceId') namespaceId: string,
@Query('limit') limit?: string,
@Query('offset') offset?: string,
@Query('order') order?: string,
) {
return await this.conversationsService.findAll(namespaceId, req.user, {
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
order,
});
}

@Post()
async create(@Req() req, @Param('namespaceId') namespaceId: string) {
return await this.conversationsService.create(namespaceId, req.user);
}

@Get(':id')
async get(
@Req() req,
@Param('namespaceId') namespaceId: string,
@Param('id') id: string,
) {
return await this.conversationsService.findOne(namespaceId, id, req.user);
}

@Delete(':id')
async remove(
@Req() req,
@Param('namespaceId') namespaceId: string,
@Param('id') id: string,
) {
return await this.conversationsService.remove(namespaceId, id, req.user);
}
}
13 changes: 13 additions & 0 deletions src/conversations/conversations.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Conversation } from 'src/conversations/entities/conversation.entity';
import { ConversationsService } from 'src/conversations/conversations.service';
import { ConversationsController } from 'src/conversations/conversations.controller';

@Module({
imports: [TypeOrmModule.forFeature([Conversation])],
providers: [ConversationsService],
controllers: [ConversationsController],
exports: [ConversationsService],
})
export class ConversationsModule {}
51 changes: 51 additions & 0 deletions src/conversations/conversations.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Conversation } from 'src/conversations/entities/conversation.entity';
import { User } from 'src/user/user.entity';

@Injectable()
export class ConversationsService {
constructor(
@InjectRepository(Conversation)
private readonly conversationRepository: Repository<Conversation>,
) {}

async create(namespaceId: string, user: User) {
const conversation = this.conversationRepository.create({
namespace: { id: namespaceId },
user: { id: user.id },
});
return await this.conversationRepository.save(conversation);
}

async findAll(
namespaceId: string,
user: User,
options?: { limit?: number; offset?: number; order?: string },
) {
const query: any = {
where: { namespace: { id: namespaceId }, user: { id: user.id } },
order: {
updatedAt: options?.order?.toUpperCase() === 'ASC' ? 'ASC' : 'DESC',
},
};
if (options?.limit !== undefined) query.take = Number(options.limit);
if (options?.offset !== undefined) query.skip = Number(options.offset);
return await this.conversationRepository.find(query);
}

async findOne(namespaceId: string, id: string, user: User) {
return await this.conversationRepository.findOneOrFail({
where: { id, namespace: { id: namespaceId }, user: { id: user.id } },
});
}

async remove(namespaceId: string, id: string, user: User) {
return await this.conversationRepository.softDelete({
id,
namespace: { id: namespaceId },
user: { id: user.id },
});
}
}
7 changes: 7 additions & 0 deletions src/conversations/dto/create-conversation.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IsString, IsOptional } from 'class-validator';

export class CreateConversationDto {
@IsString()
@IsOptional()
title?: string;
}
32 changes: 32 additions & 0 deletions src/conversations/entities/conversation.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Namespace } from 'src/namespaces/entities/namespace.entity';
import { User } from 'src/user/user.entity';
import { Message } from 'src/messages/entities/message.entity';
import { Base } from 'src/common/base.entity';

@Entity('conversations')
export class Conversation extends Base {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column({ nullable: true })
title: string;

@ManyToOne(() => Namespace)
@JoinColumn({ name: 'namespace_id' })
namespace: Namespace;

@ManyToOne(() => User)
@JoinColumn({ name: 'user_id' })
user: User;

@OneToMany(() => Message, (message) => message.conversation)
messages: Message[];
}
12 changes: 12 additions & 0 deletions src/messages/dto/create-message.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsOptional, IsString } from 'class-validator';

export class CreateMessageDto {
message: Record<string, any>;

@IsOptional()
@IsString()
parentId?: string;

@IsOptional()
attrs?: Record<string, any>;
}
33 changes: 33 additions & 0 deletions src/messages/entities/message.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Conversation } from 'src/conversations/entities/conversation.entity';
import { User } from 'src/user/user.entity';
import { Base } from 'src/common/base.entity';

@Entity('messages')
export class Message extends Base {
@PrimaryGeneratedColumn('uuid')
id: string;

@ManyToOne(() => Conversation, (conversation) => conversation.messages)
@JoinColumn({ name: 'conversation_id' })
conversation: Conversation;

@ManyToOne(() => User)
@JoinColumn({ name: 'user_id' })
user: User;

@Column('uuid', { name: 'parent_id', nullable: true })
parentId?: string;

@Column('jsonb', { nullable: false })
message: Record<string, any>;

@Column('jsonb', { nullable: true })
attrs?: Record<string, any>;
}
45 changes: 45 additions & 0 deletions src/messages/messages.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Req,
} from '@nestjs/common';
import { MessagesService } from './messages.service';
import { CreateMessageDto } from './dto/create-message.dto';

@Controller(
'api/v1/namespaces/:namespaceId/conversations/:conversationId/messages',
)
export class MessagesController {
constructor(private readonly messagesService: MessagesService) {}

@Get()
async list(@Req() req, @Param('conversationId') conversationId: string) {
return await this.messagesService.findAll(req.user, conversationId);
}

@Post()
async create(
@Req() req,
@Param('conversationId') conversationId: string,
@Body() dto: CreateMessageDto,
) {
return await this.messagesService.create(conversationId, req.user, dto);
}

@Delete(':messageId')
async remove(
@Req() req,
@Param('conversationId') conversationId: string,
@Param('messageId') messageId: string,
) {
return await this.messagesService.remove(
conversationId,
messageId,
req.user,
);
}
}
Loading