Skip to content
Open
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
46 changes: 46 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: development-deploy

on:
push:
branches:
- dev

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/sequelize_postgresql:latest
target: dev

- name: SSH into Server
uses: appleboy/[email protected]
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASSWORD }}
port: ${{ secrets.SERVER_PORT }}
script: |
cd sequelize_postgresql_sever
echo "${{ secrets.SERVER_PASSWORD }}" | sudo -S docker-compose pull
echo "${{ secrets.SERVER_PASSWORD }}" | sudo -S docker-compose up -d
echo "${{ secrets.SERVER_PASSWORD }}" | sudo -S docker image prune -f
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/node_modules
.env
.env
__test__/
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#* ✈️ Production
FROM node:20-alpine AS dev

WORKDIR /app

COPY package*.json .

RUN npm install

COPY . .

EXPOSE 3001

CMD [ "npm", "run", "start" ]
192 changes: 192 additions & 0 deletions __tests__/userService.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
const request = require('supertest');
const app = require('../app'); // Đường dẫn đến tệp app của bạn
const { User } = require('../models/sequelize/index');
const sequelize = require('../bin/run'); // Đảm bảo bạn đã khởi tạo sequelize

describe('User Routes', () => {
beforeAll(async () => {
await sequelize.sync({ force: true });
});

afterEach(async () => {
await User.destroy({ where: {} });
});

test('POST /users should create a user', async () => {
const response = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
})
.expect(201);

expect(response.body).toHaveProperty('message', 'User created successfully');
expect(response.body.user).toHaveProperty('username', 'testuser');
expect(response.body.user).toHaveProperty('email', '[email protected]');
});

test('POST /users should return error for duplicate email', async () => {
await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const response = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'anotheruser',
email: '[email protected]',
password: 'password456',
bio: 'This is another bio'
})
.expect(422);

expect(response.body.errors).toContain('Email already exists');
});

test('GET /users should retrieve a list of users', async () => {
await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser1',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const response = await request(app)
.get('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.expect(200);

expect(response.body).toHaveLength(1);
expect(response.body[0]).toHaveProperty('username', 'testuser1');
});

test('GET /users/me should retrieve the current user', async () => {
const userResponse = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const token = userResponse.headers['authorization'].split(' ')[1];

const response = await request(app)
.get('/users/me')
.set('Authorization', `Bearer ${token}`)
.expect(200);

expect(response.body).toHaveProperty('username', 'testuser');
});

test('GET /users/:id should retrieve a user by ID', async () => {
const userResponse = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const userId = userResponse.body.user.id;

const response = await request(app)
.get(`/users/${userId}`)
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.expect(200);

expect(response.body).toHaveProperty('username', 'testuser');
});

test('PATCH /users/user/:id should update user bio', async () => {
const userResponse = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const userId = userResponse.body.user.id;

const response = await request(app)
.patch(`/users/user/${userId}`)
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({ bio: 'Updated bio' })
.expect(200);

expect(response.body).toHaveProperty('message', 'Bio updated successfully');
expect(response.body.user).toHaveProperty('bio', 'Updated bio');
});

test('DELETE /users/user/:id should delete a user', async () => {
const userResponse = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const userId = userResponse.body.user.id;

await request(app)
.delete(`/users/user/${userId}`)
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.expect(200);

const response = await request(app)
.get(`/users/${userId}`)
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.expect(404);

expect(response.body.error).toBe('User not found');
});

test('POST /users/me/changepassword/:id should change user password', async () => {
const userResponse = await request(app)
.post('/users')
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
username: 'testuser',
email: '[email protected]',
password: 'password123',
bio: 'This is a bio'
});

const userId = userResponse.body.user.id;

const response = await request(app)
.post(`/users/me/changepassword/${userId}`)
.set('Authorization', 'Bearer YOUR_VALID_TOKEN') // Thay thế bằng token hợp lệ
.send({
oldPassword: 'password123',
newPassword: 'newpassword456'
})
.expect(200);

expect(response.body).toHaveProperty('message', 'Password changed successfully');
});
});
28 changes: 19 additions & 9 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
const express = require('express');

const app = express();
const helmet = require('helmet');
const routes = require('./routes');

require('dotenv').config();
const swaggerUi = require('swagger-ui-express');
const swaggerSpec = require('./config/swagger');
const config = require('./config/index')[process.env.NODE_ENV || 'development'];
const TodoService = require('./services/TodoService');
const UserService = require('./services/UserService');
const AuthService = require('./services/AuthService');
const PostService = require('./services/PostService');
const log = config.log();
module.exports = (config, sequelize) => {
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

module.exports = (config) => {
const log = config.log();

const todoService = new TodoService();
const userService = new UserService();
const todoService = new TodoService(sequelize);
const userService = new UserService(sequelize);
const authService = new AuthService(sequelize);
const postService = new PostService(sequelize);

// Add a request logging middleware in development mode
if (app.get('env') === 'development') {
Expand All @@ -19,9 +28,10 @@ module.exports = (config) => {
return next();
});
}

app.use('/', routes({todoService, userService}));


app.use('/api', routes({ todoService, userService, authService,postService }));
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.use(helmet());
// eslint-disable-next-line no-unused-vars
app.use((error, req, res, next) => {
res.status(error.status || 500);
Expand Down
37 changes: 29 additions & 8 deletions bin/run
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
#!/usr/bin/env node

const http = require('http');
const Sequelize = require('sequelize');

const config = require('../config')[process.env.NODE_ENV || 'development'];
const config = require('../config/index')[process.env.NODE_ENV || 'development'];

const log = config.log();
const app = require('../app')(config);

const server = http.createServer(app);
const sequelize = new Sequelize(config.postgres.options);

server.listen(process.env.PORT || 3000);
const app = require('../app')(config, sequelize);
function connectToPostgres() {
return sequelize
.authenticate()
.then(() => {
log.info('Connection has been established successfully.');
return sequelize;
})
.catch((error) => {
log.error('Unable to connect to the database:', error);
console.error('Error details:', error);
process.exit(1);
});
}

server.on('listening', () => {
log.info(
`Hi there! I'm listening on port ${server.address().port} in ${app.get('env')} mode.`,
);
// Connect to PostgreSQL và bắt đầu server
connectToPostgres().then((postgresClient) => {
config.postgres.client = postgresClient;

const server = http.createServer(app);

server.listen(process.env.PORT || 3001, () => {
log.info(
`Hi there! I'm listening on port ${server.address().port} in ${app.get('env')} mode.`,
);
});
});
module.exports = sequelize;
26 changes: 26 additions & 0 deletions config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"development": {
"username": "${DB_USERNAME}",
"password": "${DB_PASSWORD}",
"database": "${DB_DATABASE_DEV}",
"host": "${DB_HOST}",
"dialect": "postgres",
"port": "${DB_PORT}"
},
"test": {
"username": "${DB_USERNAME}",
"password": "${DB_PASSWORD}",
"database": "${DB_DATABASE_TEST}",
"host": "${DB_HOST}",
"dialect": "postgres",
"port": "${DB_PORT}"
},
"production": {
"username": "${DB_USERNAME}",
"password": "${DB_PASSWORD}",
"database": "${DB_DATABASE_PROD}",
"host": "${DB_HOST}",
"dialect": "postgres",
"port": "${DB_PORT}"
}
}
Loading