Skip to content
This repository was archived by the owner on Aug 14, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion .github/workflows/deno_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
deno-version: v2.3.1

- name: Run unit tests
run: deno task test
run: deno task test
44 changes: 33 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,69 @@
[![CI/Deno Test](https://github.com/PoliEats/Backend/actions/workflows/deno_test.yml/badge.svg)](https://github.com/PoliEats/Backend/actions/workflows/deno_test.yml)

# PoliEats 🍽 - Backend

## Descrição:

O PoliEats é um chatbot desenvolvido para auxiliar os alunos, alunos e demais visitantes do colégio **Poliedro** a
fazerem pedidos de comida e bebida a partir de uma interface de chat totalmente automatizada. O bot é capaz de responder perguntas frequentes, fornecer informações sobre o cardápio e realizar pedidos de forma rápida e eficiente. O objetivo principal do PoliEats é facilitar a experiência de compra dos usuários, tornando o processo mais ágil e prático.
O PoliEats é um chatbot desenvolvido para auxiliar os alunos, alunos e demais
visitantes do colégio **Poliedro** a fazerem pedidos de comida e bebida a partir
de uma interface de chat totalmente automatizada. O bot é capaz de responder
perguntas frequentes, fornecer informações sobre o cardápio e realizar pedidos
de forma rápida e eficiente. O objetivo principal do PoliEats é facilitar a
experiência de compra dos usuários, tornando o processo mais ágil e prático.

## Funcionalidades:
- **Cardápio**: O bot fornece informações detalhadas sobre o cardápio, incluindo preços e opções disponíveis.
- **Pedidos**: Os usuários podem fazer pedidos diretamente pelo bot, que irá encaminhar as informações para a equipe responsável.
- **Perguntas Frequentes**: O bot é capaz de responder perguntas frequentes sobre o colégio, cardápio e outros assuntos relacionados.
- **Horários**: O bot fornece informações sobre os horários de funcionamento do colégio e do serviço de alimentação.

- **Cardápio**: O bot fornece informações detalhadas sobre o cardápio, incluindo
preços e opções disponíveis.
- **Pedidos**: Os usuários podem fazer pedidos diretamente pelo bot, que irá
encaminhar as informações para a equipe responsável.
- **Perguntas Frequentes**: O bot é capaz de responder perguntas frequentes
sobre o colégio, cardápio e outros assuntos relacionados.
- **Horários**: O bot fornece informações sobre os horários de funcionamento do
colégio e do serviço de alimentação.

## Tecnologias Utilizadas:

- **TypeScript**: Linguagem de programação utilizada para desenvolver o backend.
- **Deno**: Ambiente de execução para o TypeScript.
- **PostgreSQL**: Banco de dados utilizado para armazenar informações sobre o cardápio, pedidos e usuários.
- **DrizzleORM**: ORM utilizado para facilitar a interação com o banco de dados PostgreSQL.
- **Mistral AI**: Modelo de linguagem utilizado para processar as mensagens dos usuários e gerar respostas.
- **LangChain**: Biblioteca utilizada para integrar o modelo de linguagem com o bot e facilitar a construção de fluxos de conversa.
- **PostgreSQL**: Banco de dados utilizado para armazenar informações sobre o
cardápio, pedidos e usuários.
- **DrizzleORM**: ORM utilizado para facilitar a interação com o banco de dados
PostgreSQL.
- **Mistral AI**: Modelo de linguagem utilizado para processar as mensagens dos
usuários e gerar respostas.
- **LangChain**: Biblioteca utilizada para integrar o modelo de linguagem com o
bot e facilitar a construção de fluxos de conversa.

## Como executar o projeto:

1. Clone o repositório:

```bash
git clone https://github.com/PoliEats/Backend.git
cd Backend
```

2. Instale as dependências:

```bash
deno install
```

3. Configure o .env:

```bash
cp .env.example .env
```

4. Configure o banco de dados:

```bash
deno task db:migrate
```

5. Execute o projeto:

```bash
deno task start
```
```
7 changes: 6 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@
"nodeModulesDir": "auto",
"unstable": [
"sloppy-imports"
]
],
"fmt": {
"options": {
"indentWidth": 2
}
}
}
16 changes: 12 additions & 4 deletions src/database/MockDatabase.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// deno-lint-ignore-file
import { PgTableWithColumns } from "drizzle-orm/pg-core/table";
import { InferInsertModel, InferSelectModel, TableConfig } from "drizzle-orm/table";
import {
InferInsertModel,
InferSelectModel,
TableConfig,
} from "drizzle-orm/table";
import { IDatabase } from "../interfaces/IDatabase.ts";

export class MockDatabase implements IDatabase {
constructor() {
constructor() {
this.data = new Map<string | number, any>();
}

Expand Down Expand Up @@ -81,7 +85,11 @@ export class MockDatabase implements IDatabase {
return Promise.resolve(results);
}

selectByField<T extends TableConfig>(table: PgTableWithColumns<T>, field: keyof InferSelectModel<PgTableWithColumns<T>>, value: string | number): Promise<InferSelectModel<PgTableWithColumns<T>>[]> {
selectByField<T extends TableConfig>(
table: PgTableWithColumns<T>,
field: keyof InferSelectModel<PgTableWithColumns<T>>,
value: string | number,
): Promise<InferSelectModel<PgTableWithColumns<T>>[]> {
const results: InferSelectModel<PgTableWithColumns<T>>[] = [];
this.data.forEach((record) => {
if (record.table === table && record[field] === value) {
Expand All @@ -90,4 +98,4 @@ export class MockDatabase implements IDatabase {
});
return Promise.resolve(results);
}
}
}
36 changes: 18 additions & 18 deletions src/database/schema.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

export const user = pgTable("user", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
document: text("document").notNull().unique(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
})
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
document: text("document").notNull().unique(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
});

export const salt = pgTable("salt", {
id: serial("id").primaryKey(),
userId: serial("user_id").references(() => user.id),
salt: text("salt").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
id: serial("id").primaryKey(),
userId: serial("user_id").references(() => user.id),
salt: text("salt").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
});

export const password = pgTable("password", {
id: serial("id").primaryKey(),
userId: serial("user_id").references(() => user.id),
password: text("password").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
});
id: serial("id").primaryKey(),
userId: serial("user_id").references(() => user.id),
password: text("password").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().$onUpdate(() => new Date()),
});
2 changes: 1 addition & 1 deletion src/interfaces/IDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ export interface IDatabase {
field: keyof InferSelectModel<PgTableWithColumns<T>>,
value: string | number,
): Promise<InferSelectModel<PgTableWithColumns<T>>[]>;
}
}
16 changes: 8 additions & 8 deletions src/interfaces/IUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
* User Interface
* This interface defines the structure of a user object.
* It includes properties such as id, name, email, document, createdAt, and updatedAt.
*/
*/

export interface IUser {
id: number;
name: string;
email: string;
document: string;
createdAt: Date;
updatedAt: Date;
}
id: number;
name: string;
email: string;
document: string;
createdAt: Date;
updatedAt: Date;
}
6 changes: 3 additions & 3 deletions src/lc/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,22 @@ export const createOrder = tool(

const isConfirmed = await new Promise((resolve) => {
pendingConfirmation.set(newOrder.id, { resolve });

setTimeout(() => {
if (pendingConfirmation.has(newOrder.id)) {
pendingConfirmation.delete(newOrder.id);
resolve(false);
}
}, 30000);
})
});

if (isConfirmed) {
orders.push(newOrder);

io.emit(
"new_order",
JSON.stringify({
newOrder
newOrder,
}),
);

Expand Down
13 changes: 10 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const db = new MockDatabase();
const authenticationService = new AuthenticationService(db);
const JWTmiddleware = new ValidateJWT(authenticationService);

app.use(cookieParser())
app.use(cookieParser());
app.use(express.json());

app.use(cors({
Expand All @@ -37,12 +37,19 @@ app.post("/auth/register", async (req, res) => {
const { name, email, document, password } = req.body;
try {
const userId = await authenticationService.registerUser(
{ id: 123, name, email, document, createdAt: new Date(), updatedAt: new Date() },
{
id: 123,
name,
email,
document,
createdAt: new Date(),
updatedAt: new Date(),
},
password,
);

const token = await authenticationService.createJWT(userId);

res.cookie("token", token, {
httpOnly: true,
secure: false,
Expand Down
42 changes: 21 additions & 21 deletions src/middlewares/ValidateJWT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@ import { NextFunction, Request, Response } from "express";
import { AuthenticationService } from "../services/AuthenticationService.ts";

export class ValidateJWT {
private authService: AuthenticationService;
constructor(authService: AuthenticationService) {
this.authService = authService;
this.validateToken = this.validateToken.bind(this);
}
private authService: AuthenticationService;

constructor(authService: AuthenticationService) {
this.authService = authService;
this.validateToken = this.validateToken.bind(this);
}

async validateToken(req: Request, res: Response, next: NextFunction) {
const token = req.cookies.token;

if (!token) {
res.status(401).json({ error: "Unauthorized" });
return;
}
async validateToken(req: Request, res: Response, next: NextFunction) {
const token = req.cookies.token;

await this.authService.verifyJWT(token)
.then(() => {
next();
})
.catch(() => {
res.status(401).json({ error: "Invalid token" });
});
if (!token) {
res.status(401).json({ error: "Unauthorized" });
return;
}
}

await this.authService.verifyJWT(token)
.then(() => {
next();
})
.catch(() => {
res.status(401).json({ error: "Invalid token" });
});
}
}
40 changes: 20 additions & 20 deletions src/mocks/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@ import { Faker, pt_BR } from "@faker-js/faker";
import { IUser } from "../interfaces/IUser.ts";

export function generateMockUser(): IUser {
const faker = new Faker({
locale: pt_BR,
})
return {
id: faker.number.int({ min: 1, max: 1000 }),
name: faker.person.fullName(),
email: faker.internet.email(),
document: faker.string.numeric(11),
createdAt: faker.date.past(),
updatedAt: faker.date.recent(),
const faker = new Faker({
locale: pt_BR,
});

return {
id: faker.number.int({ min: 1, max: 1000 }),
name: faker.person.fullName(),
email: faker.internet.email(),
document: faker.string.numeric(11),
createdAt: faker.date.past(),
updatedAt: faker.date.recent(),
};
}

export function generateMockPassword(): string {
const faker = new Faker({
locale: pt_BR,
})
return faker.internet.password({
length: 8,
memorable: true,
});
}
const faker = new Faker({
locale: pt_BR,
});

return faker.internet.password({
length: 8,
memorable: true,
});
}
17 changes: 17 additions & 0 deletions src/schemas/zodSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import z from "zod";

export const userSchema = z.object({
id: z.number().describe("ID do usuário").optional(),
name: z.string().describe("Nome do usuário"),
email: z.string().email().describe("Email do usuário"),
document: z.string().refine(
(value) => {
const regex = /^\d{3}.?\d{3}.?\d{3}\-?\d{2}$/;
return regex.test(value);
},
).transform((value) => value.replace(/\D/g, "")).describe(
"Documento do usuário",
),
createdAt: z.date().describe("Data de criação do usuário"),
updatedAt: z.date().describe("Data de atualização do usuário"),
});
Loading