1
0
Fork 0
mirror of synced 2024-09-29 08:51:28 +13:00

vcs.createRepository()

This commit is contained in:
Matej Bačo 2023-06-18 13:38:37 +02:00
parent 860b4fb1c0
commit a174562c4e
6 changed files with 196 additions and 52 deletions

View file

@ -1534,7 +1534,40 @@ $collections = [
'default' => null,
'array' => false,
'filters' => [],
]
],
[
'$id' => ID::custom('vcsGithubAccessToken'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('vcsGithubAccessTokenExpiry'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('vcsGithubRefreshToken'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
],
'indexes' => [
[
@ -2282,16 +2315,16 @@ $collections = [
'filters' => [],
],
[
'$id' => ID::custom('accessToken'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'$id' => ID::custom('personal'),
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => false,
'default' => null,
'default' => false,
'array' => false,
'filters' => ['encrypt']
]
],
],
'indexes' => [],
],

View file

@ -1,5 +1,6 @@
<?php
use Appwrite\Auth\OAuth2\Github as OAuth2Github;
use Swoole\Coroutine as Co;
use Utopia\App;
use Appwrite\Event\Build;
@ -14,6 +15,7 @@ use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Host;
use Appwrite\Utopia\Database\Validator\Queries\Installations;
use Appwrite\Vcs\Comment;
use Utopia\Database\DateTime;
use Utopia\Database\Query;
use Utopia\Database\ID;
use Utopia\Database\Permission;
@ -30,6 +32,7 @@ use Utopia\Detector\Adapter\Python;
use Utopia\Detector\Adapter\Ruby;
use Utopia\Detector\Adapter\Swift;
use Utopia\Detector\Detector;
use Utopia\Validator\Boolean;
use function Swoole\Coroutine\batch;
@ -65,23 +68,26 @@ App::get('/v1/vcs/github/installations')
]));
});
App::get('/v1/vcs/github/incominginstallation')
->desc('Capture installation id and state after GitHub App Installation')
App::get('/v1/vcs/github/redirect')
->desc('Capture installation and authorization from GitHub App')
->groups(['api', 'vcs'])
->label('scope', 'public')
->param('installation_id', '', new Text(256), 'GitHub installation ID')
->param('setup_action', '', new Text(256), 'GitHub setup actuon type')
->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.')
->param('code', '', new Text(2048), 'OAuth2 code.', true)
->inject('gitHub')
->inject('user')
->inject('project')
->inject('request')
->inject('response')
->inject('dbForConsole')
->action(function (string $installationId, string $setupAction, string $state, GitHub $github, Document $project, Request $request, Response $response, Database $dbForConsole) {
->action(function (string $installationId, string $setupAction, string $state, string $code, GitHub $github, Document $user, Document $project, Request $request, Response $response, Database $dbForConsole) {
$state = \json_decode($state, true);
$redirect = $state['redirect'] ?? '';
$projectId = $state['projectId'] ?? '';
$projectId = $project->getId();
$project = $dbForConsole->getDocument('projects', $projectId);
if (empty($redirect)) {
$redirect = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/settings/git-installations";
@ -94,38 +100,64 @@ App::get('/v1/vcs/github/incominginstallation')
->redirect($redirect);
}
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$personalSlug = '';
$projectInternalId = $project->getInternalId();
// OAuth Authroization
if (!empty($code)) {
$oauth2 = new OAuth2Github(App::getEnv('VCS_GITHUB_CLIENT_ID', ''), App::getEnv('VCS_GITHUB_CLIENT_SECRET', ''), "");
$accessToken = $oauth2->getAccessToken($code);
$refreshToken = $oauth2->getRefreshToken($code);
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
$personalSlug = $oauth2->getUserSlug($accessToken);
$vcsInstallation = $dbForConsole->findOne('vcsInstallations', [
Query::equal('installationId', [$installationId]),
Query::equal('projectInternalId', [$projectInternalId])
]);
$user = $user
->setAttribute('vcsGithubAccessToken', $accessToken)
->setAttribute('vcsGithubRefreshToken', $refreshToken)
->setAttribute('vcsGithubAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry));
if ($vcsInstallation === false || $vcsInstallation->isEmpty()) {
$vcsInstallation = new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'installationId' => $installationId,
'projectId' => $projectId,
'projectInternalId' => $projectInternalId,
'provider' => 'github',
'organization' => $owner,
'accessToken' => null
$dbForConsole->updateDocument('users', $user->getId(), $user);
}
// Create / Update installation
if (!empty($installationId)) {
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$projectInternalId = $project->getInternalId();
$vcsInstallation = $dbForConsole->findOne('vcsInstallations', [
Query::equal('installationId', [$installationId]),
Query::equal('projectInternalId', [$projectInternalId])
]);
$vcsInstallation = $dbForConsole->createDocument('vcsInstallations', $vcsInstallation);
} else {
$vcsInstallation = $vcsInstallation->setAttribute('organization', $owner);
$vcsInstallation = $dbForConsole->updateDocument('vcsInstallations', $vcsInstallation->getId(), $vcsInstallation);
if ($vcsInstallation === false || $vcsInstallation->isEmpty()) {
$teamId = $project->getAttribute('teamId', '');
$vcsInstallation = new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
],
'installationId' => $installationId,
'projectId' => $projectId,
'projectInternalId' => $projectInternalId,
'provider' => 'github',
'organization' => $owner,
'personal' => $personalSlug === $owner
]);
$vcsInstallation = $dbForConsole->createDocument('vcsInstallations', $vcsInstallation);
} else {
$vcsInstallation = $vcsInstallation->setAttribute('organization', $owner);
$vcsInstallation = $vcsInstallation->setAttribute('personal', $personalSlug === $owner);
$vcsInstallation = $dbForConsole->updateDocument('vcsInstallations', $vcsInstallation->getId(), $vcsInstallation);
}
}
$response
@ -151,8 +183,6 @@ App::get('/v1/vcs/github/installations/:installationId/repositories')
->inject('project')
->inject('dbForConsole')
->action(function (string $vcsInstallationId, string $search, GitHub $github, Response $response, Document $project, Database $dbForConsole) {
$start = \microtime(true);
if (empty($search)) {
$search = "";
}
@ -225,14 +255,84 @@ App::get('/v1/vcs/github/installations/:installationId/repositories')
return new Document($repo);
}, $repos);
\var_dump(\microtime(true) - $start);
$response->dynamic(new Document([
'repositories' => $repos,
'total' => \count($repos),
]), Response::MODEL_REPOSITORY_LIST);
});
App::post('/v1/vcs/github/installations/:installationId/repositories')
->desc('Create repository')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('sdk.namespace', 'vcs')
->label('sdk.method', 'createRepository')
->label('sdk.description', '')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_REPOSITORY)
->param('installationId', '', new Text(256), 'Installation Id')
->param('name', '', new Text(256), 'Repository name (slug)')
->param('private', '', new Boolean(false), 'Mark repository public or private')
->inject('gitHub')
->inject('user')
->inject('response')
->inject('project')
->inject('dbForConsole')
->action(function (string $vcsInstallationId, string $name, bool $private, GitHub $github, Document $user, Response $response, Document $project, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
if ($installation->getAttribute('personal', false) === true) {
$oauth2 = new OAuth2Github(App::getEnv('VCS_GITHUB_CLIENT_ID', ''), App::getEnv('VCS_GITHUB_CLIENT_SECRET', ''), "");
$accessToken = $user->getAttribute('vcsGithubAccessToken');
$refreshToken = $user->getAttribute('vcsGithubRefreshToken');
$accessTokenExpiry = $user->getAttribute('vcsGithubAccessTokenExpiry');
$isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now');
if ($isExpired) {
$oauth2->refreshTokens($refreshToken);
$accessToken = $oauth2->getAccessToken('');
$refreshToken = $oauth2->getRefreshToken('');
$verificationId = $oauth2->getUserID($accessToken);
if (empty($verificationId)) {
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, "Another request is currently refreshing OAuth token. Please try again.");
}
$user = $user
->setAttribute('vcsGithubAccessToken', $accessToken)
->setAttribute('vcsGithubRefreshToken', $refreshToken)
->setAttribute('vcsGithubAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
$dbForConsole->updateDocument('users', $user->getId(), $user);
}
$repository = $oauth2->createRepository($accessToken, $name, $private);
} else {
$installationId = $installation->getAttribute('installationId');
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$repository = $github->createRepository($owner, $name, $private);
}
$repository['id'] = \strval($repository['id']);
$repository['pushedAt'] = $repository['pushed_at'];
$response->dynamic(new Document($repository), Response::MODEL_REPOSITORY);
});
App::get('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
->desc('Get repository')
->groups(['api', 'vcs'])
@ -371,7 +471,7 @@ $createGitDeployments = function (GitHub $github, string $installationId, string
$repositoryName = $github->getRepositoryName($repositoryId);
$comment = new Comment();
// TODO: Add all builds reeeeee
// TODO: Add all builds
$comment->addBuild($project, $function, 'waiting', $deploymentId);
if (empty($latestCommentId)) {

View file

@ -936,7 +936,11 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
if ($project->isEmpty()) {
$user = new Document(['$id' => ID::custom(''), '$collection' => 'users']);
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
if($project->getId() === 'console') {
$user = $dbForConsole->getDocument('users', Auth::$unique);
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
}
}
} else {
$user = $dbForConsole->getDocument('users', Auth::$unique);
@ -994,11 +998,6 @@ App::setResource('project', function ($dbForConsole, $request, $console) {
$projectId = $request->getParam('project', '');
} elseif (!empty($request->getHeader('x-appwrite-project', ''))) {
$projectId = $request->getHeader('x-appwrite-project', '');
} elseif (!empty($request->getParam('state', ''))) {
$state = \json_decode($request->getParam('state', ''), true);
if (!empty($state['projectId'])) {
$projectId = $state['projectId'];
}
}
if ($projectId === 'console') {

4
composer.lock generated
View file

@ -2705,7 +2705,7 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "2966e482920a9dd028a237083a81de723a9bcbf2"
"reference": "7fb5b733ab0676b257d8bc8fc4e0c4cafd4c756c"
},
"require": {
"adhocore/jwt": "^1.1",
@ -2750,7 +2750,7 @@
"utopia",
"vcs"
],
"time": "2023-06-16T11:02:30+00:00"
"time": "2023-06-17T14:27:48+00:00"
},
{
"name": "utopia-php/websocket",

View file

@ -82,6 +82,7 @@ services:
- traefik.http.routers.appwrite_api_https.tls.domains[0].main=$_APP_DOMAIN_FUNCTIONS
- traefik.http.routers.appwrite_api_https.tls.domains[0].sans=*.$_APP_DOMAIN_FUNCTIONS
volumes:
- ./vendor/utopia-php/vcs:/usr/src/code/vendor/utopia-php/vcs
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
- appwrite-config:/storage/config:rw

View file

@ -208,4 +208,15 @@ class Github extends OAuth2
return $this->user;
}
public function createRepository(string $accessToken, string $repositoryName, bool $private): array
{
$repository = $this->request('POST', 'https://api.github.com/user/repos', ['Authorization: token ' . \urlencode($accessToken)], \json_encode([
'name' => $repositoryName,
'private' => $private
]));
$repository = \json_decode($repository, true);
return $repository;
}
}