ftp download added

file info added
date compare added
This commit is contained in:
Toki 2025-01-26 15:21:56 +09:00
parent c26e0c8de8
commit adaebf497e
12 changed files with 215 additions and 66 deletions

View file

@ -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<String, dynamic>) {
subMap = value;
} else {
data = value;
setted = true;
}
}
}
}
}
if (!setted) {
Application.log('"$value" is not exist in property');
data = null;
setted = true;

View file

@ -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 = <String, dynamic>{};
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);
}
}
}

View file

@ -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 = <String, String>{};
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);
}
}

View file

@ -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';

View file

@ -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 = <String, String>{};
var map = data.uploadMap;
var map = data.map;
int port = data.port == 0 ? (data.sftp ? 22 : 21) : data.port;
var ftp = data.sftp

View file

@ -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');

View file

@ -9,23 +9,20 @@ import 'package:http/http.dart' as http;
import 'package:http_parser/src/media_type.dart';
class WebBase extends Command {
Future<int> request(DataWebBase data, http.BaseRequest request) async
{
Future<int> request(DataWebBase data, http.BaseRequest request) async {
var c = Completer<int>();
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');
}
}

View file

@ -342,22 +342,22 @@ class DataPublishiOS extends DataParam {
Map<String, dynamic> 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<String, String> uploadMap;
late Map<String, String> map;
factory DataUploader.fromJson(Map<String, dynamic> json) =>
_$DataUploaderFromJson(json);
factory DataFTP.fromJson(Map<String, dynamic> json) =>
_$DataFTPFromJson(json);
@override
Map<String, dynamic> toJson() => _$DataUploaderToJson(this);
Map<String, dynamic> toJson() => _$DataFTPToJson(this);
}
class DataFile extends DataParam {
@ -464,6 +464,20 @@ class DataFiles extends DataParam {
Map<String, dynamic> toJson() => _$DataFilesToJson(this);
}
//File Info
@JsonSerializable()
class DataFileInfo extends DataParam {
DataFileInfo();
late String path;
late String setInfo;
factory DataFileInfo.fromJson(Map<String, dynamic> json) =>
_$DataFileInfoFromJson(json);
@override
Map<String, dynamic> toJson() => _$DataFileInfoToJson(this);
}
//Directory Create
@JsonSerializable()
class DataDirectoryCreate extends DataParam {

View file

@ -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<String, dynamic> _$DataPublishiOSToJson(DataPublishiOS instance) =>
'destinationPath': instance.destinationPath,
};
DataUploader _$DataUploaderFromJson(Map<String, dynamic> json) => DataUploader()
DataFTP _$DataFTPFromJson(Map<String, dynamic> json) => DataFTP()
..workspace = json['workspace'] as String?
..passExitCodes = (json['passExitCodes'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
@ -622,10 +624,9 @@ DataUploader _$DataUploaderFromJson(Map<String, dynamic> json) => DataUploader()
..host = json['host'] as String
..user = json['user'] as String
..password = json['password'] as String
..uploadMap = Map<String, String>.from(json['uploadMap'] as Map);
..map = Map<String, String>.from(json['map'] as Map);
Map<String, dynamic> _$DataUploaderToJson(DataUploader instance) =>
<String, dynamic>{
Map<String, dynamic> _$DataFTPToJson(DataFTP instance) => <String, dynamic>{
'workspace': instance.workspace,
'passExitCodes': instance.passExitCodes,
'setResult': instance.setResult,
@ -636,7 +637,7 @@ Map<String, dynamic> _$DataUploaderToJson(DataUploader instance) =>
'host': instance.host,
'user': instance.user,
'password': instance.password,
'uploadMap': instance.uploadMap,
'map': instance.map,
};
DataCopy _$DataCopyFromJson(Map<String, dynamic> json) => DataCopy()
@ -809,6 +810,28 @@ Map<String, dynamic> _$DataFilesToJson(DataFiles instance) => <String, dynamic>{
'setFiles': instance.setFiles,
};
DataFileInfo _$DataFileInfoFromJson(Map<String, dynamic> json) => DataFileInfo()
..workspace = json['workspace'] as String?
..passExitCodes = (json['passExitCodes'] as List<dynamic>?)
?.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<String, dynamic> _$DataFileInfoToJson(DataFileInfo instance) =>
<String, dynamic>{
'workspace': instance.workspace,
'passExitCodes': instance.passExitCodes,
'setResult': instance.setResult,
'setExitCode': instance.setExitCode,
'showPrint': instance.showPrint,
'path': instance.path,
'setInfo': instance.setInfo,
};
DataDirectoryCreate _$DataDirectoryCreateFromJson(Map<String, dynamic> json) =>
DataDirectoryCreate()
..workspace = json['workspace'] as String?

View file

@ -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<String, dynamic> json) => _$DataExeHandleFromJson(json);
factory DataExeHandle.fromJson(Map<String, dynamic> json) =>
_$DataExeHandleFromJson(json);
@override
Map<String, dynamic> 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<String, dynamic> json) => _$DataContainFromJson(json);
factory DataContain.fromJson(Map<String, dynamic> json) =>
_$DataContainFromJson(json);
@override
Map<String, dynamic> 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;

View file

@ -23,6 +23,7 @@ DataCondition _$DataConditionFromJson(Map<String, dynamic> 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<String, dynamic> _$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<String, dynamic> 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<String, dynamic> _$DataIfToJson(DataIf instance) => <String, dynamic>{
'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<String, dynamic> 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<String, dynamic> _$DataWhileToJson(DataWhile instance) => <String, dynamic>{
'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<String, dynamic> 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<String, dynamic> _$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,

View file

@ -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';