From adaebf497ebd4fc4362f963f671bcaaf7f3e5c89 Mon Sep 17 00:00:00 2001 From: Toki Date: Sun, 26 Jan 2025 15:21:56 +0900 Subject: [PATCH] ftp download added file info added date compare added --- lib/oto/commands/command.dart | 27 ++++++++++- lib/oto/commands/file/file.dart | 24 ++++++++++ lib/oto/commands/ftp/download.dart | 45 +++++++++++++++++ lib/oto/commands/{uploader => ftp}/ftp.dart | 1 + .../uploader.dart => ftp/upload.dart} | 8 ++-- lib/oto/commands/util/json.dart | 2 +- lib/oto/commands/web/web.dart | 48 ++++++++----------- lib/oto/data/command_data.dart | 28 ++++++++--- lib/oto/data/command_data.g.dart | 33 +++++++++++-- lib/oto/data/pipeline_data.dart | 18 ++++--- lib/oto/data/pipeline_data.g.dart | 8 ++++ lib/oto/pipeline/pipeline_condition.dart | 39 ++++++++++----- 12 files changed, 215 insertions(+), 66 deletions(-) create mode 100644 lib/oto/commands/ftp/download.dart rename lib/oto/commands/{uploader => ftp}/ftp.dart (99%) rename lib/oto/commands/{uploader/uploader.dart => ftp/upload.dart} (93%) diff --git a/lib/oto/commands/command.dart b/lib/oto/commands/command.dart index c9a445f..be77fae 100644 --- a/lib/oto/commands/command.dart +++ b/lib/oto/commands/command.dart @@ -16,6 +16,7 @@ import 'package:oto_cli/oto/commands/build/build_msbuild.dart'; import 'package:oto_cli/oto/commands/file/directory.dart'; import 'package:oto_cli/oto/commands/file/file.dart'; import 'package:oto_cli/oto/commands/file/file_diff_check.dart'; +import 'package:oto_cli/oto/commands/ftp/download.dart'; import 'package:oto_cli/oto/commands/git/git.dart'; import 'package:oto_cli/oto/commands/aws/awscli.dart'; import 'package:oto_cli/oto/commands/docker/docker.dart'; @@ -39,7 +40,7 @@ import 'package:oto_cli/oto/commands/file/copy.dart'; import 'package:oto_cli/oto/commands/util/publish_ios.dart'; import 'package:oto_cli/oto/commands/util/slack.dart'; import 'package:oto_cli/oto/commands/util/zip.dart'; -import 'package:oto_cli/oto/commands/uploader/uploader.dart'; +import 'package:oto_cli/oto/commands/ftp/upload.dart'; import 'package:oto_cli/oto/pipeline/pipeline_exe_handle.dart'; enum CommandType { @@ -61,6 +62,7 @@ enum CommandType { ExportiOS, PublishiOS, Upload, + Download, Copy, Rename, Delete, @@ -68,6 +70,7 @@ enum CommandType { FileRead, FileWrite, Files, + FileInfo, DirectoryCreate, Slack, SlackBuild, @@ -124,7 +127,8 @@ abstract class Command { CommandType.ArchiveiOS: () => ArchiveiOS(), CommandType.ExportiOS: () => ExportiOS(), CommandType.PublishiOS: () => PublishiOS(), - CommandType.Upload: () => Uploader(), + CommandType.Upload: () => Upload(), + CommandType.Download: () => Download(), CommandType.Copy: () => Copy(), CommandType.Rename: () => Rename(), CommandType.Delete: () => Delete(), @@ -132,6 +136,7 @@ abstract class Command { CommandType.FileRead: () => FileRead(), CommandType.FileWrite: () => FileWrite(), CommandType.Files: () => Files(), + CommandType.FileInfo: () => FileInfo(), CommandType.DirectoryCreate: () => DirectoryCreate(), CommandType.Slack: () => Slack(), CommandType.SlackBuild: () => SlackBuild(), @@ -269,6 +274,24 @@ abstract class Command { data = map[param]; setted = true; } else { + var params = param.split('.'); + if (params.length > 1) { + var subMap = map; + for (var index = 0; index < params.length; index++) { + if (subMap.containsKey(params[index])) { + var value = subMap[params[index]]; + if (value is Map) { + subMap = value; + } else { + data = value; + setted = true; + } + } + } + } + } + + if (!setted) { Application.log('"$value" is not exist in property'); data = null; setted = true; diff --git a/lib/oto/commands/file/file.dart b/lib/oto/commands/file/file.dart index a96ba8c..01a7858 100644 --- a/lib/oto/commands/file/file.dart +++ b/lib/oto/commands/file/file.dart @@ -66,3 +66,27 @@ class Files extends Command { return complete(0, command); } } + +class FileInfo extends Command { + @override + Future execute(DataCommand command) async { + var data = DataFileInfo.fromJson(getParam(command)); + var file = File(data.path); + if (file.existsSync()) { + var stat = await file.stat(); + var map = {}; + map['path'] = file.path; + map['symbolicPath'] = await file.resolveSymbolicLinks(); + map['size'] = stat.size; + map['accessed'] = stat.accessed.toString(); + map['changed'] = stat.changed.toString(); + map['modified'] = stat.modified.toString(); + map['type'] = stat.type.toString(); + map['mode'] = stat.modeString(); + setProperty(data.setInfo, map); + return complete(0, command); + } else { + return complete(-1, command); + } + } +} diff --git a/lib/oto/commands/ftp/download.dart b/lib/oto/commands/ftp/download.dart new file mode 100644 index 0000000..20fbfc7 --- /dev/null +++ b/lib/oto/commands/ftp/download.dart @@ -0,0 +1,45 @@ +// ignore_for_file: depend_on_referenced_packages + +import 'dart:core'; +import 'package:path/path.dart' as path; +import 'package:oto_cli/oto/application.dart'; +import 'package:oto_cli/oto/commands/command.dart'; +import 'package:oto_cli/oto/data/command_data.dart'; +import 'package:oto_cli/oto/commands/ftp/ftp.dart'; + +class Download extends Command { + @override + Future execute(DataCommand command) async { + var data = DataFTP.fromJson(getParam(command)); + + //Check asterisk + var downloadMap = {}; + var map = data.map; + int port = data.port == 0 ? (data.sftp ? 22 : 21) : data.port; + + var ftp = data.sftp + ? SFTP(data.host, port: port, user: data.user, password: data.password) + : FTP(data.host, port: port, user: data.user, password: data.password); + Application.log( + '========================= [Try connection] ========================='); + await ftp.connect(); + Application.log( + '========================= [Connected! Start Download] ================'); + var entries = map.entries; + for (var entry in entries) { + var local = + path.join(Application.instance.commonData!.workspace, entry.key); + var remote = entry.value; + Application.log('[Download]: $local ====> $remote'); + + downloadMap[local] = remote; + } + + if (downloadMap.isNotEmpty) await ftp.download(downloadMap); + + await ftp.disconnect(); + Application.log( + '========================= [Upload completed!] ======================'); + return complete(0, command); + } +} diff --git a/lib/oto/commands/uploader/ftp.dart b/lib/oto/commands/ftp/ftp.dart similarity index 99% rename from lib/oto/commands/uploader/ftp.dart rename to lib/oto/commands/ftp/ftp.dart index d1c1711..e71f757 100644 --- a/lib/oto/commands/uploader/ftp.dart +++ b/lib/oto/commands/ftp/ftp.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +// ignore: depend_on_referenced_packages import 'package:ftpconnect/ftpconnect.dart'; // ignore: implementation_imports import 'package:dart_framework/utils/path.dart'; diff --git a/lib/oto/commands/uploader/uploader.dart b/lib/oto/commands/ftp/upload.dart similarity index 93% rename from lib/oto/commands/uploader/uploader.dart rename to lib/oto/commands/ftp/upload.dart index 9476851..d4e849e 100644 --- a/lib/oto/commands/uploader/uploader.dart +++ b/lib/oto/commands/ftp/upload.dart @@ -6,17 +6,17 @@ import 'package:path/path.dart' as path; import 'package:oto_cli/oto/application.dart'; import 'package:oto_cli/oto/commands/command.dart'; import 'package:oto_cli/oto/data/command_data.dart'; -import 'package:oto_cli/oto/commands/uploader/ftp.dart'; +import 'package:oto_cli/oto/commands/ftp/ftp.dart'; import 'package:dart_framework/utils/path.dart'; -class Uploader extends Command { +class Upload extends Command { @override Future execute(DataCommand command) async { - var data = DataUploader.fromJson(getParam(command)); + var data = DataFTP.fromJson(getParam(command)); //Check asterisk var uploadMap = {}; - var map = data.uploadMap; + var map = data.map; int port = data.port == 0 ? (data.sftp ? 22 : 21) : data.port; var ftp = data.sftp diff --git a/lib/oto/commands/util/json.dart b/lib/oto/commands/util/json.dart index 3bb9d13..ac67a7c 100644 --- a/lib/oto/commands/util/json.dart +++ b/lib/oto/commands/util/json.dart @@ -100,7 +100,7 @@ class JsonWriterFile extends Command { } } } - content = jsonPerttyEncode(json); + content = jsonPrettyEncode(json); file.writeAsStringSync(content, flush: true); } on Exception catch (e) { throw Exception('Json parsing error: $e'); diff --git a/lib/oto/commands/web/web.dart b/lib/oto/commands/web/web.dart index a896c73..fb40031 100644 --- a/lib/oto/commands/web/web.dart +++ b/lib/oto/commands/web/web.dart @@ -9,23 +9,20 @@ import 'package:http/http.dart' as http; import 'package:http_parser/src/media_type.dart'; class WebBase extends Command { - Future request(DataWebBase data, http.BaseRequest request) async - { + Future request(DataWebBase data, http.BaseRequest request) async { var c = Completer(); - if(data.headers != null) - { - for(var head in data.headers!.entries) - { + if (data.headers != null) { + for (var head in data.headers!.entries) { request.headers[head.key] = head.value; } } var response = await request.send(); var exitCode = 0; data.successCodes ??= []; - if ((response.statusCode < 300 && response.statusCode >= 200) || + if ((response.statusCode < 300 && response.statusCode >= 200) || data.successCodes!.contains(response.statusCode)) { exitCode = 0; - if(data.setResponse != null) { + if (data.setResponse != null) { var responseBody = await response.stream.bytesToString(); await setProperty(data.setResponse!, responseBody); } @@ -44,10 +41,8 @@ class WebRequest extends WebBase { Application.log('Request URL: ${data.url}'); data.method ??= 'GET'; var request = http.Request(data.method!, Uri.parse(data.url)); - if(data.fields != null) - { - for(var field in data.fields!.entries) - { + if (data.fields != null) { + for (var field in data.fields!.entries) { request.bodyFields[field.key] = field.value; } } @@ -60,27 +55,24 @@ class WebFile extends WebBase { Future execute(DataCommand command) async { var data = DataWebFile.fromJson(getParam(command)); Application.log('Upload URL: ${data.url}'); - + var form = http.MultipartRequest('POST', Uri.parse(data.url)); - if(data.fields != null) - { - for(var field in data.fields!.entries) - { + if (data.fields != null) { + for (var field in data.fields!.entries) { form.fields[field.key] = field.value; } } - if(data.filePart != null && data.fileMimeType != null && data.files != null) - { - for(var filePath in data.files!) - { + if (data.filePart != null && + data.fileMimeType != null && + data.files != null) { + for (var filePath in data.files!) { var file = File(filePath); - if(file.existsSync()) - { - form.files.add(await http.MultipartFile.fromPath(data.filePart!, file.path, - contentType: MediaType(HttpHeaders.contentTypeHeader, data.fileMimeType!))); - } - else - { + if (file.existsSync()) { + form.files.add(await http.MultipartFile.fromPath( + data.filePart!, file.path, + contentType: MediaType( + HttpHeaders.contentTypeHeader, data.fileMimeType!))); + } else { throw Exception('File not found: $filePath'); } } diff --git a/lib/oto/data/command_data.dart b/lib/oto/data/command_data.dart index 1d107a8..a4d8e0e 100644 --- a/lib/oto/data/command_data.dart +++ b/lib/oto/data/command_data.dart @@ -342,22 +342,22 @@ class DataPublishiOS extends DataParam { Map toJson() => _$DataPublishiOSToJson(this); } -//Uploader +//FTP @JsonSerializable() -class DataUploader extends DataParam { - DataUploader(); +class DataFTP extends DataParam { + DataFTP(); late bool sftp; late int port; late String host; late String user; late String password; - late Map uploadMap; + late Map map; - factory DataUploader.fromJson(Map json) => - _$DataUploaderFromJson(json); + factory DataFTP.fromJson(Map json) => + _$DataFTPFromJson(json); @override - Map toJson() => _$DataUploaderToJson(this); + Map toJson() => _$DataFTPToJson(this); } class DataFile extends DataParam { @@ -464,6 +464,20 @@ class DataFiles extends DataParam { Map toJson() => _$DataFilesToJson(this); } +//File Info +@JsonSerializable() +class DataFileInfo extends DataParam { + DataFileInfo(); + + late String path; + late String setInfo; + + factory DataFileInfo.fromJson(Map json) => + _$DataFileInfoFromJson(json); + @override + Map toJson() => _$DataFileInfoToJson(this); +} + //Directory Create @JsonSerializable() class DataDirectoryCreate extends DataParam { diff --git a/lib/oto/data/command_data.g.dart b/lib/oto/data/command_data.g.dart index b396f98..a4fa566 100644 --- a/lib/oto/data/command_data.g.dart +++ b/lib/oto/data/command_data.g.dart @@ -71,6 +71,7 @@ const _$CommandTypeEnumMap = { CommandType.ExportiOS: 'ExportiOS', CommandType.PublishiOS: 'PublishiOS', CommandType.Upload: 'Upload', + CommandType.Download: 'Download', CommandType.Copy: 'Copy', CommandType.Rename: 'Rename', CommandType.Delete: 'Delete', @@ -78,6 +79,7 @@ const _$CommandTypeEnumMap = { CommandType.FileRead: 'FileRead', CommandType.FileWrite: 'FileWrite', CommandType.Files: 'Files', + CommandType.FileInfo: 'FileInfo', CommandType.DirectoryCreate: 'DirectoryCreate', CommandType.Slack: 'Slack', CommandType.SlackBuild: 'SlackBuild', @@ -609,7 +611,7 @@ Map _$DataPublishiOSToJson(DataPublishiOS instance) => 'destinationPath': instance.destinationPath, }; -DataUploader _$DataUploaderFromJson(Map json) => DataUploader() +DataFTP _$DataFTPFromJson(Map json) => DataFTP() ..workspace = json['workspace'] as String? ..passExitCodes = (json['passExitCodes'] as List?) ?.map((e) => (e as num).toInt()) @@ -622,10 +624,9 @@ DataUploader _$DataUploaderFromJson(Map json) => DataUploader() ..host = json['host'] as String ..user = json['user'] as String ..password = json['password'] as String - ..uploadMap = Map.from(json['uploadMap'] as Map); + ..map = Map.from(json['map'] as Map); -Map _$DataUploaderToJson(DataUploader instance) => - { +Map _$DataFTPToJson(DataFTP instance) => { 'workspace': instance.workspace, 'passExitCodes': instance.passExitCodes, 'setResult': instance.setResult, @@ -636,7 +637,7 @@ Map _$DataUploaderToJson(DataUploader instance) => 'host': instance.host, 'user': instance.user, 'password': instance.password, - 'uploadMap': instance.uploadMap, + 'map': instance.map, }; DataCopy _$DataCopyFromJson(Map json) => DataCopy() @@ -809,6 +810,28 @@ Map _$DataFilesToJson(DataFiles instance) => { 'setFiles': instance.setFiles, }; +DataFileInfo _$DataFileInfoFromJson(Map json) => DataFileInfo() + ..workspace = json['workspace'] as String? + ..passExitCodes = (json['passExitCodes'] as List?) + ?.map((e) => (e as num).toInt()) + .toList() + ..setResult = json['setResult'] as String? + ..setExitCode = json['setExitCode'] as String? + ..showPrint = json['showPrint'] as bool? + ..path = json['path'] as String + ..setInfo = json['setInfo'] as String; + +Map _$DataFileInfoToJson(DataFileInfo instance) => + { + 'workspace': instance.workspace, + 'passExitCodes': instance.passExitCodes, + 'setResult': instance.setResult, + 'setExitCode': instance.setExitCode, + 'showPrint': instance.showPrint, + 'path': instance.path, + 'setInfo': instance.setInfo, + }; + DataDirectoryCreate _$DataDirectoryCreateFromJson(Map json) => DataDirectoryCreate() ..workspace = json['workspace'] as String? diff --git a/lib/oto/data/pipeline_data.dart b/lib/oto/data/pipeline_data.dart index bcebefb..2e59cc9 100644 --- a/lib/oto/data/pipeline_data.dart +++ b/lib/oto/data/pipeline_data.dart @@ -32,6 +32,8 @@ class DataCondition implements DataValidator { late String? condition_float; @JsonKey(name: 'condition-string') late String? condition_string; + @JsonKey(name: 'condition-date') + late String? condition_date; @JsonKey(name: 'condition-version') late String? condition_version; @JsonKey(name: 'condition-bool') @@ -53,6 +55,9 @@ class DataCondition implements DataValidator { } else if (condition_string != null) { condition = condition_string!; type = 'string'; + } else if (condition_date != null) { + condition = condition_date!; + type = 'date'; } else if (condition_version != null) { condition = condition_version!; type = 'version'; @@ -92,7 +97,8 @@ class DataExeHandle implements DataValidator { return result; } - factory DataExeHandle.fromJson(Map json) => _$DataExeHandleFromJson(json); + factory DataExeHandle.fromJson(Map json) => + _$DataExeHandleFromJson(json); @override Map toJson() => _$DataExeHandleToJson(this); } @@ -140,8 +146,7 @@ class DataContain extends DataValidator { @override PipelineValidateResult validate() { var result = PipelineValidateResult(); - if(!(list is List || list is String)) - { + if (!(list is List || list is String)) { result.message = 'list field Must be type of "List" or "String".'; result.enable = false; return result; @@ -155,7 +160,8 @@ class DataContain extends DataValidator { return result; } - factory DataContain.fromJson(Map json) => _$DataContainFromJson(json); + factory DataContain.fromJson(Map json) => + _$DataContainFromJson(json); @override Map toJson() => _$DataContainToJson(this); } @@ -190,7 +196,6 @@ class DataWhile extends DataCondition { @JsonSerializable() class DataForeach extends DataValidator { - late String iterator; late dynamic setKey; late dynamic setValue; @@ -204,8 +209,7 @@ class DataForeach extends DataValidator { PipelineValidateResult validate() { var result = PipelineValidateResult(); - if(setValue == null) - { + if (setValue == null) { result.message = 'Syntax requires a settable element name.'; result.enable = false; return result; diff --git a/lib/oto/data/pipeline_data.g.dart b/lib/oto/data/pipeline_data.g.dart index ea3e9b7..29c73f7 100644 --- a/lib/oto/data/pipeline_data.g.dart +++ b/lib/oto/data/pipeline_data.g.dart @@ -23,6 +23,7 @@ DataCondition _$DataConditionFromJson(Map json) => ..condition_int = json['condition-int'] as String? ..condition_float = json['condition-float'] as String? ..condition_string = json['condition-string'] as String? + ..condition_date = json['condition-date'] as String? ..condition_version = json['condition-version'] as String? ..condition_bool = json['condition-bool'] as String? ..condition_object = json['condition-object']; @@ -34,6 +35,7 @@ Map _$DataConditionToJson(DataCondition instance) => 'condition-int': instance.condition_int, 'condition-float': instance.condition_float, 'condition-string': instance.condition_string, + 'condition-date': instance.condition_date, 'condition-version': instance.condition_version, 'condition-bool': instance.condition_bool, 'condition-object': instance.condition_object, @@ -58,6 +60,7 @@ DataIf _$DataIfFromJson(Map json) => DataIf() ..condition_int = json['condition-int'] as String? ..condition_float = json['condition-float'] as String? ..condition_string = json['condition-string'] as String? + ..condition_date = json['condition-date'] as String? ..condition_version = json['condition-version'] as String? ..condition_bool = json['condition-bool'] as String? ..condition_object = json['condition-object'] @@ -70,6 +73,7 @@ Map _$DataIfToJson(DataIf instance) => { 'condition-int': instance.condition_int, 'condition-float': instance.condition_float, 'condition-string': instance.condition_string, + 'condition-date': instance.condition_date, 'condition-version': instance.condition_version, 'condition-bool': instance.condition_bool, 'condition-object': instance.condition_object, @@ -97,6 +101,7 @@ DataWhile _$DataWhileFromJson(Map json) => DataWhile() ..condition_int = json['condition-int'] as String? ..condition_float = json['condition-float'] as String? ..condition_string = json['condition-string'] as String? + ..condition_date = json['condition-date'] as String? ..condition_version = json['condition-version'] as String? ..condition_bool = json['condition-bool'] as String? ..condition_object = json['condition-object'] @@ -108,6 +113,7 @@ Map _$DataWhileToJson(DataWhile instance) => { 'condition-int': instance.condition_int, 'condition-float': instance.condition_float, 'condition-string': instance.condition_string, + 'condition-date': instance.condition_date, 'condition-version': instance.condition_version, 'condition-bool': instance.condition_bool, 'condition-object': instance.condition_object, @@ -134,6 +140,7 @@ DataSwitch _$DataSwitchFromJson(Map json) => DataSwitch() ..condition_int = json['condition-int'] as String? ..condition_float = json['condition-float'] as String? ..condition_string = json['condition-string'] as String? + ..condition_date = json['condition-date'] as String? ..condition_version = json['condition-version'] as String? ..condition_bool = json['condition-bool'] as String? ..condition_object = json['condition-object'] @@ -148,6 +155,7 @@ Map _$DataSwitchToJson(DataSwitch instance) => 'condition-int': instance.condition_int, 'condition-float': instance.condition_float, 'condition-string': instance.condition_string, + 'condition-date': instance.condition_date, 'condition-version': instance.condition_version, 'condition-bool': instance.condition_bool, 'condition-object': instance.condition_object, diff --git a/lib/oto/pipeline/pipeline_condition.dart b/lib/oto/pipeline/pipeline_condition.dart index e97d38b..1f1d55d 100644 --- a/lib/oto/pipeline/pipeline_condition.dart +++ b/lib/oto/pipeline/pipeline_condition.dart @@ -23,12 +23,29 @@ abstract class PipelineCondition extends PipelineExecutor { ' != ': (v0, v1, caster) => caster(v0) != caster(v1), }; typeCaster = { - 'int': (value) => (value == 'null' || value == null) ? null : (value is String) ? int.parse(value) as int : value, - 'float': (value) => (value == 'null' || value == null) ? null : (value is String) ? double.parse(value) as double? : value, - 'string': (value) => (value == 'null' || value == null) ? null : value, - 'bool': (value) => (value == 'null' || value == null) ? null : (value is String) ? (value.toLowerCase() == 'true') as bool? : value, - 'version': (value) => (value == 'null' || value == null) ? null : AppDataManager.to.versionToNumber(value) as double?, - 'object': (value) => (value == 'null' || value == null) ? null : value, + 'int': (value) => (value == 'null' || value == null) + ? null + : (value is String) + ? int.parse(value) as int + : value, + 'float': (value) => (value == 'null' || value == null) + ? null + : (value is String) + ? double.parse(value) as double? + : value, + 'string': (value) => (value == 'null' || value == null) ? null : value, + 'bool': (value) => (value == 'null' || value == null) + ? null + : (value is String) + ? (value.toLowerCase() == 'true') as bool? + : value, + 'date': (value) => (value == 'null' || value == null) + ? null + : DateTime.parse(value).microsecondsSinceEpoch, + 'version': (value) => (value == 'null' || value == null) + ? null + : AppDataManager.to.versionToNumber(value) as double?, + 'object': (value) => (value == 'null' || value == null) ? null : value, }; return PipelineValidateResult(); } @@ -40,14 +57,12 @@ abstract class PipelineCondition extends PipelineExecutor { var list = condition.split(item.key); dynamic value0; dynamic value1; - if(data.type == 'object') - { + if (data.type == 'object') { value0 = Command.replaceSingleTagValue(list[0]); value1 = Command.replaceSingleTagValue(list[1]); - result.conditionValue = '${value0.toString()}${item.key}${value1.toString()}'; - } - else - { + result.conditionValue = + '${value0.toString()}${item.key}${value1.toString()}'; + } else { value0 = Command.replaceTagValue(list[0]); value1 = Command.replaceTagValue(list[1]); result.conditionValue = '$value0${item.key}$value1';