Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e87534b
user-authentication
rifasania Feb 7, 2025
4b87b16
fixing:user-authentication
rifasania Feb 11, 2025
de17149
implement linter and prettier
rifasania Feb 11, 2025
cd1e3b7
fixing2:user-authentication
rifasania Feb 12, 2025
e52dd01
fixing3:user-authentication
rifasania Feb 12, 2025
fbd294b
Test husky pre-commit
rifasania Feb 13, 2025
0ca604b
test husky pre-commmit
rifasania Feb 13, 2025
22bd248
test husky pre-committt
rifasania Feb 13, 2025
ddb4707
test prettier jebal
rifasania Feb 13, 2025
f3169a7
test linter prettier
rifasania Feb 13, 2025
52c8390
pre-commit linter prettier done
rifasania Feb 13, 2025
0da64bc
pre-commit linter and prettier
rifasania Feb 13, 2025
bff3aa8
cek
rifasania Feb 13, 2025
a3d5f3e
fixed all
rifasania Feb 13, 2025
17bcfc1
fixed error
rifasania Feb 13, 2025
1af47b5
fixed:all
rifasania Feb 13, 2025
8fc9dea
ddl-operation
rifasania Feb 17, 2025
85e5f2a
resolve conflict pt.2
rifasania Feb 18, 2025
dfbc3b3
finished resolve conflict
rifasania Feb 18, 2025
92ce8e5
fixing:ddl-operation
rifasania Feb 19, 2025
0404e2e
fixed ddl operation
rifasania Feb 21, 2025
a8fb6e7
fixing3:ddl-operation
rifasania Feb 24, 2025
5f946f2
fixed code + testing
rifasania Feb 25, 2025
d3bae85
fixed github actions
rifasania Feb 25, 2025
d83a0c4
fixed github action: jwt
rifasania Feb 25, 2025
5ac8582
fixing:github action
rifasania Feb 26, 2025
5e225e3
fixing:github-action:add-sync
rifasania Feb 26, 2025
da489c3
fixing:github-action:fix-postgres
rifasania Feb 26, 2025
25582f8
fixing:github-action:debug-yaml
rifasania Feb 26, 2025
a62d103
test
rifasania Feb 26, 2025
560713c
test2
rifasania Feb 26, 2025
3b94935
rollback
rifasania Feb 26, 2025
9cec989
test4
rifasania Feb 26, 2025
6eb0509
test5
rifasania Feb 26, 2025
723f114
md
rifasania Feb 26, 2025
66c0890
test7
rifasania Feb 26, 2025
3463452
test8
rifasania Feb 26, 2025
06c7d15
url
rifasania Feb 26, 2025
986cb20
tolong
rifasania Feb 26, 2025
2e209e1
fixed:github-action:add-migrations
rifasania Feb 26, 2025
fb20f56
fixed:testing
rifasania Feb 26, 2025
e7071ea
fixed:refactor-function-test
rifasania Feb 26, 2025
5f0783c
add-testing
rifasania Feb 27, 2025
e0f11f6
finished-ddl-operation
rifasania Feb 28, 2025
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
5 changes: 3 additions & 2 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const path = require('path');
const path = require("path");
require('dotenv').config();
require("ts-node/register");

module.exports = {
"config": path.resolve(__dirname, "src/config/sequelize.config.js"),
config: path.resolve(__dirname, "src/config/sequelize.config.js"),
"models-path": path.resolve(__dirname, "src/models"),
"seeders-path": path.resolve(__dirname, "src/seeders"),
"migrations-path": path.resolve(__dirname, "src/migrations"),
Expand Down
9 changes: 8 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import js from "@eslint/js";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsparser from "@typescript-eslint/parser";
import prettier from "eslint-plugin-prettier";

export default [
js.configs.recommended,
{
files: ["src/**/*.ts"],
ignores: ["dist/**", "node_modules/**"],
languageOptions: {
parser: tsparser,
},
plugins: {
"@typescript-eslint": tseslint,
prettier,
},
rules: {
"prettier/prettier": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"no-undef": "off",
},
},
{
files: ["src/migrations/*.js", "src/config/sequelize.config.js"],
files: ["src/config/sequelize.config.js"],
languageOptions: {
sourceType: "commonjs",
},
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"migrate": "sequelize-cli db:migrate --config=src/config/sequelize.config.js",
"migrate": "sequelize-cli db:migrate --config src/config/sequelize.config.js",
"seed": "sequelize-cli db:seed:all",
"prepare": "husky install",
"lint": "eslint src",
Expand All @@ -30,7 +30,7 @@
"@types/bcryptjs": "^2.4.6",
"@types/express": "^5.0.0",
"@types/jsonwebtoken": "^9.0.8",
"@types/node": "^22.13.1",
"@types/node": "^22.13.2",
"@types/sequelize": "^4.28.20",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.0.1",
Expand Down
66 changes: 66 additions & 0 deletions src/controllers/ddl-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Request, Response } from 'express';
import { sequelize } from '../config/database';
import { successResponse, errorResponse } from '../utils/response';
import { Operations } from '../types/ddl';
import {
CreateTable,
CreateColumn,
AlterTable,
AlterColumn,
DropColumn,
DropTable,
} from '../operations/ddl';

export const migrate = async (req: Request, res: Response) => {
const { operations }: { operations: Operations[] } = req.body;
if (!operations || !Array.isArray(operations))
return errorResponse(res, 'Invalid payload structure', 400);

const transaction = await sequelize.transaction();

try {
for (const { operation, resource, migration } of operations) {
const { name, table, column, from, to } = migration;

let finalColumnDefinition = {};
if (column && typeof column === 'object' && 'definition' in column) {
finalColumnDefinition = (column as any).definition;
}

switch (`${operation}-${resource}`) {
case 'Create-Table':
await CreateTable.execute(name!, migration!, transaction);
break;
case 'Create-Column':
await CreateColumn.execute(table!, migration!, transaction);
break;
case 'Alter-Table':
await AlterTable.execute(from!, to!, transaction);
break;
case 'Alter-Column':
await AlterColumn.execute(
table!,
from!,
to!,
finalColumnDefinition,
transaction,
);
break;
case 'Drop-Column':
await DropColumn.execute(table!, column!, transaction);
break;
case 'Drop-Table':
await DropTable.execute(name!, transaction);
break;
default:
throw new Error(`Unsupported operation: ${operation} on ${resource}`);
}
Comment thread
rifasania marked this conversation as resolved.
Outdated
}

await transaction.commit();
return successResponse(res, {}, 'DDL operations completed successfully');
} catch (error: any) {
await transaction.rollback();
return errorResponse(res, error.message, 500);
}
};
8 changes: 4 additions & 4 deletions src/controllers/user-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export const registerUser = async (req: Request, res: Response) => {
return errorResponse(res, 'Name, email, and password are required', 400);
}

const existingUser = await UserRepository.findByEmail(email);
const existingUser = await UserRepository.findOne({ email });
if (existingUser) {
return errorResponse(res, 'Email is already registered', 400);
}

const hashedPassword = await hashPassword(password);
const apiKey = await generateApiKey();

const newUser = await UserRepository.createUser({
const newUser = await UserRepository.insert({
name,
email,
password: hashedPassword,
Expand All @@ -44,7 +44,7 @@ export const loginUser = async (req: Request, res: Response) => {
try {
const { email, password } = req.body;

const user = await UserRepository.findByEmail(email);
const user = await UserRepository.findOne({ email });
if (!user) {
return errorResponse(res, 'Unauthorized', 401);
}
Expand Down Expand Up @@ -79,7 +79,7 @@ export const getUserDetails = async (req: Request, res: Response) => {
}

const decoded: any = jwt.verify(token, ENV.JWT_SECRET);
const user = await UserRepository.findById(decoded.userId);
const user = await UserRepository.findOne({ id: decoded.userId });

if (!user) {
return errorResponse(res, 'Unauthorized', 401);
Expand Down
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import express, { Request, Response } from 'express';
import dotenv from 'dotenv';
import userRoutes from './routes/user-routes';
import authRoutes from './routes/auth-routes';
import ddlRoutes from './routes/ddl-routes';
import { sequelize } from './config/database';

sequelize
.sync({ force: true })
.then(async () => {
console.log('Database synchronized successfully.');
})
.catch((err) => {
console.error('Failed to synchronize database:', err);
});

dotenv.config();

Expand All @@ -14,6 +25,7 @@ const apiRouter = express.Router();

apiRouter.use('/auth', authRoutes);
apiRouter.use('/users', userRoutes);
apiRouter.use('/migrate', ddlRoutes);

app.use('/api', apiRouter);

Expand Down
1 change: 1 addition & 0 deletions src/middlewares/auth-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const authMiddleware = (
(req as any).user = decoded;
next();
} catch (error) {
console.error('Error:', error);
return errorResponse(res, 'Unauthorized', 401);
}
};
49 changes: 0 additions & 49 deletions src/migrations/20250206065327-create-user.js

This file was deleted.

51 changes: 51 additions & 0 deletions src/migrations/20250206065327-create-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { QueryInterface, DataTypes, Sequelize } from 'sequelize';

/** @type {import('sequelize-cli').Migration} */
export default {
async up(queryInterface: QueryInterface, sequelize: Sequelize) {
await queryInterface.sequelize.query(
'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";',
);

await queryInterface.createTable('users', {
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: sequelize.literal('uuid_generate_v4()'),
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
apikey: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
created_at: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
},
updated_at: {
allowNull: false,
type: DataTypes.DATE,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
},
});
},

async down(queryInterface: QueryInterface) {
await queryInterface.dropTable('users');
},
};
76 changes: 76 additions & 0 deletions src/models/metadata-column.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../config/database';
import MetadataTable from './metadata-table';

class MetadataColumn extends Model {
public id!: string;
public table_id!: string;
public column_name!: string;
public data_type!: string;
public is_primary!: boolean;
public is_nullable!: boolean;
public is_unique!: boolean;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}

MetadataColumn.init(
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
table_id: {
Comment thread
rifasania marked this conversation as resolved.
type: DataTypes.UUID,
allowNull: false,
references: {
model: MetadataTable,
key: 'id',
},
onDelete: 'CASCADE',
},
column_name: {
type: DataTypes.STRING,
allowNull: false,
},
data_type: {
type: DataTypes.STRING,
allowNull: false,
},
is_primary: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
is_nullable: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
is_unique: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updated_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'MetadataColumn',
tableName: 'metadata_column',
timestamps: true,
underscored: true,
},
);

MetadataColumn.belongsTo(MetadataTable, {
foreignKey: 'table_id',
onDelete: 'CASCADE',
});

export default MetadataColumn;
Loading