@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 +4 -0
- package/.git-blame-ignore-revs +0 -0
- package/.github/workflows/check-build.yml +41 -0
- package/.sqlfluff +5 -0
- package/CHANGELOG.md +3 -0
- package/README.md +62 -0
- package/db/migrations/20250717133450_create_authorization.sql +85 -0
- package/db/migrations/20250717151352_insert_authorization.sql +83 -0
- package/db/migrations/20251015170040_create_seo.sql +138 -0
- package/db/migrations/20251215100000_robots_actor_columns_uuid.sql +192 -0
- package/db/migrations/20260128115118_schema_names.sql +57 -0
- package/db/seeds/20251015170040_seed_robots.sql +40 -0
- package/package.json +15 -0
package/.env.example
ADDED
|
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
package/CHANGELOG.md
ADDED
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
|
+
}
|