Files
GitJournal/lib/apis/github.dart
Vishesh Handa 763cbf8493 Use flutter_web_auth instead of our own OAuth mechanism
This works slightly better on iOS and on Android it has a keep alive,
which will prevent our app from being killed. Additionally, this way
there is less for me to maintain, which is always nicer.

The API for flutter_web_auth is also much simpler.

This also inolves some custom logic for parsing the Query Parameters
from the GitLab callback, as it doesn't seem to be a proper URI. Not
sure what is going on with Gitlab.
2020-06-09 17:09:38 +02:00

246 lines
6.5 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:flutter_web_auth/flutter_web_auth.dart';
import 'package:gitjournal/utils/logger.dart';
import 'githost.dart';
class GitHub implements GitHost {
static const _clientID = "aa3072cbfb02b1db14ed";
static const _clientSecret = "010d303ea99f82330f2b228977cef9ddbf7af2cd";
var _accessCode = "";
@override
Future<void> init() async {
var url = "https://github.com/login/oauth/authorize?client_id=" +
_clientID +
"&scope=repo";
var resultUrl = await FlutterWebAuth.authenticate(
url: url, callbackUrlScheme: "gitjournal");
var uri = Uri.parse(resultUrl);
var authCode = uri.queryParameters['code'];
if (authCode == null) {
Log.d("GitHub: Missing auth code. Now what?");
throw GitHostException.OAuthFailed;
}
_accessCode = await _getAccessCode(authCode);
if (_accessCode == null || _accessCode.isEmpty) {
Log.d("GitHub: AccessCode is invalid: " + _accessCode);
throw GitHostException.OAuthFailed;
}
}
Future<String> _getAccessCode(String authCode) async {
var url =
"https://github.com/login/oauth/access_token?client_id=$_clientID&client_secret=$_clientSecret&code=$authCode";
var response = await http.post(url);
if (response.statusCode != 200) {
Log.d("Github getAccessCode: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
throw GitHostException.OAuthFailed;
}
Log.d("GithubResponse: " + response.body);
var map = Uri.splitQueryString(response.body);
return map["access_token"];
}
@override
Future<List<GitHostRepo>> listRepos() async {
if (_accessCode.isEmpty) {
throw GitHostException.MissingAccessCode;
}
var url = "https://api.github.com/user/repos?page=1&per_page=100";
var headers = {
HttpHeaders.authorizationHeader: _buildAuthHeader(),
};
var response = await http.get(url, headers: headers);
if (response.statusCode != 200) {
Log.d("Github listRepos: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
return null;
}
List<dynamic> list = jsonDecode(response.body);
var repos = <GitHostRepo>[];
list.forEach((dynamic d) {
var map = Map<String, dynamic>.from(d);
var repo = _repoFromJson(map);
repos.add(repo);
});
// FIXME: Sort these based on some criteria
return repos;
}
@override
Future<GitHostRepo> createRepo(String name) async {
if (_accessCode.isEmpty) {
throw GitHostException.MissingAccessCode;
}
var url = "https://api.github.com/user/repos";
var data = <String, dynamic>{
'name': name,
'private': true,
};
var headers = {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: _buildAuthHeader(),
};
var response =
await http.post(url, headers: headers, body: json.encode(data));
if (response.statusCode != 201) {
Log.d("Github createRepo: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
if (response.statusCode == 422) {
if (response.body.contains("name already exists")) {
throw GitHostException.RepoExists;
}
}
throw GitHostException.CreateRepoFailed;
}
Log.d("GitHub createRepo: " + response.body);
Map<String, dynamic> map = json.decode(response.body);
return _repoFromJson(map);
}
@override
Future<GitHostRepo> getRepo(String name) async {
if (_accessCode.isEmpty) {
throw GitHostException.MissingAccessCode;
}
var userInfo = await getUserInfo();
var owner = userInfo.username;
var url = "https://api.github.com/repos/$owner/$name";
var headers = {
HttpHeaders.authorizationHeader: _buildAuthHeader(),
};
var response = await http.get(url, headers: headers);
if (response.statusCode != 200) {
Log.d("Github getRepo: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
throw GitHostException.GetRepoFailed;
}
Log.d("GitHub getRepo: " + response.body);
Map<String, dynamic> map = json.decode(response.body);
return _repoFromJson(map);
}
@override
Future addDeployKey(String sshPublicKey, String repo) async {
if (_accessCode.isEmpty) {
throw GitHostException.MissingAccessCode;
}
var url = "https://api.github.com/repos/$repo/keys";
var data = <String, dynamic>{
'title': "GitJournal",
'key': sshPublicKey,
'read_only': false,
};
var headers = {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: _buildAuthHeader(),
};
var response =
await http.post(url, headers: headers, body: json.encode(data));
if (response.statusCode != 201) {
Log.d("Github addDeployKey: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
throw GitHostException.DeployKeyFailed;
}
Log.d("GitHub addDeployKey: " + response.body);
return json.decode(response.body);
}
GitHostRepo _repoFromJson(Map<String, dynamic> parsedJson) {
DateTime updatedAt;
try {
updatedAt = DateTime.parse(parsedJson['updated_at'].toString());
} catch (e) {
Log.e(e);
}
return GitHostRepo(
fullName: parsedJson['full_name'],
cloneUrl: parsedJson['ssh_url'],
updatedAt: updatedAt,
);
}
@override
Future<UserInfo> getUserInfo() async {
if (_accessCode.isEmpty) {
throw GitHostException.MissingAccessCode;
}
var url = "https://api.github.com/user";
var headers = {
HttpHeaders.authorizationHeader: _buildAuthHeader(),
};
var response = await http.get(url, headers: headers);
if (response.statusCode != 200) {
Log.d("Github getUserInfo: Invalid response " +
response.statusCode.toString() +
": " +
response.body);
return null;
}
Map<String, dynamic> map = jsonDecode(response.body);
if (map == null || map.isEmpty) {
Log.d("Github getUserInfo: jsonDecode Failed " +
response.statusCode.toString() +
": " +
response.body);
return null;
}
return UserInfo(
name: map['name'],
email: map['email'],
username: map['login'],
);
}
String _buildAuthHeader() {
return 'token $_accessCode';
}
}