store data encoded
This commit is contained in:
parent
0220f468ac
commit
297f1d8bda
8 changed files with 124 additions and 14 deletions
|
@ -24,6 +24,8 @@ JWT_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 15m
|
||||||
REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 1d
|
REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 1d
|
||||||
PWA_REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 5d
|
PWA_REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 5d
|
||||||
|
|
||||||
|
CODING_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 # besitzt default
|
||||||
|
|
||||||
MAIL_USERNAME = mail_username
|
MAIL_USERNAME = mail_username
|
||||||
MAIL_PASSWORD = mail_password
|
MAIL_PASSWORD = mail_password
|
||||||
MAIL_HOST = mail_hoststring
|
MAIL_HOST = mail_hoststring
|
||||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -12,6 +12,7 @@
|
||||||
"@ff-admin/webapi-client": "^1.1.1",
|
"@ff-admin/webapi-client": "^1.1.1",
|
||||||
"@socket.io/admin-ui": "^0.5.1",
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^5.0.0-beta.3",
|
"express": "^5.0.0-beta.3",
|
||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
|
@ -1407,6 +1408,13 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
|
||||||
|
"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/dayjs": {
|
"node_modules/dayjs": {
|
||||||
"version": "1.11.12",
|
"version": "1.11.12",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"@ff-admin/webapi-client": "^1.1.1",
|
"@ff-admin/webapi-client": "^1.1.1",
|
||||||
"@socket.io/admin-ui": "^0.5.1",
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^5.0.0-beta.3",
|
"express": "^5.0.0-beta.3",
|
||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { mission_vehicle } from "./mission_vehicle";
|
||||||
import { mission_equipment } from "./mission_equipment";
|
import { mission_equipment } from "./mission_equipment";
|
||||||
import { mission_contact } from "./mission_contact";
|
import { mission_contact } from "./mission_contact";
|
||||||
import { getTypeByORM } from "../../migrations/ormHelper";
|
import { getTypeByORM } from "../../migrations/ormHelper";
|
||||||
|
import { CodingHelper } from "../../helpers/codingHelper";
|
||||||
|
import { CODING_SECRET } from "../../env.defaults";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class mission {
|
export class mission {
|
||||||
|
@ -29,9 +31,12 @@ export class mission {
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
keyword: string;
|
keyword: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
location: string;
|
location: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
|
city: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "varchar", length: 255, default: "" })
|
||||||
others: string;
|
others: string;
|
||||||
|
|
||||||
|
@ -41,7 +46,7 @@ export class mission {
|
||||||
@Column({ type: "int", default: 0 })
|
@Column({ type: "int", default: 0 })
|
||||||
recovered: number;
|
recovered: number;
|
||||||
|
|
||||||
@Column({ type: "text", default: "[]" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET, "[]") })
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
@Column({ type: "bigint", default: 0 })
|
@Column({ type: "bigint", default: 0 })
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
|
import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
|
||||||
import { mission } from "./mission";
|
import { mission } from "./mission";
|
||||||
|
import { CodingHelper } from "../../helpers/codingHelper";
|
||||||
|
import { CODING_SECRET } from "../../env.defaults";
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class mission_contact {
|
export class mission_contact {
|
||||||
|
@ -9,19 +11,19 @@ export class mission_contact {
|
||||||
@PrimaryColumn({ type: "varchar", length: 36 })
|
@PrimaryColumn({ type: "varchar", length: 36 })
|
||||||
contactId: string;
|
contactId: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
firstname: string;
|
firstname: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
lastname: string;
|
lastname: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
phone: string;
|
phone: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
address: string;
|
address: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, default: "" })
|
@Column({ type: "text", default: "", transformer: CodingHelper.entityBaseCoding(CODING_SECRET) })
|
||||||
note: string;
|
note: string;
|
||||||
|
|
||||||
@ManyToOne(() => mission, {
|
@ManyToOne(() => mission, {
|
||||||
|
|
|
@ -16,6 +16,9 @@ export const JWT_EXPIRATION = process.env.JWT_EXPIRATION ?? "15m";
|
||||||
export const REFRESH_EXPIRATION = process.env.REFRESH_EXPIRATION ?? "1d";
|
export const REFRESH_EXPIRATION = process.env.REFRESH_EXPIRATION ?? "1d";
|
||||||
export const PWA_REFRESH_EXPIRATION = process.env.PWA_REFRESH_EXPIRATION ?? "5d";
|
export const PWA_REFRESH_EXPIRATION = process.env.PWA_REFRESH_EXPIRATION ?? "5d";
|
||||||
|
|
||||||
|
export const CODING_SECRET =
|
||||||
|
process.env.CODING_SECRET ?? "my_coding_secret_string_41YzO6JiE6iGNUZsZXX8EQa6L2DpqtdZiPK6VYRS";
|
||||||
|
|
||||||
export const MAIL_USERNAME = process.env.MAIL_USERNAME ?? "";
|
export const MAIL_USERNAME = process.env.MAIL_USERNAME ?? "";
|
||||||
export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? "";
|
export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? "";
|
||||||
export const MAIL_HOST = process.env.MAIL_HOST ?? "";
|
export const MAIL_HOST = process.env.MAIL_HOST ?? "";
|
||||||
|
@ -75,6 +78,8 @@ export function configCheck() {
|
||||||
checkMS(REFRESH_EXPIRATION, "REFRESH_EXPIRATION");
|
checkMS(REFRESH_EXPIRATION, "REFRESH_EXPIRATION");
|
||||||
checkMS(PWA_REFRESH_EXPIRATION, "PWA_REFRESH_EXPIRATION");
|
checkMS(PWA_REFRESH_EXPIRATION, "PWA_REFRESH_EXPIRATION");
|
||||||
|
|
||||||
|
if (CODING_SECRET == "" || typeof CODING_SECRET != "string") throw new Error("set valid value to JWT_SECRET");
|
||||||
|
|
||||||
if (MAIL_USERNAME == "" || typeof MAIL_USERNAME != "string") throw new Error("set valid value to MAIL_USERNAME");
|
if (MAIL_USERNAME == "" || typeof MAIL_USERNAME != "string") throw new Error("set valid value to MAIL_USERNAME");
|
||||||
if (MAIL_PASSWORD == "" || typeof MAIL_PASSWORD != "string") throw new Error("set valid value to MAIL_PASSWORD");
|
if (MAIL_PASSWORD == "" || typeof MAIL_PASSWORD != "string") throw new Error("set valid value to MAIL_PASSWORD");
|
||||||
if (MAIL_HOST == "" || typeof MAIL_HOST != "string") throw new Error("set valid value to MAIL_HOST");
|
if (MAIL_HOST == "" || typeof MAIL_HOST != "string") throw new Error("set valid value to MAIL_HOST");
|
||||||
|
|
86
src/helpers/codingHelper.ts
Normal file
86
src/helpers/codingHelper.ts
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { createCipheriv, createDecipheriv, scryptSync, randomBytes } from "crypto";
|
||||||
|
import { ValueTransformer } from "typeorm";
|
||||||
|
|
||||||
|
export abstract class CodingHelper {
|
||||||
|
private static readonly algorithm = "aes-256-gcm";
|
||||||
|
private static readonly ivLength = 16;
|
||||||
|
private static readonly authTagLength = 16;
|
||||||
|
|
||||||
|
static entityBaseCoding(key: string = "", fallback: string = ""): ValueTransformer {
|
||||||
|
return {
|
||||||
|
from(val: string | null | undefined): string {
|
||||||
|
if (!val) return fallback;
|
||||||
|
try {
|
||||||
|
return CodingHelper.decrypt(key, val) || fallback;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Decryption error:", error);
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
to(val: string | null | undefined): string {
|
||||||
|
const valueToEncrypt = val || fallback;
|
||||||
|
if (valueToEncrypt === "") return "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
return CodingHelper.encrypt(key, valueToEncrypt);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Encryption error:", error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static encrypt(phrase: string, content: string): string {
|
||||||
|
if (!content) return "";
|
||||||
|
|
||||||
|
// Generiere zufälligen IV für jede Verschlüsselung (sicherer als statischer IV)
|
||||||
|
const iv = randomBytes(this.ivLength);
|
||||||
|
const key = scryptSync(phrase, "salt", 32);
|
||||||
|
|
||||||
|
const cipher = createCipheriv(this.algorithm, Uint8Array.from(key), Uint8Array.from(iv));
|
||||||
|
|
||||||
|
// Verschlüssele den Inhalt
|
||||||
|
let encrypted = cipher.update(content, "utf8", "hex");
|
||||||
|
encrypted += cipher.final("hex");
|
||||||
|
|
||||||
|
// Speichere das Auth-Tag für GCM (wichtig für die Entschlüsselung)
|
||||||
|
const authTag = cipher.getAuthTag();
|
||||||
|
|
||||||
|
// Gib das Format: iv:verschlüsselter_text:authTag zurück
|
||||||
|
return Buffer.concat([
|
||||||
|
Uint8Array.from(iv),
|
||||||
|
Uint8Array.from(Buffer.from(encrypted, "hex")),
|
||||||
|
Uint8Array.from(authTag),
|
||||||
|
]).toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static decrypt(phrase: string, content: string): string {
|
||||||
|
if (!content) return "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dekodiere den Base64-String
|
||||||
|
const buffer = Buffer.from(content, "base64");
|
||||||
|
|
||||||
|
// Extrahiere IV, verschlüsselten Text und Auth-Tag
|
||||||
|
const iv = buffer.subarray(0, this.ivLength);
|
||||||
|
const authTag = buffer.subarray(buffer.length - this.authTagLength);
|
||||||
|
const encryptedText = buffer.subarray(this.ivLength, buffer.length - this.authTagLength).toString("hex");
|
||||||
|
|
||||||
|
const key = scryptSync(phrase, "salt", 32);
|
||||||
|
|
||||||
|
// Erstelle Decipher und setze Auth-Tag
|
||||||
|
const decipher = createDecipheriv(this.algorithm, Uint8Array.from(key), Uint8Array.from(iv));
|
||||||
|
decipher.setAuthTag(Uint8Array.from(authTag));
|
||||||
|
|
||||||
|
// Entschlüssele den Text
|
||||||
|
let decrypted = decipher.update(encryptedText, "hex", "utf8");
|
||||||
|
decrypted += decipher.final("utf8");
|
||||||
|
|
||||||
|
return decrypted;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Decryption failed:", error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,12 @@ export const mission_table = new Table({
|
||||||
{ name: "mission_start", ...getTypeByORM("datetime", true, 6), default: getDefaultByORM("null") },
|
{ name: "mission_start", ...getTypeByORM("datetime", true, 6), default: getDefaultByORM("null") },
|
||||||
{ name: "mission_end", ...getTypeByORM("datetime", true, 6), default: getDefaultByORM("null") },
|
{ name: "mission_end", ...getTypeByORM("datetime", true, 6), default: getDefaultByORM("null") },
|
||||||
{ name: "keyword", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "keyword", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
||||||
{ name: "location", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "location", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
|
{ name: "city", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
||||||
{ name: "others", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "others", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
||||||
{ name: "rescued", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
|
{ name: "rescued", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
|
||||||
{ name: "recovered", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
|
{ name: "recovered", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
|
||||||
{ name: "description", ...getTypeByORM("text"), default: getDefaultByORM("string", "[]") },
|
{ name: "description", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
{ name: "last_update", ...getTypeByORM("bigint"), default: getDefaultByORM("number", 0) },
|
{ name: "last_update", ...getTypeByORM("bigint"), default: getDefaultByORM("number", 0) },
|
||||||
{ name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp") },
|
{ name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp") },
|
||||||
],
|
],
|
||||||
|
@ -117,11 +118,11 @@ export const mission_contact_table = new Table({
|
||||||
columns: [
|
columns: [
|
||||||
{ name: "missionId", ...getTypeByORM("uuid"), isPrimary: true },
|
{ name: "missionId", ...getTypeByORM("uuid"), isPrimary: true },
|
||||||
{ name: "contactId", ...getTypeByORM("uuid"), isPrimary: true },
|
{ name: "contactId", ...getTypeByORM("uuid"), isPrimary: true },
|
||||||
{ name: "firstname", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "firstname", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
{ name: "lastname", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "lastname", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
{ name: "phone", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "phone", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
{ name: "address", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "address", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
{ name: "note", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
|
{ name: "note", ...getTypeByORM("text"), default: getDefaultByORM("string") },
|
||||||
],
|
],
|
||||||
foreignKeys: [
|
foreignKeys: [
|
||||||
new TableForeignKey({
|
new TableForeignKey({
|
||||||
|
|
Loading…
Add table
Reference in a new issue