Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 5618842

Browse files
committed
1 parent 102033e commit 5618842

12 files changed

+147
-101
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,12 @@ Direct ID migration can be done with script located in **topcoder-x-processor**
9393
```shell
9494
npm run direct-connect-migration
9595
```
96+
By default it takes 15 projects at time, but you can change this by specifying BATCH_SIZE environment variable.
97+
98+
## Repository url collisions scan
99+
100+
To scan and log which projects has colliding (the same) repository urls run
101+
```shell
102+
npm run log-repository-collisions
103+
```
96104
By default it takes 15 projects at time, but you can change this by specifying BATCH_SIZE environment variable.

TeamsVsGroups.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
## Github's Teams and Gitlab's Groups differences
3+
4+
Github's Teams are groups of organization members whereas Gitlab's Groups are just groups of Gitlab users (Groups are more like Github' organizations).
5+
Each Github organization can have repositories and assign teams to them and each team can have subteams.
6+
Gitlab groups can have repositories and create subgroups (nested).
7+
8+
## Setup guide
9+
10+
### Github
11+
12+
1. Go to 'https://github.com/settings/organizations' and click 'New organization' button
13+
2. Select your plan.
14+
3. Enter organization's name, contact email address, solve the captcha and click Next
15+
4. Click 'Skip this step'
16+
5. If you receive a survey, you can just go to the bottom and click Submit without filling anything.
17+
6. On your new organization page go to 'Teams' tab and click 'New team' button.
18+
7. Fill in your team's name, description (optional) and visibility. Submit by clicking 'Create team'.
19+
Now you have your team created and you should get redirect to its page.
20+
You can assign it to an organization's repository by clicking 'Add Repository' and entering repository's name, in 'Repositories' tab of a team's page.
21+
22+
### Gitlab
23+
24+
1. Go to 'https://gitlab.com/dashboard/groups' and click on 'New group' button.
25+
2. Enter group's name and set visibility level. Finish by clicking 'Create group'.
26+
You can now create repositories for this group or subgroups.
27+
28+
## Roles
29+
30+
In Topcoder X you can select role which user who joins via specific invitation link receives.
31+
32+
### Github
33+
34+
For github team you can set two roles: Member and Maintainer.
35+
You can read about them here: https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/permission-levels-for-an-organization and https://docs.github.com/en/github/setting-up-and-managing-organizations-and-teams/giving-team-maintainer-permissions-to-an-organization-member
36+
37+
### Gitlab
38+
39+
For gitlab group you can set five roles: Guest, Reporter, Developer, Maintainer, Owner
40+
You can read about them here: https://docs.gitlab.com/ee/user/permissions.html

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"test:fe": "gulp protractor",
2222
"lint": "gulp lint",
2323
"heroku-postbuild": "gulp build",
24-
"create-tables": "CREATE_DB=true node scripts/create-update-tables.js"
24+
"create-tables": "CREATE_DB=true node scripts/create-update-tables.js",
25+
"log-repository-collisions": "node scripts/log-repository-collisions.js"
2526
},
2627
"dependencies": {
2728
"angular": "~1.8.0",

scripts/log-repository-collisions.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const logger = require('../src/common/logger')
2+
const models = require('../src/models')
3+
const _ = require('lodash')
4+
5+
async function main() {
6+
const BATCH_SIZE = process.env.BATCH_SIZE || 15;
7+
let previousSize = BATCH_SIZE;
8+
let previousKey = null;
9+
// Array containing project ids already found colliding
10+
const collidingUrls = []
11+
let batch = 1;
12+
// Run this loop as long as there can be more objects in a database
13+
while (previousSize === BATCH_SIZE) {
14+
logger.debug(`Running batch no. ${batch}`)
15+
// Go over all active projects, limiting to BATCH_SIZE
16+
const projects = await models.Project.scan({
17+
archived: 'false'
18+
}).consistent().limit(BATCH_SIZE).startAt(previousKey).exec()
19+
for (const project of projects) {
20+
// If url was already found colliding go to a next iteration
21+
if (collidingUrls.includes(project.repoUrl)) continue;
22+
const collisions = await models.Project.scan({
23+
repoUrl: project.repoUrl,
24+
archived: 'false'
25+
}).exec()
26+
// If scan found only this project go to a next interation
27+
if (collisions.length < 2) continue;
28+
logger.info(`Repository ${project.repoUrl} has ${collisions.length} collisions`);
29+
_.forEach(collisions, collision => {
30+
logger.info(`--- ID: ${collision.id}`)
31+
})
32+
collidingUrls.push(project.repoUrl)
33+
}
34+
previousKey = projects.lastKey
35+
previousSize = projects.scannedCount
36+
batch++
37+
}
38+
}
39+
main().then(() => {
40+
logger.info('Collision scan completed')
41+
}).catch(err => {
42+
logger.logFullError(err, 'collision scan')
43+
})

src/config.js

+13-95
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ module.exports = {
2222

2323
// used as base to construct various URLs
2424
WEBSITE: process.env.WEBSITE || 'http://topcoderx.topcoder-dev.com',
25-
WEBSITE_SECURE: process.env.WEBSITE_SECURE || 'https://topcoderx.topcoder-dev.com',
2625
GITLAB_API_BASE_URL: process.env.GITLAB_API_BASE_URL || 'https://gitlab.com',
2726

2827
// kafka configuration
@@ -50,7 +49,6 @@ module.exports = {
5049
OPEN_FOR_PICKUP_ISSUE_LABEL: process.env.OPEN_FOR_PICKUP_ISSUE_LABEL || 'tcx_OpenForPickup',
5150
ALLOWED_TOPCODER_ROLES: process.env.ALLOWED_TOPCODER_ROLES || ['administrator', 'admin', 'connect manager', 'connect admin', 'copilot', 'connect copilot'],
5251
COPILOT_ROLE: process.env.COPILOT_ROLE || 'copilot',
53-
HELP_LINK: process.env.HELP_LINK || 'https://github.com/topcoder-platform/topcoder-x-ui/wiki',
5452
ADMINISTRATOR_ROLES: process.env.ADMINISTRATOR_ROLES || ['administrator', 'admin'],
5553
DYNAMODB: {
5654
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
@@ -69,100 +67,20 @@ module.exports = {
6967
},
7068
};
7169

72-
const frontendConfigs = {
73-
local: {
74-
JWT_V3_NAME: 'v3jwt',
75-
JWT_V2_NAME: 'tcjwt',
76-
COOKIES_SECURE: false,
77-
TC_LOGIN_URL: 'https://accounts-auth0.topcoder-dev.com/',
78-
API_URL: 'https://127.0.0.1:8443',
79-
ADMIN_TOOL_URL: 'http://localhost:8080/api/v2',
80-
ACCOUNTS_CONNECTOR_URL: 'https://accounts.topcoder-dev.com/connector.html',
81-
CONNECT_URL_BASE: 'https://connect.topcoder-dev/projects/',
82-
OWNER_LOGIN_GITHUB_URL: '/api/v1/github/owneruser/login',
83-
OWNER_LOGIN_GITLAB_URL: '/api/v1/gitlab/owneruser/login',
84-
TOPCODER_URL: 'https://topcoder-dev.com',
85-
GITHUB_TEAM_URL: 'https://github.com/orgs/',
86-
GITLAB_GROUP_URL: 'https://gitlab.com/groups/',
87-
},
88-
heroku: {
89-
JWT_V3_NAME: 'v3jwt',
90-
JWT_V2_NAME: 'tcjwt',
91-
COOKIES_SECURE: false,
92-
TC_LOGIN_URL: 'https://accounts-auth0.topcoder-dev.com/',
93-
API_URL: 'https://api.topcoder-dev.com',
94-
ADMIN_TOOL_URL: 'https://api.topcoder-dev.com/v2',
95-
ACCOUNTS_CONNECTOR_URL: 'https://accounts.topcoder-dev.com/connector.html',
96-
CONNECT_URL_BASE: 'https://connect.topcoder-dev.com/projects/',
97-
OWNER_LOGIN_GITHUB_URL: '/api/v1/github/owneruser/login',
98-
OWNER_LOGIN_GITLAB_URL: '/api/v1/gitlab/owneruser/login',
99-
TOPCODER_URL: 'https://topcoder-dev.com',
100-
GITHUB_TEAM_URL: 'https://github.com/orgs/',
101-
GITLAB_GROUP_URL: 'https://gitlab.com/groups/',
102-
},
103-
dev: {
104-
JWT_V3_NAME: 'v3jwt',
105-
JWT_V2_NAME: 'tcjwt',
106-
COOKIES_SECURE: false,
107-
TC_LOGIN_URL: 'https://accounts-auth0.topcoder-dev.com/',
108-
API_URL: 'https://api.topcoder-dev.com',
109-
ADMIN_TOOL_URL: 'https://api.topcoder-dev.com/v2',
110-
ACCOUNTS_CONNECTOR_URL: 'https://accounts.topcoder-dev.com/connector.html',
111-
CONNECT_URL_BASE: 'https://connect.topcoder-dev.com/projects/',
112-
OWNER_LOGIN_GITHUB_URL: '/api/v1/github/owneruser/login',
113-
OWNER_LOGIN_GITLAB_URL: '/api/v1/gitlab/owneruser/login',
114-
TOPCODER_URL: 'https://topcoder-dev.com',
115-
GITHUB_TEAM_URL: 'https://github.com/orgs/',
116-
GITLAB_GROUP_URL: 'https://gitlab.com/groups/',
117-
},
118-
qa: {
119-
JWT_V3_NAME: 'v3jwt',
120-
JWT_V2_NAME: 'tcjwt',
121-
COOKIES_SECURE: false,
122-
TC_LOGIN_URL: 'https://accounts-auth0.topcoder-dev.com/',
123-
API_URL: 'https://api.topcoder-qa.com',
124-
ADMIN_TOOL_URL: 'https://api.topcoder-qa.com/v2',
125-
ACCOUNTS_CONNECTOR_URL: 'https://accounts.topcoder-qa.com/connector.html',
126-
CONNECT_URL_BASE: 'https://connect.topcoder-dev.com/projects/',
127-
OWNER_LOGIN_GITHUB_URL: '/api/v1/github/owneruser/login',
128-
OWNER_LOGIN_GITLAB_URL: '/api/v1/gitlab/owneruser/login',
129-
TOPCODER_URL: 'https://topcoder-dev.com',
130-
GITHUB_TEAM_URL: 'https://github.com/orgs/',
131-
GITLAB_GROUP_URL: 'https://gitlab.com/groups/',
132-
},
133-
prod: {
134-
JWT_V3_NAME: 'v3jwt',
135-
JWT_V2_NAME: 'tcjwt',
136-
COOKIES_SECURE: false,
137-
TC_LOGIN_URL: 'https://accounts.topcoder.com/member',
138-
API_URL: 'https://api.topcoder.com',
139-
ADMIN_TOOL_URL: 'https://api.topcoder.com/v2',
140-
ACCOUNTS_CONNECTOR_URL: 'https://accounts.topcoder.com/connector.html',
141-
CONNECT_URL_BASE: 'https://connect.topcoder.com/projects/',
142-
OWNER_LOGIN_GITHUB_URL: '/api/v1/github/owneruser/login',
143-
OWNER_LOGIN_GITLAB_URL: '/api/v1/gitlab/owneruser/login',
144-
TOPCODER_URL: 'https://topcoder-dev.com',
145-
GITHUB_TEAM_URL: 'https://github.com/orgs/',
146-
GITLAB_GROUP_URL: 'https://gitlab.com/groups/',
147-
},
148-
};
149-
150-
const activeEnv = module.exports.TOPCODER_ENV;
15170
module.exports.frontendConfigs = {
152-
helpLink: module.exports.HELP_LINK,
15371
copilotRole: module.exports.COPILOT_ROLE,
15472
administratorRoles: module.exports.ADMINISTRATOR_ROLES,
155-
JWT_V3_NAME: process.env.JWT_V3_NAME || frontendConfigs[activeEnv].JWT_V3_NAME,
156-
JWT_V2_NAME: process.env.JWT_V2_NAME || frontendConfigs[activeEnv].JWT_V2_NAME,
157-
COOKIES_SECURE: process.env.COOKIES_SECURE || frontendConfigs[activeEnv].COOKIES_SECURE,
158-
TC_LOGIN_URL: process.env.TC_LOGIN_URL || frontendConfigs[activeEnv].TC_LOGIN_URL,
159-
API_URL: process.env.API_URL || frontendConfigs[activeEnv].API_URL,
160-
ADMIN_TOOL_URL: process.env.ADMIN_TOOL_URL || frontendConfigs[activeEnv].ADMIN_TOOL_URL,
161-
ACCOUNTS_CONNECTOR_URL: process.env.ACCOUNTS_CONNECTOR_URL || frontendConfigs[activeEnv].ACCOUNTS_CONNECTOR_URL,
162-
CONNECT_URL_BASE: process.env.CONNECT_URL_BASE || frontendConfigs[activeEnv].CONNECT_URL_BASE,
163-
OWNER_LOGIN_GITHUB_URL: process.env.OWNER_LOGIN_GITHUB_URL || frontendConfigs[activeEnv].OWNER_LOGIN_GITHUB_URL,
164-
OWNER_LOGIN_GITLAB_URL: process.env.OWNER_LOGIN_GITLAB_URL || frontendConfigs[activeEnv].OWNER_LOGIN_GITLAB_URL,
165-
TOPCODER_URL: process.env.TOPCODER_URL || frontendConfigs[activeEnv].TOPCODER_URL,
166-
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || frontendConfigs[activeEnv].GITHUB_TEAM_URL,
167-
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || frontendConfigs[activeEnv].GITLAB_GROUP_URL,
73+
helpLink: process.env.HELP_LINK || 'https://github.com/topcoder-platform/topcoder-x-ui/wiki',
74+
JWT_V3_NAME: process.env.JWT_V3_NAME || 'v3jwt',
75+
JWT_V2_NAME: process.env.JWT_V2_NAME || 'tcjwt',
76+
COOKIES_SECURE: process.env.COOKIES_SECURE || false,
77+
TC_LOGIN_URL: process.env.TC_LOGIN_URL || 'https://accounts-auth0.topcoder-dev.com/',
78+
ADMIN_TOOL_URL: process.env.ADMIN_TOOL_URL || 'https://api.topcoder-dev.com/v2',
79+
ACCOUNTS_CONNECTOR_URL: process.env.ACCOUNTS_CONNECTOR_URL || 'https://accounts.topcoder-dev.com/connector.html',
80+
CONNECT_URL_BASE: process.env.CONNECT_URL_BASE || 'https://connect.topcoder-dev.com/projects/',
81+
OWNER_LOGIN_GITHUB_URL: process.env.OWNER_LOGIN_GITHUB_URL || '/api/v1/github/owneruser/login',
82+
OWNER_LOGIN_GITLAB_URL: process.env.OWNER_LOGIN_GITLAB_URL || '/api/v1/gitlab/owneruser/login',
83+
TOPCODER_URL: process.env.TOPCODER_URL || 'https://topcoder-dev.com',
84+
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || 'https://github.com/orgs/',
85+
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || 'https://gitlab.com/groups/',
16886
};

src/controllers/GithubController.js

+4
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ async function addUserToTeamCallback(req, res) {
151151
.query({client_id: config.GITHUB_CLIENT_ID, client_secret: config.GITHUB_CLIENT_SECRET, code})
152152
.set('Accept', 'application/json')
153153
.end();
154+
// Throw error if github access token was not returned (e.g. invalid code)
155+
if (!result.body.access_token) {
156+
throw new errors.UnauthorizedError('Github authorization failed.', result.body.error_description);
157+
}
154158
const token = result.body.access_token;
155159
// add user to team
156160
console.log(`adding ${token} to ${team.teamId} with ${team.ownerToken}`); /* eslint-disable-line no-console */

src/controllers/GitlabController.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ async function addUserToGroupCallback(req, res) {
188188
redirect_uri: `${config.WEBSITE}/api/${config.API_VERSION}/gitlab/normaluser/callback`,
189189
})
190190
.end();
191+
// Throw error if github access token was not returned (ex. invalid code)
192+
if (!result.body.access_token) {
193+
throw new errors.UnauthorizedError('Gitlab authorization failed.', result.body.error_description);
194+
}
191195
const token = result.body.access_token;
192196

193197
// get group name
@@ -235,7 +239,8 @@ async function addUserToGroupCallback(req, res) {
235239
});
236240
}
237241
// redirect to success page
238-
res.redirect(`${constants.USER_ADDED_TO_TEAM_SUCCESS_URL}/gitlab/${currentGroup.full_path}`);
242+
// For gitlab subgroups we need to replace / with something different. Default encoding doesn't work as angular route fails to match %2F
243+
res.redirect(`${constants.USER_ADDED_TO_TEAM_SUCCESS_URL}/gitlab/${currentGroup.full_path.replace('/', '@!2F')}`);
239244
}
240245

241246

src/front/src/app/less/custom.less

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
color: green;
2121
font-size: 16px;
2222
}
23-
23+
.red-times-icon {
24+
color: red;
25+
font-size: 16px;
26+
}
2427
.orange-warning-icon {
2528
color: orange;
2629
font-size: 16px;

src/front/src/app/members/member.controller.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ angular.module('topcoderX')
1111
const org = params[0];
1212
const team = url.replace(org, '').substring(1);
1313
$scope.link = $rootScope.appConfig.GITHUB_TEAM_URL + org + '/teams/' + team;
14-
} else if (provider === 'github') {
15-
$scope.link = $rootScope.appConfig.GITLAB_GROUP_URL + url;
14+
} else if (provider === 'gitlab') {
15+
// For gitlab subgroups we can't just pass encoded link to this route because anguler doesn't match %2F, so we use @!2F as a workaround
16+
$scope.link = $rootScope.appConfig.GITLAB_GROUP_URL + url.replace('@!2F', '/');
1617
}
1718
};
1819
_getUrl($scope.provider, $stateParams.url);

src/front/src/app/projects/projects.html

+9
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ <h4>You don't have active projects right now. Please
6161
<th class="col-lg-2">Service Provider</th>
6262
<th class="col-lg-2" ng-show="isAdminUser">Owner</th>
6363
<th class="col-lg-2">Copilot</th>
64+
<th class="col-lg-2">Copilot payments</th>
6465
<th class="col-lg-3" data-sort-ignore="true"></th>
6566
</tr>
6667
</thead>
@@ -78,6 +79,14 @@ <h4>You don't have active projects right now. Please
7879
</td>
7980
<td class="col-lg-2" ng-show="isAdminUser">{{project.owner}}</td>
8081
<td class="col-lg-2">{{project.copilot}}</td>
82+
<td class="col-lg-2" data-value="{{project.createCopilotPayments}}">
83+
<div ng-if="project.createCopilotPayments === 'true'">
84+
<i class="fa fa-check green-check-icon"/>
85+
</div>
86+
<div ng-if="project.createCopilotPayments === 'false'">
87+
<i class="fa fa-times red-times-icon"/>
88+
</div>
89+
</td>
8190
<td class="col-lg-2">
8291
<button class="btn btn-sm btn-success" ng-click="goProject(project)">
8392
<strong>Manage</strong>

src/services/GithubService.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ getTeamRegistrationUrl.schema = Joi.object().keys({
178178
token: Joi.string().required(),
179179
ownerUsername: Joi.string().required(),
180180
teamId: Joi.string().required(),
181-
accessLevel: Joi.string().required(),
181+
accessLevel: Joi.string().valid('member', 'maintainer').required(),
182182
});
183183

184184
/**

src/services/ProjectService.js

+14
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ const createProjectSchema = {
7272
* @private
7373
*/
7474
async function _validateProjectData(project) {
75+
const filter = {
76+
repoUrl: project.repoUrl,
77+
archived: 'false'
78+
}
79+
if (project.id) {
80+
filter.id = {
81+
ne: project.id
82+
}
83+
}
84+
const existsInDatabase = await dbHelper.scanOne(models.Project, filter)
85+
if (existsInDatabase) {
86+
throw new errors.ValidationError(`This repo already has a Topcoder-X project associated with it.
87+
Copilot: ${existsInDatabase.copilot}, Owner: ${existsInDatabase.owner}`)
88+
}
7589
const provider = await helper.getProviderType(project.repoUrl);
7690
const userRole = project.copilot ? project.copilot : project.owner;
7791
const setting = await userService.getUserSetting(userRole);

0 commit comments

Comments
 (0)