Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore #13

Merged
merged 40 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2a5148e
docs: readme badge
Mar 19, 2024
015d6ca
ci: versions upgrade
Mar 20, 2024
a518e0d
feat: entity id exists validator
Mar 22, 2024
5028f91
feat: endpoint and auth implementation
Mar 24, 2024
f4cf280
refactor: remove plugin module
Mar 26, 2024
7a2cc4e
refactor: move plugin folder
Mar 26, 2024
f2f1c9b
feat: botlet api actions module
Apr 1, 2024
4e30056
refactor: plugin to adaptor
Apr 1, 2024
9772a95
refactor: endpoints module
Apr 1, 2024
a7b79b0
test: botlet endpoints test cases
Apr 1, 2024
7a871de
feat: rest client controller
Apr 1, 2024
cdb1d1d
test: test config
Apr 2, 2024
2aad5f3
fix: bypass tenancy compatable on win
Apr 2, 2024
9c481bd
test: test case pass, add a new rest-api server endpoint
Apr 2, 2024
ca64f6b
feat: ai agents module
Apr 4, 2024
2140d48
feat: task-actions module
Apr 9, 2024
61f7ffa
refactor: rename botlet-api-actions to botlet-methods
Apr 10, 2024
ce538da
feat: task action executions
Apr 11, 2024
5af8825
feat: llm services
Apr 13, 2024
86681c4
feat: invocation multi-botlets
Apr 17, 2024
febf27d
refactor: botlet-methods => botlet-functions
Apr 17, 2024
2bfd732
refactor: botlet-methods => botlet-functions
Apr 17, 2024
1e84387
feat: single invocation execution
Apr 19, 2024
e4ed3a3
feat: progressive event
Apr 22, 2024
d1ef18c
feat: persisted-async, event-listener
Apr 24, 2024
4bc3e1d
test: prisma log, task-action case
Apr 25, 2024
245012b
feat: x-botlet-authorization
Apr 26, 2024
02f507e
feat: x-botlet-authorization
Apr 27, 2024
3bd1421
feat: event store module
Apr 29, 2024
fc1f7d4
feat: persistent-event processing
Apr 30, 2024
181b495
test: restAPI invoke test
May 5, 2024
2ff9563
fix: logs
May 9, 2024
128201c
fix: ms import
May 9, 2024
56154b5
ci: dockerfile
May 10, 2024
1e577cd
docs(devlog): add x-botlet-* to system headers
May 10, 2024
9a5cef2
feat: validation/rest-pwd email
May 20, 2024
3d0f8b0
feat: validation/rest-pwd email
May 20, 2024
7e6b9dd
ci: rename a env var
dev-callgent May 23, 2024
9357bbe
fix: code scanning
dev-callgent May 23, 2024
bd08836
fix: code scanning
dev-callgent May 23, 2024
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
13 changes: 12 additions & 1 deletion .env.dev
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
TZ=Etc/Universal
SITE_ROOT_DOMAIN=localhost
SITE_API_URL=http://${SITE_ROOT_DOMAIN}:3000/api
FRONTEND_SITE_URL=http://${SITE_ROOT_DOMAIN}:3030

LOG_LEVEL=debug
# default to stdout
Expand All @@ -25,7 +26,7 @@ DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/$
DEFAULT_API_VERSION='1'
DOCUMENTATION_VERSION='0.0.1' # DEV_ONLY no doc/test data generated if null

JWT_COOKIE_NAME=jwt # empty means no cookie
JWT_COOKIE_NAME=x-botlet-jwt # empty means no cookie
# NOT_HTTP_ONLY=1
JWT_SECRET='DENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secret'
JWT_EXPIRES_IN='1y'
Expand All @@ -38,3 +39,13 @@ GITHUB_OAUTH_CLIENT_SECRET = 76dc98b5554636b5c924cd4854ad3f62e4f0cc3b
# need scope to get user email
GOOGLE_OAUTH_CLIENT_ID = 183236050953-rg2khnknb0tif2a5urepho0k22call16.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET = 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

LLM_MODEL=mistralai/mistral-small
LLM_CACHE_ENABLE=1
OPENROUTER_API_KEY=sk-or-v1-your-api-key
BOTLET_SITE_URL=https://botlet.io
BOTLET_SITE_NAME=Botlet.IO

EMAIL_DEFAULT_SENDER={"name": "Callgent", "email": "noreply@callgent.com"}
# https://app.brevo.com/settings/keys/api
EMAIL_BREVO_API_KEY=key
15 changes: 14 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
TZ=Etc/Universal
SITE_ROOT_DOMAIN=localhost
SITE_API_URL=http://${SITE_ROOT_DOMAIN}:3000/api
FRONTEND_SITE_URL=http://${SITE_ROOT_DOMAIN}:3030

LOG_LEVEL=debug
# default to stdout
Expand All @@ -24,7 +25,7 @@ DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/$
DEFAULT_API_VERSION='1'
DOCUMENTATION_VERSION='0.0.1' # DEV_ONLY no doc/test data generated if null

JWT_COOKIE_NAME=jwt # empty means no cookie
JWT_COOKIE_NAME=x-botlet-jwt # empty means no cookie
# NOT_HTTP_ONLY=1
JWT_SECRET='DENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secretDENGER-NON-production-jwt-secret'
JWT_EXPIRES_IN='1y'
Expand All @@ -37,3 +38,15 @@ GITHUB_OAUTH_CLIENT_SECRET = 76dc98b5554636b5c924cd4854ad3f62e4f0cc3b
# need scope to get user email
GOOGLE_OAUTH_CLIENT_ID = 183236050953-rg2khnknb0tif2a5urepho0k22call16.apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET = 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

TEST_CANNY_IO_API_KEY=567b9c2f-9708-3ccb-4189-e0a5f4e130ba

LLM_MODEL=mistralai/mistral-small
LLM_CACHE_ENABLE=1
OPENROUTER_API_KEY=sk-or-v1-your-api-key
BOTLET_SITE_URL=https://botlet.io
BOTLET_SITE_NAME=Botlet.IO

EMAIL_DEFAULT_SENDER={"name": "Callgent", "email": "noreply@callgent.com"}
# https://app.brevo.com/settings/keys/api
EMAIL_BREVO_API_KEY=key
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ contact_links:
# url: https://forum.botlet.io
# about: Please ask and answer questions on the community forums.
- name: Join the Community Discord
url: https://discord.botlet.io
url: https://botlet.io/discord
about: Come and chat with other community members!
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ lerna-debug.log*

# OS
.env
.env.prod*
.DS_Store
.coverage-e2e

Expand Down
100 changes: 99 additions & 1 deletion DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
npx prisma db seed # init db data
```

- init db test data

```shell
SEED_TEST_DATA=1 npx prisma db seed
```

- start server

```shell
Expand Down Expand Up @@ -113,6 +119,56 @@ all validation is based on bearer token, with payload:
}
```

##### change `authorization` to `x-botlet-authorization` header

extract token from header:

```typescript
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
// ExtractJwt.fromAuthHeaderAsBearerToken(), // For bearer token
(request) => request?.headers['x-botlet-authorization'],
})}
```

###### swagger support

1. config swagger

```typescript
const config = new DocumentBuilder()
// ...
// .addBearerAuth(schema, 'defaultBearerAuth')
.addSecurity('defaultBearerAuth', {
type: 'apiKey',
in: 'header',
name: 'x-botlet-authorization',
})
.build();

```

2. set default token for API test

```typescript
const devJwtToken = 'test-jwt-token';
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs/api', app, document, {
swaggerOptions: {
authAction: {
defaultBearerAuth: {
schema: { type: 'apiKey', in: 'header' },
value: devJwtToken,
},
},
},
});
```

3. On controller, change from `@ApiBearerAuth('defaultBearerAuth')` to `@ApiSecurity('defaultBearerAuth')`.

#### local auth

login with email and password, TODO: email verification is required.
Expand All @@ -121,7 +177,7 @@ login with email and password, TODO: email verification is required.

need NOT scope to get user email:

- google: add 'https://www.googleapis.com/auth/userinfo.email' scope on oauth screen <https://console.cloud.google.com/apis/credentials/consent/edit?hl=zh-cn&project=skilled-bonus-381610>
- google: add '<https://www.googleapis.com/auth/userinfo.email>' scope on oauth screen <https://console.cloud.google.com/apis/credentials/consent/edit?hl=zh-cn&project=skilled-bonus-381610>
- github, contains email by default, <https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user>
- facebook?: add 'email' scope on oauth screen <https://developers.facebook.com/docs/facebook-login/permissions/overview>

Expand All @@ -138,3 +194,45 @@ need NOT scope to get user email:
3. add provider config in `oauth-client.module.ts`
4. add user info retrieval logic in `AutLoginListener#retrieveUserInfoFromOauth()`
5. finally, `/api/auth/{PROVIDER_KEY}` is ready to use

### `EntityIdExists` validator

We defined a parameter validator `EntityIdExists` to check if an entity exists in the database.

#### Validation on DTO

You may annotate it to your DTO property like this:

```typescript
export class CreateTaskDto {
// ...

// automatically validation check if the botlet exists in db on controller requesting
@EntityIdExists('botlet', 'uuid') // @EntityIdExists('entityType', 'fieldName')
botletUuid: string;
}
```

#### Validation on prisma generated DTO

Based on `prisma-generator-nestjs-dto`, you may also annotate this decorator in `schema.prisma` file:

```prisma
model Task {
// ...
/// @CustomValidator(EntityIdExists, 'botlet', 'uuid', ../../infra/repo/validators/entity-exists.validator)
botletUuid String @db.VarChar(36)
}
```

This makes the generated DTO to be annotated with `@EntityIdExists` decorator.

#### Retrieves the entity instance

This makes sure the `botletUuid` field is a valid UUID of a botlet in the database.
you can retrieve the entity instance directly from the dto:

```typescript
const botlet = EntityIdExists.entity<Botlet>(dto, 'botletUuid') ||
(await prisma.botlet.findUnique({ where: {uuid: dto.botletUuid} }));
```
13 changes: 3 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,16 @@ WORKDIR /app

# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/

RUN npm install -g pnpm
# Install app dependencies
RUN npm install -g pnpm
RUN pnpm install

COPY . .

RUN pnpm run build
# Generate Prisma client using the Prisma CLI.
RUN npx prisma generate

FROM node:18.19.1
RUN npm install -g pnpm

COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
RUN pnpm run build

EXPOSE 3000
CMD [ "pnpm", "run", "start:prod" ]
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Botlet API

<p align="center"><a href="#license">
<p align="center">
<a href="https://botlet.io" target="_blank">
<img alt="Static Badge" src="https://img.shields.io/badge/IO-IO?logo=IO&logoColor=%20%23f5f5f5&label=Botlet&labelColor=%20%23155EEF&color=%23EAECF0"></a>
<a href="https://discord.gg/V9HKBukSRp" target="_blank">
<img src="https://img.shields.io/discord/1215998670265127102?logo=discord"
alt="chat on Discord"></a>
<a href="https://twitter.com/intent/follow?screen_name=callgent_com" target="_blank">
<img src="https://img.shields.io/twitter/follow/callgent_com?style=social&logo=X"
alt="follow on Twitter"></a>
<!-- <a href="https://hub.docker.com/u/langgenius" target="_blank">
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/langgenius/dify-web"></a> -->
<a href="https://app.snyk.io/test/github/Botlet-IO/botlet-api" alt="FOSSA Status"><img src="https://snyk.io/test/github/Botlet-IO/botlet-api/badge.svg"/></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FBotlet-IO%2Fbotlet-api?ref=badge_shield&issueType=license" alt="FOSSA Status"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FBotlet-IO%2Fbotlet-api.svg?type=shield&issueType=license"/></a>
<a href="https://github.com/Botlet-IO/botlet-api/issues">
Expand All @@ -10,7 +20,7 @@
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square" height="20px">
</p>

Botlet API is an open-source SaaS project built using NestJS, Prisma, PostgreSQL, and Swagger. It is licensed under the Apache-2.0 License and based on the Contributor Development Agreement (CDO).
Botlet API is an open-source SaaS project built using NestJS, Prisma, PostgreSQL, and Swagger. It is licensed under the Apache-2.0 License.

## Table of Contents

Expand Down Expand Up @@ -111,4 +121,4 @@ Botlet.IO is licensed under the Apache-2.0 License. See the [LICENSE](LICENSE) f

---

For more information, please visit our [documentation](https://docs.botlet.io/) or join our [community](https://discord.botlet.io/).
For more information, please visit our [documentation](https://docs.botlet.io/) or join our [community](https://botlet.io/discord).
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = { extends: ['@commitlint/config-angular'] };
module.exports = { extends: ['@commitlint/config-angular'] };
1 change: 1 addition & 0 deletions nest-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"assets": [ { "include": "**/*.dot", "watchAssets": true } ],
"plugins": [ {
"name": "@nestjs/swagger/plugin",
"options": {
Expand Down
24 changes: 13 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@
"release": "standard-version"
},
"dependencies": {
"@automapper/classes": "^8.8.1",
"@automapper/core": "^8.8.1",
"@automapper/nestjs": "^8.8.1",
"@apidevtools/json-schema-ref-parser": "^11.5.4",
"@fastify/compress": "^7.0.0",
"@fastify/cookie": "^9.3.1",
"@fastify/cors": "^9.0.1",
"@fastify/helmet": "^11.1.1",
"@fastify/static": "^6.12.0",
Expand All @@ -45,23 +44,24 @@
"@nestjs/event-emitter": "^2.0.3",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-fastify": "^10.3.3",
"@nestjs/swagger": "^7.2.0",
"@nestjs/platform-fastify": "^10.3.4",
"@nestjs/swagger": "^7.3.0",
"@nodeteam/nestjs-prisma-pagination": "^1.0.6",
"@prisma/client": "5.10.2",
"axios": "^1.6.7",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"fastify": "4.26.1",
"fastify-cookie": "^5.7.0",
"dot": "^1.1.3",
"fastify": "4.26.2",
"fastify-ip": "^1.0.0",
"glob": "^10.3.10",
"jsonrepair": "^3.7.0",
"module": "^1.2.5",
"ms": "^2.1.3",
"nanoid": "3",
"nestjs-cls": "^4.2.0",
"nestjs-oauth2": "^0.0.31",
"nestjs-oauth2": "^0.0.7",
"nestjs-pino": "^4.0.0",
"nestjs-prisma": "^0.23.0",
"passport-jwt": "^4.0.1",
Expand All @@ -70,8 +70,9 @@
"pino-http": "^9.0.0",
"pino-pretty": "^10.3.1",
"prisma-soft-delete-middleware": "^1.3.1",
"reflect-metadata": "^0.2.1",
"rxjs": "^7.8.1"
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"v8-sandbox": "^3.2.10"
},
"devDependencies": {
"@chax-at/transactional-prisma-testing": "^1.1.0",
Expand All @@ -82,6 +83,7 @@
"@nestjs/testing": "^10.0.0",
"@pond918/prisma-generator-nestjs-dto": "^1.17.513",
"@types/bcrypt": "^5.0.2",
"@types/dot": "^1.1.7",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/ms": "^0.7.34",
Expand All @@ -98,7 +100,7 @@
"eslint-plugin-prettier": "^5.0.0",
"husky": "^9.0.11",
"jest": "^29.5.0",
"pactum": "^3.6.1",
"pactum": "^3.6.3",
"pactum-matchers": "^1.1.6",
"prettier": "^3.0.0",
"prisma": "^5.10.2",
Expand Down
Loading
Loading