fix(exports): stabilize published types and compatibility with updated dependencies

This commit is contained in:
2026-04-16 19:41:55 +00:00
parent cb6b3db15a
commit 40ffc2b355
25 changed files with 4539 additions and 5354 deletions
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@fin.cx/skr',
version: '1.2.1',
version: '1.2.2',
description: 'SKR03 and SKR04 German accounting standards for double-entry bookkeeping'
}
+43 -15
View File
@@ -1,16 +1,44 @@
export * from './skr.types.js';
export * from './skr.classes.account.js';
export * from './skr.classes.transaction.js';
export * from './skr.classes.journalentry.js';
export * from './skr.classes.chartofaccounts.js';
export * from './skr.classes.ledger.js';
export * from './skr.classes.reports.js';
export * from './skr.api.js';
export * from './skr03.data.js';
export * from './skr04.data.js';
export * from './skr.export.js';
export * from './skr.export.ledger.js';
export * from './skr.export.accounts.js';
export * from './skr.export.balances.js';
export * from './skr.export.pdf.js';
export * from './skr.security.js';
export { Account } from './skr.classes.account.js';
export { Transaction } from './skr.classes.transaction.js';
export { JournalEntry } from './skr.classes.journalentry.js';
export { ChartOfAccounts } from './skr.classes.chartofaccounts.js';
export { Ledger } from './skr.classes.ledger.js';
export { Reports } from './skr.classes.reports.js';
export { SkrApi } from './skr.api.js';
export { SKR03_ACCOUNTS, SKR03_ACCOUNT_CLASSES } from './skr03.data.js';
export { SKR04_ACCOUNTS, SKR04_ACCOUNT_CLASSES } from './skr04.data.js';
export { SkrExport } from './skr.export.js';
export type {
IExportOptions,
IExportMetadata,
IBagItManifest,
IDocumentIndex,
} from './skr.export.js';
export { LedgerExporter } from './skr.export.ledger.js';
export type {
ITransactionDataExport,
IJournalEntryExport,
IJournalEntryLineExport,
ILedgerEntry,
ILedgerLine,
IDocumentRef,
} from './skr.export.ledger.js';
export { AccountsExporter } from './skr.export.accounts.js';
export type {
IAccountDataExport,
IAccountExportRow,
} from './skr.export.accounts.js';
export { BalancesExporter } from './skr.export.balances.js';
export type {
IAccountBalanceExport,
IBalanceExportRow,
} from './skr.export.balances.js';
export { PdfReportGenerator } from './skr.export.pdf.js';
export type { IPdfReportOptions } from './skr.export.pdf.js';
export { SecurityManager } from './skr.security.js';
export type {
ISigningOptions,
ISignatureResult,
ITimestampResponse,
} from './skr.security.js';
+53 -16
View File
@@ -3,26 +3,63 @@ import * as smartdata from '@push.rocks/smartdata';
import * as smartunique from '@push.rocks/smartunique';
import * as smarttime from '@push.rocks/smarttime';
import * as smartlog from '@push.rocks/smartlog';
import * as smartfile from '@push.rocks/smartfile';
import * as smartfsModule from '@push.rocks/smartfs';
import * as smarthash from '@push.rocks/smarthash';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpdf from '@push.rocks/smartpdf';
import * as path from 'path';
// third party
import * as nodeForge from 'node-forge';
import { MerkleTree } from 'merkletreejs';
import * as einvoice from '@fin.cx/einvoice';
export {
smartdata,
smartunique,
smarttime,
smartlog,
smartfile,
smarthash,
smartpath,
smartpdf,
nodeForge,
MerkleTree,
einvoice
const smartfs = new smartfsModule.SmartFs(
new smartfsModule.SmartFsProviderNode(),
);
const smartfile = {
fs: {
ensureDir: async (dirPath: string): Promise<void> => {
await smartfs.directory(dirPath).create();
},
toBuffer: async (filePath: string): Promise<Buffer> => {
return (await smartfs.file(filePath).read()) as Buffer;
},
toStringSync: async (filePath: string): Promise<string> => {
return (await smartfs.file(filePath).encoding('utf8').read()) as string;
},
fileExists: async (filePath: string): Promise<boolean> => {
return await smartfs.file(filePath).exists();
},
listFileTree: async (dirPath: string, pattern: string): Promise<string[]> => {
const suffix = pattern.replace(/^\*\*\/\*/, '');
try {
const entries = await smartfs.directory(dirPath).recursive().list();
return entries
.filter((entry) => entry.isFile && entry.path.endsWith(suffix))
.map((entry) => path.relative(dirPath, entry.path));
} catch (error) {
if (error instanceof Error && error.message.includes('ENOENT')) {
return [];
}
throw error;
}
},
},
memory: {
toFs: async (content: string | Buffer, filePath: string): Promise<void> => {
await smartfs.directory(path.dirname(filePath)).create();
await smartfs.file(filePath).write(content);
},
},
};
export {
smartdata,
smartunique,
smarttime,
smartlog,
smartfs,
smartfile,
smarthash,
smartpath,
MerkleTree,
};
+9 -5
View File
@@ -534,6 +534,10 @@ export class SkrApi {
*/
private async generatePdfReports(exporter: SkrExport, options: IExportOptions): Promise<void> {
if (!this.reports) throw new Error('Reports not initialized');
const skrType = this.currentSKRType;
if (!skrType) {
throw new Error('API not initialized. Call initialize() first.');
}
const pdfOptions: IPdfReportOptions = {
companyName: options.companyInfo?.name || 'Unternehmen',
@@ -554,19 +558,19 @@ export class SkrApi {
const trialBalance = await this.reports.getTrialBalance({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
skrType,
});
const incomeStatement = await this.reports.getIncomeStatement({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
skrType,
});
const balanceSheet = await this.reports.getBalanceSheet({
dateFrom: options.dateFrom,
dateTo: options.dateTo,
skrType: this.currentSKRType
skrType,
});
// Generate PDFs
@@ -702,7 +706,7 @@ export class SkrApi {
const transaction = await this.postTransaction(transactions[i]);
results.push(transaction);
} catch (error) {
errors.push({ index: i, error: error.message });
errors.push({ index: i, error: error instanceof Error ? error.message : String(error) });
}
}
@@ -735,7 +739,7 @@ export class SkrApi {
const account = await this.createAccount(accounts[i]);
results.push(account);
} catch (error) {
errors.push({ index: i, error: error.message });
errors.push({ index: i, error: error instanceof Error ? error.message : String(error) });
}
}
+39 -21
View File
@@ -2,68 +2,86 @@ import * as plugins from './plugins.js';
import { getDb, getDbSync } from './skr.database.js';
import type { TAccountType, TSKRType, IAccountData } from './skr.types.js';
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
declare abstract class SmartDataDbDocBase {
public save(): Promise<void>;
public delete(): Promise<void>;
public static getInstance<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T | null>;
public static getInstances<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T[]>;
}
@plugins.smartdata.Collection(() => getDbSync())
export class Account extends SmartDataDbDoc<Account, Account> {
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
const Collection = plugins.smartdata.Collection as any;
const svDb = plugins.smartdata.svDb as any;
const unI = plugins.smartdata.unI as any;
const index = plugins.smartdata.index as any;
const searchable = plugins.smartdata.searchable as any;
@Collection(() => getDbSync())
export class Account extends SmartDataDbDoc {
@unI()
public id: string;
public id!: string;
@svDb()
@index()
public accountNumber: string;
public accountNumber!: string;
@svDb()
@searchable()
public accountName: string;
public accountName!: string;
@svDb()
@index()
public accountClass: number;
public accountClass!: number;
@svDb()
public accountGroup: number;
public accountGroup!: number;
@svDb()
public accountSubgroup: number;
public accountSubgroup!: number;
@svDb()
public accountType: TAccountType;
public accountType!: TAccountType;
@svDb()
@index()
public skrType: TSKRType;
public skrType!: TSKRType;
@svDb()
@searchable()
public description: string;
public description!: string;
@svDb()
public vatRate: number;
public vatRate!: number;
@svDb()
public balance: number;
public balance!: number;
@svDb()
public debitTotal: number;
public debitTotal!: number;
@svDb()
public creditTotal: number;
public creditTotal!: number;
@svDb()
public isActive: boolean;
public isActive!: boolean;
@svDb()
public isSystemAccount: boolean;
public isSystemAccount!: boolean;
@svDb()
public isAutomaticAccount: boolean;
public isAutomaticAccount!: boolean;
@svDb()
public createdAt: Date;
public createdAt!: Date;
@svDb()
public updatedAt: Date;
public updatedAt!: Date;
constructor(data?: Partial<IAccountData>) {
super();
+8 -3
View File
@@ -262,6 +262,9 @@ export class ChartOfAccounts {
* Search accounts
*/
public async searchAccounts(searchTerm: string): Promise<Account[]> {
if (!this.skrType) {
throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.');
}
return await Account.searchAccounts(searchTerm, this.skrType);
}
@@ -287,10 +290,11 @@ export class ChartOfAccounts {
// Apply text search if provided
if (filter?.searchTerm) {
const lowerSearchTerm = filter.searchTerm.toLowerCase();
const searchTerm = filter.searchTerm;
const lowerSearchTerm = searchTerm.toLowerCase();
return accounts.filter(
(account) =>
account.accountNumber.includes(filter.searchTerm) ||
account.accountNumber.includes(searchTerm) ||
account.accountName.toLowerCase().includes(lowerSearchTerm) ||
account.description.toLowerCase().includes(lowerSearchTerm),
);
@@ -468,9 +472,10 @@ export class ChartOfAccounts {
await this.createCustomAccount(accountData);
importedCount++;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.log(
'warn',
`Failed to import account ${parts[0]}: ${error.message}`,
`Failed to import account ${parts[0]}: ${errorMessage}`,
);
}
}
+40 -22
View File
@@ -13,67 +13,85 @@ import type {
IJournalEntryLine,
} from './skr.types.js';
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
declare abstract class SmartDataDbDocBase {
public save(): Promise<void>;
public delete(): Promise<void>;
public static getInstance<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T | null>;
public static getInstances<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T[]>;
}
@plugins.smartdata.Collection(() => getDbSync())
export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
const Collection = plugins.smartdata.Collection as any;
const svDb = plugins.smartdata.svDb as any;
const unI = plugins.smartdata.unI as any;
const index = plugins.smartdata.index as any;
const searchable = plugins.smartdata.searchable as any;
@Collection(() => getDbSync())
export class JournalEntry extends SmartDataDbDoc {
@unI()
public id: string;
public id!: string;
@svDb()
@index()
public journalNumber: string;
public journalNumber!: string;
@svDb()
@index()
public date: Date;
public date!: Date;
@svDb()
@searchable()
public description: string;
public description!: string;
@svDb()
@index()
public reference: string;
public reference!: string;
@svDb()
public lines: IJournalEntryLine[];
public lines!: IJournalEntryLine[];
@svDb()
@index()
public skrType: TSKRType;
public skrType!: TSKRType;
@svDb()
public totalDebits: number;
public totalDebits!: number;
@svDb()
public totalCredits: number;
public totalCredits!: number;
@svDb()
public isBalanced: boolean;
public isBalanced!: boolean;
@svDb()
@index()
public status: 'draft' | 'posted' | 'reversed';
public status!: 'draft' | 'posted' | 'reversed';
@svDb()
public transactionIds: string[];
public transactionIds!: string[];
@svDb()
@index()
public period: string;
public period!: string;
@svDb()
public fiscalYear: number;
public fiscalYear!: number;
@svDb()
public createdAt: Date;
public createdAt!: Date;
@svDb()
public postedAt: Date;
public postedAt!: Date | null;
@svDb()
public createdBy: string;
public createdBy!: string;
constructor(data?: Partial<IJournalEntry>) {
super();
@@ -240,7 +258,7 @@ export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
try {
await Account.validateAccountForPosting(line.accountNumber, this.skrType);
} catch (error) {
validationErrors.push(error.message);
validationErrors.push(error instanceof Error ? error.message : String(error));
continue; // Skip further validation for this line
}
@@ -329,7 +347,7 @@ export class JournalEntry extends SmartDataDbDoc<JournalEntry, JournalEntry> {
date: this.date,
debitAccount: debitLines[0].accountNumber,
creditAccount: creditLines[0].accountNumber,
amount: debitLines[0].debit,
amount: debitLines[0].debit || 0,
description: this.description,
reference: this.reference,
skrType: this.skrType,
+22 -2
View File
@@ -410,7 +410,20 @@ export class Reports {
isActive: true,
});
const ledgerEntries = [];
const ledgerEntries: Array<{
accountNumber: string;
accountName: string;
accountType: string;
entries: Array<{
date: Date;
reference: string;
description: string;
debit: number;
credit: number;
balance: number;
}>;
finalBalance: number;
}> = [];
for (const account of accounts) {
const transactions = await this.getAccountTransactions(
@@ -420,7 +433,14 @@ export class Reports {
if (transactions.length > 0) {
let runningBalance = 0;
const accountEntries = [];
const accountEntries: Array<{
date: Date;
reference: string;
description: string;
debit: number;
credit: number;
balance: number;
}> = [];
for (const transaction of transactions) {
const isDebit = transaction.debitAccount === account.accountNumber;
+40 -22
View File
@@ -7,75 +7,93 @@ import type {
ITransactionData,
} from './skr.types.js';
const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;
declare abstract class SmartDataDbDocBase {
public save(): Promise<void>;
public delete(): Promise<void>;
public static getInstance<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T | null>;
public static getInstances<T>(
this: new (...args: any[]) => T,
query: Record<string, any>,
): Promise<T[]>;
}
@plugins.smartdata.Collection(() => getDbSync())
export class Transaction extends SmartDataDbDoc<Transaction, Transaction> {
const SmartDataDbDoc = plugins.smartdata.SmartDataDbDoc as unknown as typeof SmartDataDbDocBase;
const Collection = plugins.smartdata.Collection as any;
const svDb = plugins.smartdata.svDb as any;
const unI = plugins.smartdata.unI as any;
const index = plugins.smartdata.index as any;
const searchable = plugins.smartdata.searchable as any;
@Collection(() => getDbSync())
export class Transaction extends SmartDataDbDoc {
@unI()
public id: string;
public id!: string;
@svDb()
@index()
public transactionNumber: string;
public transactionNumber!: string;
@svDb()
@index()
public date: Date;
public date!: Date;
@svDb()
@index()
public debitAccount: string;
public debitAccount!: string;
@svDb()
@index()
public creditAccount: string;
public creditAccount!: string;
@svDb()
public amount: number;
public amount!: number;
@svDb()
@searchable()
public description: string;
public description!: string;
@svDb()
@index()
public reference: string;
public reference!: string;
@svDb()
@index()
public skrType: TSKRType;
public skrType!: TSKRType;
@svDb()
public vatAmount: number;
public vatAmount!: number;
@svDb()
public costCenter: string;
public costCenter!: string;
@svDb()
@index()
public status: TTransactionStatus;
public status!: TTransactionStatus;
@svDb()
public reversalOf: string;
public reversalOf!: string;
@svDb()
public reversedBy: string;
public reversedBy!: string;
@svDb()
@index()
public period: string; // Format: YYYY-MM
public period!: string; // Format: YYYY-MM
@svDb()
public fiscalYear: number;
public fiscalYear!: number;
@svDb()
public createdAt: Date;
public createdAt!: Date;
@svDb()
public postedAt: Date;
public postedAt!: Date | null;
@svDb()
public createdBy: string;
public createdBy!: string;
constructor(data?: Partial<ITransactionData>) {
super();
+4 -3
View File
@@ -1,5 +1,6 @@
import * as plugins from './plugins.js';
import * as path from 'path';
import { SmartPdf } from '@push.rocks/smartpdf';
import type { ITrialBalanceReport, IIncomeStatement, IBalanceSheet } from './skr.types.js';
export interface IPdfReportOptions {
@@ -17,7 +18,7 @@ export interface IPdfReportOptions {
export class PdfReportGenerator {
private exportPath: string;
private options: IPdfReportOptions;
private pdfInstance: plugins.smartpdf.SmartPdf | null = null;
private pdfInstance: SmartPdf | null = null;
constructor(exportPath: string, options: IPdfReportOptions) {
this.exportPath = exportPath;
@@ -28,7 +29,7 @@ export class PdfReportGenerator {
* Initializes the PDF generator
*/
public async initialize(): Promise<void> {
this.pdfInstance = new plugins.smartpdf.SmartPdf();
this.pdfInstance = new SmartPdf();
await this.pdfInstance.start();
}
@@ -598,4 +599,4 @@ export class PdfReportGenerator {
this.pdfInstance = null;
}
}
}
}
+31 -11
View File
@@ -18,11 +18,25 @@ import type {
*/
export class InvoiceAdapter {
private logger: plugins.smartlog.ConsoleLog;
private readonly einvoiceModuleName = '@fin.cx/einvoice';
constructor() {
this.logger = new plugins.smartlog.ConsoleLog();
}
private async getEInvoiceClass(): Promise<{
new (): any;
fromXml(xmlString: string): Promise<any>;
}> {
const { EInvoice } = (await import(this.einvoiceModuleName)) as {
EInvoice: {
new (): any;
fromXml(xmlString: string): Promise<any>;
};
};
return EInvoice;
}
private readonly MAX_XML_SIZE = 10 * 1024 * 1024; // 10MB max
private readonly MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB max
@@ -44,13 +58,14 @@ export class InvoiceAdapter {
}
// Parse the invoice using @fin.cx/einvoice
let einvoice;
const EInvoice = await this.getEInvoiceClass();
let einvoice: any;
if (typeof file === 'string') {
einvoice = await plugins.einvoice.EInvoice.fromXml(file);
einvoice = await EInvoice.fromXml(file);
} else {
// Convert buffer to string first
const xmlString = file.toString('utf-8');
einvoice = await plugins.einvoice.EInvoice.fromXml(xmlString);
einvoice = await EInvoice.fromXml(xmlString);
}
// Get detected format
@@ -74,7 +89,7 @@ export class InvoiceAdapter {
invoice.xmlContent = einvoice.getXml();
// Calculate content hash
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent);
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent!);
// Classify tax scenario
invoice.taxScenario = this.classifyTaxScenario(invoice);
@@ -82,7 +97,8 @@ export class InvoiceAdapter {
return invoice;
} catch (error) {
this.logger.log('error', `Failed to parse invoice: ${error}`);
throw new Error(`Invoice parsing failed: ${error.message}`);
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Invoice parsing failed: ${errorMessage}`);
}
}
@@ -310,7 +326,7 @@ export class InvoiceAdapter {
* Get exemption reason for VAT category
*/
private getExemptionReason(categoryCode: string): string | undefined {
const exemptionReasons: Record<string, string> = {
const exemptionReasons: Record<string, string | undefined> = {
'E': 'Tax exempt',
'Z': 'Zero rated',
'AE': 'Reverse charge (§13b UStG)',
@@ -516,7 +532,8 @@ export class InvoiceAdapter {
): Promise<string> {
try {
// Load from existing XML
const einvoice = await plugins.einvoice.EInvoice.fromXml(invoice.xmlContent!);
const EInvoice = await this.getEInvoiceClass();
const einvoice: any = await EInvoice.fromXml(invoice.xmlContent!);
// Convert to target format (takes ~0.6ms)
const convertedXml = await einvoice.exportXml(targetFormat as any);
@@ -524,7 +541,8 @@ export class InvoiceAdapter {
return convertedXml;
} catch (error) {
this.logger.log('error', `Failed to convert invoice format: ${error}`);
throw new Error(`Format conversion failed: ${error.message}`);
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Format conversion failed: ${errorMessage}`);
}
}
@@ -537,7 +555,8 @@ export class InvoiceAdapter {
): Promise<{ xml: string; pdf?: Buffer }> {
try {
// Create a new invoice instance
const einvoice = new plugins.einvoice.EInvoice();
const EInvoice = await this.getEInvoiceClass();
const einvoice: any = new EInvoice();
// Set invoice data
const businessTerms = this.mapToBusinessTerms(invoiceData);
@@ -558,7 +577,8 @@ export class InvoiceAdapter {
return { xml, pdf };
} catch (error) {
this.logger.log('error', `Failed to generate invoice: ${error}`);
throw new Error(`Invoice generation failed: ${error.message}`);
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Invoice generation failed: ${errorMessage}`);
}
}
@@ -578,4 +598,4 @@ export class InvoiceAdapter {
// This would be a comprehensive mapping in production
};
}
}
}
+5 -3
View File
@@ -131,10 +131,11 @@ export class InvoiceBookingEngine {
};
} catch (error) {
this.logger.log('error', `Failed to book invoice: ${error}`);
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
confidence: 0,
errors: [`Booking failed: ${error.message}`]
errors: [`Booking failed: ${errorMessage}`]
};
}
}
@@ -574,10 +575,11 @@ export class InvoiceBookingEngine {
};
} catch (error) {
this.logger.log('error', `Failed to book payment: ${error}`);
const errorMessage = error instanceof Error ? error.message : String(error);
return {
success: false,
confidence: 0,
errors: [`Payment booking failed: ${error.message}`]
errors: [`Payment booking failed: ${errorMessage}`]
};
}
}
@@ -757,4 +759,4 @@ export class InvoiceBookingEngine {
return Array.from(accounts);
}
}
}
+3 -2
View File
@@ -224,7 +224,8 @@ export class InvoiceStorage {
return contentHash;
} catch (error) {
this.logger.log('error', `Failed to store invoice: ${error}`);
throw new Error(`Invoice storage failed: ${error.message}`);
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Invoice storage failed: ${errorMessage}`);
}
}
@@ -707,4 +708,4 @@ export class InvoiceStorage {
this.logger.log('info', `Updated metadata for invoice: ${contentHash}`);
}
}
}
+23 -22
View File
@@ -2,6 +2,7 @@ import * as plugins from './plugins.js';
import * as path from 'path';
import * as crypto from 'crypto';
import * as https from 'https';
import * as nodeForge from 'node-forge';
export interface ISigningOptions {
certificatePem?: string;
@@ -57,19 +58,19 @@ export class SecurityManager {
try {
// Parse certificate and key
const certificate = plugins.nodeForge.pki.certificateFromPem(cert);
const certificate = nodeForge.pki.certificateFromPem(cert);
const privateKey = this.options.privateKeyPassphrase
? plugins.nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase)
: plugins.nodeForge.pki.privateKeyFromPem(key);
? nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase)
: nodeForge.pki.privateKeyFromPem(key);
// Create PKCS#7 signed data (CMS)
const p7 = plugins.nodeForge.pkcs7.createSignedData();
const p7 = nodeForge.pkcs7.createSignedData();
// Add content
if (typeof data === 'string') {
p7.content = plugins.nodeForge.util.createBuffer(data, 'utf8');
p7.content = nodeForge.util.createBuffer(data, 'utf8');
} else {
p7.content = plugins.nodeForge.util.createBuffer(data.toString('latin1'));
p7.content = nodeForge.util.createBuffer(data.toString('latin1'));
}
// Add certificate
@@ -79,17 +80,17 @@ export class SecurityManager {
p7.addSigner({
key: privateKey,
certificate: certificate,
digestAlgorithm: plugins.nodeForge.pki.oids.sha256,
digestAlgorithm: nodeForge.pki.oids.sha256,
authenticatedAttributes: [
{
type: plugins.nodeForge.pki.oids.contentType,
value: plugins.nodeForge.pki.oids.data
type: nodeForge.pki.oids.contentType,
value: nodeForge.pki.oids.data
},
{
type: plugins.nodeForge.pki.oids.messageDigest
type: nodeForge.pki.oids.messageDigest
},
{
type: plugins.nodeForge.pki.oids.signingTime,
type: nodeForge.pki.oids.signingTime,
value: new Date().toISOString()
}
]
@@ -99,7 +100,7 @@ export class SecurityManager {
p7.sign({ detached: true });
// Convert to PEM
const pem = plugins.nodeForge.pkcs7.messageToPem(p7);
const pem = nodeForge.pkcs7.messageToPem(p7);
// Extract base64 signature
const signature = pem
@@ -237,14 +238,14 @@ export class SecurityManager {
}
// Parse the PKCS#7 message
const p7 = plugins.nodeForge.pkcs7.messageFromPem(pemSignature);
const p7 = nodeForge.pkcs7.messageFromPem(pemSignature);
// Prepare content for verification
let content: plugins.nodeForge.util.ByteStringBuffer;
let content: nodeForge.util.ByteStringBuffer;
if (typeof data === 'string') {
content = plugins.nodeForge.util.createBuffer(data, 'utf8');
content = nodeForge.util.createBuffer(data, 'utf8');
} else {
content = plugins.nodeForge.util.createBuffer(data.toString('latin1'));
content = nodeForge.util.createBuffer(data.toString('latin1'));
}
// Verify the signature
@@ -267,8 +268,8 @@ export class SecurityManager {
commonName: string = 'SKR Export System',
validDays: number = 365
): Promise<{ certificate: string; privateKey: string }> {
const keys = plugins.nodeForge.pki.rsa.generateKeyPair(2048);
const cert = plugins.nodeForge.pki.createCertificate();
const keys = nodeForge.pki.rsa.generateKeyPair(2048);
const cert = nodeForge.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '01';
@@ -326,11 +327,11 @@ export class SecurityManager {
]);
// Self-sign certificate
cert.sign(keys.privateKey, plugins.nodeForge.md.sha256.create());
cert.sign(keys.privateKey, nodeForge.md.sha256.create());
// Convert to PEM
const certificatePem = plugins.nodeForge.pki.certificateToPem(cert);
const privateKeyPem = plugins.nodeForge.pki.privateKeyToPem(keys.privateKey);
const certificatePem = nodeForge.pki.certificateToPem(cert);
const privateKeyPem = nodeForge.pki.privateKeyToPem(keys.privateKey);
return {
certificate: certificatePem,
@@ -402,4 +403,4 @@ export class SecurityManager {
return ltv;
}
}
}