Skip to content

Commit 0091969

Browse files
committed
functional static & resolver link uploads impl
1 parent 2d5f0b5 commit 0091969

6 files changed

Lines changed: 197 additions & 57 deletions

File tree

lib/definitons/constants.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// ! s5-deploy node version
2-
const s5Version = '0.0.1';
2+
const s5Version = '0.1.0';

lib/functions/cli.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,37 @@ ArgParser defineArguments() {
1010
abbr: 'V',
1111
defaultsTo: false,
1212
negatable: false,
13-
help: 'Gets the version number of package.');
13+
help: 'Gets the version number of package');
1414

1515
// -h || --help
1616
topParser.addFlag('help',
17+
negatable: false,
1718
abbr: 'h',
1819
defaultsTo: false,
19-
negatable: false,
2020
help: 'Print help dialoge.');
2121

2222
// --reset
2323
topParser.addFlag('reset',
24-
defaultsTo: false,
2524
negatable: false,
25+
defaultsTo: false,
2626
help: 'Resets local node ${red("BE CAREFUL")}');
2727

28+
// --static
29+
topParser.addFlag('static',
30+
negatable: false, defaultsTo: false, help: 'Skips resolver deploy');
31+
2832
// -s || --server
2933
topParser.addOption('node',
3034
abbr: 'n',
3135
defaultsTo: 'https://s5.ninja',
32-
help: 'Which S5 node to deploy to.');
36+
help: 'Which S5 node to deploy to');
37+
38+
// -S || --seed
39+
topParser.addOption(
40+
'seed',
41+
abbr: 'S',
42+
help: 'Set seed to recover DNS Link Entry',
43+
);
3344

3445
return topParser;
3546
}

lib/functions/s5.dart

Lines changed: 109 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
import 'dart:io';
2+
import 'dart:typed_data';
3+
4+
import 'package:convert/convert.dart';
15
import 'package:dcli/dcli.dart';
26
import 'package:hive/hive.dart';
37
import 'package:http_parser/http_parser.dart';
8+
import 'package:lib5/constants.dart';
9+
import 'package:lib5/identity.dart';
10+
import 'package:lib5/node.dart';
11+
import 'package:lib5/registry.dart';
412
import 'package:path/path.dart';
513
import 'package:s5_deploy/definitons/logging.dart';
614
import 'package:s5/s5.dart';
715
import 'dart:convert';
8-
import 'dart:io';
916
import 'package:http/http.dart' as http;
10-
import 'package:s5_deploy/functions/filesystem.dart';
17+
import 'package:bip39/bip39.dart' as bip39;
1118

1219
Future<S5> initS5(String nodeURL, String dbPath, String logPath) async {
1320
final nowInMilliseconds = DateTime.now().millisecondsSinceEpoch;
@@ -28,6 +35,8 @@ Future<S5> initS5(String nodeURL, String dbPath, String logPath) async {
2835
print(
2936
"No like I'm ${red("serious")}, write this down. But to each their own.");
3037
}
38+
Box<dynamic> box = await Hive.openBox('s5_deploy');
39+
box.put('seed', seed);
3140
await s5.recoverIdentityFromSeedPhrase(seed);
3241
await s5.registerOnNewStorageService(
3342
nodeURL,
@@ -36,61 +45,115 @@ Future<S5> initS5(String nodeURL, String dbPath, String logPath) async {
3645
return s5;
3746
}
3847

39-
// Future<CID> uploadDirectory(
40-
// Map<String, Stream<List<int>>> fileStreams,
41-
// Map<String, int> lengths,
42-
// String name, {
43-
// List<String>? tryFiles,
44-
// Map<String, String>? errorPages,
45-
// required Function lookupMimeType,
46-
// }) async {
47-
// final params = {
48-
// 'name': name,
49-
// };
48+
Future<CID> uploadDirectory(
49+
Map<String, Stream<List<int>>> fileStreams,
50+
Map<String, int> lengths,
51+
S5NodeAPIWithIdentity nodewIdentity,
52+
String name, {
53+
List<String>? tryFiles,
54+
Map<String, String>? errorPages,
55+
required Function lookupMimeType,
56+
}) async {
57+
final params = {
58+
'name': name,
59+
};
60+
61+
if (tryFiles != null) {
62+
params['tryfiles'] = json.encode(tryFiles);
63+
}
64+
if (errorPages != null) {
65+
params['errorpages'] = json.encode(errorPages);
66+
}
67+
68+
final uc = nodewIdentity.accountConfigs.values.first;
69+
70+
var uri = uc.getAPIUrl('/s5/upload/directory').replace(
71+
queryParameters: params,
72+
);
73+
74+
var request = http.MultipartRequest("POST", uri);
75+
76+
request.headers.addAll(uc.headers);
77+
78+
for (final filename in fileStreams.keys) {
79+
var stream = http.ByteStream(fileStreams[filename]!);
5080

51-
// if (tryFiles != null) {
52-
// params['tryfiles'] = json.encode(tryFiles);
53-
// }
54-
// if (errorPages != null) {
55-
// params['errorpages'] = json.encode(errorPages);
56-
// }
81+
final mimeType = lookupMimeType(filename);
5782

58-
// final uc = storageServiceConfigs.first;
83+
var multipartFile = http.MultipartFile(
84+
filename,
85+
stream,
86+
lengths[filename]!,
87+
filename: filename,
88+
contentType: mimeType == null ? null : MediaType.parse(mimeType),
89+
);
5990

60-
// var uri = uc.getAPIUrl('/s5/upload/directory').replace(
61-
// queryParameters: params,
62-
// );
91+
request.files.add(multipartFile);
92+
}
6393

64-
// var request = http.MultipartRequest("POST", uri);
94+
final response = await request.send();
6595

66-
// request.headers.addAll(uc.headers);
96+
if (response.statusCode != 200) {
97+
throw Exception('HTTP ${response.statusCode}');
98+
}
6799

68-
// for (final filename in fileStreams.keys) {
69-
// var stream = http.ByteStream(fileStreams[filename]!);
100+
final res = await response.stream.transform(utf8.decoder).join();
70101

71-
// final mimeType = lookupMimeType(filename);
102+
final resData = json.decode(res);
103+
104+
if (resData['cid'] == null) throw Exception('Directory upload failed');
105+
return CID.decode(resData['cid']);
106+
}
72107

73-
// var multipartFile = http.MultipartFile(
74-
// filename,
75-
// stream,
76-
// lengths[filename]!,
77-
// filename: filename,
78-
// contentType: mimeType == null ? null : MediaType.parse(mimeType),
79-
// );
108+
Future<CID> updateResolver(S5 s5, String dir, CID staticCID) async {
109+
Box<dynamic> box = await Hive.openBox('s5_deploy');
110+
String seed = box.get('seed');
111+
String dataKey = "";
112+
try {
113+
dataKey = box.get('datakey');
114+
} catch (_) {
115+
dataKey = 'project-${Directory(dir).absolute.path}';
116+
}
80117

81-
// request.files.add(multipartFile);
82-
// }
118+
final resolverSeed = s5.api.crypto.hashBlake3Sync(
119+
Uint8List.fromList(
120+
validatePhrase(seed, crypto: s5.api.crypto) + utf8.encode(dataKey),
121+
),
122+
);
83123

84-
// final response = await HttpClient.send(request);
124+
final s5User = await s5.api.crypto.newKeyPairEd25519(seed: resolverSeed);
85125

86-
// if (response.statusCode != 200) {
87-
// throw Exception('HTTP ${response.statusCode}');
88-
// }
126+
SignedRegistryEntry? existing;
89127

90-
// final res = await response.stream.transform(utf8.decoder).join();
128+
try {
129+
final res = await s5.api.registryGet(s5User.publicKey);
130+
existing = res;
131+
if (existing != null) {
132+
print(
133+
'Revision ${existing.revision} -> ${existing.revision + 1}',
134+
);
135+
}
136+
} catch (e) {
137+
existing = null;
91138

92-
// final resData = json.decode(res);
139+
print('Revision 1');
140+
}
93141

94-
// if (resData['cid'] == null) throw Exception('Directory upload failed');
95-
// return CID.decode(resData['cid']);
96-
// }
142+
final sre = await signRegistryEntry(
143+
kp: s5User,
144+
data: staticCID.toRegistryEntry(),
145+
revision: (existing?.revision ?? -1) + 1,
146+
crypto: s5.api.crypto,
147+
);
148+
149+
await s5.api.registrySet(sre);
150+
151+
final resolverCID = CID(
152+
cidTypeResolver,
153+
Multihash(
154+
Uint8List.fromList(
155+
s5User.publicKey,
156+
),
157+
));
158+
return resolverCID;
159+
}

lib/s5_deploy.dart

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:args/args.dart';
44
import 'package:cli_spin/cli_spin.dart';
55
import 'package:dcli/dcli.dart';
66
import 'package:lib5/node.dart';
7+
import 'package:mime/mime.dart';
78
import 'package:path/path.dart';
89
import 'package:s5/s5.dart';
910
import 'package:s5_deploy/definitons/constants.dart';
@@ -86,9 +87,47 @@ void s5Deploy(List<String> args) async {
8687

8788
// now upload to S5 & set registry
8889
spinner = spinStart("Uploading to S5...");
89-
print(s5.hasIdentity);
90-
print((s5.api as S5NodeAPIWithIdentity).accountConfigs);
91-
spinner.success();
90+
S5NodeAPIWithIdentity nodewIden = (s5.api as S5NodeAPIWithIdentity);
91+
final CID staticCID = await uploadDirectory(
92+
procDir.fileStreams, procDir.lengths, nodewIden, "",
93+
lookupMimeType: lookupMimeType);
94+
95+
if (staticCID.hash.toString() != "") {
96+
spinner.success();
97+
} else {
98+
spinner.fail();
99+
}
100+
101+
// Then give the CIDs to the users
102+
if ((results['static'] as bool)) {
103+
print(green("Sucsesss!"));
104+
print("${green("Static Link:")} s5://${staticCID.toBase58()}");
105+
} else {
106+
// get resolver link
107+
spinner = spinStart("Updating resolver link...");
108+
final resolverCID =
109+
await updateResolver(s5, results.arguments.first, staticCID);
110+
spinner.success();
111+
112+
// Then a little url manipulation
113+
final nodeURI = Uri.parse(nodeURL);
114+
final finalURI =
115+
nodeURI.replace(host: "${resolverCID.toBase32()}.${nodeURI.host}");
116+
117+
// now print out to the user
118+
print(green("Sucsesss!"));
119+
print("${green("Static Link:")} s5://${staticCID.toBase58()}");
120+
print("${green("Resolver Link:")} s5://${resolverCID.toBase58()}");
121+
print("${green("Resolver Link Subdomain:")} ${finalURI.toString()}");
122+
print(
123+
"NOTE: This subdomain may not work depending on wildcard permissions");
124+
var confirmed =
125+
confirm('Would you like DNSLink instructions?', defaultValue: true);
126+
if (confirmed) {
127+
print(
128+
"Please put this ${magenta('TXT')} record on your domain: ${green('dnslink=/s5/${resolverCID.toBase58()}')}");
129+
}
130+
}
92131

93132
// And it's over folks
94133
exit(0);

pubspec.lock

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ packages:
7878
url: "https://pub.dev"
7979
source: hosted
8080
version: "1.0.1"
81+
bip39:
82+
dependency: "direct main"
83+
description:
84+
name: bip39
85+
sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
86+
url: "https://pub.dev"
87+
source: hosted
88+
version: "1.0.6"
8189
boolean_selector:
8290
dependency: transitive
8391
description:
@@ -135,7 +143,7 @@ packages:
135143
source: hosted
136144
version: "1.18.0"
137145
convert:
138-
dependency: transitive
146+
dependency: "direct main"
139147
description:
140148
name: convert
141149
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
@@ -294,6 +302,14 @@ packages:
294302
url: "https://pub.dev"
295303
source: hosted
296304
version: "1.0.0"
305+
hex:
306+
dependency: transitive
307+
description:
308+
name: hex
309+
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
310+
url: "https://pub.dev"
311+
source: hosted
312+
version: "0.2.0"
297313
hive:
298314
dependency: "direct main"
299315
description:
@@ -431,7 +447,7 @@ packages:
431447
source: hosted
432448
version: "1.15.0"
433449
mime:
434-
dependency: transitive
450+
dependency: "direct main"
435451
description:
436452
name: mime
437453
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
@@ -470,6 +486,14 @@ packages:
470486
url: "https://pub.dev"
471487
source: hosted
472488
version: "1.9.0"
489+
pointycastle:
490+
dependency: transitive
491+
description:
492+
name: pointycastle
493+
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
494+
url: "https://pub.dev"
495+
source: hosted
496+
version: "3.9.1"
473497
pool:
474498
dependency: transitive
475499
description:

pubspec.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: s5_deploy
22
description: A sample command-line application.
3-
version: 0.0.1
3+
version: 0.1.0
44
repository: https://github.com/s5-dev/s5_deploy
55

66
environment:
@@ -10,13 +10,16 @@ environment:
1010
dependencies:
1111
ansicolor: ^2.0.2
1212
args: ^2.5.0
13+
bip39: ^1.0.6
1314
cli_spin: ^1.0.1
15+
convert: ^3.1.1
1416
dcli: ^4.0.1-beta.4
1517
hive: ^2.2.3
1618
http: ^1.2.1
1719
http_parser: ^4.0.2
1820
lib5: ^0.2.0
1921
lib5_crypto_implementation_dart: ^0.2.0
22+
mime: ^1.0.5
2023
path: ^1.9.0
2124
s5: ^0.2.0
2225
xdg_directories: ^1.0.4

0 commit comments

Comments
 (0)