Added the support for generating docs from template doc

This commit is contained in:
Simon Pocrnjič
2025-10-06 21:46:28 +02:00
parent 0c8d1e0b5d
commit cec5796acf
69 changed files with 4570 additions and 374 deletions
@@ -0,0 +1,51 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
Schema::create('permissions', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
Schema::create('permission_role', function (Blueprint $table) {
$table->id();
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->timestamps();
$table->unique(['permission_id', 'role_id']);
});
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
$table->unique(['role_id', 'user_id']);
});
}
public function down(): void
{
Schema::dropIfExists('role_user');
Schema::dropIfExists('permission_role');
Schema::dropIfExists('permissions');
Schema::dropIfExists('roles');
}
};
@@ -0,0 +1,151 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (Schema::getConnection()->getDriverName() === 'sqlite') {
// Rebuild role_user without surrogate id
if (Schema::hasTable('role_user')) {
Schema::create('role_user_temp', function (Blueprint $table) {
$table->foreignId('role_id');
$table->foreignId('user_id');
$table->timestamps();
});
// Copy distinct rows
try {
\DB::statement('INSERT INTO role_user_temp (role_id, user_id, created_at, updated_at) SELECT role_id, user_id, created_at, updated_at FROM role_user GROUP BY role_id, user_id');
} catch (Throwable $e) { /* ignore */
}
Schema::drop('role_user');
Schema::rename('role_user_temp', 'role_user');
// Add composite primary key via raw SQL
try {
\DB::statement('CREATE UNIQUE INDEX role_user_role_user_unique ON role_user(role_id, user_id)');
} catch (Throwable $e) { /* ignore */
}
}
if (Schema::hasTable('permission_role')) {
Schema::create('permission_role_temp', function (Blueprint $table) {
$table->foreignId('permission_id');
$table->foreignId('role_id');
$table->timestamps();
});
try {
\DB::statement('INSERT INTO permission_role_temp (permission_id, role_id, created_at, updated_at) SELECT permission_id, role_id, created_at, updated_at FROM permission_role GROUP BY permission_id, role_id');
} catch (Throwable $e) { /* ignore */
}
Schema::drop('permission_role');
Schema::rename('permission_role_temp', 'permission_role');
try {
\DB::statement('CREATE UNIQUE INDEX permission_role_permission_role_unique ON permission_role(permission_id, role_id)');
} catch (Throwable $e) { /* ignore */
}
}
return; // sqlite path done
}
// role_user
if (Schema::hasColumn('role_user', 'id')) {
// Drop id column; Postgres requires dropping pk constraint implicitly named maybe 'role_user_pkey'
// Attempt raw drop primary if exists
try {
\DB::statement('ALTER TABLE role_user DROP CONSTRAINT role_user_pkey');
} catch (Throwable $e) { /* ignore */
}
Schema::table('role_user', function (Blueprint $table) {
$table->dropColumn('id');
});
}
// Add composite primary key if not present
try {
\DB::statement('ALTER TABLE role_user ADD PRIMARY KEY (role_id, user_id)');
} catch (Throwable $e) { /* ignore */
}
// permission_role
if (Schema::hasColumn('permission_role', 'id')) {
try {
\DB::statement('ALTER TABLE permission_role DROP CONSTRAINT permission_role_pkey');
} catch (Throwable $e) { /* ignore */
}
Schema::table('permission_role', function (Blueprint $table) {
$table->dropColumn('id');
});
}
try {
\DB::statement('ALTER TABLE permission_role ADD PRIMARY KEY (permission_id, role_id)');
} catch (Throwable $e) { /* ignore */
}
}
public function down(): void
{
if (Schema::getConnection()->getDriverName() === 'sqlite') {
// Recreate tables with id column again (best-effort)
if (Schema::hasTable('role_user')) {
Schema::create('role_user_orig', function (Blueprint $table) {
$table->id();
$table->foreignId('role_id');
$table->foreignId('user_id');
$table->timestamps();
});
try {
\DB::statement('INSERT INTO role_user_orig (role_id, user_id, created_at, updated_at) SELECT role_id, user_id, created_at, updated_at FROM role_user');
} catch (Throwable $e) { /* ignore */
}
Schema::drop('role_user');
Schema::rename('role_user_orig', 'role_user');
try {
\DB::statement('CREATE UNIQUE INDEX role_user_role_user_unique ON role_user(role_id, user_id)');
} catch (Throwable $e) { /* ignore */
}
}
if (Schema::hasTable('permission_role')) {
Schema::create('permission_role_orig', function (Blueprint $table) {
$table->id();
$table->foreignId('permission_id');
$table->foreignId('role_id');
$table->timestamps();
});
try {
\DB::statement('INSERT INTO permission_role_orig (permission_id, role_id, created_at, updated_at) SELECT permission_id, role_id, created_at, updated_at FROM permission_role');
} catch (Throwable $e) { /* ignore */
}
Schema::drop('permission_role');
Schema::rename('permission_role_orig', 'permission_role');
try {
\DB::statement('CREATE UNIQUE INDEX permission_role_permission_role_unique ON permission_role(permission_id, role_id)');
} catch (Throwable $e) { /* ignore */
}
}
return;
}
// Re-add id columns (simple auto increment) and drop composite PKs
Schema::table('role_user', function (Blueprint $table) {
try {
$table->dropPrimary();
} catch (Throwable $e) { /* ignore */
}
if (! Schema::hasColumn('role_user', 'id')) {
$table->id()->first();
}
$table->unique(['role_id', 'user_id']);
});
Schema::table('permission_role', function (Blueprint $table) {
try {
$table->dropPrimary();
} catch (Throwable $e) { /* ignore */
}
if (! Schema::hasColumn('permission_role', 'id')) {
$table->id()->first();
}
$table->unique(['permission_id', 'role_id']);
});
}
};
@@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('document_templates', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('custom_name')->nullable();
$table->text('description')->nullable();
$table->string('core_entity'); // e.g. 'contract'
$table->json('entities'); // list of related entities allowed
$table->json('columns'); // map entity => allowed columns
$table->unsignedInteger('version')->default(1);
$table->string('engine')->default('tokens');
$table->string('file_path');
$table->string('file_hash', 64);
$table->unsignedBigInteger('file_size');
$table->string('mime_type', 120);
$table->boolean('active')->default(true);
$table->foreignId('created_by')->constrained('users');
$table->foreignId('updated_by')->nullable()->constrained('users');
$table->timestamps();
$table->index(['core_entity', 'active']);
});
Schema::table('documents', function (Blueprint $table) {
if (! Schema::hasColumn('documents', 'template_id')) {
$table->foreignId('template_id')->nullable()->after('uuid')->constrained('document_templates');
}
if (! Schema::hasColumn('documents', 'template_version')) {
$table->unsignedInteger('template_version')->nullable()->after('template_id');
}
});
}
public function down(): void
{
Schema::table('documents', function (Blueprint $table) {
if (Schema::hasColumn('documents', 'template_version')) {
$table->dropColumn('template_version');
}
if (Schema::hasColumn('documents', 'template_id')) {
$table->dropConstrainedForeignId('template_id');
}
});
Schema::dropIfExists('document_templates');
}
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->json('tokens')->nullable()->after('columns');
});
}
public function down(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->dropColumn('tokens');
});
}
};
@@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->string('output_filename_pattern')->nullable()->after('file_size');
$table->string('date_format')->nullable()->after('output_filename_pattern');
$table->boolean('fail_on_unresolved')->default(true)->after('date_format');
});
}
public function down(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->dropColumn(['output_filename_pattern', 'date_format', 'fail_on_unresolved']);
});
}
};
@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('document_generation_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('document_id')->constrained('documents')->cascadeOnDelete();
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
$table->string('ip')->nullable();
$table->string('user_agent')->nullable();
$table->json('context')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('document_generation_logs');
}
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->json('formatting_options')->nullable()->after('fail_on_unresolved');
});
}
public function down(): void
{
Schema::table('document_templates', function (Blueprint $table) {
$table->dropColumn('formatting_options');
});
}
};
@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('document_settings', function (Blueprint $table) {
$table->id();
$table->string('file_name_pattern')->nullable();
$table->string('date_format')->nullable();
$table->string('unresolved_policy')->nullable();
$table->boolean('preview_enabled')->default(true);
$table->json('whitelist')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('document_settings');
}
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('document_settings', function (Blueprint $table) {
$table->json('date_formats')->nullable()->after('whitelist');
});
}
public function down(): void
{
Schema::table('document_settings', function (Blueprint $table) {
$table->dropColumn('date_formats');
});
}
};
@@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('document_templates', function (Blueprint $table) {
if (! Schema::hasColumn('document_templates', 'meta')) {
$table->json('meta')->nullable()->after('formatting_options');
}
if (! Schema::hasColumn('document_templates', 'action_id')) {
$table->foreignId('action_id')->nullable()->after('meta')->constrained()->nullOnDelete();
}
if (! Schema::hasColumn('document_templates', 'decision_id')) {
$table->foreignId('decision_id')->nullable()->after('action_id')->constrained()->nullOnDelete();
}
if (! Schema::hasColumn('document_templates', 'activity_note_template')) {
$table->text('activity_note_template')->nullable()->after('decision_id');
}
$table->index(['action_id', 'decision_id'], 'document_templates_action_decision_idx');
});
}
public function down(): void
{
Schema::table('document_templates', function (Blueprint $table) {
if (Schema::hasColumn('document_templates', 'activity_note_template')) {
$table->dropColumn('activity_note_template');
}
if (Schema::hasColumn('document_templates', 'decision_id')) {
$table->dropConstrainedForeignId('decision_id');
}
if (Schema::hasColumn('document_templates', 'action_id')) {
$table->dropConstrainedForeignId('action_id');
}
if (Schema::hasColumn('document_templates', 'meta')) {
$table->dropColumn('meta');
}
if (Schema::hasColumn('document_templates', 'action_id') || Schema::hasColumn('document_templates', 'decision_id')) {
$table->dropIndex('document_templates_action_decision_idx');
}
});
}
};