@things-factory/auth-base 10.0.0-beta.6 → 10.0.0-beta.67
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-client/apply-domain-theme.js +11 -0
- package/dist-client/apply-domain-theme.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/constants/max-age.js +5 -1
- package/dist-server/constants/max-age.js.map +1 -1
- package/dist-server/middlewares/domain-authenticate-middleware.js +35 -1
- package/dist-server/middlewares/domain-authenticate-middleware.js.map +1 -1
- package/dist-server/middlewares/index.d.ts +2 -1
- package/dist-server/middlewares/index.js.map +1 -1
- package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.d.ts +14 -0
- package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js +60 -0
- package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js.map +1 -0
- package/dist-server/service/domain-owner/domain-owner-mutation.d.ts +16 -0
- package/dist-server/service/domain-owner/domain-owner-mutation.js +151 -0
- package/dist-server/service/domain-owner/domain-owner-mutation.js.map +1 -0
- package/dist-server/service/domain-owner/domain-owner-query.d.ts +12 -0
- package/dist-server/service/domain-owner/domain-owner-query.js +70 -0
- package/dist-server/service/domain-owner/domain-owner-query.js.map +1 -0
- package/dist-server/service/domain-owner/domain-owner.d.ts +29 -0
- package/dist-server/service/domain-owner/domain-owner.js +77 -0
- package/dist-server/service/domain-owner/domain-owner.js.map +1 -0
- package/dist-server/service/domain-owner/index.d.ts +5 -0
- package/dist-server/service/domain-owner/index.js +9 -0
- package/dist-server/service/domain-owner/index.js.map +1 -0
- package/dist-server/service/index.d.ts +2 -1
- package/dist-server/service/index.js +6 -2
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/user/user-mutation.js +2 -2
- package/dist-server/service/user/user-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/spec/unit/domain-owner-entity.spec.ts +179 -0
- package/spec/unit/domain-owner-granted.spec.ts +215 -0
- package/spec/unit/domain-owner-migration.spec.ts +236 -0
- package/spec/unit/domain-owner-mutation.spec.ts +337 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DomainOwner } from './domain-owner.js';
|
|
2
|
+
export declare class DomainOwnerMutation {
|
|
3
|
+
/**
|
|
4
|
+
* 현재 도메인에 새 owner를 추가한다. 기존 도메인 오너 또는 수퍼유저만 호출 가능.
|
|
5
|
+
*
|
|
6
|
+
* 하위 호환: `Domain.owner` 캐시 필드가 비어 있으면 추가된 user를 세팅한다.
|
|
7
|
+
*/
|
|
8
|
+
addDomainOwner(username: string, context: any, reason?: string): Promise<DomainOwner>;
|
|
9
|
+
/**
|
|
10
|
+
* 현재 도메인의 owner를 제거한다.
|
|
11
|
+
* - 기존 도메인 오너 또는 수퍼유저만 호출 가능
|
|
12
|
+
* - 마지막 남은 owner는 제거 불가 (최소 1명 유지)
|
|
13
|
+
* - `Domain.owner` 캐시가 제거되는 user였다면, 남은 owner 중 하나로 재설정
|
|
14
|
+
*/
|
|
15
|
+
removeDomainOwner(username: string, context: any, _reason?: string): Promise<boolean>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DomainOwnerMutation = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const domain_owner_js_1 = require("./domain-owner.js");
|
|
8
|
+
const user_js_1 = require("../user/user.js");
|
|
9
|
+
let DomainOwnerMutation = class DomainOwnerMutation {
|
|
10
|
+
/**
|
|
11
|
+
* 현재 도메인에 새 owner를 추가한다. 기존 도메인 오너 또는 수퍼유저만 호출 가능.
|
|
12
|
+
*
|
|
13
|
+
* 하위 호환: `Domain.owner` 캐시 필드가 비어 있으면 추가된 user를 세팅한다.
|
|
14
|
+
*/
|
|
15
|
+
async addDomainOwner(username, context, reason) {
|
|
16
|
+
const { domain, user: actor } = context.state;
|
|
17
|
+
return (0, shell_1.getRepository)(shell_1.Domain).manager.transaction(async (tx) => {
|
|
18
|
+
const userRepo = tx.getRepository(user_js_1.User);
|
|
19
|
+
const ownerRepo = tx.getRepository(domain_owner_js_1.DomainOwner);
|
|
20
|
+
const domainRepo = tx.getRepository(shell_1.Domain);
|
|
21
|
+
// username 컬럼이 nullable — 사용자는 username 을 가지지 않고
|
|
22
|
+
// email 만 갖는 경우가 많음. User.get username() 도 `username || email`
|
|
23
|
+
// 을 리턴하므로 UI 가 email 을 보낼 수 있음. 둘 다 매칭한다.
|
|
24
|
+
const targetUser = await userRepo
|
|
25
|
+
.createQueryBuilder('u')
|
|
26
|
+
.innerJoin('u.domains', 'd')
|
|
27
|
+
.where('(u.username = :key OR u.email = :key) AND d.id = :domainId', {
|
|
28
|
+
key: username,
|
|
29
|
+
domainId: domain.id
|
|
30
|
+
})
|
|
31
|
+
.getOne();
|
|
32
|
+
if (!targetUser) {
|
|
33
|
+
throw new Error(`User '${username}' is not a member of this domain.`);
|
|
34
|
+
}
|
|
35
|
+
const existing = await ownerRepo.findOne({
|
|
36
|
+
where: { domain: { id: domain.id }, user: { id: targetUser.id } }
|
|
37
|
+
});
|
|
38
|
+
if (existing) {
|
|
39
|
+
throw new Error(`User '${username}' is already an owner of this domain.`);
|
|
40
|
+
}
|
|
41
|
+
const entry = ownerRepo.create({
|
|
42
|
+
domain: { id: domain.id },
|
|
43
|
+
user: { id: targetUser.id },
|
|
44
|
+
grantedBy: actor,
|
|
45
|
+
reason
|
|
46
|
+
});
|
|
47
|
+
const saved = await ownerRepo.save(entry);
|
|
48
|
+
// 하위 호환 캐시: domain.owner가 비어 있으면 세팅
|
|
49
|
+
if (!domain.owner) {
|
|
50
|
+
await domainRepo.update(domain.id, { owner: targetUser.id });
|
|
51
|
+
}
|
|
52
|
+
// GraphQL 응답을 위해 relations 포함해 재조회.
|
|
53
|
+
// (save() 반환값은 { user: {id}, grantedBy: actor } 인데 user/grantedBy 의
|
|
54
|
+
// non-nullable 필드 email 등이 채워져 있지 않아 직렬화 에러 발생)
|
|
55
|
+
return ownerRepo.findOneOrFail({
|
|
56
|
+
where: { id: saved.id },
|
|
57
|
+
relations: ['user', 'grantedBy', 'domain']
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 현재 도메인의 owner를 제거한다.
|
|
63
|
+
* - 기존 도메인 오너 또는 수퍼유저만 호출 가능
|
|
64
|
+
* - 마지막 남은 owner는 제거 불가 (최소 1명 유지)
|
|
65
|
+
* - `Domain.owner` 캐시가 제거되는 user였다면, 남은 owner 중 하나로 재설정
|
|
66
|
+
*/
|
|
67
|
+
async removeDomainOwner(username, context, _reason) {
|
|
68
|
+
const { domain } = context.state;
|
|
69
|
+
return (0, shell_1.getRepository)(shell_1.Domain).manager.transaction(async (tx) => {
|
|
70
|
+
const userRepo = tx.getRepository(user_js_1.User);
|
|
71
|
+
const ownerRepo = tx.getRepository(domain_owner_js_1.DomainOwner);
|
|
72
|
+
const domainRepo = tx.getRepository(shell_1.Domain);
|
|
73
|
+
// username 또는 email 둘 다 허용 — addDomainOwner 와 대칭.
|
|
74
|
+
const targetUser = await userRepo.findOne({
|
|
75
|
+
where: [{ username }, { email: username }]
|
|
76
|
+
});
|
|
77
|
+
if (!targetUser) {
|
|
78
|
+
throw new Error(`User '${username}' not found.`);
|
|
79
|
+
}
|
|
80
|
+
const entry = await ownerRepo.findOne({
|
|
81
|
+
where: { domain: { id: domain.id }, user: { id: targetUser.id } }
|
|
82
|
+
});
|
|
83
|
+
const hasLegacyOwner = domain.owner === targetUser.id;
|
|
84
|
+
if (!entry && !hasLegacyOwner) {
|
|
85
|
+
throw new Error(`User '${username}' is not an owner of this domain.`);
|
|
86
|
+
}
|
|
87
|
+
// 마지막 1인 보호: DomainOwner 테이블 기준 count + legacy owner 포함 여부
|
|
88
|
+
const ownerCount = await ownerRepo
|
|
89
|
+
.createQueryBuilder('ow')
|
|
90
|
+
.where('ow.domain_id = :d', { d: domain.id })
|
|
91
|
+
.getCount();
|
|
92
|
+
// 남을 owner 수 계산:
|
|
93
|
+
// - DomainOwner 테이블에서 제거 대상이 있으면 ownerCount - 1
|
|
94
|
+
// - legacy owner가 별도로 있고 DomainOwner에 없다면 +1
|
|
95
|
+
let remaining = ownerCount;
|
|
96
|
+
if (entry)
|
|
97
|
+
remaining -= 1;
|
|
98
|
+
// legacy owner가 지금 제거 대상과 다른 user라면, 그 user도 하나의 owner로 카운트
|
|
99
|
+
if (domain.owner && domain.owner !== targetUser.id) {
|
|
100
|
+
// legacy owner가 DomainOwner 테이블에도 이미 있는지
|
|
101
|
+
const legacyInTable = await ownerRepo
|
|
102
|
+
.createQueryBuilder('ow')
|
|
103
|
+
.where('ow.domain_id = :d AND ow.user_id = :u', { d: domain.id, u: domain.owner })
|
|
104
|
+
.getExists();
|
|
105
|
+
if (!legacyInTable)
|
|
106
|
+
remaining += 1;
|
|
107
|
+
}
|
|
108
|
+
if (remaining < 1) {
|
|
109
|
+
throw new Error('Cannot remove the last owner of a domain. Add another owner first.');
|
|
110
|
+
}
|
|
111
|
+
if (entry) {
|
|
112
|
+
await ownerRepo.delete(entry.id);
|
|
113
|
+
}
|
|
114
|
+
if (hasLegacyOwner) {
|
|
115
|
+
// 캐시 재설정: 남은 DomainOwner 중 가장 먼저 추가된 사람
|
|
116
|
+
const next = await ownerRepo
|
|
117
|
+
.createQueryBuilder('ow')
|
|
118
|
+
.where('ow.domain_id = :d', { d: domain.id })
|
|
119
|
+
.orderBy('ow.grantedAt', 'ASC')
|
|
120
|
+
.getOne();
|
|
121
|
+
await domainRepo.update(domain.id, { owner: next?.userId ?? null });
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
exports.DomainOwnerMutation = DomainOwnerMutation;
|
|
128
|
+
tslib_1.__decorate([
|
|
129
|
+
(0, type_graphql_1.Directive)('@privilege(domainOwnerGranted: true, superUserGranted: true)'),
|
|
130
|
+
(0, type_graphql_1.Mutation)(returns => domain_owner_js_1.DomainOwner, { description: 'Add a user as owner of the current domain.' }),
|
|
131
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('username')),
|
|
132
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
133
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('reason', { nullable: true })),
|
|
134
|
+
tslib_1.__metadata("design:type", Function),
|
|
135
|
+
tslib_1.__metadata("design:paramtypes", [String, Object, String]),
|
|
136
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
137
|
+
], DomainOwnerMutation.prototype, "addDomainOwner", null);
|
|
138
|
+
tslib_1.__decorate([
|
|
139
|
+
(0, type_graphql_1.Directive)('@privilege(domainOwnerGranted: true, superUserGranted: true)'),
|
|
140
|
+
(0, type_graphql_1.Mutation)(returns => Boolean, { description: 'Remove a user from the owners of the current domain.' }),
|
|
141
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('username')),
|
|
142
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
143
|
+
tslib_1.__param(2, (0, type_graphql_1.Arg)('reason', { nullable: true })),
|
|
144
|
+
tslib_1.__metadata("design:type", Function),
|
|
145
|
+
tslib_1.__metadata("design:paramtypes", [String, Object, String]),
|
|
146
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
147
|
+
], DomainOwnerMutation.prototype, "removeDomainOwner", null);
|
|
148
|
+
exports.DomainOwnerMutation = DomainOwnerMutation = tslib_1.__decorate([
|
|
149
|
+
(0, type_graphql_1.Resolver)(of => domain_owner_js_1.DomainOwner)
|
|
150
|
+
], DomainOwnerMutation);
|
|
151
|
+
//# sourceMappingURL=domain-owner-mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-owner-mutation.js","sourceRoot":"","sources":["../../../server/service/domain-owner/domain-owner-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { DomainOwner } from './domain-owner.js'\nimport { User } from '../user/user.js'\n\n@Resolver(of => DomainOwner)\nexport class DomainOwnerMutation {\n /**\n * 현재 도메인에 새 owner를 추가한다. 기존 도메인 오너 또는 수퍼유저만 호출 가능.\n *\n * 하위 호환: `Domain.owner` 캐시 필드가 비어 있으면 추가된 user를 세팅한다.\n */\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => DomainOwner, { description: 'Add a user as owner of the current domain.' })\n async addDomainOwner(\n @Arg('username') username: string,\n @Ctx() context: any,\n @Arg('reason', { nullable: true }) reason?: string\n ): Promise<DomainOwner> {\n const { domain, user: actor } = context.state\n\n return (getRepository(Domain) as any).manager.transaction(async (tx: any) => {\n const userRepo = tx.getRepository(User)\n const ownerRepo = tx.getRepository(DomainOwner)\n const domainRepo = tx.getRepository(Domain)\n\n // username 컬럼이 nullable — 사용자는 username 을 가지지 않고\n // email 만 갖는 경우가 많음. User.get username() 도 `username || email`\n // 을 리턴하므로 UI 가 email 을 보낼 수 있음. 둘 다 매칭한다.\n const targetUser = await userRepo\n .createQueryBuilder('u')\n .innerJoin('u.domains', 'd')\n .where('(u.username = :key OR u.email = :key) AND d.id = :domainId', {\n key: username,\n domainId: domain.id\n })\n .getOne()\n if (!targetUser) {\n throw new Error(`User '${username}' is not a member of this domain.`)\n }\n\n const existing = await ownerRepo.findOne({\n where: { domain: { id: domain.id }, user: { id: targetUser.id } }\n })\n if (existing) {\n throw new Error(`User '${username}' is already an owner of this domain.`)\n }\n\n const entry = ownerRepo.create({\n domain: { id: domain.id } as Domain,\n user: { id: targetUser.id } as User,\n grantedBy: actor,\n reason\n })\n const saved = await ownerRepo.save(entry)\n\n // 하위 호환 캐시: domain.owner가 비어 있으면 세팅\n if (!domain.owner) {\n await domainRepo.update(domain.id, { owner: targetUser.id })\n }\n\n // GraphQL 응답을 위해 relations 포함해 재조회.\n // (save() 반환값은 { user: {id}, grantedBy: actor } 인데 user/grantedBy 의\n // non-nullable 필드 email 등이 채워져 있지 않아 직렬화 에러 발생)\n return ownerRepo.findOneOrFail({\n where: { id: saved.id },\n relations: ['user', 'grantedBy', 'domain']\n })\n })\n }\n\n /**\n * 현재 도메인의 owner를 제거한다.\n * - 기존 도메인 오너 또는 수퍼유저만 호출 가능\n * - 마지막 남은 owner는 제거 불가 (최소 1명 유지)\n * - `Domain.owner` 캐시가 제거되는 user였다면, 남은 owner 중 하나로 재설정\n */\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, { description: 'Remove a user from the owners of the current domain.' })\n async removeDomainOwner(\n @Arg('username') username: string,\n @Ctx() context: any,\n @Arg('reason', { nullable: true }) _reason?: string\n ): Promise<boolean> {\n const { domain } = context.state\n\n return (getRepository(Domain) as any).manager.transaction(async (tx: any) => {\n const userRepo = tx.getRepository(User)\n const ownerRepo = tx.getRepository(DomainOwner)\n const domainRepo = tx.getRepository(Domain)\n\n // username 또는 email 둘 다 허용 — addDomainOwner 와 대칭.\n const targetUser = await userRepo.findOne({\n where: [{ username }, { email: username }]\n })\n if (!targetUser) {\n throw new Error(`User '${username}' not found.`)\n }\n\n const entry = await ownerRepo.findOne({\n where: { domain: { id: domain.id }, user: { id: targetUser.id } }\n })\n const hasLegacyOwner = domain.owner === targetUser.id\n\n if (!entry && !hasLegacyOwner) {\n throw new Error(`User '${username}' is not an owner of this domain.`)\n }\n\n // 마지막 1인 보호: DomainOwner 테이블 기준 count + legacy owner 포함 여부\n const ownerCount = await ownerRepo\n .createQueryBuilder('ow')\n .where('ow.domain_id = :d', { d: domain.id })\n .getCount()\n\n // 남을 owner 수 계산:\n // - DomainOwner 테이블에서 제거 대상이 있으면 ownerCount - 1\n // - legacy owner가 별도로 있고 DomainOwner에 없다면 +1\n let remaining = ownerCount\n if (entry) remaining -= 1\n // legacy owner가 지금 제거 대상과 다른 user라면, 그 user도 하나의 owner로 카운트\n if (domain.owner && domain.owner !== targetUser.id) {\n // legacy owner가 DomainOwner 테이블에도 이미 있는지\n const legacyInTable = await ownerRepo\n .createQueryBuilder('ow')\n .where('ow.domain_id = :d AND ow.user_id = :u', { d: domain.id, u: domain.owner })\n .getExists()\n if (!legacyInTable) remaining += 1\n }\n\n if (remaining < 1) {\n throw new Error(\n 'Cannot remove the last owner of a domain. Add another owner first.'\n )\n }\n\n if (entry) {\n await ownerRepo.delete(entry.id)\n }\n\n if (hasLegacyOwner) {\n // 캐시 재설정: 남은 DomainOwner 중 가장 먼저 추가된 사람\n const next = await ownerRepo\n .createQueryBuilder('ow')\n .where('ow.domain_id = :d', { d: domain.id })\n .orderBy('ow.grantedAt', 'ASC')\n .getOne()\n await domainRepo.update(domain.id, { owner: next?.userId ?? null })\n }\n\n return true\n })\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DomainOwner } from './domain-owner.js';
|
|
2
|
+
export declare class DomainOwnerQuery {
|
|
3
|
+
/**
|
|
4
|
+
* 현재 도메인의 owner 목록 조회 — **도메인 오너 또는 수퍼유저만** 가능.
|
|
5
|
+
* 오너 구성은 관리 정보라 비공개가 기본.
|
|
6
|
+
*/
|
|
7
|
+
domainOwners(context: any): Promise<DomainOwner[]>;
|
|
8
|
+
/**
|
|
9
|
+
* 특정 user가 현재 도메인의 owner인지 확인 — 도메인 오너 또는 수퍼유저만.
|
|
10
|
+
*/
|
|
11
|
+
isDomainOwner(username: string, context: any): Promise<boolean>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DomainOwnerQuery = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const shell_1 = require("@things-factory/shell");
|
|
7
|
+
const domain_owner_js_1 = require("./domain-owner.js");
|
|
8
|
+
const user_js_1 = require("../user/user.js");
|
|
9
|
+
let DomainOwnerQuery = class DomainOwnerQuery {
|
|
10
|
+
/**
|
|
11
|
+
* 현재 도메인의 owner 목록 조회 — **도메인 오너 또는 수퍼유저만** 가능.
|
|
12
|
+
* 오너 구성은 관리 정보라 비공개가 기본.
|
|
13
|
+
*/
|
|
14
|
+
async domainOwners(context) {
|
|
15
|
+
const { domain } = context.state;
|
|
16
|
+
return (0, shell_1.getRepository)(domain_owner_js_1.DomainOwner)
|
|
17
|
+
.createQueryBuilder('ow')
|
|
18
|
+
.leftJoinAndSelect('ow.user', 'user')
|
|
19
|
+
.leftJoinAndSelect('ow.grantedBy', 'grantedBy')
|
|
20
|
+
.where('ow.domain_id = :domainId', { domainId: domain.id })
|
|
21
|
+
.orderBy('ow.grantedAt', 'ASC')
|
|
22
|
+
.getMany();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 특정 user가 현재 도메인의 owner인지 확인 — 도메인 오너 또는 수퍼유저만.
|
|
26
|
+
*/
|
|
27
|
+
async isDomainOwner(username, context) {
|
|
28
|
+
const { domain } = context.state;
|
|
29
|
+
// username 또는 email 둘 다 허용 (username 컬럼이 nullable 이라 email 만
|
|
30
|
+
// 세팅된 사용자도 있음).
|
|
31
|
+
const user = await (0, shell_1.getRepository)(user_js_1.User).findOne({
|
|
32
|
+
where: [
|
|
33
|
+
{ username, domains: { id: domain.id } },
|
|
34
|
+
{ email: username, domains: { id: domain.id } }
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
if (!user)
|
|
38
|
+
return false;
|
|
39
|
+
// Domain.owner 캐시 또는 DomainOwner 테이블 둘 다 확인
|
|
40
|
+
if (domain.owner === user.id)
|
|
41
|
+
return true;
|
|
42
|
+
const exists = await (0, shell_1.getRepository)(domain_owner_js_1.DomainOwner)
|
|
43
|
+
.createQueryBuilder('ow')
|
|
44
|
+
.where('ow.domain_id = :d AND ow.user_id = :u', { d: domain.id, u: user.id })
|
|
45
|
+
.getExists();
|
|
46
|
+
return exists;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
exports.DomainOwnerQuery = DomainOwnerQuery;
|
|
50
|
+
tslib_1.__decorate([
|
|
51
|
+
(0, type_graphql_1.Directive)('@privilege(domainOwnerGranted: true, superUserGranted: true)'),
|
|
52
|
+
(0, type_graphql_1.Query)(returns => [domain_owner_js_1.DomainOwner], { description: 'List owners of the current domain.' }),
|
|
53
|
+
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
54
|
+
tslib_1.__metadata("design:type", Function),
|
|
55
|
+
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
56
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
57
|
+
], DomainOwnerQuery.prototype, "domainOwners", null);
|
|
58
|
+
tslib_1.__decorate([
|
|
59
|
+
(0, type_graphql_1.Directive)('@privilege(domainOwnerGranted: true, superUserGranted: true)'),
|
|
60
|
+
(0, type_graphql_1.Query)(returns => Boolean, { description: 'Check if a user is an owner of the current domain.' }),
|
|
61
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('username')),
|
|
62
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
63
|
+
tslib_1.__metadata("design:type", Function),
|
|
64
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
65
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
66
|
+
], DomainOwnerQuery.prototype, "isDomainOwner", null);
|
|
67
|
+
exports.DomainOwnerQuery = DomainOwnerQuery = tslib_1.__decorate([
|
|
68
|
+
(0, type_graphql_1.Resolver)(of => domain_owner_js_1.DomainOwner)
|
|
69
|
+
], DomainOwnerQuery);
|
|
70
|
+
//# sourceMappingURL=domain-owner-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-owner-query.js","sourceRoot":"","sources":["../../../server/service/domain-owner/domain-owner-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAmE;AACnE,iDAA6D;AAE7D,uDAA+C;AAC/C,6CAAsC;AAG/B,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B;;;OAGG;IAGG,AAAN,KAAK,CAAC,YAAY,CAAQ,OAAY;QACpC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,OAAO,IAAA,qBAAa,EAAC,6BAAW,CAAC;aAC9B,kBAAkB,CAAC,IAAI,CAAC;aACxB,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC;aACpC,iBAAiB,CAAC,cAAc,EAAE,WAAW,CAAC;aAC9C,KAAK,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;aAC1D,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC;aAC9B,OAAO,EAAE,CAAA;IACd,CAAC;IAED;;OAEG;IAGG,AAAN,KAAK,CAAC,aAAa,CACA,QAAgB,EAC1B,OAAY;QAEnB,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,6DAA6D;QAC7D,gBAAgB;QAChB,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAI,CAAC,CAAC,OAAO,CAAC;YAC7C,KAAK,EAAE;gBACL,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;gBACxC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE;aAChD;SACF,CAAC,CAAA;QACF,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA;QAEvB,4CAA4C;QAC5C,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QAEzC,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,6BAAW,CAAC;aAC5C,kBAAkB,CAAC,IAAI,CAAC;aACxB,KAAK,CAAC,uCAAuC,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;aAC5E,SAAS,EAAE,CAAA;QACd,OAAO,MAAM,CAAA;IACf,CAAC;CACF,CAAA;AAjDY,4CAAgB;AAOrB;IAFL,IAAA,wBAAS,EAAC,8DAA8D,CAAC;IACzE,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,6BAAW,CAAC,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;IACnE,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;oDAUxB;AAOK;IAFL,IAAA,wBAAS,EAAC,8DAA8D,CAAC;IACzE,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;IAE9F,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;qDAsBP;2BAhDU,gBAAgB;IAD5B,IAAA,uBAAQ,EAAC,EAAE,CAAC,EAAE,CAAC,6BAAW,CAAC;GACf,gBAAgB,CAiD5B","sourcesContent":["import { Resolver, Query, Arg, Ctx, Directive } from 'type-graphql'\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { DomainOwner } from './domain-owner.js'\nimport { User } from '../user/user.js'\n\n@Resolver(of => DomainOwner)\nexport class DomainOwnerQuery {\n /**\n * 현재 도메인의 owner 목록 조회 — **도메인 오너 또는 수퍼유저만** 가능.\n * 오너 구성은 관리 정보라 비공개가 기본.\n */\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => [DomainOwner], { description: 'List owners of the current domain.' })\n async domainOwners(@Ctx() context: any): Promise<DomainOwner[]> {\n const { domain } = context.state\n\n return getRepository(DomainOwner)\n .createQueryBuilder('ow')\n .leftJoinAndSelect('ow.user', 'user')\n .leftJoinAndSelect('ow.grantedBy', 'grantedBy')\n .where('ow.domain_id = :domainId', { domainId: domain.id })\n .orderBy('ow.grantedAt', 'ASC')\n .getMany()\n }\n\n /**\n * 특정 user가 현재 도메인의 owner인지 확인 — 도메인 오너 또는 수퍼유저만.\n */\n @Directive('@privilege(domainOwnerGranted: true, superUserGranted: true)')\n @Query(returns => Boolean, { description: 'Check if a user is an owner of the current domain.' })\n async isDomainOwner(\n @Arg('username') username: string,\n @Ctx() context: any\n ): Promise<boolean> {\n const { domain } = context.state\n\n // username 또는 email 둘 다 허용 (username 컬럼이 nullable 이라 email 만\n // 세팅된 사용자도 있음).\n const user = await getRepository(User).findOne({\n where: [\n { username, domains: { id: domain.id } },\n { email: username, domains: { id: domain.id } }\n ]\n })\n if (!user) return false\n\n // Domain.owner 캐시 또는 DomainOwner 테이블 둘 다 확인\n if (domain.owner === user.id) return true\n\n const exists = await getRepository(DomainOwner)\n .createQueryBuilder('ow')\n .where('ow.domain_id = :d AND ow.user_id = :u', { d: domain.id, u: user.id })\n .getExists()\n return exists\n }\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { User } from '../user/user.js';
|
|
3
|
+
/**
|
|
4
|
+
* 도메인의 소유자(Owner)를 여러 명 허용하기 위한 join entity.
|
|
5
|
+
*
|
|
6
|
+
* 기존 `Domain.owner`(단일 UUID 문자열) 모델의 한계를 해소하고,
|
|
7
|
+
* 여러 User를 같은 Domain의 owner로 등록 가능하게 한다. 모든 owner는 동등한
|
|
8
|
+
* 권한을 가진다 (primary 개념 없음).
|
|
9
|
+
*
|
|
10
|
+
* `Domain.owner` 컬럼은 하위 호환을 위해 유지되며 "대표 owner 표시용 캐시"로
|
|
11
|
+
* 활용된다:
|
|
12
|
+
* - owner 추가 시, 기존 owner 컬럼이 비어있으면 자동 세팅
|
|
13
|
+
* - owner 제거 시, 그 사람이 owner 컬럼이었다면 남은 owners 중 첫 사람으로 재설정
|
|
14
|
+
* 권위 있는 소유권 정보는 이 테이블이 가지며, `domainOwnerGranted` 권한 체크는
|
|
15
|
+
* 양쪽을 OR로 확인해 마이그레이션 기간 동안의 안전성을 보장한다.
|
|
16
|
+
*
|
|
17
|
+
* User 삭제 시 CASCADE로 소유권 엔트리 자동 해제.
|
|
18
|
+
*/
|
|
19
|
+
export declare class DomainOwner {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
domain: Domain;
|
|
22
|
+
domainId: string;
|
|
23
|
+
user: User;
|
|
24
|
+
userId: string;
|
|
25
|
+
grantedBy: User;
|
|
26
|
+
grantedById: string;
|
|
27
|
+
grantedAt: Date;
|
|
28
|
+
reason: string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DomainOwner = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const shell_1 = require("@things-factory/shell");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const type_graphql_1 = require("type-graphql");
|
|
8
|
+
const user_js_1 = require("../user/user.js");
|
|
9
|
+
/**
|
|
10
|
+
* 도메인의 소유자(Owner)를 여러 명 허용하기 위한 join entity.
|
|
11
|
+
*
|
|
12
|
+
* 기존 `Domain.owner`(단일 UUID 문자열) 모델의 한계를 해소하고,
|
|
13
|
+
* 여러 User를 같은 Domain의 owner로 등록 가능하게 한다. 모든 owner는 동등한
|
|
14
|
+
* 권한을 가진다 (primary 개념 없음).
|
|
15
|
+
*
|
|
16
|
+
* `Domain.owner` 컬럼은 하위 호환을 위해 유지되며 "대표 owner 표시용 캐시"로
|
|
17
|
+
* 활용된다:
|
|
18
|
+
* - owner 추가 시, 기존 owner 컬럼이 비어있으면 자동 세팅
|
|
19
|
+
* - owner 제거 시, 그 사람이 owner 컬럼이었다면 남은 owners 중 첫 사람으로 재설정
|
|
20
|
+
* 권위 있는 소유권 정보는 이 테이블이 가지며, `domainOwnerGranted` 권한 체크는
|
|
21
|
+
* 양쪽을 OR로 확인해 마이그레이션 기간 동안의 안전성을 보장한다.
|
|
22
|
+
*
|
|
23
|
+
* User 삭제 시 CASCADE로 소유권 엔트리 자동 해제.
|
|
24
|
+
*/
|
|
25
|
+
let DomainOwner = class DomainOwner {
|
|
26
|
+
};
|
|
27
|
+
exports.DomainOwner = DomainOwner;
|
|
28
|
+
tslib_1.__decorate([
|
|
29
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
30
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier.' }),
|
|
31
|
+
tslib_1.__metadata("design:type", String)
|
|
32
|
+
], DomainOwner.prototype, "id", void 0);
|
|
33
|
+
tslib_1.__decorate([
|
|
34
|
+
(0, typeorm_1.ManyToOne)(type => shell_1.Domain, { onDelete: 'CASCADE' }),
|
|
35
|
+
(0, typeorm_1.JoinColumn)(),
|
|
36
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { description: 'Domain that the user owns.' }),
|
|
37
|
+
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
38
|
+
], DomainOwner.prototype, "domain", void 0);
|
|
39
|
+
tslib_1.__decorate([
|
|
40
|
+
(0, typeorm_1.RelationId)((ow) => ow.domain),
|
|
41
|
+
tslib_1.__metadata("design:type", String)
|
|
42
|
+
], DomainOwner.prototype, "domainId", void 0);
|
|
43
|
+
tslib_1.__decorate([
|
|
44
|
+
(0, typeorm_1.ManyToOne)(type => user_js_1.User, { onDelete: 'CASCADE' }),
|
|
45
|
+
(0, typeorm_1.JoinColumn)(),
|
|
46
|
+
(0, type_graphql_1.Field)(type => user_js_1.User, { description: 'User who owns the domain.' }),
|
|
47
|
+
tslib_1.__metadata("design:type", user_js_1.User)
|
|
48
|
+
], DomainOwner.prototype, "user", void 0);
|
|
49
|
+
tslib_1.__decorate([
|
|
50
|
+
(0, typeorm_1.RelationId)((ow) => ow.user),
|
|
51
|
+
tslib_1.__metadata("design:type", String)
|
|
52
|
+
], DomainOwner.prototype, "userId", void 0);
|
|
53
|
+
tslib_1.__decorate([
|
|
54
|
+
(0, typeorm_1.ManyToOne)(type => user_js_1.User, { nullable: true }),
|
|
55
|
+
(0, type_graphql_1.Field)(type => user_js_1.User, { nullable: true, description: 'User who granted this ownership (audit).' }),
|
|
56
|
+
tslib_1.__metadata("design:type", user_js_1.User)
|
|
57
|
+
], DomainOwner.prototype, "grantedBy", void 0);
|
|
58
|
+
tslib_1.__decorate([
|
|
59
|
+
(0, typeorm_1.RelationId)((ow) => ow.grantedBy),
|
|
60
|
+
tslib_1.__metadata("design:type", String)
|
|
61
|
+
], DomainOwner.prototype, "grantedById", void 0);
|
|
62
|
+
tslib_1.__decorate([
|
|
63
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
64
|
+
(0, type_graphql_1.Field)({ description: 'When the ownership was granted.' }),
|
|
65
|
+
tslib_1.__metadata("design:type", Date)
|
|
66
|
+
], DomainOwner.prototype, "grantedAt", void 0);
|
|
67
|
+
tslib_1.__decorate([
|
|
68
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
69
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Optional reason/memo for granting ownership.' }),
|
|
70
|
+
tslib_1.__metadata("design:type", String)
|
|
71
|
+
], DomainOwner.prototype, "reason", void 0);
|
|
72
|
+
exports.DomainOwner = DomainOwner = tslib_1.__decorate([
|
|
73
|
+
(0, typeorm_1.Entity)(),
|
|
74
|
+
(0, typeorm_1.Index)('ix_domain_owner_unique', (ow) => [ow.domain, ow.user], { unique: true }),
|
|
75
|
+
(0, type_graphql_1.ObjectType)({ description: 'An ownership record binding a User to a Domain (multi-owner support).' })
|
|
76
|
+
], DomainOwner);
|
|
77
|
+
//# sourceMappingURL=domain-owner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-owner.js","sourceRoot":"","sources":["../../../server/service/domain-owner/domain-owner.ts"],"names":[],"mappings":";;;;AAAA,iDAA8C;AAC9C,qCASgB;AAChB,+CAAoD;AAEpD,6CAAsC;AAEtC;;;;;;;;;;;;;;;GAeG;AAII,IAAM,WAAW,GAAjB,MAAM,WAAW;CAmCvB,CAAA;AAnCY,kCAAW;AAGb;IAFR,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAE,EAAE,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC;;uCACtC;AAKnB;IAHC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAClD,IAAA,oBAAU,GAAE;IACZ,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC;sCAC7D,cAAM;2CAAA;AAGd;IADC,IAAA,oBAAU,EAAC,CAAC,EAAe,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC;;6CAC3B;AAKhB;IAHC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAChD,IAAA,oBAAU,GAAE;IACZ,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;sCAC5D,cAAI;yCAAA;AAGV;IADC,IAAA,oBAAU,EAAC,CAAC,EAAe,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;;2CAC3B;AAId;IAFC,IAAA,mBAAS,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,cAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;sCACtF,cAAI;8CAAA;AAGf;IADC,IAAA,oBAAU,EAAC,CAAC,EAAe,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC;;gDAC3B;AAInB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC;sCAC/C,IAAI;8CAAA;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;2CACzE;sBAlCH,WAAW;IAHvB,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,wBAAwB,EAAE,CAAC,EAAe,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5F,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;GACxF,WAAW,CAmCvB","sourcesContent":["import { Domain } from '@things-factory/shell'\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n JoinColumn,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId\n} from 'typeorm'\nimport { ObjectType, Field, ID } from 'type-graphql'\n\nimport { User } from '../user/user.js'\n\n/**\n * 도메인의 소유자(Owner)를 여러 명 허용하기 위한 join entity.\n *\n * 기존 `Domain.owner`(단일 UUID 문자열) 모델의 한계를 해소하고,\n * 여러 User를 같은 Domain의 owner로 등록 가능하게 한다. 모든 owner는 동등한\n * 권한을 가진다 (primary 개념 없음).\n *\n * `Domain.owner` 컬럼은 하위 호환을 위해 유지되며 \"대표 owner 표시용 캐시\"로\n * 활용된다:\n * - owner 추가 시, 기존 owner 컬럼이 비어있으면 자동 세팅\n * - owner 제거 시, 그 사람이 owner 컬럼이었다면 남은 owners 중 첫 사람으로 재설정\n * 권위 있는 소유권 정보는 이 테이블이 가지며, `domainOwnerGranted` 권한 체크는\n * 양쪽을 OR로 확인해 마이그레이션 기간 동안의 안전성을 보장한다.\n *\n * User 삭제 시 CASCADE로 소유권 엔트리 자동 해제.\n */\n@Entity()\n@Index('ix_domain_owner_unique', (ow: DomainOwner) => [ow.domain, ow.user], { unique: true })\n@ObjectType({ description: 'An ownership record binding a User to a Domain (multi-owner support).' })\nexport class DomainOwner {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier.' })\n readonly id: string\n\n @ManyToOne(type => Domain, { onDelete: 'CASCADE' })\n @JoinColumn()\n @Field(type => Domain, { description: 'Domain that the user owns.' })\n domain: Domain\n\n @RelationId((ow: DomainOwner) => ow.domain)\n domainId: string\n\n @ManyToOne(type => User, { onDelete: 'CASCADE' })\n @JoinColumn()\n @Field(type => User, { description: 'User who owns the domain.' })\n user: User\n\n @RelationId((ow: DomainOwner) => ow.user)\n userId: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'User who granted this ownership (audit).' })\n grantedBy: User\n\n @RelationId((ow: DomainOwner) => ow.grantedBy)\n grantedById: string\n\n @CreateDateColumn()\n @Field({ description: 'When the ownership was granted.' })\n grantedAt: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Optional reason/memo for granting ownership.' })\n reason: string\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { DomainOwner } from './domain-owner.js';
|
|
2
|
+
import { DomainOwnerQuery } from './domain-owner-query.js';
|
|
3
|
+
import { DomainOwnerMutation } from './domain-owner-mutation.js';
|
|
4
|
+
export declare const entities: (typeof DomainOwner)[];
|
|
5
|
+
export declare const resolvers: (typeof DomainOwnerQuery | typeof DomainOwnerMutation)[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvers = exports.entities = void 0;
|
|
4
|
+
const domain_owner_js_1 = require("./domain-owner.js");
|
|
5
|
+
const domain_owner_query_js_1 = require("./domain-owner-query.js");
|
|
6
|
+
const domain_owner_mutation_js_1 = require("./domain-owner-mutation.js");
|
|
7
|
+
exports.entities = [domain_owner_js_1.DomainOwner];
|
|
8
|
+
exports.resolvers = [domain_owner_query_js_1.DomainOwnerQuery, domain_owner_mutation_js_1.DomainOwnerMutation];
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/domain-owner/index.ts"],"names":[],"mappings":";;;AAAA,uDAA+C;AAC/C,mEAA0D;AAC1D,yEAAgE;AAEnD,QAAA,QAAQ,GAAG,CAAC,6BAAW,CAAC,CAAA;AACxB,QAAA,SAAS,GAAG,CAAC,wCAAgB,EAAE,8CAAmB,CAAC,CAAA","sourcesContent":["import { DomainOwner } from './domain-owner.js'\nimport { DomainOwnerQuery } from './domain-owner-query.js'\nimport { DomainOwnerMutation } from './domain-owner-mutation.js'\n\nexport const entities = [DomainOwner]\nexport const resolvers = [DomainOwnerQuery, DomainOwnerMutation]\n"]}
|
|
@@ -14,6 +14,7 @@ export * from './verification-token/verification-token.js';
|
|
|
14
14
|
export * from './login-history/login-history.js';
|
|
15
15
|
export * from './web-auth-credential/web-auth-credential.js';
|
|
16
16
|
export * from './domain-link/domain-link.js';
|
|
17
|
+
export * from './domain-owner/domain-owner.js';
|
|
17
18
|
export * from './app-binding/app-binding-types.js';
|
|
18
19
|
export * from './appliance/appliance-types.js';
|
|
19
20
|
export * from './application/application-types.js';
|
|
@@ -24,7 +25,7 @@ export * from './privilege/privilege-types.js';
|
|
|
24
25
|
export * from './role/role-types.js';
|
|
25
26
|
export * from './user/user-types.js';
|
|
26
27
|
export * from './domain-link/domain-link-types.js';
|
|
27
|
-
export declare const entities: (typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./user/user.js").User | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./role/role.js").Role | typeof import("./privilege/privilege.js").Privilege | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./invitation/invitation.js").Invitation | typeof import("./application/application.js").Application | typeof import("./login-history/login-history.js").LoginHistory | typeof import("./appliance/appliance.js").Appliance | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./partner/partner.js").Partner | typeof import("./domain-link/domain-link.js").DomainLink)[];
|
|
28
|
+
export declare const entities: (typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./user/user.js").User | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./role/role.js").Role | typeof import("./privilege/privilege.js").Privilege | typeof import("./domain-owner/domain-owner.js").DomainOwner | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./invitation/invitation.js").Invitation | typeof import("./application/application.js").Application | typeof import("./login-history/login-history.js").LoginHistory | typeof import("./appliance/appliance.js").Appliance | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./partner/partner.js").Partner | typeof import("./domain-link/domain-link.js").DomainLink)[];
|
|
28
29
|
export declare const schema: {
|
|
29
30
|
typeDefs: {
|
|
30
31
|
privilegeDirectiveTypeDefs: import("graphql").DocumentNode;
|
|
@@ -21,6 +21,7 @@ const index_js_14 = require("./user/index.js");
|
|
|
21
21
|
const index_js_15 = require("./verification-token/index.js");
|
|
22
22
|
const index_js_16 = require("./web-auth-credential/index.js");
|
|
23
23
|
const index_js_17 = require("./domain-link/index.js");
|
|
24
|
+
const index_js_18 = require("./domain-owner/index.js");
|
|
24
25
|
/* EXPORT ENTITY TYPES */
|
|
25
26
|
tslib_1.__exportStar(require("./users-auth-providers/users-auth-providers.js"), exports);
|
|
26
27
|
tslib_1.__exportStar(require("./auth-provider/auth-provider.js"), exports);
|
|
@@ -38,6 +39,7 @@ tslib_1.__exportStar(require("./verification-token/verification-token.js"), expo
|
|
|
38
39
|
tslib_1.__exportStar(require("./login-history/login-history.js"), exports);
|
|
39
40
|
tslib_1.__exportStar(require("./web-auth-credential/web-auth-credential.js"), exports);
|
|
40
41
|
tslib_1.__exportStar(require("./domain-link/domain-link.js"), exports);
|
|
42
|
+
tslib_1.__exportStar(require("./domain-owner/domain-owner.js"), exports);
|
|
41
43
|
/* EXPORT TYPES */
|
|
42
44
|
tslib_1.__exportStar(require("./app-binding/app-binding-types.js"), exports);
|
|
43
45
|
tslib_1.__exportStar(require("./appliance/appliance-types.js"), exports);
|
|
@@ -65,7 +67,8 @@ exports.entities = [
|
|
|
65
67
|
...index_js_15.entities,
|
|
66
68
|
...index_js_9.entities,
|
|
67
69
|
...index_js_16.entities,
|
|
68
|
-
...index_js_17.entities
|
|
70
|
+
...index_js_17.entities,
|
|
71
|
+
...index_js_18.entities
|
|
69
72
|
];
|
|
70
73
|
exports.schema = {
|
|
71
74
|
typeDefs: {
|
|
@@ -86,7 +89,8 @@ exports.schema = {
|
|
|
86
89
|
...index_js_3.resolvers,
|
|
87
90
|
...index_js_6.resolvers,
|
|
88
91
|
...index_js_9.resolvers,
|
|
89
|
-
...index_js_17.resolvers
|
|
92
|
+
...index_js_17.resolvers,
|
|
93
|
+
...index_js_18.resolvers
|
|
90
94
|
],
|
|
91
95
|
directives: {
|
|
92
96
|
privilege: privilege_directive_js_1.privilegeDirectiveResolver
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,8DAGwC;AACxC,uDAA+G;AAC/G,qDAAwE;AACxE,mDAAqG;AACrG,qDAA2G;AAC3G,0DAAkF;AAClF,sDAA2G;AAC3G,oDAAuG;AACvG,uDAA8G;AAC9G,kDAA+F;AAC/F,2DAAiF;AACjF,oDAAqG;AACrG,+EAA2G;AAC3G,+CAAsF;AACtF,+CAAsF;AACtF,6DAAqF;AACrF,8DAAsF;AACtF,sDAAwG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,mCAAmC;AACnC,8DAGwC;AACxC,uDAA+G;AAC/G,qDAAwE;AACxE,mDAAqG;AACrG,qDAA2G;AAC3G,0DAAkF;AAClF,sDAA2G;AAC3G,oDAAuG;AACvG,uDAA8G;AAC9G,kDAA+F;AAC/F,2DAAiF;AACjF,oDAAqG;AACrG,+EAA2G;AAC3G,+CAAsF;AACtF,+CAAsF;AACtF,6DAAqF;AACrF,8DAAsF;AACtF,sDAAwG;AACxG,uDAA4G;AAE5G,yBAAyB;AACzB,yFAA8D;AAC9D,2EAAgD;AAChD,uEAA4C;AAC5C,mEAAwC;AACxC,mEAAwC;AACxC,yDAA8B;AAC9B,yDAA8B;AAC9B,+DAAoC;AACpC,yEAA8C;AAC9C,qEAA0C;AAC1C,uEAA4C;AAC5C,iFAAsD;AACtD,qFAA0D;AAC1D,2EAAgD;AAChD,uFAA4D;AAC5D,uEAA4C;AAC5C,yEAA8C;AAE9C,kBAAkB;AAClB,6EAAkD;AAClD,yEAA8C;AAC9C,6EAAkD;AAClD,uFAA4D;AAC5D,2EAAgD;AAChD,qEAA0C;AAC1C,yEAA8C;AAC9C,+DAAoC;AACpC,+DAAoC;AACpC,6EAAkD;AAErC,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAA0B;IAC7B,GAAG,mBAAoB;IACvB,GAAG,mBAAmB;IACtB,GAAG,mBAAiB;IACpB,GAAG,oBAAiB;IACpB,GAAG,oBAAY;IACf,GAAG,oBAAY;IACf,GAAG,oBAAe;IAClB,GAAG,mBAAmB;IACtB,GAAG,mBAAkB;IACrB,GAAG,oBAAuB;IAC1B,GAAG,oBAAyB;IAC5B,GAAG,mBAAoB;IACvB,GAAG,oBAAyB;IAC5B,GAAG,oBAAkB;IACrB,GAAG,oBAAmB;CACvB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE;QACR,0BAA0B,EAA1B,mDAA0B;KAC3B;IAED,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAA2B;QAC9B,GAAG,oBAAqB;QACxB,GAAG,oBAAoB;QACvB,GAAG,oBAAkB;QACrB,GAAG,qBAAkB;QACrB,GAAG,qBAAa;QAChB,GAAG,qBAAa;QAChB,GAAG,qBAAgB;QACnB,GAAG,oBAAmB;QACtB,GAAG,oBAAkB;QACrB,GAAG,oBAAkB;QACrB,GAAG,oBAAuB;QAC1B,GAAG,oBAAoB;QACvB,GAAG,qBAAkB;QACrB,GAAG,qBAAoB;KACxB;IACD,UAAU,EAAE;QACV,SAAS,EAAE,mDAA0B;KACtC;CACF,CAAA","sourcesContent":["/* IMPORT ENTITIES AND RESOLVERS */\nimport {\n entities as UsersAuthProvidersEntities,\n resolvers as UsersAuthProvidersResolvers\n} from './users-auth-providers/index.js'\nimport { entities as AuthProviderEntities, resolvers as AuthProviderResolvers } from './auth-provider/index.js'\nimport { resolvers as AppbindingResolver } from './app-binding/index.js'\nimport { entities as ApplianceEntities, resolvers as ApplianceResolvers } from './appliance/index.js'\nimport { entities as ApplicationEntities, resolvers as ApplicationResolvers } from './application/index.js'\nimport { resolvers as DomainGeneratorResolver } from './domain-generator/index.js'\nimport { entities as GrantedRoleEntities, resolvers as GrantedRoleResolver } from './granted-role/index.js'\nimport { entities as InvitationEntities, resolvers as InvitationResolver } from './invitation/index.js'\nimport { entities as LoginHistoryEntities, resolvers as LoginHistoryResolver } from './login-history/index.js'\nimport { entities as PartnerEntities, resolvers as PartnerResolvers } from './partner/index.js'\nimport { entities as PasswordHistoryEntities } from './password-history/index.js'\nimport { entities as PrivilegeEntities, resolvers as PrivilegeResolvers } from './privilege/index.js'\nimport { privilegeDirectiveResolver, privilegeDirectiveTypeDefs } from './privilege/privilege-directive.js'\nimport { entities as RoleEntities, resolvers as RoleResolvers } from './role/index.js'\nimport { entities as UserEntities, resolvers as UserResolvers } from './user/index.js'\nimport { entities as VerificationTokenEntities } from './verification-token/index.js'\nimport { entities as WebAuthCredentialEntities } from './web-auth-credential/index.js'\nimport { entities as DomainLinkEntities, resolvers as DomainLinkResolver } from './domain-link/index.js'\nimport { entities as DomainOwnerEntities, resolvers as DomainOwnerResolvers } from './domain-owner/index.js'\n\n/* EXPORT ENTITY TYPES */\nexport * from './users-auth-providers/users-auth-providers.js'\nexport * from './auth-provider/auth-provider.js'\nexport * from './application/application.js'\nexport * from './appliance/appliance.js'\nexport * from './privilege/privilege.js'\nexport * from './role/role.js'\nexport * from './user/user.js'\nexport * from './partner/partner.js'\nexport * from './granted-role/granted-role.js'\nexport * from './invitation/invitation.js'\nexport * from './app-binding/app-binding.js'\nexport * from './password-history/password-history.js'\nexport * from './verification-token/verification-token.js'\nexport * from './login-history/login-history.js'\nexport * from './web-auth-credential/web-auth-credential.js'\nexport * from './domain-link/domain-link.js'\nexport * from './domain-owner/domain-owner.js'\n\n/* EXPORT TYPES */\nexport * from './app-binding/app-binding-types.js'\nexport * from './appliance/appliance-types.js'\nexport * from './application/application-types.js'\nexport * from './domain-generator/domain-generator-types.js'\nexport * from './invitation/invitation-types.js'\nexport * from './partner/partner-types.js'\nexport * from './privilege/privilege-types.js'\nexport * from './role/role-types.js'\nexport * from './user/user-types.js'\nexport * from './domain-link/domain-link-types.js'\n\nexport const entities = [\n /* ENTITIES */\n ...UsersAuthProvidersEntities,\n ...AuthProviderEntities,\n ...ApplicationEntities,\n ...ApplianceEntities,\n ...PrivilegeEntities,\n ...RoleEntities,\n ...UserEntities,\n ...PartnerEntities,\n ...GrantedRoleEntities,\n ...InvitationEntities,\n ...PasswordHistoryEntities,\n ...VerificationTokenEntities,\n ...LoginHistoryEntities,\n ...WebAuthCredentialEntities,\n ...DomainLinkEntities,\n ...DomainOwnerEntities\n]\n\nexport const schema = {\n typeDefs: {\n privilegeDirectiveTypeDefs\n },\n\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...UsersAuthProvidersResolvers,\n ...AuthProviderResolvers,\n ...ApplicationResolvers,\n ...ApplianceResolvers,\n ...PrivilegeResolvers,\n ...RoleResolvers,\n ...UserResolvers,\n ...PartnerResolvers,\n ...GrantedRoleResolver,\n ...InvitationResolver,\n ...AppbindingResolver,\n ...DomainGeneratorResolver,\n ...LoginHistoryResolver,\n ...DomainLinkResolver,\n ...DomainOwnerResolvers\n ],\n directives: {\n privilege: privilegeDirectiveResolver\n }\n}\n"]}
|
|
@@ -217,12 +217,12 @@ let UserMutation = class UserMutation {
|
|
|
217
217
|
const userRepository = (0, shell_1.getRepository)(user_js_1.User, tx);
|
|
218
218
|
var user = await userRepository.findOne({
|
|
219
219
|
where: { username },
|
|
220
|
-
relations: ['domains']
|
|
220
|
+
relations: ['domains', 'roles']
|
|
221
221
|
});
|
|
222
222
|
if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
|
|
223
223
|
user = await userRepository.findOne({
|
|
224
224
|
where: { email: (0, typeorm_1.ILike)(username) },
|
|
225
|
-
relations: ['domains']
|
|
225
|
+
relations: ['domains', 'roles']
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
228
|
if (!user) {
|