@evenicanpm/admin-db-migrations 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,4 @@
1
+ # Connects to local Strapi PostgreSQL database server
2
+ # Ensure the database server is running and accessible at the specified URL
3
+ # Adjust the credentials and database name as necessary
4
+ DATABASE_URL=postgres://admin:admin@127.0.0.1:5432/e4-admin?sslmode=disable
File without changes
@@ -0,0 +1,41 @@
1
+ name: e4-Admin-Panel-db format and db migration
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "**"
7
+
8
+ jobs:
9
+ build:
10
+ environment: Integrate
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Checkout
15
+ uses: actions/checkout@v5
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Install SQLFluff
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install sqlfluff
26
+
27
+ - name: Run SQLFluff Lint
28
+ run: |
29
+ sqlfluff lint --dialect postgres ./db/migrations/
30
+
31
+ - name: Use Node.js
32
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
33
+ uses: actions/setup-node@v4
34
+ with:
35
+ node-version: 22
36
+
37
+ - name: Create database and run migrations
38
+ if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
39
+ run: |
40
+ npx dbmate -u ${{ secrets.DATABASE_URL }} up
41
+ shell: bash
package/.sqlfluff ADDED
@@ -0,0 +1,5 @@
1
+ [sqlfluff]
2
+ dialect = postgres
3
+
4
+ [sqlfluff:rules:references.keywords]
5
+ ignore_words = name, level
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # @evenicanpm/admin-db-migrations
2
+
3
+ ## 2.4.0
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # e4-Admin-Database-Product
2
+
3
+ This repository contains the database schema and PostgreSQL migration scripts for the admin panel.
4
+ All database migrations are managed using [Dbmate](https://github.com/amacneil/dbmate), a lightweight, language-agnostic migration tool.
5
+
6
+ ## 🚀 Getting Started
7
+
8
+ Make sure the following tools are installed:
9
+
10
+ #### 1. PostgreSQL
11
+
12
+ #### 2. Dbmate
13
+
14
+ #### Install Dbmate on Linux (Ubuntu/Debian)
15
+
16
+ ```bash
17
+ # Download the latest Dbmate binary
18
+ curl -fsSL https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-amd64 -o dbmate
19
+
20
+ # Make it executable
21
+ chmod +x dbmate
22
+
23
+ # Move to a directory in your PATH
24
+ sudo mv dbmate /usr/local/bin/
25
+
26
+ # Check version
27
+ dbmate --version
28
+ ```
29
+
30
+ ## Migrations
31
+
32
+ #### 1. Create a new migration
33
+
34
+ dbmate new <migration_name>
35
+ This will generate a new migration file.
36
+ Add both migrate:up and migrate:down scripts to the file.
37
+
38
+ Use migrations for inserts when the data is structural or environment-agnostic (same in dev/stage/prod).
39
+ Use seeds for inserts when the data is sample or environment-specific.
40
+ For seeds use db/seeds folder instead of db/migrations.
41
+
42
+ #### 2. Run migrations
43
+
44
+ Make sure ADMIN_DB_URL is added to .env file. Copy over from the .env.example file.
45
+
46
+ ```bash
47
+ # To create the database (if it does not already exist) and run any pending migrations
48
+ dbmate -u "$ADMIN_DB_URL" up
49
+
50
+ # To run any pending migrations
51
+ dbmate -u "$ADMIN_DB_URL" migrate
52
+
53
+ # To roll back the most recent migration
54
+ dbmate -u "$ADMIN_DB_URL" rollback or dbmate -u "$ADMIN_DB_URL" down
55
+ ```
56
+
57
+ Alternatively, you can specify the URL directly.
58
+ Like this:
59
+
60
+ ```bash
61
+ dbmate -u "postgres://username:password@host:port/database_name?sslmode=disable" up
62
+ ```
@@ -0,0 +1,85 @@
1
+ -- migrate:up
2
+
3
+ CREATE TABLE users (
4
+ -- Using UUID for security and scalability
5
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
6
+ email VARCHAR(255) NOT NULL UNIQUE,
7
+ display_name VARCHAR(255),
8
+ is_active BOOLEAN DEFAULT TRUE,
9
+ created_at TIMESTAMP DEFAULT current_timestamp,
10
+ updated_at TIMESTAMP DEFAULT current_timestamp
11
+ );
12
+
13
+ CREATE TABLE modules (
14
+ id SERIAL PRIMARY KEY,
15
+ -- e.g., 'admin', 'storefront', 'integrate'
16
+ module_name VARCHAR(50) NOT NULL UNIQUE,
17
+ description TEXT,
18
+ is_active BOOLEAN DEFAULT TRUE,
19
+ created_at TIMESTAMP DEFAULT current_timestamp
20
+ );
21
+
22
+ -- Roles (now tied to a module)
23
+ CREATE TABLE roles (
24
+ id SERIAL PRIMARY KEY,
25
+ -- 'admin', 'editor', 'viewer', etc.
26
+ role_name VARCHAR(50) NOT NULL,
27
+ module_id INTEGER NOT NULL REFERENCES modules (id) ON DELETE CASCADE,
28
+ is_editable BOOLEAN DEFAULT TRUE,
29
+ description TEXT,
30
+ created_at TIMESTAMP DEFAULT current_timestamp,
31
+ updated_at TIMESTAMP DEFAULT current_timestamp,
32
+ created_by UUID REFERENCES users (id),
33
+ updated_by UUID REFERENCES users (id),
34
+ -- Same role name can exist for different modules
35
+ UNIQUE (role_name, module_id)
36
+ );
37
+
38
+ -- Permissions (optional: module-specific permissions)
39
+ CREATE TABLE permissions (
40
+ id SERIAL PRIMARY KEY,
41
+
42
+ -- Stable internal identifier. Never changed once created.
43
+ permission_key VARCHAR(200) NOT NULL UNIQUE
44
+ CHECK (permission_key ~ '^[A-Z0-9_]+$'), -- ENFORCE SNAKE_CASE CAPS
45
+
46
+ description TEXT,
47
+
48
+ -- Hide from UI instead of deleting
49
+ is_active BOOLEAN NOT NULL DEFAULT TRUE,
50
+
51
+ created_at TIMESTAMP DEFAULT now(),
52
+ updated_at TIMESTAMP DEFAULT now()
53
+ );
54
+
55
+
56
+ -- Role-Permissions mapping
57
+ CREATE TABLE role_permissions (
58
+ role_id INTEGER NOT NULL REFERENCES roles (id) ON DELETE CASCADE,
59
+ permission_id INTEGER NOT NULL REFERENCES permissions (
60
+ id
61
+ ) ON DELETE CASCADE,
62
+ assigned_at TIMESTAMP DEFAULT current_timestamp,
63
+ assigned_by UUID REFERENCES users (id),
64
+ PRIMARY KEY (role_id, permission_id)
65
+ );
66
+
67
+ -- User-Roles mapping (now scoped to module via role)
68
+ CREATE TABLE user_roles (
69
+ user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
70
+ role_id INTEGER NOT NULL REFERENCES roles (id) ON DELETE CASCADE,
71
+ assigned_at TIMESTAMP DEFAULT current_timestamp,
72
+ assigned_by UUID REFERENCES users (id),
73
+ PRIMARY KEY (user_id, role_id)
74
+ );
75
+
76
+
77
+ -- migrate:down
78
+
79
+ DROP TABLE IF EXISTS user_roles;
80
+ DROP TABLE IF EXISTS role_permissions;
81
+ DROP TABLE IF EXISTS permission_groups;
82
+ DROP TABLE IF EXISTS permissions;
83
+ DROP TABLE IF EXISTS roles;
84
+ DROP TABLE IF EXISTS modules;
85
+ DROP TABLE IF EXISTS users;
@@ -0,0 +1,83 @@
1
+ -- migrate:up
2
+
3
+ -- Seed modules
4
+ INSERT INTO modules (module_name, description) VALUES
5
+ ('admin', 'e4 Admin panel'),
6
+ ('storefront', 'e4 Customer-facing ecommerce store'),
7
+ ('integrate', 'e4 Integrate portal');
8
+
9
+ -- Seed SUPER ADMIN role for 'admin' module (non-editable)
10
+ INSERT INTO roles (role_name, module_id, is_editable, description)
11
+ SELECT
12
+ 'super admin' AS role_name,
13
+ id AS module_id,
14
+ FALSE AS is_editable,
15
+ 'Full access to all permissions. Cannot be edited or deleted.'
16
+ AS description
17
+ FROM modules
18
+ WHERE
19
+ module_name = 'admin'
20
+ AND NOT EXISTS (
21
+ SELECT 1 FROM roles AS r
22
+ WHERE
23
+ r.role_name = 'super admin'
24
+ AND r.module_id = modules.id
25
+ );
26
+
27
+ -------------------------------------------------
28
+ -- DB PROTECTIONS for SUPER ADMIN ROLE
29
+ -------------------------------------------------
30
+
31
+ -- 1. Prevent duplicates (partial unique index)
32
+ CREATE UNIQUE INDEX uniq_super_admin_role
33
+ ON roles (module_id)
34
+ WHERE role_name = 'super admin';
35
+
36
+ -- 2. Prevent updates
37
+ CREATE OR REPLACE FUNCTION prevent_super_admin_update()
38
+ RETURNS trigger AS $$
39
+ BEGIN
40
+ IF OLD.role_name = 'super admin' THEN
41
+ RAISE EXCEPTION 'Super Admin role cannot be updated';
42
+ END IF;
43
+ RETURN NEW;
44
+ END;
45
+ $$ LANGUAGE plpgsql;
46
+
47
+ CREATE TRIGGER trg_prevent_super_admin_update
48
+ BEFORE UPDATE ON roles
49
+ FOR EACH ROW
50
+ WHEN (old.role_name = 'super admin')
51
+ EXECUTE FUNCTION prevent_super_admin_update();
52
+
53
+ -- 3. Prevent delete
54
+ CREATE OR REPLACE FUNCTION prevent_super_admin_delete()
55
+ RETURNS trigger AS $$
56
+ BEGIN
57
+ RAISE EXCEPTION 'Super Admin role cannot be deleted';
58
+ END;
59
+ $$ LANGUAGE plpgsql;
60
+
61
+ CREATE TRIGGER trg_prevent_super_admin_delete
62
+ BEFORE DELETE ON roles
63
+ FOR EACH ROW
64
+ WHEN (old.role_name = 'super admin')
65
+ EXECUTE FUNCTION prevent_super_admin_delete();
66
+
67
+ -- migrate:down
68
+
69
+ DROP TRIGGER IF EXISTS trg_prevent_super_admin_delete ON roles;
70
+ DROP FUNCTION IF EXISTS prevent_super_admin_delete;
71
+
72
+ DROP TRIGGER IF EXISTS trg_prevent_super_admin_update ON roles;
73
+ DROP FUNCTION IF EXISTS prevent_super_admin_update;
74
+
75
+ DROP INDEX IF EXISTS uniq_super_admin_role;
76
+
77
+ -- Remove the seeded super admin role
78
+ DELETE FROM roles
79
+ WHERE role_name = 'super admin';
80
+
81
+ -- Remove modules
82
+ DELETE FROM modules
83
+ WHERE module_name IN ('admin', 'storefront', 'integrate');
@@ -0,0 +1,138 @@
1
+ -- migrate:up
2
+
3
+ -- Robots tables used by the UI/background worker.
4
+
5
+ CREATE TABLE IF NOT EXISTS robots_config (
6
+ id BIGSERIAL PRIMARY KEY,
7
+ robots_text TEXT NOT NULL,
8
+ builder_state TEXT NOT NULL,
9
+ history_limit INTEGER NOT NULL DEFAULT 5,
10
+ notify_google BOOLEAN NOT NULL DEFAULT TRUE,
11
+ notify_bing BOOLEAN NOT NULL DEFAULT FALSE,
12
+ last_published_at TIMESTAMP,
13
+ last_published_by BIGINT,
14
+ last_published_by_name VARCHAR(256),
15
+ created_at TIMESTAMP NOT NULL DEFAULT NOW(),
16
+ created_by UUID REFERENCES users (id),
17
+ updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
18
+ updated_by UUID REFERENCES users (id)
19
+ );
20
+
21
+ CREATE TABLE IF NOT EXISTS robots_version (
22
+ id BIGSERIAL PRIMARY KEY,
23
+ robots_config_id BIGINT NOT NULL REFERENCES robots_config (id),
24
+ version_label VARCHAR(32),
25
+ robots_text TEXT NOT NULL,
26
+ builder_state TEXT NOT NULL,
27
+ notify_google BOOLEAN NOT NULL DEFAULT TRUE,
28
+ notify_bing BOOLEAN NOT NULL DEFAULT FALSE,
29
+ created_at TIMESTAMP NOT NULL DEFAULT NOW(),
30
+ created_by UUID REFERENCES users (id),
31
+ created_by_name VARCHAR(256)
32
+ );
33
+
34
+ CREATE INDEX IF NOT EXISTS ix_robots_version_config_created ON robots_version (
35
+ robots_config_id, created_at DESC
36
+ );
37
+
38
+ CREATE TABLE IF NOT EXISTS robots_log (
39
+ id BIGSERIAL PRIMARY KEY,
40
+ robots_config_id BIGINT NOT NULL REFERENCES robots_config (
41
+ id
42
+ ) ON DELETE CASCADE,
43
+ level VARCHAR(16) NOT NULL,
44
+ message VARCHAR(512) NOT NULL,
45
+ logged_at TIMESTAMP NOT NULL DEFAULT NOW(),
46
+ logged_by UUID REFERENCES users (id),
47
+ logged_by_name VARCHAR(256),
48
+ CONSTRAINT robots_log_level_check CHECK (level IN ('info', 'warn', 'error'))
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS ix_robots_log_config_logged ON robots_log (
52
+ robots_config_id, logged_at DESC
53
+ );
54
+
55
+ CREATE TABLE sitemap_history (
56
+ id BIGSERIAL PRIMARY KEY,
57
+
58
+ generation_type VARCHAR(20) NOT NULL, -- manual or schedule
59
+ -- e.g., all, products, categories, cms, marketing
60
+ data_type VARCHAR(20) NOT NULL,
61
+ data_source VARCHAR(10) NOT NULL -- only d365 or e4
62
+ CHECK (data_source IN ('d365', 'e4')),
63
+ status VARCHAR(20) NOT NULL, -- e.g., started, failed, finished
64
+ message TEXT,
65
+ job_process_id VARCHAR(50), -- Redis job ID or process ID
66
+ -- links to users.id, can be null for system process
67
+ generated_by UUID REFERENCES users (id),
68
+
69
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
70
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
71
+ );
72
+
73
+ CREATE TABLE sitemap_rules (
74
+ id BIGSERIAL PRIMARY KEY,
75
+
76
+ -- e.g. 'homepage', 'category', 'category overide', 'category exclude'
77
+ rule_name VARCHAR(50) NOT NULL,
78
+ data_source VARCHAR(10) NOT NULL -- only d365 or e4
79
+ CHECK (data_source IN ('d365', 'e4')),
80
+
81
+ -- e.g. '/', '/products', '/products/mens'
82
+ url TEXT NOT NULL,
83
+ -- TRUE = include, FALSE = exclude
84
+ exclude_url BOOLEAN NOT NULL DEFAULT FALSE,
85
+
86
+ priority DECIMAL(2, 1) -- range 0.0 to 1.0
87
+ CHECK (priority BETWEEN 0.0 AND 1.0),
88
+ changefreq VARCHAR(20),
89
+
90
+ -- optional: explanation or business reason for this rule
91
+ notes TEXT,
92
+ created_by UUID REFERENCES users (id),
93
+ updated_by UUID REFERENCES users (id),
94
+
95
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
96
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
97
+
98
+ CONSTRAINT unique_rule UNIQUE (data_source, url)
99
+ );
100
+
101
+ CREATE TABLE sitemap_audit_log (
102
+ id BIGSERIAL PRIMARY KEY,
103
+
104
+ action_type VARCHAR(30) NOT NULL,
105
+ data_source VARCHAR(10) NOT NULL -- only d365 or e4
106
+ CHECK (data_source IN ('d365', 'e4')),
107
+
108
+ -- If action relates to a rule
109
+ rule_id BIGINT,
110
+ CONSTRAINT sitemap_audit_log_rule_id_fkey
111
+ FOREIGN KEY (rule_id)
112
+ REFERENCES sitemap_rules (id)
113
+ -- allow deleting rules without breaking audit log
114
+ ON DELETE SET NULL,
115
+
116
+ -- JSON snapshots of values before & after
117
+ value_from JSONB,
118
+ value_to JSONB,
119
+
120
+ -- Who triggered this (null for system/scheduled)
121
+ modified_by UUID REFERENCES users (id),
122
+
123
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
124
+ );
125
+
126
+ -- migrate:down
127
+
128
+ DROP TABLE IF EXISTS sitemap_audit_log;
129
+ DROP TABLE IF EXISTS sitemap_rules;
130
+ DROP TABLE IF EXISTS sitemap_history;
131
+
132
+ DROP INDEX IF EXISTS ix_robots_log_config_logged;
133
+ DROP TABLE IF EXISTS robots_log;
134
+
135
+ DROP INDEX IF EXISTS ix_robots_version_config_created;
136
+ DROP TABLE IF EXISTS robots_version;
137
+
138
+ DROP TABLE IF EXISTS robots_config;
@@ -0,0 +1,192 @@
1
+ -- migrate:up
2
+
3
+ -- Align robots audit columns with users.id (UUID).
4
+ -- Existing BIGINT values (if any) are set to NULL since they can't be mapped
5
+ -- to users.id.
6
+
7
+ DO $$
8
+ BEGIN
9
+ IF to_regclass('public.robots_config') IS NOT NULL THEN
10
+ IF EXISTS (
11
+ SELECT 1
12
+ FROM information_schema.columns
13
+ WHERE table_schema = 'public'
14
+ AND table_name = 'robots_config'
15
+ AND column_name = 'created_by'
16
+ AND data_type <> 'uuid'
17
+ ) THEN
18
+ ALTER TABLE robots_config
19
+ ALTER COLUMN created_by TYPE UUID
20
+ USING (
21
+ CASE
22
+ WHEN created_by IS NULL THEN NULL::UUID
23
+ WHEN created_by::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
24
+ THEN created_by::text::UUID
25
+ ELSE NULL::UUID
26
+ END
27
+ );
28
+ END IF;
29
+
30
+ IF EXISTS (
31
+ SELECT 1
32
+ FROM information_schema.columns
33
+ WHERE table_schema = 'public'
34
+ AND table_name = 'robots_config'
35
+ AND column_name = 'updated_by'
36
+ AND data_type <> 'uuid'
37
+ ) THEN
38
+ ALTER TABLE robots_config
39
+ ALTER COLUMN updated_by TYPE UUID
40
+ USING (
41
+ CASE
42
+ WHEN updated_by IS NULL THEN NULL::UUID
43
+ WHEN updated_by::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
44
+ THEN updated_by::text::UUID
45
+ ELSE NULL::UUID
46
+ END
47
+ );
48
+ END IF;
49
+
50
+ IF EXISTS (
51
+ SELECT 1
52
+ FROM information_schema.columns
53
+ WHERE table_schema = 'public'
54
+ AND table_name = 'robots_config'
55
+ AND column_name = 'created_by'
56
+ ) AND NOT EXISTS (
57
+ SELECT 1
58
+ FROM pg_constraint
59
+ WHERE conrelid = 'public.robots_config'::regclass
60
+ AND conname = 'robots_config_created_by_fkey'
61
+ ) THEN
62
+ ALTER TABLE robots_config
63
+ ADD CONSTRAINT robots_config_created_by_fkey
64
+ FOREIGN KEY (created_by)
65
+ REFERENCES users (id);
66
+ END IF;
67
+
68
+ IF EXISTS (
69
+ SELECT 1
70
+ FROM information_schema.columns
71
+ WHERE table_schema = 'public'
72
+ AND table_name = 'robots_config'
73
+ AND column_name = 'updated_by'
74
+ ) AND NOT EXISTS (
75
+ SELECT 1
76
+ FROM pg_constraint
77
+ WHERE conrelid = 'public.robots_config'::regclass
78
+ AND conname = 'robots_config_updated_by_fkey'
79
+ ) THEN
80
+ ALTER TABLE robots_config
81
+ ADD CONSTRAINT robots_config_updated_by_fkey
82
+ FOREIGN KEY (updated_by)
83
+ REFERENCES users (id);
84
+ END IF;
85
+ END IF;
86
+
87
+ IF to_regclass('public.robots_version') IS NOT NULL THEN
88
+ IF EXISTS (
89
+ SELECT 1
90
+ FROM information_schema.columns
91
+ WHERE table_schema = 'public'
92
+ AND table_name = 'robots_version'
93
+ AND column_name = 'created_by'
94
+ AND data_type <> 'uuid'
95
+ ) THEN
96
+ ALTER TABLE robots_version
97
+ ALTER COLUMN created_by TYPE UUID
98
+ USING (
99
+ CASE
100
+ WHEN created_by IS NULL THEN NULL::UUID
101
+ WHEN created_by::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
102
+ THEN created_by::text::UUID
103
+ ELSE NULL::UUID
104
+ END
105
+ );
106
+ END IF;
107
+
108
+ IF EXISTS (
109
+ SELECT 1
110
+ FROM information_schema.columns
111
+ WHERE table_schema = 'public'
112
+ AND table_name = 'robots_version'
113
+ AND column_name = 'created_by'
114
+ ) AND NOT EXISTS (
115
+ SELECT 1
116
+ FROM pg_constraint
117
+ WHERE conrelid = 'public.robots_version'::regclass
118
+ AND conname = 'robots_version_created_by_fkey'
119
+ ) THEN
120
+ ALTER TABLE robots_version
121
+ ADD CONSTRAINT robots_version_created_by_fkey
122
+ FOREIGN KEY (created_by)
123
+ REFERENCES users (id);
124
+ END IF;
125
+ END IF;
126
+
127
+ IF to_regclass('public.robots_log') IS NOT NULL THEN
128
+ IF EXISTS (
129
+ SELECT 1
130
+ FROM information_schema.columns
131
+ WHERE table_schema = 'public'
132
+ AND table_name = 'robots_log'
133
+ AND column_name = 'logged_by'
134
+ AND data_type <> 'uuid'
135
+ ) THEN
136
+ ALTER TABLE robots_log
137
+ ALTER COLUMN logged_by TYPE UUID
138
+ USING (
139
+ CASE
140
+ WHEN logged_by IS NULL THEN NULL::UUID
141
+ WHEN logged_by::text ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
142
+ THEN logged_by::text::UUID
143
+ ELSE NULL::UUID
144
+ END
145
+ );
146
+ END IF;
147
+
148
+ IF EXISTS (
149
+ SELECT 1
150
+ FROM information_schema.columns
151
+ WHERE table_schema = 'public'
152
+ AND table_name = 'robots_log'
153
+ AND column_name = 'logged_by'
154
+ ) AND NOT EXISTS (
155
+ SELECT 1
156
+ FROM pg_constraint
157
+ WHERE conrelid = 'public.robots_log'::regclass
158
+ AND conname = 'robots_log_logged_by_fkey'
159
+ ) THEN
160
+ ALTER TABLE robots_log
161
+ ADD CONSTRAINT robots_log_logged_by_fkey
162
+ FOREIGN KEY (logged_by)
163
+ REFERENCES users (id);
164
+ END IF;
165
+ END IF;
166
+ END $$;
167
+
168
+ -- migrate:down
169
+
170
+ ALTER TABLE robots_log DROP CONSTRAINT IF EXISTS robots_log_logged_by_fkey;
171
+ ALTER TABLE robots_version
172
+ DROP CONSTRAINT IF EXISTS robots_version_created_by_fkey;
173
+ ALTER TABLE robots_config
174
+ DROP CONSTRAINT IF EXISTS robots_config_updated_by_fkey;
175
+ ALTER TABLE robots_config
176
+ DROP CONSTRAINT IF EXISTS robots_config_created_by_fkey;
177
+
178
+ ALTER TABLE robots_log
179
+ ALTER COLUMN logged_by TYPE BIGINT
180
+ USING NULL::BIGINT;
181
+
182
+ ALTER TABLE robots_version
183
+ ALTER COLUMN created_by TYPE BIGINT
184
+ USING NULL::BIGINT;
185
+
186
+ ALTER TABLE robots_config
187
+ ALTER COLUMN updated_by TYPE BIGINT
188
+ USING NULL::BIGINT;
189
+
190
+ ALTER TABLE robots_config
191
+ ALTER COLUMN created_by TYPE BIGINT
192
+ USING NULL::BIGINT;
@@ -0,0 +1,57 @@
1
+ -- migrate:up
2
+
3
+ -- =========================
4
+ -- 1. Create schema
5
+ -- core → system/platform (ex: auth)
6
+ -- commerce → everything that affects storefront behavior
7
+ -- =========================
8
+ CREATE SCHEMA IF NOT EXISTS core;
9
+ CREATE SCHEMA IF NOT EXISTS commerce;
10
+
11
+ -- =========================
12
+ -- 2. Move existing tables from public → core or commerce
13
+ -- (NO DATA LOSS)
14
+ -- =========================
15
+
16
+ ALTER TABLE IF EXISTS public.users SET SCHEMA core;
17
+ ALTER TABLE IF EXISTS public.modules SET SCHEMA core;
18
+ ALTER TABLE IF EXISTS public.roles SET SCHEMA core;
19
+ ALTER TABLE IF EXISTS public.permissions SET SCHEMA core;
20
+ ALTER TABLE IF EXISTS public.role_permissions SET SCHEMA core;
21
+ ALTER TABLE IF EXISTS public.user_roles SET SCHEMA core;
22
+
23
+ ALTER TABLE IF EXISTS public.robots_config SET SCHEMA commerce;
24
+ ALTER TABLE IF EXISTS public.robots_version SET SCHEMA commerce;
25
+ ALTER TABLE IF EXISTS public.robots_log SET SCHEMA commerce;
26
+
27
+ ALTER TABLE IF EXISTS public.sitemap_history SET SCHEMA commerce;
28
+ ALTER TABLE IF EXISTS public.sitemap_rules SET SCHEMA commerce;
29
+ ALTER TABLE IF EXISTS public.sitemap_audit_log SET SCHEMA commerce;
30
+
31
+
32
+ -- migrate:down
33
+
34
+ -- =========================
35
+ -- 1. Move tables back to public
36
+ -- =========================
37
+
38
+ ALTER TABLE IF EXISTS commerce.sitemap_audit_log SET SCHEMA public;
39
+ ALTER TABLE IF EXISTS commerce.sitemap_rules SET SCHEMA public;
40
+ ALTER TABLE IF EXISTS commerce.sitemap_history SET SCHEMA public;
41
+
42
+ ALTER TABLE IF EXISTS commerce.robots_log SET SCHEMA public;
43
+ ALTER TABLE IF EXISTS commerce.robots_version SET SCHEMA public;
44
+ ALTER TABLE IF EXISTS commerce.robots_config SET SCHEMA public;
45
+
46
+ ALTER TABLE IF EXISTS core.user_roles SET SCHEMA public;
47
+ ALTER TABLE IF EXISTS core.role_permissions SET SCHEMA public;
48
+ ALTER TABLE IF EXISTS core.permissions SET SCHEMA public;
49
+ ALTER TABLE IF EXISTS core.roles SET SCHEMA public;
50
+ ALTER TABLE IF EXISTS core.modules SET SCHEMA public;
51
+ ALTER TABLE IF EXISTS core.users SET SCHEMA public;
52
+
53
+ -- =========================
54
+ -- 2. Drop schema
55
+ -- =========================
56
+ DROP SCHEMA IF EXISTS core;
57
+ DROP SCHEMA IF EXISTS commerce;
@@ -0,0 +1,40 @@
1
+ -- Seed data for robots tables (run separately from migrations).
2
+
3
+ WITH inserted AS (
4
+ INSERT INTO robots_config (
5
+ robots_text,
6
+ builder_state,
7
+ notify_google,
8
+ notify_bing,
9
+ history_limit,
10
+ last_published_at,
11
+ last_published_by_name
12
+ ) VALUES (
13
+ E'# robots.txt\nUser-agent: *\nDisallow: /checkout\nDisallow: /cart\nDisallow: /account\nAllow: /collections\nAllow: /products\nCrawl-delay: 2',
14
+ '{"agentRules":[{"id":"ua1","agent":"*","crawlDelay":2}],"blockedAreas":[],"collections":[],"pdpAllowed":true,"agentAutomation":{"ua1":true}}',
15
+ TRUE,
16
+ FALSE,
17
+ 5,
18
+ NOW(),
19
+ 'system'
20
+ ) RETURNING id, robots_text, last_published_by_name
21
+ )
22
+ INSERT INTO robots_version (
23
+ robots_config_id,
24
+ version_label,
25
+ robots_text,
26
+ builder_state,
27
+ notify_google,
28
+ notify_bing,
29
+ created_at,
30
+ created_by_name
31
+ ) SELECT
32
+ id,
33
+ 'v1',
34
+ robots_text,
35
+ builder_state,
36
+ notify_google,
37
+ notify_bing,
38
+ NOW(),
39
+ last_published_by_name
40
+ FROM inserted;
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@evenicanpm/admin-db-migrations",
3
+ "version": "2.4.0",
4
+ "description": "This repository contains the database schema and PostgreSQL migration scripts for the admin panel. All database migrations are managed using [Dbmate](https://github.com/amacneil/dbmate), a lightweight, language-agnostic migration tool.",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "commonjs",
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "migrate:up": "dbmate -e DATABASE_URL up"
11
+ },
12
+ "devDependencies": {
13
+ "dbmate": "^2.33.0"
14
+ }
15
+ }