create-nodets-app-nish 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/nodejs-typescript/.env.example +12 -0
- package/templates/nodejs-typescript/src/admin/model.register.ts +5 -0
- package/templates/nodejs-typescript/src/app.ts +17 -0
- package/templates/nodejs-typescript/src/db/index.ts +13 -0
- package/templates/nodejs-typescript/src/index.ts +16 -0
- package/templates/nodejs-typescript/src/interface/admin.interface.ts +12 -0
- package/templates/nodejs-typescript/src/models/admin.models.ts +46 -0
- package/templates/nodejs-typescript/src/utils/apiResponse.ts +22 -0
- package/templates/nodejs-typescript/src/utils/asyncHandler.ts +9 -0
- package/templates/nodejs-typescript/src/utils/login.ts +43 -0
- package/templates/nodejs-typescript/src/utils/sendMail.ts +47 -0
- package/templates/nodejs-typescript/tsconfig.json +24 -0
- package/tsconfig.json +16 -0
package/package.json
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
PORT=3000
|
|
2
|
+
MONGODB_URI=mongodb://localhost:27017/your-app-name
|
|
3
|
+
JWT_SECRET=your-secret-key-here
|
|
4
|
+
CORS_ORIGIN=http://localhost:3000
|
|
5
|
+
|
|
6
|
+
# Email Configuration
|
|
7
|
+
SMTP_HOST=smtp.gmail.com
|
|
8
|
+
SMTP_PORT=587
|
|
9
|
+
SMTP_SECURE=false
|
|
10
|
+
SMTP_USER=your-email@gmail.com
|
|
11
|
+
SMTP_PASS=your-app-password
|
|
12
|
+
SMTP_FROM=noreply@yourapp.com
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import cookieParser from "cookie-parser";
|
|
4
|
+
|
|
5
|
+
const app = express();
|
|
6
|
+
|
|
7
|
+
app.use(cors({
|
|
8
|
+
origin: process.env.CORS_ORIGIN,
|
|
9
|
+
credentials: true
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
app.use(express.json({limit:"16kb"}))
|
|
13
|
+
app.use(express.urlencoded({extended:true,limit:"16kb"}))
|
|
14
|
+
app.use(express.static("public"))
|
|
15
|
+
app.use(cookieParser())
|
|
16
|
+
|
|
17
|
+
export {app}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { app } from "./app.js";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
import { connectDB } from "./db/index.js";
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
|
|
7
|
+
const port = process.env.PORT || 3000;
|
|
8
|
+
|
|
9
|
+
connectDB().then(() => {
|
|
10
|
+
app.listen(port, () => {
|
|
11
|
+
console.log(`Server is running on port ${port}`);
|
|
12
|
+
});
|
|
13
|
+
})
|
|
14
|
+
.catch((err) => {
|
|
15
|
+
console.log("Error connecting to database", err);
|
|
16
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { IAdmin } from "../interface/admin.interface.js";
|
|
2
|
+
import mongoose, { Document } from "mongoose";
|
|
3
|
+
import jwt from "jsonwebtoken";
|
|
4
|
+
import bcrypt from "bcrypt";
|
|
5
|
+
|
|
6
|
+
type AdminType = IAdmin & Document;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const AdminSchema = new mongoose.Schema<AdminType>({
|
|
10
|
+
username: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: true,
|
|
13
|
+
unique: true
|
|
14
|
+
},
|
|
15
|
+
email: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: true,
|
|
18
|
+
unique: true
|
|
19
|
+
},
|
|
20
|
+
password: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
AdminSchema.pre("save", async function () {
|
|
27
|
+
if (this.isModified("password")) {
|
|
28
|
+
this.password = await bcrypt.hash(this.password, 10)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
AdminSchema.methods.generateAccessToken = function (): string {
|
|
33
|
+
return jwt.sign({
|
|
34
|
+
_id: this._id,
|
|
35
|
+
username: this.username,
|
|
36
|
+
email: this.email
|
|
37
|
+
}, process.env.JWT_SECRET as string, {
|
|
38
|
+
expiresIn: "1d"
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
AdminSchema.methods.isPasswordCorrect = async function (password: string): Promise<boolean> {
|
|
43
|
+
return await bcrypt.compare(password, this.password)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const Admin = mongoose.model<AdminType>("Admin", AdminSchema)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Response } from "express";
|
|
2
|
+
|
|
3
|
+
class ApiResponse{
|
|
4
|
+
constructor(
|
|
5
|
+
public statusCode: number,
|
|
6
|
+
public message: string,
|
|
7
|
+
public data: any
|
|
8
|
+
){
|
|
9
|
+
this.statusCode = statusCode
|
|
10
|
+
this.message = message
|
|
11
|
+
this.data = data
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
function returnResponse(res:Response,statusCode:number,message:string,data:any){
|
|
17
|
+
return res.status(statusCode).json(
|
|
18
|
+
new ApiResponse(statusCode,message,data)
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {returnResponse}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction, RequestHandler } from "express";
|
|
2
|
+
|
|
3
|
+
function asyncHandler(fn: RequestHandler) {
|
|
4
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
5
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { asyncHandler };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Model } from "mongoose";
|
|
2
|
+
import * as modeldata from "../admin/model.register.js";
|
|
3
|
+
|
|
4
|
+
// Accepts either string or object for identifier
|
|
5
|
+
type Identifier = string | Record<string, any>;
|
|
6
|
+
|
|
7
|
+
async function login(
|
|
8
|
+
model: keyof typeof modeldata.models, // TS now knows only valid models can be used
|
|
9
|
+
uniqueIdentifier: Identifier,
|
|
10
|
+
password: string
|
|
11
|
+
) {
|
|
12
|
+
const Model: Model<any> = modeldata.models[model]; // cast to Model<any> to avoid TS errors
|
|
13
|
+
if (!Model) {
|
|
14
|
+
throw new Error("Model not found");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If uniqueIdentifier is a string, assume it's an 'id' search
|
|
18
|
+
const query = typeof uniqueIdentifier === "string"
|
|
19
|
+
? { id: uniqueIdentifier }
|
|
20
|
+
: uniqueIdentifier;
|
|
21
|
+
|
|
22
|
+
const user = await Model.findOne(query);
|
|
23
|
+
if (!user) {
|
|
24
|
+
throw new Error("User not found");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check password
|
|
28
|
+
const isPasswordCorrect = await user.isPasswordCorrect(password);
|
|
29
|
+
if (!isPasswordCorrect) {
|
|
30
|
+
throw new Error("Password is not correct");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Generate token
|
|
34
|
+
const token = user.generateAccessToken();
|
|
35
|
+
|
|
36
|
+
if (!token) {
|
|
37
|
+
throw new Error("Token not generated");
|
|
38
|
+
}
|
|
39
|
+
return token;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
export { login }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
import type { Transporter } from "nodemailer";
|
|
3
|
+
|
|
4
|
+
interface MailOptions {
|
|
5
|
+
to: string;
|
|
6
|
+
subject: string;
|
|
7
|
+
text?: string;
|
|
8
|
+
html?: string;
|
|
9
|
+
from?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let transporter: Transporter | null = null;
|
|
13
|
+
|
|
14
|
+
// Initialize transporter (only once)
|
|
15
|
+
function getTransporter(): Transporter {
|
|
16
|
+
if (!transporter) {
|
|
17
|
+
transporter = nodemailer.createTransport({
|
|
18
|
+
host: process.env.SMTP_HOST,
|
|
19
|
+
port: Number(process.env.SMTP_PORT) || 587,
|
|
20
|
+
secure: process.env.SMTP_SECURE === "true", // true for 465, false for other ports
|
|
21
|
+
auth: {
|
|
22
|
+
user: process.env.SMTP_USER,
|
|
23
|
+
pass: process.env.SMTP_PASS,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return transporter;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function sendMail(options: MailOptions): Promise<void> {
|
|
31
|
+
const mailOptions = {
|
|
32
|
+
from: options.from || process.env.SMTP_FROM || "no-reply@example.com",
|
|
33
|
+
to: options.to,
|
|
34
|
+
subject: options.subject,
|
|
35
|
+
text: options.text,
|
|
36
|
+
html: options.html,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const transporter = getTransporter();
|
|
41
|
+
const info = await transporter.sendMail(mailOptions);
|
|
42
|
+
console.log("Email sent:", info.messageId);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("Error sending email:", error);
|
|
45
|
+
throw new Error("Failed to send email");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDir": "./src",
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"module": "nodenext",
|
|
6
|
+
"target": "esnext",
|
|
7
|
+
"types": [],
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"emitDeclarationOnly": false,
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
"exactOptionalPropertyTypes": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"verbatimModuleSyntax": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"noUncheckedSideEffectImports": true,
|
|
19
|
+
"moduleDetection": "force",
|
|
20
|
+
"skipLibCheck": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src/**/*"],
|
|
23
|
+
"exclude": ["node_modules", "dist"]
|
|
24
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules"]
|
|
16
|
+
}
|