Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
|
89ff6d27d4 |
10
.env.example
10
.env.example
|
@ -4,7 +4,6 @@ DB_USER=
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
DB_HOST=
|
DB_HOST=
|
||||||
DB_PORT=
|
DB_PORT=
|
||||||
NODE_ENV=
|
|
||||||
|
|
||||||
# twilio creds
|
# twilio creds
|
||||||
|
|
||||||
|
@ -20,4 +19,11 @@ ACCESS_TOKEN_EXPIRY=
|
||||||
|
|
||||||
SUPABASE_URL=
|
SUPABASE_URL=
|
||||||
SUPABASE_KEY=
|
SUPABASE_KEY=
|
||||||
DATABASE_URL=
|
DATABASE_URL=
|
||||||
|
|
||||||
|
# aws s3 creds
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_REGION=
|
||||||
|
AWS_S3_BUCKET=
|
127
.gitignore
vendored
127
.gitignore
vendored
|
@ -1,127 +1,2 @@
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
|
||||||
web_modules/
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional stylelint cache
|
|
||||||
.stylelintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
.env
|
||||||
.env.*
|
node_modules
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# Next.js build output
|
|
||||||
.next
|
|
||||||
out
|
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
|
||||||
.nuxt
|
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
|
||||||
.cache/
|
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
||||||
# public
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
|
||||||
.temp
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# vitepress build output
|
|
||||||
**/.vitepress/dist
|
|
||||||
|
|
||||||
# vitepress cache directory
|
|
||||||
**/.vitepress/cache
|
|
||||||
|
|
||||||
# Docusaurus cache and generated files
|
|
||||||
.docusaurus
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# TernJS port file
|
|
||||||
.tern-port
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
|
|
||||||
# yarn v2
|
|
||||||
.yarn/cache
|
|
||||||
.yarn/unplugged
|
|
||||||
.yarn/build-state.yml
|
|
||||||
.yarn/install-state.gz
|
|
||||||
.pnp.*
|
|
1933
package-lock.json
generated
1933
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,18 +13,22 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.832.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.832.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"joi": "^17.13.3",
|
"joi": "^17.13.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^2.0.1",
|
||||||
"pg": "^8.16.0",
|
"pg": "^8.16.0",
|
||||||
"pg-hstore": "^2.3.4",
|
"pg-hstore": "^2.3.4",
|
||||||
"postgres": "^3.4.7",
|
"postgres": "^3.4.7",
|
||||||
"sequelize": "^6.37.7",
|
"sequelize": "^6.37.7",
|
||||||
"swagger-jsdoc": "^6.2.8",
|
"swagger-jsdoc": "^6.2.8",
|
||||||
"swagger-ui-express": "^5.0.1",
|
"swagger-ui-express": "^5.0.1",
|
||||||
"twilio": "^5.7.1"
|
"twilio": "^5.7.1",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.1.10"
|
"nodemon": "^3.1.10"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
dotenv.config()
|
import path from "path";
|
||||||
import path from 'path';
|
dotenv.config({ path: path.join(process.cwd(), "../.env") });
|
||||||
dotenv.config({ path: path.join(process.cwd(), "../.env") })
|
|
||||||
|
|
||||||
const getDb = () => {
|
const getDb = () => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import dotenv from 'dotenv'
|
import dotenv from "dotenv";
|
||||||
dotenv.config()
|
dotenv.config();
|
||||||
import db from "../models/index.js";
|
import User from "../models/user.js";
|
||||||
const { users } = db;
|
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import twilio from "twilio";
|
import twilio from "twilio";
|
||||||
// console.log('process.env: ', process.env);
|
|
||||||
|
|
||||||
const JWT_SECRET = process.env.JWT_SECRET;
|
const JWT_SECRET = process.env.JWT_SECRET;
|
||||||
const accountSid = process.env.TWILIO_ACCOUNT_SID;
|
const accountSid = process.env.TWILIO_ACCOUNT_SID;
|
||||||
|
@ -35,21 +33,22 @@ const registerLogin = async (req, res) => {
|
||||||
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
||||||
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
||||||
|
|
||||||
// const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
||||||
// console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
||||||
|
|
||||||
let user = await users.findOne({ where: { country_code:countryCode, phone_number:phoneNumber } });
|
let user = await User.findOne({ where: { full_number } });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await users.create({
|
user = await User.create({
|
||||||
country_code: countryCode,
|
country_code: countryCode,
|
||||||
phone_number: phoneNumber,
|
phone_number: phoneNumber,
|
||||||
|
full_number: full_number,
|
||||||
otp,
|
otp,
|
||||||
otp_expiry
|
otp_expiry,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await users.update({ otp, otp_expiry }, { country_code:countryCode, phone_number:phoneNumber } );
|
await User.update({ otp, otp_expiry }, { where: { full_number } });
|
||||||
user = await users.findOne({ country_code:countryCode, phone_number:phoneNumber } );
|
user = await User.findOne({ where: { full_number } });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
|
@ -57,7 +56,7 @@ const registerLogin = async (req, res) => {
|
||||||
user: { id: user.id, full_number },
|
user: { id: user.id, full_number },
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("registerLogin error:",err);
|
console.error("registerLogin error:", err);
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -73,7 +72,7 @@ const verifyOtp = async (req, res) => {
|
||||||
.json({ error: "Phone number and OTP are required" });
|
.json({ error: "Phone number and OTP are required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await users.findOne({ where: { full_number } });
|
const user = await User.findOne({ where: { full_number } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(404).json({ error: "User not found" });
|
return res.status(404).json({ error: "User not found" });
|
||||||
}
|
}
|
||||||
|
@ -134,14 +133,14 @@ const triggerOtp = async (req, res) => {
|
||||||
const { countryCode, phoneNumber } = req.body;
|
const { countryCode, phoneNumber } = req.body;
|
||||||
|
|
||||||
const full_number = countryCode.toString() + phoneNumber;
|
const full_number = countryCode.toString() + phoneNumber;
|
||||||
const exist = await users.findOne({ where: { country_code:countryCode,phone_number:phoneNumber } });
|
const exist = await User.findOne({ where: { full_number: full_number } });
|
||||||
if (!exist) return res.status(400).json({ message: "Number Not exist" });
|
if (!exist) return res.status(400).json({ message: "Number Not exist" });
|
||||||
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
const otp = Math.floor(100000 + Math.random() * 900000).toString();
|
||||||
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
const otp_expiry = new Date(Date.now() + 10 * 60 * 1000); // 10 mins
|
||||||
|
|
||||||
const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
const twilioResponse = await sendOtpFromTwilio(full_number, otp);
|
||||||
console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
console.log("twilioResponse:", twilioResponse.sid || twilioResponse);
|
||||||
await users.update(
|
await User.update(
|
||||||
{
|
{
|
||||||
otp: otp,
|
otp: otp,
|
||||||
otp_expiry: otp_expiry,
|
otp_expiry: otp_expiry,
|
||||||
|
@ -159,42 +158,41 @@ const triggerOtp = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateAccessToken = (user) => {
|
const generateAccessToken = (user) => {
|
||||||
try {
|
try {
|
||||||
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
|
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.ACCESS_TOKEN_EXPIRY });
|
||||||
expiresIn: process.env.ACCESS_TOKEN_EXPIRY,
|
} catch (error) {
|
||||||
});
|
return res.status(400).json({ message: 'Invalid or expired token' });
|
||||||
} catch (error) {
|
}
|
||||||
return res.status(400).json({ message: "Invalid or expired token" });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateRefreshToken = (user) => {
|
const generateRefreshToken = (user) => {
|
||||||
try {
|
try {
|
||||||
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
|
return jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.REFRESH_TOKEN_EXPIRY });
|
||||||
expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
|
} catch (err) {
|
||||||
});
|
return res.status(400).json({ message: 'Invalid or expired refresh token' });
|
||||||
} catch (err) {
|
}
|
||||||
return res
|
|
||||||
.status(400)
|
|
||||||
.json({ message: "Invalid or expired refresh token" });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshAccessToken = async (req, res) => {
|
const refreshAccessToken = async (req, res) => {
|
||||||
const refreshToken = req.body.refreshToken;
|
const refreshToken = req.body.refreshToken;
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
return res.status(400).json({ message: "Refresh token not provided" });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ message: "Refresh token not provided" });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET);
|
const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET);
|
||||||
|
|
||||||
const user = await User.findByPk(decoded.id);
|
const user = await User.findByPk(decoded.id);
|
||||||
if (!user) return res.status(404).json({ message: "User not found" });
|
if (!user)
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ message: "User not found" });
|
||||||
|
|
||||||
const newAccessToken = generateAccessToken({
|
const newAccessToken = generateAccessToken({
|
||||||
id: decoded.id,
|
id: decoded.id
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).json({ accessToken: newAccessToken });
|
return res.status(200).json({ accessToken: newAccessToken });
|
||||||
|
@ -212,5 +210,5 @@ export default {
|
||||||
// login,
|
// login,
|
||||||
triggerOtp,
|
triggerOtp,
|
||||||
refreshAccessToken,
|
refreshAccessToken,
|
||||||
generateRefreshToken,
|
generateRefreshToken
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import User from '../models/users.js';
|
import User from '../models/user.js';
|
||||||
|
import { uploadFile, getSignedFileUrl } from "../service/s3.service.js";
|
||||||
|
|
||||||
// UPDATE USER
|
// UPDATE USER
|
||||||
const updateUser = async (req, res) => {
|
const updateUser = async (req, res) => {
|
||||||
|
@ -47,9 +48,48 @@ const deleteUser = async (req, res) => {
|
||||||
res.json({ message: 'User deleted' });
|
res.json({ message: 'User deleted' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload file to S3 and return fileKey
|
||||||
|
*/
|
||||||
|
const uploadS3File = async (req, res) => {
|
||||||
|
try {
|
||||||
|
console.log("Received file:", req.file?.originalname);
|
||||||
|
|
||||||
|
if (!req.file) {
|
||||||
|
return res.status(400).json({ error: "No file provided" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileKey = await uploadFile(req.file);
|
||||||
|
res.json({ message: "File uploaded successfully", fileKey });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("S3 upload error:", err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a signed URL for S3 file
|
||||||
|
*/
|
||||||
|
const getS3SignedUrl = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { key } = req.params;
|
||||||
|
if (!key) {
|
||||||
|
return res.status(400).json({ error: "File key is required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = await getSignedFileUrl(key);
|
||||||
|
res.json({ url });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("S3 signed URL error:", err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
updateUser,
|
updateUser,
|
||||||
getAllUsers,
|
getAllUsers,
|
||||||
getUserById,
|
getUserById,
|
||||||
deleteUser,
|
deleteUser,
|
||||||
|
uploadS3File,
|
||||||
|
getS3SignedUrl
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
/** @type {import('sequelize-cli').Migration} */
|
/** @type {import('sequelize-cli').Migration} */
|
||||||
export async function up(queryInterface, Sequelize) {
|
export async function up(queryInterface, Sequelize) {
|
||||||
await queryInterface.createTable('users', {
|
await queryInterface.createTable('Users', {
|
||||||
id: {
|
id: {
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
autoIncrement: true,
|
autoIncrement: true,
|
||||||
|
@ -17,6 +17,9 @@ export async function up(queryInterface, Sequelize) {
|
||||||
phone_number: {
|
phone_number: {
|
||||||
type: Sequelize.STRING
|
type: Sequelize.STRING
|
||||||
},
|
},
|
||||||
|
full_number: {
|
||||||
|
type: Sequelize.STRING
|
||||||
|
},
|
||||||
otp: {
|
otp: {
|
||||||
type: Sequelize.STRING
|
type: Sequelize.STRING
|
||||||
},
|
},
|
||||||
|
@ -34,5 +37,5 @@ export async function up(queryInterface, Sequelize) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function down(queryInterface, Sequelize) {
|
export async function down(queryInterface, Sequelize) {
|
||||||
await queryInterface.dropTable('users');
|
await queryInterface.dropTable('Users');
|
||||||
}
|
}
|
|
@ -33,7 +33,6 @@ for (const file of files) {
|
||||||
const model = modelModule.default(sequelize, DataTypes);
|
const model = modelModule.default(sequelize, DataTypes);
|
||||||
db[model.name] = model;
|
db[model.name] = model;
|
||||||
}
|
}
|
||||||
console.log(db)
|
|
||||||
|
|
||||||
Object.keys(db).forEach((modelName) => {
|
Object.keys(db).forEach((modelName) => {
|
||||||
if (db[modelName].associate) {
|
if (db[modelName].associate) {
|
||||||
|
|
32
src/models/user.js
Normal file
32
src/models/user.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
"use strict";
|
||||||
|
import { Model } from "sequelize";
|
||||||
|
export default (sequelize, DataTypes) => {
|
||||||
|
class User extends Model {
|
||||||
|
/**
|
||||||
|
* Helper method for defining associations.
|
||||||
|
* This method is not a part of Sequelize lifecycle.
|
||||||
|
* The `models/index` file will call this method automatically.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static associate(models) {
|
||||||
|
// define association here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
User.init(
|
||||||
|
{
|
||||||
|
name: DataTypes.STRING,
|
||||||
|
country_code: DataTypes.STRING,
|
||||||
|
phone_number: DataTypes.STRING,
|
||||||
|
full_number: DataTypes.STRING,
|
||||||
|
otp: DataTypes.STRING,
|
||||||
|
otp_expiry: DataTypes.DATE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sequelize,
|
||||||
|
modelName: "User",
|
||||||
|
underscored: true,
|
||||||
|
timestamps: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return User;
|
||||||
|
};
|
|
@ -1,25 +0,0 @@
|
||||||
'use strict';
|
|
||||||
import { Model } from 'sequelize';
|
|
||||||
export default (sequelize, DataTypes) => {
|
|
||||||
class users extends Model {
|
|
||||||
/**
|
|
||||||
* Helper method for defining associations.
|
|
||||||
* This method is not a part of Sequelize lifecycle.
|
|
||||||
* The `models/index` file will call this method automatically.
|
|
||||||
*/
|
|
||||||
static associate(models) {
|
|
||||||
// define association here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
users.init({
|
|
||||||
name: DataTypes.STRING,
|
|
||||||
country_code: DataTypes.STRING,
|
|
||||||
phone_number: DataTypes.STRING,
|
|
||||||
otp: DataTypes.STRING,
|
|
||||||
otp_expiry: DataTypes.DATE
|
|
||||||
}, {
|
|
||||||
sequelize,
|
|
||||||
modelName: 'users',
|
|
||||||
});
|
|
||||||
return users;
|
|
||||||
};
|
|
|
@ -4,7 +4,8 @@ import userController from "../controllers/user.controller.js";
|
||||||
import validate from "../middleware/validate.js";
|
import validate from "../middleware/validate.js";
|
||||||
import { updateUserSchema } from "../validators/user.validator.js";
|
import { updateUserSchema } from "../validators/user.validator.js";
|
||||||
import verifyToken from "../middleware/authenticate.js";
|
import verifyToken from "../middleware/authenticate.js";
|
||||||
|
import multer from "multer";
|
||||||
|
const upload = multer(); // memory storage
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
"/update-user",
|
"/update-user",
|
||||||
|
@ -13,5 +14,12 @@ router.put(
|
||||||
userController.updateUser
|
userController.updateUser
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.post("/upload", upload.single("file"), userController.uploadS3File);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/get-upload/:key",
|
||||||
|
userController.getS3SignedUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import path from 'path'
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
// console.log(process.env)
|
|
||||||
import express, { urlencoded, json } from "express";
|
import express, { urlencoded, json } from "express";
|
||||||
// import { connectDB } from "./models/index.js";
|
// import { connectDB } from "./models/index.js";
|
||||||
import indexRoutes from "./routes/index.js";
|
import indexRoutes from "./routes/index.js";
|
||||||
|
|
40
src/service/s3.service.js
Normal file
40
src/service/s3.service.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
|
||||||
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import s3Client from "../utils/s3Client.js";
|
||||||
|
|
||||||
|
const bucketName = process.env.AWS_S3_BUCKET;
|
||||||
|
|
||||||
|
export const uploadFile = async (file) => {
|
||||||
|
if (!file || !file.buffer) {
|
||||||
|
throw new Error("Invalid file data");
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileKey = `${uuidv4()}-${file.originalname}`;
|
||||||
|
|
||||||
|
const command = new PutObjectCommand({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Key: fileKey,
|
||||||
|
Body: file.buffer,
|
||||||
|
ContentType: file.mimetype,
|
||||||
|
});
|
||||||
|
|
||||||
|
await s3Client.send(command);
|
||||||
|
|
||||||
|
return fileKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSignedFileUrl = async (fileKey, expiresIn = 3600) => {
|
||||||
|
if (!fileKey) {
|
||||||
|
throw new Error("File key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = new GetObjectCommand({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Key: fileKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
const signedUrl = await getSignedUrl(s3Client, command, { expiresIn });
|
||||||
|
|
||||||
|
return signedUrl;
|
||||||
|
};
|
11
src/utils/s3Client.js
Normal file
11
src/utils/s3Client.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { S3Client } from "@aws-sdk/client-s3";
|
||||||
|
|
||||||
|
const s3Client = new S3Client({
|
||||||
|
region: process.env.AWS_REGION,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||||
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default s3Client;
|
Loading…
Reference in a new issue