@canmingir/link-express 1.6.0 → 1.6.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canmingir/link-express",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "NucTeam",
package/src/lib/test.js CHANGED
@@ -7,10 +7,21 @@ init({
7
7
  jwt: {
8
8
  identifier: "email",
9
9
  },
10
- tokenUrl: "https://github.com/login/oauth/access_token",
11
- userUrl: "https://api.github.com/user",
12
- clientId: "0c2844d3d19dc9293fc5",
13
- redirectUri: "http://localhost:5173/callback",
10
+ providers: {
11
+ github: {
12
+ tokenUrl: "https://github.com/login/oauth/access_token",
13
+ userUrl: "https://api.github.com/user",
14
+ clientId: "0c2844d3d19dc9293fc5",
15
+ redirectUri: "http://localhost:5173/callback",
16
+ userIdentifier: "id",
17
+ userFields: {
18
+ name: "name",
19
+ displayName: "login",
20
+ avatarUrl: "avatar_url",
21
+ email: "email",
22
+ },
23
+ },
24
+ },
14
25
  },
15
26
  },
16
27
  postgres: {
@@ -3,82 +3,140 @@ const router = require("express").Router();
3
3
  const jwt = require("jsonwebtoken");
4
4
  const axios = require("axios");
5
5
  const config = require("../config");
6
- const { AuthenticationError } = require("../error");
6
+ const { AuthenticationError, AuthorizationError } = require("../error");
7
7
  const Permission = require("../models/Permission");
8
8
  const { project } = config();
9
9
 
10
10
  router.post("/", async (req, res) => {
11
- let { appId, projectId, code, refreshToken, redirectUri } = Joi.attempt(
12
- req.body,
13
- Joi.object({
14
- appId: Joi.string().required(),
15
- projectId: Joi.string().optional(),
16
- code: Joi.string().optional(),
17
- refreshToken: Joi.string().optional(),
18
- redirectUri: Joi.string().optional(),
19
- })
20
- .required()
21
- .options({ stripUnknown: true })
22
- );
11
+ let { appId, projectId, code, refreshToken, redirectUri, provider } =
12
+ Joi.attempt(
13
+ req.body,
14
+ Joi.object({
15
+ appId: Joi.string().required(),
16
+ projectId: Joi.string().optional(),
17
+ code: Joi.string().optional(),
18
+ refreshToken: Joi.string().optional(),
19
+ redirectUri: Joi.string().optional(),
20
+ provider: Joi.string().required(),
21
+ })
22
+ .required()
23
+ .options({ stripUnknown: true })
24
+ );
25
+
23
26
  if (!code && !refreshToken) {
24
27
  return res.status(400).send("Missing OAuth Code and Refresh Token");
25
28
  }
29
+
30
+ const providerConfig = project.oauth.providers[provider];
31
+ if (!providerConfig) {
32
+ return res.status(400).send("Unsupported OAuth provider");
33
+ }
34
+
35
+ let accessTokenForAPI;
36
+ let newRefreshToken = refreshToken;
37
+
26
38
  if (code && redirectUri) {
27
39
  const params = new URLSearchParams();
28
- params.append("client_id", project.oauth.clientId);
29
- params.append("client_secret", process.env.OAUTH_CLIENT_SECRET);
40
+ params.append("grant_type", "authorization_code");
41
+ params.append("client_id", providerConfig.clientId);
42
+ params.append(
43
+ "client_secret",
44
+ process.env[`${provider.toUpperCase()}_CLIENT_SECRET`]
45
+ );
30
46
  params.append("code", code);
31
47
  params.append("redirect_uri", redirectUri);
32
- params.append("grant_type", "authorization_code");
33
- const { data } = await axios.post(
34
- project.oauth.tokenUrl,
48
+
49
+ const tokenResponse = await axios.post(
50
+ providerConfig.tokenUrl,
35
51
  params.toString(),
36
52
  {
37
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
53
+ headers: {
54
+ "Content-Type": "application/x-www-form-urlencoded",
55
+ Accept: "application/json",
56
+ },
57
+ timeout: 10000,
38
58
  }
39
59
  );
40
60
 
41
- const urlParams = new URLSearchParams(data);
61
+ if (tokenResponse.data.error) {
62
+ throw new AuthorizationError(
63
+ tokenResponse.data.error_description || tokenResponse.data.error
64
+ );
65
+ }
42
66
 
43
- if (urlParams.get("error")) {
44
- throw new AuthenticationError(urlParams.get("error_description"));
67
+ if (!tokenResponse.data.access_token) {
68
+ throw new AuthenticationError(
69
+ "No access token received from OAuth provider"
70
+ );
45
71
  }
46
72
 
47
- refreshToken = urlParams.get("access_token");
73
+ accessTokenForAPI = tokenResponse.data.access_token;
74
+ newRefreshToken =
75
+ tokenResponse.data.refresh_token || tokenResponse.data.access_token;
76
+ } else {
77
+ accessTokenForAPI = refreshToken;
48
78
  }
49
79
 
50
- const { data } = await axios.get(project.oauth.userUrl, {
80
+ let userResponse = await axios.get(providerConfig.userUrl, {
51
81
  headers: {
52
- Authorization: `Bearer ${refreshToken}`,
82
+ Authorization: `Bearer ${accessTokenForAPI}`,
83
+ Accept: "application/json",
53
84
  },
85
+ timeout: 10000,
54
86
  });
55
87
 
56
- const userId = data[project.oauth.jwt.identifier].toString();
88
+ console.log("User info response:", userResponse.data);
89
+
90
+ let userId;
91
+ const identifierField = providerConfig.userIdentifier;
92
+
93
+ if (userResponse.data[identifierField]) {
94
+ userId = userResponse.data[identifierField].toString();
95
+ } else if (userResponse.data[project.oauth.jwt.identifier]) {
96
+ userId = userResponse.data[project.oauth.jwt.identifier].toString();
97
+ } else {
98
+ console.error("User identifier extraction failed:", {
99
+ availableFields: Object.keys(userResponse.data),
100
+ expectedField: identifierField,
101
+ fallbackField: project.oauth.jwt.identifier,
102
+ });
103
+ throw new Error(
104
+ `Cannot find user identifier in ${provider} OAuth response`
105
+ );
106
+ }
107
+
108
+ const prefixedUserId = `${provider}_${userId}`;
57
109
 
58
110
  let accessToken;
59
111
 
60
112
  if (projectId) {
61
113
  const permissions = await Permission.findAll({
62
- where: { userId, projectId, appId },
114
+ where: { userId: prefixedUserId, projectId, appId },
63
115
  });
64
116
 
65
117
  if (!permissions.length) {
66
118
  accessToken = jwt.sign(
67
- { sub: userId, iss: "nuc", aid: appId },
68
- process.env.JWT_SECRET,
69
119
  {
70
- expiresIn: "12h",
71
- }
120
+ sub: prefixedUserId,
121
+ iss: "nuc",
122
+ aid: appId,
123
+ provider: provider,
124
+ iat: Math.floor(Date.now() / 1000),
125
+ },
126
+ process.env.JWT_SECRET,
127
+ { expiresIn: "12h" }
72
128
  );
73
129
  } else {
74
130
  accessToken = jwt.sign(
75
131
  {
76
- sub: userId,
132
+ sub: prefixedUserId,
77
133
  iss: "nuc",
78
134
  aud: projectId,
79
135
  oid: permissions[0].organizationId,
80
136
  aid: appId,
81
137
  rls: permissions.map((permission) => permission.role),
138
+ provider: provider,
139
+ iat: Math.floor(Date.now() / 1000),
82
140
  },
83
141
  process.env.JWT_SECRET,
84
142
  { expiresIn: "12h" }
@@ -86,15 +144,72 @@ router.post("/", async (req, res) => {
86
144
  }
87
145
  } else {
88
146
  accessToken = jwt.sign(
89
- { sub: userId, iss: "nuc", aid: appId },
90
- process.env.JWT_SECRET,
91
147
  {
92
- expiresIn: "12h",
93
- }
148
+ sub: prefixedUserId,
149
+ iss: "nuc",
150
+ aid: appId,
151
+ provider: provider,
152
+ iat: Math.floor(Date.now() / 1000),
153
+ },
154
+ process.env.JWT_SECRET,
155
+ { expiresIn: "12h" }
94
156
  );
95
157
  }
96
158
 
97
- res.status(200).json({ accessToken, refreshToken });
159
+ res.status(200).json({
160
+ accessToken,
161
+ refreshToken: newRefreshToken,
162
+ });
163
+ });
164
+
165
+ router.get("/user", async (req, res) => {
166
+ const authHeader = req.headers.authorization;
167
+ const refreshTokenHeader = req.headers["x-refresh-token"];
168
+
169
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
170
+ return res.status(401).end();
171
+ }
172
+
173
+ if (!refreshTokenHeader) {
174
+ return res.status(400).send("Missing refresh token");
175
+ }
176
+
177
+ const token = authHeader.split(" ")[1];
178
+
179
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
180
+ const userId = decoded.sub;
181
+ const provider = decoded.provider;
182
+
183
+ if (!userId || !provider) {
184
+ return res.status(401).end();
185
+ }
186
+
187
+ const providerConfig = project.oauth.providers[provider];
188
+ if (!providerConfig) {
189
+ return res.status(400).send("Unsupported OAuth provider");
190
+ }
191
+
192
+ const userResponse = await axios.get(providerConfig.userUrl, {
193
+ headers: {
194
+ Authorization: `Bearer ${refreshTokenHeader}`,
195
+ Accept: "application/json",
196
+ },
197
+ timeout: 10000,
198
+ });
199
+
200
+ const userFieldMapping = providerConfig.userFields;
201
+ const userDetails = {
202
+ id: userId,
203
+ provider: provider,
204
+ name: userResponse.data[userFieldMapping.name] || null,
205
+ displayName: userResponse.data[userFieldMapping.displayName] || null,
206
+ avatarUrl: userResponse.data[userFieldMapping.avatarUrl] || null,
207
+ email: userResponse.data[userFieldMapping.email] || null,
208
+ };
209
+
210
+ res.status(200).json({
211
+ user: userDetails,
212
+ });
98
213
  });
99
214
 
100
215
  module.exports = router;
@@ -5,7 +5,7 @@ const app = platform.express();
5
5
 
6
6
  const request = require("supertest");
7
7
  const jwt = require("jsonwebtoken");
8
- const { equal } = require("assert");
8
+ const { equal, deepEqual } = require("assert");
9
9
  const config = require("../../config");
10
10
  const { project } = config();
11
11
 
@@ -19,14 +19,16 @@ describe("OAuth", () => {
19
19
  });
20
20
 
21
21
  it("returns accessToken and refreshToken with code", async () => {
22
- mock
23
- .onPost(project.oauth.tokenUrl)
24
- .reply(
25
- 200,
26
- "access_token=c9Q2KuluvCGdM4YZiUnGWxImvuFnbv&scope=user&token_type=bearer"
27
- );
22
+ const provider = "github";
23
+ const providerConfig = project.oauth.providers[provider];
24
+
25
+ mock.onPost(providerConfig.tokenUrl).reply(200, {
26
+ access_token: "c9Q2KuluvCGdM4YZiUnGWxImvuFnbv",
27
+ token_type: "bearer",
28
+ scope: "user",
29
+ });
28
30
 
29
- mock.onGet(project.oauth.userUrl).reply(200, { email: "1001" });
31
+ mock.onGet(providerConfig.userUrl).reply(200, { id: "1001" });
30
32
 
31
33
  const {
32
34
  body: { accessToken, refreshToken },
@@ -35,8 +37,9 @@ describe("OAuth", () => {
35
37
  .send({
36
38
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
37
39
  projectId: "cb16e069-6214-47f1-9922-1f7fe7629525",
38
- redirectUri: project.oauth.redirectUri,
40
+ redirectUri: providerConfig.redirectUri,
39
41
  code: "vImIDQtMVcYnUCI3Brp6",
42
+ provider: provider,
40
43
  })
41
44
  .expect(200);
42
45
 
@@ -44,17 +47,21 @@ describe("OAuth", () => {
44
47
 
45
48
  equal(payload.iss, "nuc");
46
49
  equal(payload.aud, "cb16e069-6214-47f1-9922-1f7fe7629525");
47
- equal(payload.sub, "1001");
48
- equal(payload.rls, "OWNER");
50
+ equal(payload.sub, `${provider}_1001`);
51
+ deepEqual(payload.rls, ["OWNER"]);
49
52
  equal(payload.aid, "977f5f57-8936-4388-8eb0-00a512cf01cc");
50
53
  equal(payload.oid, "dfb990bb-81dd-4584-82ce-050eb8f6a12f");
54
+ equal(payload.provider, provider);
51
55
  equal(refreshToken, "c9Q2KuluvCGdM4YZiUnGWxImvuFnbv");
52
56
  });
53
57
 
54
58
  it("returns accessToken and refreshToken with refresh token", async () => {
59
+ const provider = "github";
60
+ const providerConfig = project.oauth.providers[provider];
61
+
55
62
  mock
56
- .onGet(project.oauth.userUrl)
57
- .reply(200, { email: "liam@rebellioncoffee.shop" });
63
+ .onGet(providerConfig.userUrl)
64
+ .reply(200, { id: "liam@rebellioncoffee.shop" });
58
65
 
59
66
  const {
60
67
  body: { accessToken, refreshToken },
@@ -63,42 +70,59 @@ describe("OAuth", () => {
63
70
  .send({
64
71
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
65
72
  refreshToken: "lzk7FZGga5hHrfiAePtswijiJHIOev",
73
+ provider: provider,
66
74
  })
67
75
  .expect(200);
68
76
 
69
77
  const payload = jwt.decode(accessToken);
70
78
 
71
- equal(payload.sub, "liam@rebellioncoffee.shop");
79
+ equal(payload.sub, `${provider}_liam@rebellioncoffee.shop`);
72
80
  equal(payload.iss, "nuc");
81
+ equal(payload.provider, provider);
73
82
  equal(refreshToken, "lzk7FZGga5hHrfiAePtswijiJHIOev");
74
83
  });
75
84
 
76
85
  it("returns 400 if code and refreshToken are missing", async () => {
77
- await request(app).post("/oauth").send({}).expect(400);
86
+ await request(app)
87
+ .post("/oauth")
88
+ .send({
89
+ appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
90
+ provider: "github",
91
+ })
92
+ .expect(400);
78
93
  });
79
94
 
80
95
  it("returns 401 if code is invalid", async () => {
81
- mock
82
- .onPost(project.oauth.tokenUrl)
83
- .reply(200, "error=bad_verification_code");
84
- mock.onGet(project.oauth.userUrl).reply(401);
96
+ const provider = "github";
97
+ const providerConfig = project.oauth.providers[provider];
98
+
99
+ mock.onPost(providerConfig.tokenUrl).reply(200, {
100
+ error: "bad_verification_code",
101
+ });
102
+ mock.onGet(providerConfig.userUrl).reply(401);
85
103
 
86
104
  const res = await request(app).post("/oauth").send({
87
105
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
88
106
  code: "ZpodRqsLu2EJxbVrcqnV",
107
+ redirectUri: providerConfig.redirectUri,
108
+ provider: provider,
89
109
  });
90
110
  equal(res.status, 401);
91
111
  });
92
112
 
93
113
  it("returns 503 if OAuth Provider is not accessible", async () => {
94
- mock.onPost(project.oauth.tokenUrl).networkError();
95
- mock.onGet(project.oauth.userUrl).networkError();
114
+ const provider = "github";
115
+ const providerConfig = project.oauth.providers[provider];
116
+
117
+ mock.onPost(providerConfig.tokenUrl).networkError();
118
+ mock.onGet(providerConfig.userUrl).networkError();
96
119
 
97
120
  await request(app)
98
121
  .post("/oauth")
99
122
  .send({
100
123
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
101
124
  refreshToken: "WnhGHF55s6HFRgpRL9AcV2N2VcYemj",
125
+ provider: provider,
102
126
  })
103
127
  .expect(503);
104
128
 
@@ -107,6 +131,8 @@ describe("OAuth", () => {
107
131
  .send({
108
132
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
109
133
  code: "RwlaK2waOdbAa4tt19RF",
134
+ redirectUri: providerConfig.redirectUri,
135
+ provider: provider,
110
136
  })
111
137
  .expect(503);
112
138
  });
@@ -38,6 +38,14 @@ describe("Permissions", () => {
38
38
  userId: "1001",
39
39
  role: "OWNER",
40
40
  },
41
+ {
42
+ id: "f81887da-d05f-4959-9def-6cd137857099",
43
+ appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
44
+ organizationId: "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
45
+ projectId: "cb16e069-6214-47f1-9922-1f7fe7629525",
46
+ userId: "github_1001",
47
+ role: "OWNER",
48
+ },
41
49
  {
42
50
  id: "a1b60c53-66e2-4034-8654-38b83577f279",
43
51
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
@@ -88,6 +96,14 @@ describe("Permissions", () => {
88
96
  userId: "1001",
89
97
  role: "OWNER",
90
98
  },
99
+ {
100
+ id: "f81887da-d05f-4959-9def-6cd137857099",
101
+ appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
102
+ organizationId: "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
103
+ projectId: "cb16e069-6214-47f1-9922-1f7fe7629525",
104
+ userId: "github_1001",
105
+ role: "OWNER",
106
+ },
91
107
  {
92
108
  id: "a1b60c53-66e2-4034-8654-38b83577f279",
93
109
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
@@ -125,6 +141,14 @@ describe("Permissions", () => {
125
141
  .expect(200);
126
142
 
127
143
  deepEqual(permissions, [
144
+ {
145
+ id: "f81887da-d05f-4959-9def-6cd137857099",
146
+ appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
147
+ organizationId: "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
148
+ projectId: "cb16e069-6214-47f1-9922-1f7fe7629525",
149
+ userId: "github_1001",
150
+ role: "OWNER",
151
+ },
128
152
  {
129
153
  id: "a1b60c53-66e2-4034-8654-38b83577f279",
130
154
  appId: "977f5f57-8936-4388-8eb0-00a512cf01cc",
@@ -1,165 +1,173 @@
1
1
  {
2
- "sequence": 9007199254740991,
3
- "seed": [
4
- {
5
- "id": "e81887da-d05f-4959-9def-6cd137857088",
6
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
7
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
8
- "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
9
- "userId": "1001",
10
- "role": "OWNER"
11
- },
12
- {
13
- "id": "b8e498d4-2d79-4e6e-b098-6a97e1d89a94",
14
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
15
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
16
- "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
17
- "userId": "1001",
18
- "role": "OWNER"
19
- },
20
- {
21
- "id": "30044ea7-aa19-4696-876b-26fa76bb91f3",
22
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
23
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
24
- "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
25
- "userId": "1001",
26
- "role": "OWNER"
27
- },
28
- {
29
- "id": "973dfaff-0718-4d2d-a714-587cf7a44b98",
30
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
31
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
32
- "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
33
- "userId": "1001",
34
- "role": "OWNER"
35
- },
36
- {
37
- "id": "4ecc380a-b480-4faa-ad47-878dd2ee4264",
38
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
39
- "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
40
- "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
41
- "userId": "1001",
42
- "role": "OWNER"
43
- },
44
- {
45
- "id": "a1b60c53-66e2-4034-8654-38b83577f279",
46
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
47
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
48
- "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
49
- "userId": "49736917",
50
- "role": "OWNER"
51
- },
52
- {
53
- "id": "e2809e32-b025-4592-855f-dd2b8f49e1b2",
54
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
55
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
56
- "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
57
- "userId": "49736917",
58
- "role": "OWNER"
59
- },
60
- {
61
- "id": "9fc7dd4e-97ef-47a5-987c-99f8015fc736",
62
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
63
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
64
- "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
65
- "userId": "49736917",
66
- "role": "OWNER"
67
- },
68
- {
69
- "id": "b8302e81-4a44-4358-978f-21e9219ead61",
70
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
71
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
72
- "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
73
- "userId": "49736917",
74
- "role": "OWNER"
75
- },
76
- {
77
- "id": "4758a390-4968-44bd-aafe-abdee8044a01",
78
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
79
- "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
80
- "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
81
- "userId": "49736917",
82
- "role": "OWNER"
83
- },
84
- {
85
- "id": "38b1cc0c-46d8-4f95-9be9-0df1a6c91d7b",
86
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
87
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
88
- "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
89
- "userId": "54210920",
90
- "role": "OWNER"
91
- },
92
- {
93
- "id": "03a1eb7b-b25a-4b44-9484-f8b93d8a9e5b",
94
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
95
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
96
- "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
97
- "userId": "54210920",
98
- "role": "OWNER"
99
- },
100
- {
101
- "id": "e54b340d-e1d7-452d-8422-13cae24b0a9f",
102
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
103
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
104
- "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
105
- "userId": "54210920",
106
- "role": "OWNER"
107
- },
108
- {
109
- "id": "fce2b3bb-2776-43d8-bb35-14633882b899",
110
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
111
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
112
- "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
113
- "userId": "54210920",
114
- "role": "OWNER"
115
- },
116
- {
117
- "id": "9067f420-9c22-4578-81b6-aa0d88213f24",
118
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
119
- "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
120
- "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
121
- "userId": "54210920",
122
- "role": "OWNER"
123
- },
124
- {
125
- "id": "7f7bd2c8-3c27-455c-814e-1874e96246ed",
126
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
127
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
128
- "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
129
- "userId": "90180086",
130
- "role": "OWNER"
131
- },
132
- {
133
- "id": "f09e5539-a983-424c-943f-f9c0e5cffa4e",
134
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
135
- "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
136
- "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
137
- "userId": "90180086",
138
- "role": "OWNER"
139
- },
140
- {
141
- "id": "bd4d6afe-4592-4739-84af-011760879b3d",
142
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
143
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
144
- "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
145
- "userId": "90180086",
146
- "role": "OWNER"
147
- },
148
- {
149
- "id": "5c76772d-40af-464d-ae35-4415bbddc3cf",
150
- "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
151
- "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
152
- "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
153
- "userId": "90180086",
154
- "role": "OWNER"
155
- },
156
- {
157
- "id": "20d0920f-00bb-404f-a01e-1ab40eaa8583",
158
- "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
159
- "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
160
- "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
161
- "userId": "90180086",
162
- "role": "OWNER"
163
- }
164
- ]
165
- }
2
+ "sequence": 9007199254740991,
3
+ "seed": [
4
+ {
5
+ "id": "e81887da-d05f-4959-9def-6cd137857088",
6
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
7
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
8
+ "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
9
+ "userId": "1001",
10
+ "role": "OWNER"
11
+ },
12
+ {
13
+ "id": "f81887da-d05f-4959-9def-6cd137857099",
14
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
15
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
16
+ "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
17
+ "userId": "github_1001",
18
+ "role": "OWNER"
19
+ },
20
+ {
21
+ "id": "b8e498d4-2d79-4e6e-b098-6a97e1d89a94",
22
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
23
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
24
+ "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
25
+ "userId": "1001",
26
+ "role": "OWNER"
27
+ },
28
+ {
29
+ "id": "30044ea7-aa19-4696-876b-26fa76bb91f3",
30
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
31
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
32
+ "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
33
+ "userId": "1001",
34
+ "role": "OWNER"
35
+ },
36
+ {
37
+ "id": "973dfaff-0718-4d2d-a714-587cf7a44b98",
38
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
39
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
40
+ "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
41
+ "userId": "1001",
42
+ "role": "OWNER"
43
+ },
44
+ {
45
+ "id": "4ecc380a-b480-4faa-ad47-878dd2ee4264",
46
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
47
+ "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
48
+ "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
49
+ "userId": "1001",
50
+ "role": "OWNER"
51
+ },
52
+ {
53
+ "id": "a1b60c53-66e2-4034-8654-38b83577f279",
54
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
55
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
56
+ "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
57
+ "userId": "49736917",
58
+ "role": "OWNER"
59
+ },
60
+ {
61
+ "id": "e2809e32-b025-4592-855f-dd2b8f49e1b2",
62
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
63
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
64
+ "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
65
+ "userId": "49736917",
66
+ "role": "OWNER"
67
+ },
68
+ {
69
+ "id": "9fc7dd4e-97ef-47a5-987c-99f8015fc736",
70
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
71
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
72
+ "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
73
+ "userId": "49736917",
74
+ "role": "OWNER"
75
+ },
76
+ {
77
+ "id": "b8302e81-4a44-4358-978f-21e9219ead61",
78
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
79
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
80
+ "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
81
+ "userId": "49736917",
82
+ "role": "OWNER"
83
+ },
84
+ {
85
+ "id": "4758a390-4968-44bd-aafe-abdee8044a01",
86
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
87
+ "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
88
+ "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
89
+ "userId": "49736917",
90
+ "role": "OWNER"
91
+ },
92
+ {
93
+ "id": "38b1cc0c-46d8-4f95-9be9-0df1a6c91d7b",
94
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
95
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
96
+ "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
97
+ "userId": "54210920",
98
+ "role": "OWNER"
99
+ },
100
+ {
101
+ "id": "03a1eb7b-b25a-4b44-9484-f8b93d8a9e5b",
102
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
103
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
104
+ "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
105
+ "userId": "54210920",
106
+ "role": "OWNER"
107
+ },
108
+ {
109
+ "id": "e54b340d-e1d7-452d-8422-13cae24b0a9f",
110
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
111
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
112
+ "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
113
+ "userId": "54210920",
114
+ "role": "OWNER"
115
+ },
116
+ {
117
+ "id": "fce2b3bb-2776-43d8-bb35-14633882b899",
118
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
119
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
120
+ "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
121
+ "userId": "54210920",
122
+ "role": "OWNER"
123
+ },
124
+ {
125
+ "id": "9067f420-9c22-4578-81b6-aa0d88213f24",
126
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
127
+ "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
128
+ "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
129
+ "userId": "54210920",
130
+ "role": "OWNER"
131
+ },
132
+ {
133
+ "id": "7f7bd2c8-3c27-455c-814e-1874e96246ed",
134
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
135
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
136
+ "projectId": "cb16e069-6214-47f1-9922-1f7fe7629525",
137
+ "userId": "90180086",
138
+ "role": "OWNER"
139
+ },
140
+ {
141
+ "id": "f09e5539-a983-424c-943f-f9c0e5cffa4e",
142
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
143
+ "organizationId": "dfb990bb-81dd-4584-82ce-050eb8f6a12f",
144
+ "projectId": "add6dfa4-45ba-4da2-bc5c-5a529610b52f",
145
+ "userId": "90180086",
146
+ "role": "OWNER"
147
+ },
148
+ {
149
+ "id": "bd4d6afe-4592-4739-84af-011760879b3d",
150
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
151
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
152
+ "projectId": "0c756054-2d28-4f87-9b12-8023a79136a5",
153
+ "userId": "90180086",
154
+ "role": "OWNER"
155
+ },
156
+ {
157
+ "id": "5c76772d-40af-464d-ae35-4415bbddc3cf",
158
+ "appId": "10b7bc8c-a49c-4002-b0ec-63599e4b5210",
159
+ "organizationId": "1c063446-7e78-432a-a273-34f481d0f0c3",
160
+ "projectId": "e6d4744d-a11b-4c75-acad-e24a02903729",
161
+ "userId": "90180086",
162
+ "role": "OWNER"
163
+ },
164
+ {
165
+ "id": "20d0920f-00bb-404f-a01e-1ab40eaa8583",
166
+ "appId": "977f5f57-8936-4388-8eb0-00a512cf01cc",
167
+ "organizationId": "5459ab03-204a-4627-bdde-667b7802cb35",
168
+ "projectId": "21d2530b-4657-4ac0-b8cd-1a9f82786e32",
169
+ "userId": "90180086",
170
+ "role": "OWNER"
171
+ }
172
+ ]
173
+ }