new: podman
This commit is contained in:
@@ -4,85 +4,79 @@ import 'package:toolbox/core/extension/context/common.dart';
|
||||
import 'package:toolbox/core/extension/context/dialog.dart';
|
||||
import 'package:toolbox/core/extension/context/locale.dart';
|
||||
import 'package:toolbox/core/extension/context/snackbar.dart';
|
||||
import 'package:toolbox/core/extension/stringx.dart';
|
||||
import 'package:toolbox/core/route.dart';
|
||||
import 'package:toolbox/data/model/docker/image.dart';
|
||||
import 'package:toolbox/data/res/provider.dart';
|
||||
import 'package:toolbox/data/model/container/image.dart';
|
||||
import 'package:toolbox/data/model/container/type.dart';
|
||||
import 'package:toolbox/data/res/store.dart';
|
||||
import 'package:toolbox/view/widget/expand_tile.dart';
|
||||
import 'package:toolbox/view/widget/input_field.dart';
|
||||
|
||||
import '../../data/model/docker/ps.dart';
|
||||
import '../../data/model/container/ps.dart';
|
||||
import '../../data/model/server/server_private_info.dart';
|
||||
import '../../data/provider/docker.dart';
|
||||
import '../../data/model/app/error.dart';
|
||||
import '../../data/provider/container.dart';
|
||||
import '../../data/model/app/menu.dart';
|
||||
import '../../data/res/ui.dart';
|
||||
import '../../data/res/url.dart';
|
||||
import '../widget/appbar.dart';
|
||||
import '../widget/popup_menu.dart';
|
||||
import '../widget/cardx.dart';
|
||||
import '../widget/two_line_text.dart';
|
||||
import '../widget/url_text.dart';
|
||||
|
||||
class DockerManagePage extends StatefulWidget {
|
||||
class ContainerPage extends StatefulWidget {
|
||||
final ServerPrivateInfo spi;
|
||||
const DockerManagePage({required this.spi, super.key});
|
||||
const ContainerPage({required this.spi, super.key});
|
||||
|
||||
@override
|
||||
State<DockerManagePage> createState() => _DockerManagePageState();
|
||||
State<ContainerPage> createState() => _ContainerPageState();
|
||||
}
|
||||
|
||||
class _DockerManagePageState extends State<DockerManagePage> {
|
||||
class _ContainerPageState extends State<ContainerPage> {
|
||||
final _textController = TextEditingController();
|
||||
final _docker = Pros.docker;
|
||||
late final _container = ContainerProvider(
|
||||
client: widget.spi.server?.client,
|
||||
userName: widget.spi.user,
|
||||
hostId: widget.spi.id,
|
||||
context: context,
|
||||
);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_docker.clear();
|
||||
_textController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final client = widget.spi.server?.client;
|
||||
if (client == null) {
|
||||
return;
|
||||
}
|
||||
_docker
|
||||
..init(
|
||||
client,
|
||||
widget.spi.user,
|
||||
(user) async => await context.showPwdDialog(user),
|
||||
widget.spi.id,
|
||||
context,
|
||||
)
|
||||
..refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<DockerProvider>(builder: (_, ___, __) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
centerTitle: true,
|
||||
title: TwoLineText(up: 'Docker', down: widget.spi.name),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
context.showLoadingDialog();
|
||||
await _docker.refresh();
|
||||
context.pop();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: _buildMain(),
|
||||
floatingActionButton: _docker.error == null ? _buildFAB() : null,
|
||||
);
|
||||
});
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => _container,
|
||||
builder: (_, __) => Consumer<ContainerProvider>(
|
||||
builder: (_, ___, __) {
|
||||
return Scaffold(
|
||||
appBar: CustomAppBar(
|
||||
centerTitle: true,
|
||||
title: TwoLineText(up: 'Container', down: widget.spi.name),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
context.showLoadingDialog();
|
||||
await _container.refresh();
|
||||
context.pop();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: _buildMain(),
|
||||
floatingActionButton: _container.error == null ? _buildFAB() : null,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFAB() {
|
||||
@@ -92,6 +86,196 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMain() {
|
||||
if (_container.error != null && _container.items == null) {
|
||||
return SizedBox.expand(
|
||||
child: Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
const Icon(
|
||||
Icons.error,
|
||||
size: 37,
|
||||
),
|
||||
UIs.height13,
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 23),
|
||||
child: Text(_container.error?.toString() ?? l10n.unknownError),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildEditHost(),
|
||||
_buildSwitchProvider(),
|
||||
],
|
||||
),
|
||||
UIs.height13,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_container.items == null || _container.images == null) {
|
||||
return UIs.centerLoading;
|
||||
}
|
||||
|
||||
final items = <Widget>[
|
||||
_buildLoading(),
|
||||
_buildVersion(),
|
||||
_buildPs(),
|
||||
_buildImage(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildEditHost(),
|
||||
_buildSwitchProvider(),
|
||||
],
|
||||
),
|
||||
].map((e) => CardX(child: e));
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(7),
|
||||
children: items.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
return ExpandTile(
|
||||
title: Text(l10n.imagesList),
|
||||
subtitle: Text(
|
||||
l10n.dockerImagesFmt(_container.images!.length),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
initiallyExpanded: (_container.images?.length ?? 0) <= 3,
|
||||
children: _container.images?.map(_buildImageItem).toList() ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImageItem(ContainerImg e) {
|
||||
return ListTile(
|
||||
title: Text(e.repository ?? l10n.unknown),
|
||||
subtitle: Text('${e.tag} - ${e.sizeMB}', style: UIs.textGrey),
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
alignment: Alignment.centerRight,
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () => _showImageRmDialog(e),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoading() {
|
||||
if (_container.runLog == null) return UIs.placeholder;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: Column(
|
||||
children: [
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
UIs.height13,
|
||||
Text(_container.runLog ?? '...'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVersion() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_container.type.name.upperFirst),
|
||||
Text(_container.version ?? l10n.unknown),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPs() {
|
||||
final items = _container.items;
|
||||
if (items == null) return UIs.placeholder;
|
||||
return ExpandTile(
|
||||
title: Text(l10n.containerStatus),
|
||||
subtitle: Text(
|
||||
_buildPsCardSubtitle(items),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
initiallyExpanded: items.length <= 7,
|
||||
children: items.map(_buildPsItem).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPsItem(ContainerPs item) {
|
||||
return ListTile(
|
||||
title: Text(item.name ?? l10n.unknown),
|
||||
subtitle: Text(
|
||||
item.image ?? l10n.unknown,
|
||||
style: UIs.text13Grey,
|
||||
),
|
||||
trailing: _buildMoreBtn(item),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMoreBtn(ContainerPs dItem) {
|
||||
return PopupMenu(
|
||||
items: ContainerMenu.items(dItem.running).map((e) => e.widget).toList(),
|
||||
onSelected: (item) => _onTapMoreBtn(item, dItem),
|
||||
);
|
||||
}
|
||||
|
||||
String _buildPsCardSubtitle(List<ContainerPs> running) {
|
||||
final runningCount = running.where((element) => element.running).length;
|
||||
final stoped = running.length - runningCount;
|
||||
if (stoped == 0) {
|
||||
return l10n.dockerStatusRunningFmt(runningCount);
|
||||
}
|
||||
return l10n.dockerStatusRunningAndStoppedFmt(runningCount, stoped);
|
||||
}
|
||||
|
||||
Widget _buildEditHost() {
|
||||
final children = <Widget>[];
|
||||
final emptyImgs = _container.images?.isEmpty ?? false;
|
||||
final emptyPs = _container.items?.isEmpty ?? false;
|
||||
if (emptyPs && emptyImgs) {
|
||||
children.add(Padding(
|
||||
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
|
||||
child: Text(
|
||||
l10n.dockerEmptyRunningItems,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
}
|
||||
children.add(
|
||||
TextButton(
|
||||
onPressed: _showEditHostDialog,
|
||||
child: Text(l10n.dockerEditHost),
|
||||
),
|
||||
);
|
||||
return Column(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSwitchProvider() {
|
||||
late final Widget child;
|
||||
if (_container.type == ContainerType.podman) {
|
||||
child = TextButton(
|
||||
onPressed: () {
|
||||
_container.setType(ContainerType.docker);
|
||||
},
|
||||
child: Text(l10n.switchTo('Docker')),
|
||||
);
|
||||
} else {
|
||||
child = TextButton(
|
||||
onPressed: () {
|
||||
_container.setType(ContainerType.podman);
|
||||
},
|
||||
child: Text(l10n.switchTo('Podman')),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
Future<void> _showAddFAB() async {
|
||||
final imageCtrl = TextEditingController();
|
||||
final nameCtrl = TextEditingController();
|
||||
@@ -157,7 +341,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
context.showLoadingDialog();
|
||||
final result = await _docker.run(cmd);
|
||||
final result = await _container.run(cmd);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showSnackBar(result.message ?? l10n.unknownError);
|
||||
@@ -177,336 +361,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
suffix = '$args $image';
|
||||
}
|
||||
if (name.isEmpty) {
|
||||
return 'docker run -itd $suffix';
|
||||
return 'run -itd $suffix';
|
||||
}
|
||||
return 'docker run -itd --name $name $suffix';
|
||||
}
|
||||
|
||||
Widget _buildMain() {
|
||||
if (_docker.error != null && _docker.items == null) {
|
||||
return SizedBox.expand(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error,
|
||||
size: 37,
|
||||
),
|
||||
const SizedBox(height: 27),
|
||||
Text(_docker.error?.message ?? l10n.unknownError),
|
||||
const SizedBox(height: 27),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: _buildSolution(_docker.error!),
|
||||
),
|
||||
_buildEditHost(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
if (_docker.items == null || _docker.images == null) {
|
||||
return UIs.centerLoading;
|
||||
}
|
||||
|
||||
final items = <Widget>[
|
||||
_buildLoading(),
|
||||
_buildVersion(),
|
||||
_buildPs(),
|
||||
_buildImage(),
|
||||
_buildEditHost(),
|
||||
].map((e) => CardX(child: e));
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(7),
|
||||
children: items.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
return ExpandTile(
|
||||
title: Text(l10n.imagesList),
|
||||
subtitle: Text(
|
||||
l10n.dockerImagesFmt(_docker.images!.length),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
initiallyExpanded: (_docker.images?.length ?? 0) <= 3,
|
||||
children: _docker.images?.map(_buildImageItem).toList() ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImageItem(DockerImage e) {
|
||||
return ListTile(
|
||||
title: Text(e.repo),
|
||||
subtitle: Text('${e.tag} - ${e.size}', style: UIs.textGrey),
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
alignment: Alignment.centerRight,
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () => _showImageRmDialog(e),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showImageRmDialog(DockerImage e) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.attention),
|
||||
child: Text(l10n.askContinue('${l10n.delete} Image(${e.repo})')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
final result = await Pros.docker.run(
|
||||
'docker rmi ${e.id} -f',
|
||||
);
|
||||
if (result != null) {
|
||||
context.showSnackBar(result.message ?? l10n.unknownError);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoading() {
|
||||
if (Pros.docker.runLog == null) return UIs.placeholder;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: Column(
|
||||
children: [
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
UIs.height13,
|
||||
Text(_docker.runLog ?? '...'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSolution(DockerErr err) {
|
||||
switch (err.type) {
|
||||
case DockerErrType.notInstalled:
|
||||
return UrlText(
|
||||
text: l10n.installDockerWithUrl,
|
||||
replace: l10n.install,
|
||||
);
|
||||
case DockerErrType.noClient:
|
||||
return Text(l10n.waitConnection);
|
||||
case DockerErrType.invalidVersion:
|
||||
return UrlText(
|
||||
text: l10n.invalidVersionHelp(Urls.appHelp),
|
||||
replace: 'Github',
|
||||
);
|
||||
case DockerErrType.parseImages:
|
||||
return const Text('Parse images error');
|
||||
case DockerErrType.parsePsItem:
|
||||
return const Text('Parse ps item error');
|
||||
case DockerErrType.parseStats:
|
||||
return const Text('Parse stats error');
|
||||
case DockerErrType.unknown:
|
||||
return const Text('Unknown error');
|
||||
case DockerErrType.cmdNoPrefix:
|
||||
return const Text('Cmd no prefix');
|
||||
case DockerErrType.segmentsNotMatch:
|
||||
return const Text('Segments not match');
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildVersion() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_docker.edition ?? l10n.unknown),
|
||||
Text(_docker.version ?? l10n.unknown),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPs() {
|
||||
final items = Pros.docker.items;
|
||||
if (items == null) return UIs.placeholder;
|
||||
return ExpandTile(
|
||||
title: Text(l10n.containerStatus),
|
||||
subtitle: Text(
|
||||
_buildPsCardSubtitle(items),
|
||||
style: UIs.textGrey,
|
||||
),
|
||||
initiallyExpanded: items.length <= 7,
|
||||
children: items.map(_buildPsItem).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPsItem(DockerPsItem item) {
|
||||
return ListTile(
|
||||
title: Text(item.image),
|
||||
subtitle: Text(
|
||||
'${item.name} - ${item.status}',
|
||||
style: UIs.text13Grey,
|
||||
),
|
||||
trailing: _buildMoreBtn(item),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMoreBtn(DockerPsItem dItem) {
|
||||
return PopupMenu(
|
||||
items: DockerMenuType.items(dItem.running).map((e) => e.widget).toList(),
|
||||
onSelected: (item) async {
|
||||
switch (item) {
|
||||
case DockerMenuType.rm:
|
||||
var force = false;
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.attention),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(l10n.askContinue(
|
||||
'${l10n.delete} Container(${dItem.name})',
|
||||
)),
|
||||
UIs.height13,
|
||||
Row(
|
||||
children: [
|
||||
StatefulBuilder(builder: (_, setState) {
|
||||
return Checkbox(
|
||||
value: force,
|
||||
onChanged: (val) => setState(
|
||||
() => force = val ?? false,
|
||||
),
|
||||
);
|
||||
}),
|
||||
Text(l10n.force),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
context.showLoadingDialog();
|
||||
final result = await _docker.delete(
|
||||
dItem.containerId,
|
||||
force,
|
||||
);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok),
|
||||
)
|
||||
],
|
||||
);
|
||||
break;
|
||||
case DockerMenuType.start:
|
||||
context.showLoadingDialog();
|
||||
final result = await _docker.start(dItem.containerId);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case DockerMenuType.stop:
|
||||
context.showLoadingDialog();
|
||||
final result = await _docker.stop(dItem.containerId);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case DockerMenuType.restart:
|
||||
context.showLoadingDialog();
|
||||
final result = await _docker.restart(dItem.containerId);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case DockerMenuType.logs:
|
||||
AppRoute.ssh(
|
||||
spi: widget.spi,
|
||||
initCmd: 'docker logs -f --tail 100 ${dItem.containerId}',
|
||||
).go(context);
|
||||
break;
|
||||
case DockerMenuType.terminal:
|
||||
AppRoute.ssh(
|
||||
spi: widget.spi,
|
||||
initCmd: 'docker exec -it ${dItem.containerId} sh',
|
||||
).go(context);
|
||||
break;
|
||||
// case DockerMenuType.stats:
|
||||
// showRoundDialog(
|
||||
// context: context,
|
||||
// title: Text(l10n.stats),
|
||||
// child: Text(
|
||||
// 'CPU: ${dItem.cpu}\n'
|
||||
// 'Mem: ${dItem.mem}\n'
|
||||
// 'Net: ${dItem.net}\n'
|
||||
// 'Block: ${dItem.disk}',
|
||||
// ),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () => context.pop(),
|
||||
// child: Text(l10n.ok),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// break;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _buildPsCardSubtitle(List<DockerPsItem> running) {
|
||||
final runningCount = running.where((element) => element.running).length;
|
||||
final stoped = running.length - runningCount;
|
||||
if (stoped == 0) {
|
||||
return l10n.dockerStatusRunningFmt(runningCount);
|
||||
}
|
||||
return l10n.dockerStatusRunningAndStoppedFmt(runningCount, stoped);
|
||||
}
|
||||
|
||||
Widget _buildEditHost() {
|
||||
final children = <Widget>[];
|
||||
final emptyImgs = _docker.images?.isEmpty ?? false;
|
||||
final emptyPs = _docker.items?.isEmpty ?? false;
|
||||
if (emptyPs && emptyImgs) {
|
||||
children.add(Padding(
|
||||
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
|
||||
child: Text(
|
||||
l10n.dockerEmptyRunningItems,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
));
|
||||
}
|
||||
children.add(
|
||||
TextButton(
|
||||
onPressed: _showEditHostDialog,
|
||||
child: Text(l10n.dockerEditHost),
|
||||
),
|
||||
);
|
||||
return Column(
|
||||
children: children,
|
||||
);
|
||||
return 'run -itd --name $name $suffix';
|
||||
}
|
||||
|
||||
Future<void> _showEditHostDialog() async {
|
||||
@@ -533,6 +390,147 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
void _onSaveDockerHost(String val) {
|
||||
context.pop();
|
||||
Stores.docker.put(widget.spi.id, val.trim());
|
||||
_docker.refresh();
|
||||
_container.refresh();
|
||||
}
|
||||
|
||||
void _showImageRmDialog(ContainerImg e) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.attention),
|
||||
child: Text(l10n.askContinue('${l10n.delete} Image(${e.repository})')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
final result = await _container.run('rmi ${e.id} -f');
|
||||
if (result != null) {
|
||||
context.showSnackBar(result.message ?? l10n.unknownError);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onTapMoreBtn(ContainerMenu item, ContainerPs dItem) async {
|
||||
final id = dItem.id;
|
||||
if (id == null) {
|
||||
context.showSnackBar('Id is null');
|
||||
return;
|
||||
}
|
||||
switch (item) {
|
||||
case ContainerMenu.rm:
|
||||
var force = false;
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.attention),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(l10n.askContinue(
|
||||
'${l10n.delete} Container(${dItem.name})',
|
||||
)),
|
||||
UIs.height13,
|
||||
Row(
|
||||
children: [
|
||||
StatefulBuilder(builder: (_, setState) {
|
||||
return Checkbox(
|
||||
value: force,
|
||||
onChanged: (val) => setState(
|
||||
() => force = val ?? false,
|
||||
),
|
||||
);
|
||||
}),
|
||||
Text(l10n.force),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
context.showLoadingDialog();
|
||||
final result = await _container.delete(id, force);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(l10n.ok),
|
||||
)
|
||||
],
|
||||
);
|
||||
break;
|
||||
case ContainerMenu.start:
|
||||
context.showLoadingDialog();
|
||||
final result = await _container.start(id);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ContainerMenu.stop:
|
||||
context.showLoadingDialog();
|
||||
final result = await _container.stop(id);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ContainerMenu.restart:
|
||||
context.showLoadingDialog();
|
||||
final result = await _container.restart(id);
|
||||
context.pop();
|
||||
if (result != null) {
|
||||
context.showRoundDialog(
|
||||
title: Text(l10n.error),
|
||||
child: Text(result.message ?? l10n.unknownError),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ContainerMenu.logs:
|
||||
AppRoute.ssh(
|
||||
spi: widget.spi,
|
||||
initCmd: 'docker logs -f --tail 100 ${dItem.id}',
|
||||
).go(context);
|
||||
break;
|
||||
case ContainerMenu.terminal:
|
||||
AppRoute.ssh(
|
||||
spi: widget.spi,
|
||||
initCmd: 'docker exec -it ${dItem.id} sh',
|
||||
).go(context);
|
||||
break;
|
||||
// case DockerMenuType.stats:
|
||||
// showRoundDialog(
|
||||
// context: context,
|
||||
// title: Text(l10n.stats),
|
||||
// child: Text(
|
||||
// 'CPU: ${dItem.cpu}\n'
|
||||
// 'Mem: ${dItem.mem}\n'
|
||||
// 'Net: ${dItem.net}\n'
|
||||
// 'Block: ${dItem.disk}',
|
||||
// ),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () => context.pop(),
|
||||
// child: Text(l10n.ok),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,8 @@ class _ServerDetailOrderPageState extends State<ServerDetailOrderPage> {
|
||||
for (final key in keys_) {
|
||||
keys.add(key);
|
||||
}
|
||||
final disabled = Defaults.detailCardOrder.where((e) => !keys.contains(e)).toList();
|
||||
final disabled =
|
||||
Defaults.detailCardOrder.where((e) => !keys.contains(e)).toList();
|
||||
final allKeys = [...keys, ...disabled];
|
||||
return ReorderableListView.builder(
|
||||
padding: const EdgeInsets.all(7),
|
||||
|
||||
Reference in New Issue
Block a user