Skip to content

Commit

Permalink
feat: support update api token
Browse files Browse the repository at this point in the history
  • Loading branch information
stonega committed May 21, 2022
1 parent e0bcb7e commit a40b327
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 18 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ apt intall libnotify-dev
# Fedora
dnf install libnotify-devel
```
```
# create desktop entry
```
12 changes: 9 additions & 3 deletions lib/api/api_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ class ApiService {
if (response.statusCode == 200) {
return response.data;
} else if (response.statusCode == 404) {
return {

};
return {};
}
}

Expand Down Expand Up @@ -145,6 +143,14 @@ class ApiService {
}
}

static setCsrf(String crumb) {
if (crumb != '') {
dio.options.headers["Jenkins-Crumb"] = crumb;
} else {
dio.options.headers.remove('Jenkins-Crumb');
}
}

static setBaseUrl(String baseUrl) {
dio.options.baseUrl = baseUrl;
}
Expand Down
33 changes: 31 additions & 2 deletions lib/api/jenkins_api.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:convert';

import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:jenkins_board/api/api_service.dart';
import 'package:jenkins_board/model/build_task.dart';
import 'package:jenkins_board/model/queue_item.dart';
Expand Down Expand Up @@ -50,7 +53,7 @@ class JenkinsApi {
final res = await ApiService.get('${job.url}api/json');
List<Branch> branches = [];
if (res['jobs'] != null) {
branches = [for (var j in res['jobs']) Branch.fromMap(j)];
branches = [for (final j in res['jobs']) Branch.fromMap(j)];
}
return branches;
}
Expand All @@ -62,7 +65,7 @@ class JenkinsApi {

static Future<List<QueueItem>> getQueue() async {
final res = await ApiService.get('/queue/api/json');
return [for (var i in res['items']) QueueItem.fromMap(i)];
return [for (final i in res['items']) QueueItem.fromMap(i)];
}

static Future<String?> recentBuildUrl(
Expand Down Expand Up @@ -111,4 +114,30 @@ class JenkinsApi {
}
return [];
}

static Future<void> updateApiToken(String username, String password) async {
final data = {'newTokenName': 'Jenkins Board Token'};
final authorization = base64Encode("$username:$password".codeUnits);
final dio = Dio();
final cookieJar = CookieJar();
dio.interceptors.add(CookieManager(cookieJar));

final baseUrl = HiveBox.getBaseUrl();
dio.options.headers["Authorization"] = "Basic $authorization";
final crumbUrl = '${baseUrl}crumbIssuer/api/xml';
final crumbRes = (await dio.get(crumbUrl)).data;
RegExp exp = RegExp(r"<crumb>([0-9a-z])*");
final crumb = exp.stringMatch(crumbRes);
if (crumb == null) throw Error();
dio.options.headers["Jenkins-Crumb"] = crumb.replaceAll('<crumb>', '');
final result = await dio.post(
'$baseUrl/me/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken',
data: data);
if (result.data != null) {
final token = result.data['data']['tokenValue'];
ApiService.setToken(base64Encode('$username:$token'.codeUnits));
} else {
throw Error();
}
}
}
106 changes: 106 additions & 0 deletions lib/view/settings/settings.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:jenkins_board/api/jenkins_api.dart';
import 'package:jenkins_board/provider/app_state_provider.dart';
import 'package:jenkins_board/utils/extensions.dart';
import 'package:jenkins_board/utils/helper.dart';
import 'package:jenkins_board/widgets/custom_button.dart';
import 'package:jenkins_board/widgets/custom_textfield.dart';
import 'package:jenkins_board/widgets/setting_wrapper.dart';
import 'package:line_icons/line_icons.dart';

Expand Down Expand Up @@ -56,6 +59,14 @@ class SettingsPage extends ConsumerWidget {
const SizedBox(
height: 20,
),
Text('Update token', style: context.headline5),
const SizedBox(
height: 10,
),
const RegenerateTokenWidget(),
const SizedBox(
height: 20,
),
Text(
'About',
style: context.headline5,
Expand Down Expand Up @@ -91,3 +102,98 @@ class SettingsPage extends ConsumerWidget {
);
}
}

class RegenerateTokenWidget extends StatefulWidget {
const RegenerateTokenWidget({Key? key}) : super(key: key);

@override
State<RegenerateTokenWidget> createState() => _RegenerateTokenWidgetState();
}

class _RegenerateTokenWidgetState extends State<RegenerateTokenWidget> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
late String _error;
late bool _loading;

@override
void initState() {
super.initState();
_error = '';
_loading = false;
}

@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 200,
child: InputWrapper(
error: _error,
horizontalPadding: 0,
compact: true,
inputBox: CustomTextField(
controller: _usernameController,
placeHolder: 'Username',
inputType: TextInputType.name,
),
),
),
SizedBox(
width: 260,
child: InputWrapper(
error: '',
compact: true,
inputBox: CustomTextField(
obscureText: true,
controller: _passwordController,
placeHolder: 'Passsword',
inputType: TextInputType.visiblePassword,
),
),
),
SizedBox(
width: 80,
height: 40,
child: CustomButton(
loading: _loading,
onPressed: _submit,
child: const Text('Update'),
),
)
],
);
}

_submit() async {
setState(() {
_error = '';
_loading = true;
});
final username = _usernameController.text.trim();
final password = _passwordController.text.trim();
if (username == '' || password == '') {
setState(() {
_error = 'Username or password can not be empty';
});
return;
}
try {
await JenkinsApi.updateApiToken(username, password);
context.toast('Token update successfully');
_usernameController.text = '';
_passwordController.text = '';
} catch (e) {
context.toast('Token update failed, try again');
} finally {
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
}
32 changes: 19 additions & 13 deletions lib/widgets/custom_textfield.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,51 @@ import 'package:flutter/material.dart';
import '../utils/extensions.dart';

class InputWrapper extends StatelessWidget {
final String title;
final String? title;
final Widget inputBox;
final String? error;
final double horizontalPadding;
final bool compact;

const InputWrapper(
{required this.title,
{this.title,
required this.inputBox,
this.error,
this.horizontalPadding = 30,
this.compact = false,
Key? key})
: super(key: key);

@override
Widget build(BuildContext context) {
return Container(
padding:
EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: 2),
padding: EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: context.bodyText1
.copyWith(fontWeight: FontWeight.w600, fontSize: 15),
),
const SizedBox(height: 10),
if (title != null)
Text(
title!,
style: context.bodyText1
.copyWith(fontWeight: FontWeight.w600, fontSize: 15),
),
if (title != null) const SizedBox(height: 10),
ConstrainedBox(
constraints: const BoxConstraints(minHeight: 50),
constraints: BoxConstraints(minHeight: compact ? 40 : 50),
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 15),
padding: EdgeInsets.symmetric(
horizontal: 12, vertical: compact ? 4 : 15),
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(
width: 1,
color: error != null && error!.isNotEmpty
? const Color(0xFFFF2210).withOpacity(0.5)
: context.primaryColorLight),
: compact
? context.accentColor
: context.primaryColorLight),
color: context.primaryColorLight),
child: inputBox,
),
Expand Down
14 changes: 14 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cookie_jar:
dependency: "direct main"
description:
name: cookie_jar
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.1"
crypto:
dependency: transitive
description:
Expand Down Expand Up @@ -169,6 +176,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.6"
dio_cookie_manager:
dependency: "direct main"
description:
name: dio_cookie_manager
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
equatable:
dependency: "direct main"
description:
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ dependencies:
local_notifier: ^0.1.4
reorderables: ^0.4.2
popover: ^0.2.6+3
cookie_jar: ^3.0.1
dio_cookie_manager: ^2.0.0

dev_dependencies:
freezed: ^2.0.3
Expand Down

0 comments on commit a40b327

Please sign in to comment.