Skip to content

Commit

Permalink
Merge pull request #30 from Jonhvmp/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Jonhvmp authored Dec 16, 2024
2 parents 16a7940 + e36e566 commit 37aaf1b
Show file tree
Hide file tree
Showing 18 changed files with 482 additions and 189 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages

name: Node.js package

on:
release:
types: [created]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
- run: npm ci
- run: npm test

publish-npm:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

publish-gpr:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
registry-url: $registry-url(npm)
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
2 changes: 1 addition & 1 deletion backend/package-lock.json

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

2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dependencies": {
"@types/bcrypt": "^5.0.2",
"@types/validator": "^13.12.2",
"axios": "^1.7.7",
"axios": "^1.7.9",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
Expand Down
7 changes: 6 additions & 1 deletion backend/src/controllers/authController.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Request, Response, NextFunction } from 'express';
import { registerUserService, loginUserService, forgotPasswordService, resetPasswordService } from '../services/auth/authService';
import {
registerUserService,
loginUserService,
forgotPasswordService,
resetPasswordService
} from '../services/auth/authService';
import { handleValidationError } from '../utils/validationUtils';

export const registerUser = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
Expand Down
66 changes: 66 additions & 0 deletions backend/src/controllers/categoryController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Request, Response, NextFunction } from 'express';
import * as categoryService from '../services/snnipets/categoryService';

export const createCategory = async (req: Request, res: Response, next: NextFunction) => {
try {
const { name, description } = req.body;
const userId = req.user?.id;

if (!userId) {
return res.status(401).json({ message: 'Usuário não autenticado.' });
}

const category = await categoryService.createNewCategory(name, description, userId);
res.status(201).json(category);
} catch (error) {
next(error);
}
};

export const getCategories = async (req: Request, res: Response, next: NextFunction) => {
try {
const userId = req.user?.id;

if (!userId) {
return res.status(401).json({ message: 'Usuário não autenticado.' });
}

const categories = await categoryService.getUserCategories(userId);
res.json(categories);
} catch (error) {
next(error);
}
};

export const updateCategory = async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const { name, description } = req.body;
const userId = req.user?.id;

if (!userId) {
return res.status(401).json({ message: 'Usuário não autenticado.' });
}

const category = await categoryService.updateCategory(id, name, description, userId);
res.json(category);
} catch (error) {
next(error);
}
};

export const deleteCategory = async (req: Request, res: Response, next: NextFunction) => {
try {
const { id } = req.params;
const userId = req.user?.id;

if (!userId) {
return res.status(401).json({ message: 'Usuário não autenticado.' });
}

const result = await categoryService.removeCategory(id, userId);
res.json(result);
} catch (error) {
next(error);
}
};
168 changes: 27 additions & 141 deletions backend/src/controllers/snippetController.ts
Original file line number Diff line number Diff line change
@@ -1,204 +1,106 @@
// src/controllers/snippetController.ts

import { Request, Response, NextFunction } from 'express';
import { Snippet } from '../models/Snippet';
import { GitHubApiService } from '../services/github/GitHubApiService';
import { v4 as uuidv4 } from 'uuid';
// import { validationResult } from 'express-validator';
import * as snippetService from '../services/snnipets/snippetService';
import { handleValidationError } from '../utils/validationUtils';
import { Snippet } from '../models/Snippet';

// Cria um novo snippet
export const createSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const { title, description, language, tags, code } = req.body;

// Validações adicionais de segurança
if (typeof code !== 'string' || code.length === 0) {
return handleValidationError(res, 'Código inválido ou ausente.');
}

if (!req.user) {
return handleValidationError(res, 'Usuário não autenticado.');
}

const snippet = new Snippet({
title,
description,
language,
tags,
code,
favorite: false,
user: req.user.id,
});

if (snippet.title === '' || snippet.title === undefined || snippet.title === null) { // Adiciona um título padrão
snippet.title = 'Snippet sem título';
}

if (snippet.description === '' || snippet.description === undefined || snippet.description === null) {
snippet.description = 'Snippet sem descrição';
}

if (snippet.language === '' || snippet.language === undefined || snippet.language === null) {
snippet.language = 'text';
}

if (snippet.tags === '' as unknown as Array<any> || snippet.tags === undefined || snippet.tags === null) {
snippet.tags = []; // nesse caso essa array é vazia
}

if (snippet.code === '' || snippet.code === undefined || snippet.code === null) {
return handleValidationError(res, 'Código inválido ou ausente.');
}

await snippet.save();
if (!req.user) throw new Error('Usuário não autenticado.');
const snippet = await snippetService.createNewSnippet(req.body, req.user.id);
res.status(201).json(snippet);
} catch (error) {
console.error('Erro ao criar snippet:', error);
next(error); // Propaga o erro para o middleware de tratamento de erros
next(error);
}
};

// Busca todos os snippets do usuário
export const fetchMySnippets = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.user) {
return handleValidationError(res, 'Usuário não autenticado.');
}
const snippets = await Snippet.find({ user: req.user.id });
if (!req.user) throw new Error('Usuário não autenticado.');
const snippets = await snippetService.getUserSnippets(req.user.id);
res.json(snippets);
} catch (error) {
console.error('Erro ao buscar snippets do usuário:', error);
next(error);
}
};

// Retorna um snippet específico pelo ID
export const getSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const snippet = await Snippet.findById(req.params.id);
const snippet = await snippetService.getSnippetById(req.params.id);
if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

res.json(snippet);
} catch (error) {
console.error('Erro ao buscar snippet:', error);
next(error);
}
};

// Atualiza um snippet pelo ID
export const updateSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const { title, description, language, tags, code, favorite } = req.body;

// Validate input data
if (typeof title !== 'string' ||
typeof description !== 'string' ||
typeof language !== 'string' ||
!Array.isArray(tags) ||
typeof code !== 'string' ||
typeof favorite !== 'boolean') {
return handleValidationError(res, 'Dados inválidos.');
}

const snippet = await Snippet.findByIdAndUpdate(
req.params.id,
{ $set: { title, description, language, tags, code, favorite } },
{ new: true, runValidators: true }
);

const snippet = await snippetService.updateSnippet(req.params.id, req.body);
if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

res.json(snippet);
} catch (error) {
console.error('Erro ao atualizar snippet:', error);
next(error);
}
};

// Deleta um snippet pelo ID
export const deleteSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const snippet = await Snippet.findByIdAndDelete(req.params.id);
if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

await snippetService.deleteSnippet(req.params.id);
res.status(204).send();
} catch (error) {
console.error('Erro ao deletar snippet:', error);
next(error);
}
};

// Busca snippets favoritos do usuário
export const fetchMySnippetsFavorite = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.user) {
return handleValidationError(res, 'Usuário não autenticado.');
}
const snippets = await Snippet.find({ user: req.user.id, favorite: true });
if (!req.user) throw new Error('Usuário não autenticado.');
const snippets = await snippetService.getFavoriteSnippets(req.user.id);
res.json(snippets);
} catch (error) {
console.error('Erro ao buscar snippets favoritos do usuário:', error);
next(error);
}
}
};

// Marca ou desmarca um snippet como favorito
export const markFavorite = async (req: Request, res: Response, next: NextFunction) => {
try {
const snippet = await Snippet.findById(req.params.id);
if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

snippet.favorite = !snippet.favorite;
await snippet.save();

const snippet = await snippetService.toggleFavorite(req.params.id);
res.json(snippet);
} catch (error) {
console.error('Erro ao marcar snippet como favorito:', error);
next(error);
}
};

// Busca snippets públicos usando um serviço externo opcional (ex: GitHub Gist)
export const fetchPublicSnippets = async (req: Request, res: Response, next: NextFunction) => {
try {
const query = req.query.query as string;
const snippets = await GitHubApiService.fetchPublicSnippets(query);
const snippets = await snippetService.fetchPublicSnippets(req.query.query as string);
res.json(snippets);
} catch (error) {
console.error('Erro ao buscar snippets públicos:', error);
next(error);
}
};

export const shareSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const snippet = await Snippet.findById(req.params.id);
if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

// Verifica se o snippet pertence ao usuário autenticado
if (snippet.user.toString() !== req.user?.id) {
return handleValidationError(res, 'Snippet não pertence ao usuário autenticado.');
}

if (!snippet.sharedLink) {
const uniqueLink = `${req.protocol}://${req.get('host')}/api/snippets/shared/${uuidv4()}`;
snippet.sharedLink = uniqueLink;
await snippet.save();
}

res.json({ link: snippet.sharedLink });
console.log('Snippet compartilhado:', snippet);
const host = req.get('host');
if (!host) throw new Error('Host não encontrado.');
if (!req.user?.id) throw new Error('Usuário não autenticado.');
const link = await snippetService.shareSnippet(req.params.id, req.user.id, req.protocol, host);
res.json({ link });
} catch (error) {
console.error('Erro ao compartilhar snippet:', error);
next(error);
}
};

export const fetchSharedSnippet = async (req: Request, res: Response, next: NextFunction) => {
try {
const link = `${req.protocol}://${req.get('host')}/api/snippets/shared/${req.params.link}`;
const snippet = await Snippet.findOne({ sharedLink: link });
const { link } = req.params;

// Busca apenas pelo UUID no campo sharedLink
const snippet = await Snippet.findOne({
sharedLink: { $regex: `${link}$` }, // Busca o UUID no final do campo sharedLink
});

if (!snippet) {
return res.status(404).json({ message: 'Snippet compartilhado não encontrado.' });
}
Expand All @@ -216,19 +118,3 @@ export const fetchSharedSnippet = async (req: Request, res: Response, next: Next
next(error);
}
};

// deleta link compartilhado
// export const deleteSharedLink = async (req: Request, res: Response, next: NextFunction) => {
// try {
// const snippet = await Snippet.findById(req.params.id);
// if (!snippet) return handleValidationError(res, 'Snippet não encontrado.');

// snippet.sharedLink = null;
// await snippet.save();

// res.json({ message: 'Link compartilhado removido.' });
// } catch (error) {
// console.error('Erro ao deletar link compartilhado:', error);
// next(error);
// }
// };
Loading

0 comments on commit 37aaf1b

Please sign in to comment.