@sudobility/entity_service 1.0.4 → 1.0.5
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/dist/helpers/EntityHelper.cjs +29 -21
- package/dist/helpers/EntityHelper.d.ts +12 -4
- package/dist/helpers/EntityHelper.d.ts.map +1 -1
- package/dist/helpers/EntityHelper.js +29 -21
- package/dist/helpers/EntityHelper.js.map +1 -1
- package/dist/helpers/EntityMemberHelper.cjs +86 -47
- package/dist/helpers/EntityMemberHelper.d.ts +11 -3
- package/dist/helpers/EntityMemberHelper.d.ts.map +1 -1
- package/dist/helpers/EntityMemberHelper.js +86 -47
- package/dist/helpers/EntityMemberHelper.js.map +1 -1
- package/dist/helpers/InvitationHelper.cjs +15 -10
- package/dist/helpers/InvitationHelper.d.ts +6 -2
- package/dist/helpers/InvitationHelper.d.ts.map +1 -1
- package/dist/helpers/InvitationHelper.js +15 -10
- package/dist/helpers/InvitationHelper.js.map +1 -1
- package/dist/helpers/PermissionHelper.cjs +8 -12
- package/dist/helpers/PermissionHelper.d.ts +3 -0
- package/dist/helpers/PermissionHelper.d.ts.map +1 -1
- package/dist/helpers/PermissionHelper.js +8 -12
- package/dist/helpers/PermissionHelper.js.map +1 -1
- package/dist/middleware/hono.cjs +6 -6
- package/dist/middleware/hono.d.ts +3 -3
- package/dist/middleware/hono.js +6 -6
- package/dist/middleware/hono.js.map +1 -1
- package/dist/migrations/001_add_entities.cjs +41 -28
- package/dist/migrations/001_add_entities.d.ts.map +1 -1
- package/dist/migrations/001_add_entities.js +41 -28
- package/dist/migrations/001_add_entities.js.map +1 -1
- package/dist/schema/entities.cjs +30 -23
- package/dist/schema/entities.d.ts +61 -46
- package/dist/schema/entities.d.ts.map +1 -1
- package/dist/schema/entities.js +30 -23
- package/dist/schema/entities.js.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -16,12 +16,13 @@ class PermissionHelper {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Get a user's role in an entity.
|
|
19
|
+
* Only returns role for active members.
|
|
19
20
|
*/
|
|
20
21
|
async getUserRole(entityId, userId) {
|
|
21
22
|
const results = await this.config.db
|
|
22
23
|
.select({ role: this.config.membersTable.role })
|
|
23
24
|
.from(this.config.membersTable)
|
|
24
|
-
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(this.config.membersTable.entity_id, entityId), (0, drizzle_orm_1.eq)(this.config.membersTable.user_id, userId)))
|
|
25
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(this.config.membersTable.entity_id, entityId), (0, drizzle_orm_1.eq)(this.config.membersTable.user_id, userId), (0, drizzle_orm_1.eq)(this.config.membersTable.is_active, true)))
|
|
25
26
|
.limit(1);
|
|
26
27
|
if (results.length === 0) {
|
|
27
28
|
return null;
|
|
@@ -53,24 +54,19 @@ class PermissionHelper {
|
|
|
53
54
|
}
|
|
54
55
|
/**
|
|
55
56
|
* Check if user is an admin of an entity.
|
|
57
|
+
* Owner also has admin privileges.
|
|
56
58
|
*/
|
|
57
59
|
async isAdmin(entityId, userId) {
|
|
58
60
|
const role = await this.getUserRole(entityId, userId);
|
|
59
|
-
return role === types_1.EntityRole.ADMIN;
|
|
61
|
+
return role === types_1.EntityRole.ADMIN || role === types_1.EntityRole.OWNER;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
64
|
* Check if user is the owner of an entity.
|
|
65
|
+
* Determined by role = 'owner' in entity_members.
|
|
63
66
|
*/
|
|
64
67
|
async isOwner(entityId, userId) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
.from(this.config.entitiesTable)
|
|
68
|
-
.where((0, drizzle_orm_1.eq)(this.config.entitiesTable.id, entityId))
|
|
69
|
-
.limit(1);
|
|
70
|
-
if (results.length === 0) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
return results[0].ownerUserId === userId;
|
|
68
|
+
const role = await this.getUserRole(entityId, userId);
|
|
69
|
+
return role === types_1.EntityRole.OWNER;
|
|
74
70
|
}
|
|
75
71
|
/**
|
|
76
72
|
* Check if user can view an entity.
|
|
@@ -184,7 +180,7 @@ class PermissionHelper {
|
|
|
184
180
|
*/
|
|
185
181
|
getMinimumRoleForPermission(permission) {
|
|
186
182
|
// Check from lowest to highest privilege
|
|
187
|
-
const roles = [types_1.EntityRole.
|
|
183
|
+
const roles = [types_1.EntityRole.MEMBER, types_1.EntityRole.ADMIN, types_1.EntityRole.OWNER];
|
|
188
184
|
for (const role of roles) {
|
|
189
185
|
if (types_1.ROLE_PERMISSIONS[role][permission]) {
|
|
190
186
|
return role;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionHelper.js","sourceRoot":"","sources":["../../src/helpers/PermissionHelper.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAAsC;AACtC,oCAMkB;AAElB;;GAEG;AACH,MAAa,gBAAgB;IAC3B,YAA6B,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;IAAG,CAAC;IAE3D
|
|
1
|
+
{"version":3,"file":"PermissionHelper.js","sourceRoot":"","sources":["../../src/helpers/PermissionHelper.ts"],"names":[],"mappings":"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"}
|
package/dist/middleware/hono.cjs
CHANGED
|
@@ -103,18 +103,18 @@ function createRequirePermissionMiddleware(permission) {
|
|
|
103
103
|
*
|
|
104
104
|
* Usage:
|
|
105
105
|
* ```typescript
|
|
106
|
-
* const
|
|
106
|
+
* const requireAdmin = createRequireRoleMiddleware(EntityRole.ADMIN);
|
|
107
107
|
*
|
|
108
|
-
* app.post('/api/v1/entities/:entitySlug/projects',
|
|
109
|
-
* // Only
|
|
108
|
+
* app.post('/api/v1/entities/:entitySlug/projects', requireAdmin, (c) => {
|
|
109
|
+
* // Only admins and owners can reach here
|
|
110
110
|
* });
|
|
111
111
|
* ```
|
|
112
112
|
*/
|
|
113
113
|
function createRequireRoleMiddleware(minimumRole) {
|
|
114
114
|
const roleHierarchy = {
|
|
115
|
-
[types_1.EntityRole.
|
|
116
|
-
[types_1.EntityRole.
|
|
117
|
-
[types_1.EntityRole.
|
|
115
|
+
[types_1.EntityRole.MEMBER]: 0,
|
|
116
|
+
[types_1.EntityRole.ADMIN]: 1,
|
|
117
|
+
[types_1.EntityRole.OWNER]: 2,
|
|
118
118
|
};
|
|
119
119
|
return async (c, next) => {
|
|
120
120
|
const userRole = c.get('userRole');
|
|
@@ -64,10 +64,10 @@ export declare function createRequirePermissionMiddleware(permission: keyof Enti
|
|
|
64
64
|
*
|
|
65
65
|
* Usage:
|
|
66
66
|
* ```typescript
|
|
67
|
-
* const
|
|
67
|
+
* const requireAdmin = createRequireRoleMiddleware(EntityRole.ADMIN);
|
|
68
68
|
*
|
|
69
|
-
* app.post('/api/v1/entities/:entitySlug/projects',
|
|
70
|
-
* // Only
|
|
69
|
+
* app.post('/api/v1/entities/:entitySlug/projects', requireAdmin, (c) => {
|
|
70
|
+
* // Only admins and owners can reach here
|
|
71
71
|
* });
|
|
72
72
|
* ```
|
|
73
73
|
*/
|
package/dist/middleware/hono.js
CHANGED
|
@@ -103,18 +103,18 @@ function createRequirePermissionMiddleware(permission) {
|
|
|
103
103
|
*
|
|
104
104
|
* Usage:
|
|
105
105
|
* ```typescript
|
|
106
|
-
* const
|
|
106
|
+
* const requireAdmin = createRequireRoleMiddleware(EntityRole.ADMIN);
|
|
107
107
|
*
|
|
108
|
-
* app.post('/api/v1/entities/:entitySlug/projects',
|
|
109
|
-
* // Only
|
|
108
|
+
* app.post('/api/v1/entities/:entitySlug/projects', requireAdmin, (c) => {
|
|
109
|
+
* // Only admins and owners can reach here
|
|
110
110
|
* });
|
|
111
111
|
* ```
|
|
112
112
|
*/
|
|
113
113
|
function createRequireRoleMiddleware(minimumRole) {
|
|
114
114
|
const roleHierarchy = {
|
|
115
|
-
[types_1.EntityRole.
|
|
116
|
-
[types_1.EntityRole.
|
|
117
|
-
[types_1.EntityRole.
|
|
115
|
+
[types_1.EntityRole.MEMBER]: 0,
|
|
116
|
+
[types_1.EntityRole.ADMIN]: 1,
|
|
117
|
+
[types_1.EntityRole.OWNER]: 2,
|
|
118
118
|
};
|
|
119
119
|
return async (c, next) => {
|
|
120
120
|
const userRole = c.get('userRole');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,10 +35,11 @@ async function runEntityMigration(config) {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Create the entity tables.
|
|
38
|
+
* Note: Ownership is tracked via entity_members (role = 'owner'), not entities.owner_user_id.
|
|
38
39
|
*/
|
|
39
40
|
async function createEntityTables(client, prefix, indexPrefix) {
|
|
40
41
|
console.log('Creating entity tables...');
|
|
41
|
-
// Create entities table
|
|
42
|
+
// Create entities table (ownership tracked via entity_members)
|
|
42
43
|
await client.unsafe(`
|
|
43
44
|
CREATE TABLE IF NOT EXISTS ${prefix}entities (
|
|
44
45
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -47,7 +48,6 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
47
48
|
display_name VARCHAR(255) NOT NULL,
|
|
48
49
|
description TEXT,
|
|
49
50
|
avatar_url TEXT,
|
|
50
|
-
owner_user_id UUID NOT NULL,
|
|
51
51
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
52
52
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
53
53
|
)
|
|
@@ -55,22 +55,19 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
55
55
|
await client.unsafe(`
|
|
56
56
|
CREATE UNIQUE INDEX IF NOT EXISTS ${indexPrefix}_entities_slug_idx
|
|
57
57
|
ON ${prefix}entities (entity_slug)
|
|
58
|
-
`);
|
|
59
|
-
await client.unsafe(`
|
|
60
|
-
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entities_owner_idx
|
|
61
|
-
ON ${prefix}entities (owner_user_id)
|
|
62
58
|
`);
|
|
63
59
|
await client.unsafe(`
|
|
64
60
|
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entities_type_idx
|
|
65
61
|
ON ${prefix}entities (entity_type)
|
|
66
62
|
`);
|
|
67
|
-
// Create entity_members table
|
|
63
|
+
// Create entity_members table (tracks all user-entity relationships including ownership)
|
|
68
64
|
await client.unsafe(`
|
|
69
65
|
CREATE TABLE IF NOT EXISTS ${prefix}entity_members (
|
|
70
66
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
71
67
|
entity_id UUID NOT NULL REFERENCES ${prefix}entities(id) ON DELETE CASCADE,
|
|
72
|
-
user_id
|
|
73
|
-
role VARCHAR(20) NOT NULL CHECK (role IN ('
|
|
68
|
+
user_id VARCHAR(128) NOT NULL,
|
|
69
|
+
role VARCHAR(20) NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
|
|
70
|
+
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
74
71
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
|
75
72
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
76
73
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
@@ -88,6 +85,10 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
88
85
|
await client.unsafe(`
|
|
89
86
|
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entity_members_user_idx
|
|
90
87
|
ON ${prefix}entity_members (user_id)
|
|
88
|
+
`);
|
|
89
|
+
await client.unsafe(`
|
|
90
|
+
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entity_members_active_idx
|
|
91
|
+
ON ${prefix}entity_members (is_active)
|
|
91
92
|
`);
|
|
92
93
|
// Create entity_invitations table
|
|
93
94
|
await client.unsafe(`
|
|
@@ -95,9 +96,9 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
95
96
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
96
97
|
entity_id UUID NOT NULL REFERENCES ${prefix}entities(id) ON DELETE CASCADE,
|
|
97
98
|
email VARCHAR(255) NOT NULL,
|
|
98
|
-
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', '
|
|
99
|
+
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'member')),
|
|
99
100
|
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'declined', 'expired')),
|
|
100
|
-
invited_by_user_id
|
|
101
|
+
invited_by_user_id VARCHAR(128) NOT NULL,
|
|
101
102
|
token VARCHAR(64) NOT NULL UNIQUE,
|
|
102
103
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
103
104
|
accepted_at TIMESTAMPTZ,
|
|
@@ -156,18 +157,25 @@ async function addEntityIdToProjects(client, prefix, indexPrefix) {
|
|
|
156
157
|
}
|
|
157
158
|
/**
|
|
158
159
|
* Create personal entities for existing users.
|
|
160
|
+
* Uses firebase_uid as the user identifier.
|
|
161
|
+
* Personal entities use role = 'admin' (not 'owner' - owner role is for organizations).
|
|
159
162
|
*/
|
|
160
163
|
async function migrateUsersToPersonalEntities(client, prefix) {
|
|
161
164
|
console.log('Migrating users to personal entities...');
|
|
162
|
-
// Get users without personal entities
|
|
165
|
+
// Get users without personal entities (check via entity_members with role = 'admin')
|
|
163
166
|
const usersWithoutEntities = await client.unsafe(`
|
|
164
|
-
SELECT u.
|
|
167
|
+
SELECT u.firebase_uid, u.email, u.display_name,
|
|
165
168
|
COALESCE(s.organization_path, SUBSTRING(MD5(RANDOM()::TEXT) FROM 1 FOR 8)) as slug_source
|
|
166
169
|
FROM ${prefix}users u
|
|
167
|
-
LEFT JOIN ${prefix}user_settings s ON u.
|
|
168
|
-
WHERE NOT
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
LEFT JOIN ${prefix}user_settings s ON u.firebase_uid = s.firebase_uid
|
|
171
|
+
WHERE u.firebase_uid IS NOT NULL
|
|
172
|
+
AND NOT EXISTS (
|
|
173
|
+
SELECT 1 FROM ${prefix}entity_members em
|
|
174
|
+
INNER JOIN ${prefix}entities e ON em.entity_id = e.id
|
|
175
|
+
WHERE em.user_id = u.firebase_uid
|
|
176
|
+
AND em.role = 'admin'
|
|
177
|
+
AND em.is_active = true
|
|
178
|
+
AND e.entity_type = 'personal'
|
|
171
179
|
)
|
|
172
180
|
`);
|
|
173
181
|
let migratedCount = 0;
|
|
@@ -175,17 +183,18 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
175
183
|
// Generate a unique slug (8 chars, lowercase alphanumeric)
|
|
176
184
|
const slug = generateSlug(user.slug_source);
|
|
177
185
|
const displayName = user.display_name || user.email?.split('@')[0] || 'Personal';
|
|
186
|
+
const firebaseUid = user.firebase_uid;
|
|
178
187
|
try {
|
|
179
188
|
// Create personal entity
|
|
180
189
|
const [entity] = await client.unsafe(`
|
|
181
|
-
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name
|
|
182
|
-
VALUES ('${slug}', 'personal', '${displayName.replace(/'/g, "''")}'
|
|
190
|
+
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name)
|
|
191
|
+
VALUES ('${slug}', 'personal', '${displayName.replace(/'/g, "''")}')
|
|
183
192
|
RETURNING id
|
|
184
193
|
`);
|
|
185
|
-
// Add user as admin
|
|
194
|
+
// Add user as admin (personal entities use 'admin' role, not 'owner')
|
|
186
195
|
await client.unsafe(`
|
|
187
|
-
INSERT INTO ${prefix}entity_members (entity_id, user_id, role)
|
|
188
|
-
VALUES ('${entity.id}', '${
|
|
196
|
+
INSERT INTO ${prefix}entity_members (entity_id, user_id, role, is_active)
|
|
197
|
+
VALUES ('${entity.id}', '${firebaseUid}', 'admin', true)
|
|
189
198
|
`);
|
|
190
199
|
migratedCount++;
|
|
191
200
|
}
|
|
@@ -194,13 +203,13 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
194
203
|
if (error.code === '23505') {
|
|
195
204
|
const newSlug = generateSlug();
|
|
196
205
|
const [entity] = await client.unsafe(`
|
|
197
|
-
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name
|
|
198
|
-
VALUES ('${newSlug}', 'personal', '${displayName.replace(/'/g, "''")}'
|
|
206
|
+
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name)
|
|
207
|
+
VALUES ('${newSlug}', 'personal', '${displayName.replace(/'/g, "''")}')
|
|
199
208
|
RETURNING id
|
|
200
209
|
`);
|
|
201
210
|
await client.unsafe(`
|
|
202
|
-
INSERT INTO ${prefix}entity_members (entity_id, user_id, role)
|
|
203
|
-
VALUES ('${entity.id}', '${
|
|
211
|
+
INSERT INTO ${prefix}entity_members (entity_id, user_id, role, is_active)
|
|
212
|
+
VALUES ('${entity.id}', '${firebaseUid}', 'admin', true)
|
|
204
213
|
`);
|
|
205
214
|
migratedCount++;
|
|
206
215
|
}
|
|
@@ -213,15 +222,19 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
213
222
|
}
|
|
214
223
|
/**
|
|
215
224
|
* Populate entity_id for existing projects.
|
|
225
|
+
* Uses entity_members with role = 'admin' to find personal entity membership.
|
|
216
226
|
*/
|
|
217
227
|
async function populateProjectEntityIds(client, prefix) {
|
|
218
228
|
console.log('Populating entity_id for existing projects...');
|
|
219
|
-
// Update projects to use
|
|
229
|
+
// Update projects to use user's personal entity (via entity_members with admin role)
|
|
220
230
|
const result = await client.unsafe(`
|
|
221
231
|
UPDATE ${prefix}projects p
|
|
222
232
|
SET entity_id = e.id
|
|
223
233
|
FROM ${prefix}entities e
|
|
224
|
-
|
|
234
|
+
INNER JOIN ${prefix}entity_members em ON em.entity_id = e.id
|
|
235
|
+
WHERE p.user_id = em.user_id
|
|
236
|
+
AND em.role = 'admin'
|
|
237
|
+
AND em.is_active = true
|
|
225
238
|
AND e.entity_type = 'personal'
|
|
226
239
|
AND p.entity_id IS NULL
|
|
227
240
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"001_add_entities.d.ts","sourceRoot":"","sources":["../../src/migrations/001_add_entities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,MAAM,EAAE,UAAU,CAAC,cAAc,UAAU,CAAC,CAAC,CAAC;IAC9C,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB/E;
|
|
1
|
+
{"version":3,"file":"001_add_entities.d.ts","sourceRoot":"","sources":["../../src/migrations/001_add_entities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,MAAM,EAAE,UAAU,CAAC,cAAc,UAAU,CAAC,CAAC,CAAC;IAC9C,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB/E;AAkRD;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBpF"}
|
|
@@ -35,10 +35,11 @@ async function runEntityMigration(config) {
|
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* Create the entity tables.
|
|
38
|
+
* Note: Ownership is tracked via entity_members (role = 'owner'), not entities.owner_user_id.
|
|
38
39
|
*/
|
|
39
40
|
async function createEntityTables(client, prefix, indexPrefix) {
|
|
40
41
|
console.log('Creating entity tables...');
|
|
41
|
-
// Create entities table
|
|
42
|
+
// Create entities table (ownership tracked via entity_members)
|
|
42
43
|
await client.unsafe(`
|
|
43
44
|
CREATE TABLE IF NOT EXISTS ${prefix}entities (
|
|
44
45
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
@@ -47,7 +48,6 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
47
48
|
display_name VARCHAR(255) NOT NULL,
|
|
48
49
|
description TEXT,
|
|
49
50
|
avatar_url TEXT,
|
|
50
|
-
owner_user_id UUID NOT NULL,
|
|
51
51
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
52
52
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
53
53
|
)
|
|
@@ -55,22 +55,19 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
55
55
|
await client.unsafe(`
|
|
56
56
|
CREATE UNIQUE INDEX IF NOT EXISTS ${indexPrefix}_entities_slug_idx
|
|
57
57
|
ON ${prefix}entities (entity_slug)
|
|
58
|
-
`);
|
|
59
|
-
await client.unsafe(`
|
|
60
|
-
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entities_owner_idx
|
|
61
|
-
ON ${prefix}entities (owner_user_id)
|
|
62
58
|
`);
|
|
63
59
|
await client.unsafe(`
|
|
64
60
|
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entities_type_idx
|
|
65
61
|
ON ${prefix}entities (entity_type)
|
|
66
62
|
`);
|
|
67
|
-
// Create entity_members table
|
|
63
|
+
// Create entity_members table (tracks all user-entity relationships including ownership)
|
|
68
64
|
await client.unsafe(`
|
|
69
65
|
CREATE TABLE IF NOT EXISTS ${prefix}entity_members (
|
|
70
66
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
71
67
|
entity_id UUID NOT NULL REFERENCES ${prefix}entities(id) ON DELETE CASCADE,
|
|
72
|
-
user_id
|
|
73
|
-
role VARCHAR(20) NOT NULL CHECK (role IN ('
|
|
68
|
+
user_id VARCHAR(128) NOT NULL,
|
|
69
|
+
role VARCHAR(20) NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
|
|
70
|
+
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
74
71
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
|
75
72
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
76
73
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
@@ -88,6 +85,10 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
88
85
|
await client.unsafe(`
|
|
89
86
|
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entity_members_user_idx
|
|
90
87
|
ON ${prefix}entity_members (user_id)
|
|
88
|
+
`);
|
|
89
|
+
await client.unsafe(`
|
|
90
|
+
CREATE INDEX IF NOT EXISTS ${indexPrefix}_entity_members_active_idx
|
|
91
|
+
ON ${prefix}entity_members (is_active)
|
|
91
92
|
`);
|
|
92
93
|
// Create entity_invitations table
|
|
93
94
|
await client.unsafe(`
|
|
@@ -95,9 +96,9 @@ async function createEntityTables(client, prefix, indexPrefix) {
|
|
|
95
96
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
96
97
|
entity_id UUID NOT NULL REFERENCES ${prefix}entities(id) ON DELETE CASCADE,
|
|
97
98
|
email VARCHAR(255) NOT NULL,
|
|
98
|
-
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', '
|
|
99
|
+
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'member')),
|
|
99
100
|
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'declined', 'expired')),
|
|
100
|
-
invited_by_user_id
|
|
101
|
+
invited_by_user_id VARCHAR(128) NOT NULL,
|
|
101
102
|
token VARCHAR(64) NOT NULL UNIQUE,
|
|
102
103
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
103
104
|
accepted_at TIMESTAMPTZ,
|
|
@@ -156,18 +157,25 @@ async function addEntityIdToProjects(client, prefix, indexPrefix) {
|
|
|
156
157
|
}
|
|
157
158
|
/**
|
|
158
159
|
* Create personal entities for existing users.
|
|
160
|
+
* Uses firebase_uid as the user identifier.
|
|
161
|
+
* Personal entities use role = 'admin' (not 'owner' - owner role is for organizations).
|
|
159
162
|
*/
|
|
160
163
|
async function migrateUsersToPersonalEntities(client, prefix) {
|
|
161
164
|
console.log('Migrating users to personal entities...');
|
|
162
|
-
// Get users without personal entities
|
|
165
|
+
// Get users without personal entities (check via entity_members with role = 'admin')
|
|
163
166
|
const usersWithoutEntities = await client.unsafe(`
|
|
164
|
-
SELECT u.
|
|
167
|
+
SELECT u.firebase_uid, u.email, u.display_name,
|
|
165
168
|
COALESCE(s.organization_path, SUBSTRING(MD5(RANDOM()::TEXT) FROM 1 FOR 8)) as slug_source
|
|
166
169
|
FROM ${prefix}users u
|
|
167
|
-
LEFT JOIN ${prefix}user_settings s ON u.
|
|
168
|
-
WHERE NOT
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
LEFT JOIN ${prefix}user_settings s ON u.firebase_uid = s.firebase_uid
|
|
171
|
+
WHERE u.firebase_uid IS NOT NULL
|
|
172
|
+
AND NOT EXISTS (
|
|
173
|
+
SELECT 1 FROM ${prefix}entity_members em
|
|
174
|
+
INNER JOIN ${prefix}entities e ON em.entity_id = e.id
|
|
175
|
+
WHERE em.user_id = u.firebase_uid
|
|
176
|
+
AND em.role = 'admin'
|
|
177
|
+
AND em.is_active = true
|
|
178
|
+
AND e.entity_type = 'personal'
|
|
171
179
|
)
|
|
172
180
|
`);
|
|
173
181
|
let migratedCount = 0;
|
|
@@ -175,17 +183,18 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
175
183
|
// Generate a unique slug (8 chars, lowercase alphanumeric)
|
|
176
184
|
const slug = generateSlug(user.slug_source);
|
|
177
185
|
const displayName = user.display_name || user.email?.split('@')[0] || 'Personal';
|
|
186
|
+
const firebaseUid = user.firebase_uid;
|
|
178
187
|
try {
|
|
179
188
|
// Create personal entity
|
|
180
189
|
const [entity] = await client.unsafe(`
|
|
181
|
-
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name
|
|
182
|
-
VALUES ('${slug}', 'personal', '${displayName.replace(/'/g, "''")}'
|
|
190
|
+
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name)
|
|
191
|
+
VALUES ('${slug}', 'personal', '${displayName.replace(/'/g, "''")}')
|
|
183
192
|
RETURNING id
|
|
184
193
|
`);
|
|
185
|
-
// Add user as admin
|
|
194
|
+
// Add user as admin (personal entities use 'admin' role, not 'owner')
|
|
186
195
|
await client.unsafe(`
|
|
187
|
-
INSERT INTO ${prefix}entity_members (entity_id, user_id, role)
|
|
188
|
-
VALUES ('${entity.id}', '${
|
|
196
|
+
INSERT INTO ${prefix}entity_members (entity_id, user_id, role, is_active)
|
|
197
|
+
VALUES ('${entity.id}', '${firebaseUid}', 'admin', true)
|
|
189
198
|
`);
|
|
190
199
|
migratedCount++;
|
|
191
200
|
}
|
|
@@ -194,13 +203,13 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
194
203
|
if (error.code === '23505') {
|
|
195
204
|
const newSlug = generateSlug();
|
|
196
205
|
const [entity] = await client.unsafe(`
|
|
197
|
-
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name
|
|
198
|
-
VALUES ('${newSlug}', 'personal', '${displayName.replace(/'/g, "''")}'
|
|
206
|
+
INSERT INTO ${prefix}entities (entity_slug, entity_type, display_name)
|
|
207
|
+
VALUES ('${newSlug}', 'personal', '${displayName.replace(/'/g, "''")}')
|
|
199
208
|
RETURNING id
|
|
200
209
|
`);
|
|
201
210
|
await client.unsafe(`
|
|
202
|
-
INSERT INTO ${prefix}entity_members (entity_id, user_id, role)
|
|
203
|
-
VALUES ('${entity.id}', '${
|
|
211
|
+
INSERT INTO ${prefix}entity_members (entity_id, user_id, role, is_active)
|
|
212
|
+
VALUES ('${entity.id}', '${firebaseUid}', 'admin', true)
|
|
204
213
|
`);
|
|
205
214
|
migratedCount++;
|
|
206
215
|
}
|
|
@@ -213,15 +222,19 @@ async function migrateUsersToPersonalEntities(client, prefix) {
|
|
|
213
222
|
}
|
|
214
223
|
/**
|
|
215
224
|
* Populate entity_id for existing projects.
|
|
225
|
+
* Uses entity_members with role = 'admin' to find personal entity membership.
|
|
216
226
|
*/
|
|
217
227
|
async function populateProjectEntityIds(client, prefix) {
|
|
218
228
|
console.log('Populating entity_id for existing projects...');
|
|
219
|
-
// Update projects to use
|
|
229
|
+
// Update projects to use user's personal entity (via entity_members with admin role)
|
|
220
230
|
const result = await client.unsafe(`
|
|
221
231
|
UPDATE ${prefix}projects p
|
|
222
232
|
SET entity_id = e.id
|
|
223
233
|
FROM ${prefix}entities e
|
|
224
|
-
|
|
234
|
+
INNER JOIN ${prefix}entity_members em ON em.entity_id = e.id
|
|
235
|
+
WHERE p.user_id = em.user_id
|
|
236
|
+
AND em.role = 'admin'
|
|
237
|
+
AND em.is_active = true
|
|
225
238
|
AND e.entity_type = 'personal'
|
|
226
239
|
AND p.entity_id IS NULL
|
|
227
240
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"001_add_entities.js","sourceRoot":"","sources":["../../src/migrations/001_add_entities.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAiBH,gDAuBC;
|
|
1
|
+
{"version":3,"file":"001_add_entities.js","sourceRoot":"","sources":["../../src/migrations/001_add_entities.ts"],"names":[],"mappings":"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"}
|