Files
the_order/packages/database/src/document-versions.ts
defiQUG 6a8582e54d feat: comprehensive project structure improvements and Cloud for Sovereignty landing zone
- Add Cloud for Sovereignty landing zone architecture and deployment
- Implement complete legal document management system
- Reorganize documentation with improved navigation
- Add infrastructure improvements (Dockerfiles, K8s, monitoring)
- Add operational improvements (graceful shutdown, rate limiting, caching)
- Create comprehensive project structure documentation
- Add Azure deployment automation scripts
- Improve repository navigation and organization
2025-11-13 09:32:55 -08:00

269 lines
6.8 KiB
TypeScript

/**
* Document Version Management
* Handles document versioning, revision history, and version control
*/
import { query } from './client';
import { z } from 'zod';
export const DocumentVersionSchema = z.object({
id: z.string().uuid(),
document_id: z.string().uuid(),
version_number: z.number().int().positive(),
version_label: z.string().optional(),
content: z.string().optional(),
file_url: z.string().url().optional(),
file_hash: z.string().optional(),
file_size: z.number().int().nonnegative().optional(),
mime_type: z.string().optional(),
change_summary: z.string().optional(),
change_type: z.enum(['created', 'modified', 'restored', 'merged']),
created_by: z.string().uuid().optional(),
created_at: z.date(),
});
export type DocumentVersion = z.infer<typeof DocumentVersionSchema>;
export interface CreateDocumentVersionInput {
document_id: string;
version_number?: number;
version_label?: string;
content?: string;
file_url?: string;
file_hash?: string;
file_size?: number;
mime_type?: string;
change_summary?: string;
change_type?: 'created' | 'modified' | 'restored' | 'merged';
created_by?: string;
}
/**
* Create a new document version
*/
export async function createDocumentVersion(
input: CreateDocumentVersionInput
): Promise<DocumentVersion> {
// Get next version number if not provided
let version_number = input.version_number;
if (!version_number) {
const maxVersion = await query<{ max_version: number | null }>(
`SELECT MAX(version_number) as max_version
FROM document_versions
WHERE document_id = $1`,
[input.document_id]
);
version_number = (maxVersion.rows[0]?.max_version || 0) + 1;
}
const result = await query<DocumentVersion>(
`INSERT INTO document_versions
(document_id, version_number, version_label, content, file_url, file_hash,
file_size, mime_type, change_summary, change_type, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING *`,
[
input.document_id,
version_number,
input.version_label || null,
input.content || null,
input.file_url || null,
input.file_hash || null,
input.file_size || null,
input.mime_type || null,
input.change_summary || null,
input.change_type || 'modified',
input.created_by || null,
]
);
const version = result.rows[0]!;
// Update document's current version
await query(
`UPDATE documents
SET current_version = $1, latest_version_id = $2, updated_at = NOW()
WHERE id = $3`,
[version_number, version.id, input.document_id]
);
return version;
}
/**
* Get document version by ID
*/
export async function getDocumentVersion(id: string): Promise<DocumentVersion | null> {
const result = await query<DocumentVersion>(
`SELECT * FROM document_versions WHERE id = $1`,
[id]
);
return result.rows[0] || null;
}
/**
* Get all versions for a document
*/
export async function getDocumentVersions(
document_id: string,
limit = 100,
offset = 0
): Promise<DocumentVersion[]> {
const result = await query<DocumentVersion>(
`SELECT * FROM document_versions
WHERE document_id = $1
ORDER BY version_number DESC
LIMIT $2 OFFSET $3`,
[document_id, limit, offset]
);
return result.rows;
}
/**
* Get specific version by number
*/
export async function getDocumentVersionByNumber(
document_id: string,
version_number: number
): Promise<DocumentVersion | null> {
const result = await query<DocumentVersion>(
`SELECT * FROM document_versions
WHERE document_id = $1 AND version_number = $2`,
[document_id, version_number]
);
return result.rows[0] || null;
}
/**
* Get latest version of a document
*/
export async function getLatestDocumentVersion(
document_id: string
): Promise<DocumentVersion | null> {
const result = await query<DocumentVersion>(
`SELECT * FROM document_versions
WHERE document_id = $1
ORDER BY version_number DESC
LIMIT 1`,
[document_id]
);
return result.rows[0] || null;
}
/**
* Compare two document versions
*/
export interface VersionComparison {
version1: DocumentVersion;
version2: DocumentVersion;
differences: {
field: string;
old_value: unknown;
new_value: unknown;
}[];
}
export async function compareDocumentVersions(
version1_id: string,
version2_id: string
): Promise<VersionComparison | null> {
const v1 = await getDocumentVersion(version1_id);
const v2 = await getDocumentVersion(version2_id);
if (!v1 || !v2) {
return null;
}
const differences: VersionComparison['differences'] = [];
if (v1.content !== v2.content) {
differences.push({
field: 'content',
old_value: v1.content,
new_value: v2.content,
});
}
if (v1.file_url !== v2.file_url) {
differences.push({
field: 'file_url',
old_value: v1.file_url,
new_value: v2.file_url,
});
}
if (v1.file_hash !== v2.file_hash) {
differences.push({
field: 'file_hash',
old_value: v1.file_hash,
new_value: v2.file_hash,
});
}
if (v1.change_summary !== v2.change_summary) {
differences.push({
field: 'change_summary',
old_value: v1.change_summary,
new_value: v2.change_summary,
});
}
return {
version1: v1,
version2: v2,
differences,
};
}
/**
* Restore a document to a previous version
*/
export async function restoreDocumentVersion(
document_id: string,
version_id: string,
restored_by: string,
change_summary?: string
): Promise<DocumentVersion> {
const targetVersion = await getDocumentVersion(version_id);
if (!targetVersion || targetVersion.document_id !== document_id) {
throw new Error('Version not found or does not belong to document');
}
// Create new version from the restored one
return createDocumentVersion({
document_id,
version_label: `Restored from v${targetVersion.version_number}`,
content: targetVersion.content,
file_url: targetVersion.file_url,
file_hash: targetVersion.file_hash,
file_size: targetVersion.file_size,
mime_type: targetVersion.mime_type,
change_summary: change_summary || `Restored from version ${targetVersion.version_number}`,
change_type: 'restored',
created_by: restored_by,
});
}
/**
* Get version history with change summaries
*/
export interface VersionHistoryEntry extends DocumentVersion {
created_by_name?: string;
created_by_email?: string;
}
export async function getDocumentVersionHistory(
document_id: string
): Promise<VersionHistoryEntry[]> {
const result = await query<VersionHistoryEntry>(
`SELECT dv.*, u.name as created_by_name, u.email as created_by_email
FROM document_versions dv
LEFT JOIN users u ON dv.created_by = u.id
WHERE dv.document_id = $1
ORDER BY dv.version_number DESC`,
[document_id]
);
return result.rows;
}