Spaces:
Build error
Build error
File size: 6,432 Bytes
e2ce937 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | /**
npm install hono jsonwebtoken bcrypt
npm install -D typescript ts-node @types/node @types/jsonwebtoken @types/bcrypt
*/
//@ts-ignore
import bcrypt from "bcrypt";
//@ts-ignore
import jwt from "jsonwebtoken";
//@ts-ignore
import crypto from "crypto";
import { v4 as uuidv4 } from "uuid";
// π ν€ κΈΈμ΄λ₯Ό AES-256-CBC μꡬμ¬νμΈ 32λ°μ΄νΈλ‘ λ§μΆλ μ νΈλ¦¬ν° ν¨μ
// κΈΈμ΄κ° λΆμ‘±νλ©΄ 0x00μΌλ‘ ν¨λ©νκ³ , μ΄κ³Όνλ©΄ μλΌλ
λλ€. (보μ κ²½κ³ )
const getEncryptionKeyBuffer = (): Buffer => {
const KEY_BYTE_LENGTH = 32;
let keyBuffer = Buffer.from(ENCRYPTION_KEY, "utf8");
if (keyBuffer.length === KEY_BYTE_LENGTH) {
return keyBuffer;
}
if (keyBuffer.length > KEY_BYTE_LENGTH) {
// 32λ°μ΄νΈ μ΄κ³Ό μ, μ λΆλΆλ§ μ¬μ© (μλΌλ)
return keyBuffer.subarray(0, KEY_BYTE_LENGTH);
} else {
// 32λ°μ΄νΈ λ―Έλ¬ μ, 0μΌλ‘ μ±μμ (ν¨λ©) 32λ°μ΄νΈλ₯Ό λ§λλλ€.
const padding = Buffer.alloc(KEY_BYTE_LENGTH - keyBuffer.length, 0);
return Buffer.concat([keyBuffer, padding]);
}
};
const JWT_SECRET = String(process.env.JWT_SECRET);
const ENCRYPTION_KEY =
process.env.ENCRYPTION_KEY || "your_32_byte_encryption_key_123456"; // 32 bytes
const ENCRYPTION_KEY_BUFFER = getEncryptionKeyBuffer();
const IV_LENGTH = 16; // AES block size
// λ¨λ°©ν₯ μνΈν: λΉλ°λ²νΈ ν΄μ μμ±
export const hashPassword = async (password: string): Promise<string> => {
const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
};
// λ¨λ°©ν₯ μνΈν: λΉλ°λ²νΈ κ²μ¦
export const comparePassword = async (
password: string,
hash: string
): Promise<boolean> => {
return await bcrypt.compare(password, hash);
};
// μλ°©ν₯ μνΈν: λ°μ΄ν° μνΈν
export const encryptData = (data: string): string => {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(
"aes-256-cbc",
ENCRYPTION_KEY_BUFFER,
iv
);
let encrypted = cipher.update(data);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return iv.toString("hex") + ":" + encrypted.toString("hex");
};
// μλ°©ν₯ μνΈν: λ°μ΄ν° 볡νΈν
export const decryptData = (encryptedData: string): string => {
const parts = encryptedData.split(":");
const iv = Buffer.from(parts[0], "hex");
const encryptedText = Buffer.from(parts[1], "hex");
const decipher = crypto.createDecipheriv(
"aes-256-cbc",
ENCRYPTION_KEY_BUFFER,
iv
);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
};
// JWT μμ±
export const generateToken = (
payload: any,
expiresIn: string = "1h"
): string => {
//@ts-ignore
return jwt.sign(payload, JWT_SECRET, { expiresIn });
};
/** JWT κ²μ¦. return := payload */
export const verifyToken = (token: string): any => {
try {
return jwt.verify(token, JWT_SECRET);
} catch (error) {
return null;
}
};
// JWT ν΄λ
(κ²μ¦ μμ΄ νμ΄λ‘λλ§ μΆμΆ)
export const decodeToken = (token: string): object | null => {
try {
const payload = token.split(".")[1];
const decoded = Buffer.from(payload, "base64").toString("utf-8");
return JSON.parse(decoded);
} catch (error) {
return null;
}
};
/**
* νμ¬ μκ°(λ°λ¦¬μ΄)κ³Ό UUIDλ₯Ό μ‘°ν©νμ¬ νμΌ μ΄λ¦μΌλ‘ μμ νκ² μ¬μ©ν μ μλ λ¬Έμμ΄μ μμ±ν©λλ€.
* μμ±λ λ¬Έμμ΄μ κΈΈμ΄λ 255μ λ―Έλ§μ
λλ€.
* (UUID: 36μ, λ°λ¦¬μ΄: μ½ 13μ, ꡬλΆμ: 1μ = μ΅λ μ½ 50μ)
* * @returns {string} μ‘°ν©λ νμΌ μ΄λ¦ λ¬Έμμ΄ (μ: "1730635200000-a1b2c3d4-e5f6-4000-8000-000000000000")
*/
export function createUniqueFileName(): string {
// 1. νμ¬ μκ°μ λ°λ¦¬μ΄ λ¨μλ‘ κ°μ Έμ΅λλ€.
const timestamp = Date.now().toString();
// 2. UUID v4λ₯Ό μμ±ν©λλ€. (μ: "a1b2c3d4-e5f6-4000-8000-000000000000")
// μ΄ λ¬Έμμ΄μ νμΌ μ΄λ¦μΌλ‘ μμ νκ² μ¬μ©λ μ μλ νμ΄νμ ν¬ν¨ν©λλ€.
const uniqueId = uuidv4();
// 3. λ κ°μ νμ΄ν(-)μΌλ‘ μ°κ²°ν©λλ€.
const uniqueFileName = `${timestamp}-${uniqueId}`;
// λ¬Έμμ΄ κΈΈμ΄ νμΈ (255μ λ―Έλ§μ νμ€ν λ§μ‘±ν©λλ€)
// console.log(`μμ±λ νμΌ μ΄λ¦: ${uniqueFileName}, κΈΈμ΄: ${uniqueFileName.length}`);
return uniqueFileName;
}
/**
* μ΄λ―Έμ§ κ²½λ‘κ° ν΄λλ‘ λμμΌλ©΄, μ΄κ±Έ μλ²μμ μ§μ μ€νΈλ¦¬λ° νλ μ£Όμλ‘ λ°κΏλλ€.
* ν΄λΉ νλ‘μ νΈ κ²μν μ μ©μΌλ‘ λ§λ€μ΄μ‘μ΅λλ€. λ²μ© μ»΄ν¬λνΈ μλλλ€.
* localhost:3000 λΆλΆμ μμμ μμ νμΈμ.
*/
export function makeBoardImgURL(data: any): string {
try {
console.log(`# mkburl data: `, data);
let metaData: any = {};
metaData.dir = data?.imgurl ?? "";
metaData.mimetype = data?.minetype ?? "";
metaData = JSON.stringify(metaData);
metaData = Buffer.from(metaData).toString("base64url");
let imgurl = `http://localhost:3000/api/stream/img?data=${metaData}`;
return imgurl;
} catch (error: any) {
console.log(`# mkburl data err: `, data);
return "/no_img.jpg";
}
}
/**
* λ¬Έμμ΄μ΄ μΌλ°μ μΈ ν΄λ/νμΌ κ²½λ‘ νμμΈμ§ κ²μ¬ν©λλ€.
* μ΄ μ κ·ννμμ μλ²½ν μ ν¨μ± κ²μ¬(μ€μ νμΌ μμ€ν
κ·μΉ)κ° μλ,
* κ²½λ‘ κ΅¬μ‘°(μ¬λμ, μ , νμΌλͺ
λ±)λ₯Ό ν¬ν¨νλμ§ νμΈνλ λ° μ€μ μ λ‘λλ€.
* @param pathString κ²μ¬ν λ¬Έμμ΄
* @returns κ²½λ‘ νμμΈ κ²½μ° true, μλλ©΄ false
*/
export function isPathFormat(pathString: string): boolean {
if (typeof pathString !== "string" || pathString.trim() === "") {
return false;
}
// ν¬κ΄μ μΈ κ²½λ‘ νμ μ κ·ννμ
// 1. λλΌμ΄λΈ λ¬Έμ (C:\) λλ μ λμ€ λ£¨νΈ (/)λ‘ μμ
// 2. κ²½λ‘ κ΅¬λΆμ (/, \)μ μΌλ°μ μΈ λ¬Έμ(λ¬Έμ, μ«μ, νμ΄ν, μΈλλ°, λ§μΉ¨ν) ν¬ν¨
// 3. UNC κ²½λ‘ (\\server\share)λ νμ©
const pathRegex = new RegExp(
/^((?:[a-zA-Z]:)?[\\\/]|\.{1,2}[\\\/]?|(?:[a-zA-Z0-9_-]+\/|\\)+|(?:[a-zA-Z]:))?(?:[a-zA-Z0-9_\-.\s]+[\\\/]?)*[a-zA-Z0-9_\-.\s]+$/,
"i" // λμλ¬Έμ κ΅¬λΆ μμ
);
// κ²½λ‘μ '?'λ '*' κ°μ glob λ¬Έμκ° ν¬ν¨λ κ²½μ°λ₯Ό λ¨μ κ²½λ‘λ‘ κ°μ£Όνμ§ μμ μ μμ΅λλ€.
// μ¬κΈ°μλ μΌλ°μ μΈ κ²½λ‘ νμλ§ νμΈν©λλ€.
return pathRegex.test(pathString);
}
|