Repository navigation
Expand file tree
/
Copy pathcli.ts
More file actions
135 lines (123 loc) · 5.03 KB
/
Copy pathcli.ts
File metadata and controls
135 lines (123 loc) · 5.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env node
import { Command, Option } from "commander";
import fs from 'fs'
import path from 'path'
import * as mime from "mime-types";
import { ContentType, Document, VerificationResult, IIssuer, IVerifier } from "./api-spec.js";
// To support extra implementations: add imports similar to the one for solid
import { SolidApi, SolidConfiguration } from "./implementations/solid/api.js";
// To support extra implementations: extend the array below
const implementations = ["solid"];
const program = new Command();
// To support extra implementations: extend the return type (SoldApi | AnotherApi | ...)
function createApi(implementation: string, configFile: string): SolidApi {
let config;
let api;
if (!fs.existsSync(configFile)) {
throw new Error(`Configuration file not found: ${configFile}`);
}
const rawData = fs.readFileSync(configFile, 'utf-8');
try {
config = JSON.parse(rawData);
} catch (error) {
throw new Error(`Error parsing configuration file: ${error}`);
}
switch (implementation) {
// To support extra implementations: add cases, one per string in the implementations array
case "solid":
api = new SolidApi(config as SolidConfiguration);
break;
default:
throw new Error(`Unsupported implementation: ${implementation}`);
}
return api;
}
function readDocumentFromFile(filePath: string): Document {
if (!fs.existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
}
return {
content: fs.readFileSync(filePath, 'utf-8'),
contentType: mime.lookup(path.extname(filePath)) as ContentType
};
}
function createCommandWithCommonOptions(name: string, description: string): Command {
return new Command(name)
.description(description)
.addOption(new Option("-m, --implementation <implementation>", "The API implementation").choices(implementations).makeOptionMandatory())
.addOption(new Option("-c, --config-file <configuration file>", "Configuration file compliant with the API implementation").makeOptionMandatory());
}
const setupCommand = createCommandWithCommonOptions("setup", "Setup issuer and verifier")
.action(async (options: any) => {
try {
if (!fs.existsSync(options.configFile)) {
throw new Error(`Configuration file not found: ${options.configFile}`);
}
const api = createApi(options.implementation, options.configFile);
await api.setup();
console.log("Setup completed.");
} catch (error) {
console.error("Setup error:", error);
process.exit(2);
};
});
const issueCommand = createCommandWithCommonOptions("issue", "Issue a verifiable credential")
.addOption(new Option("-i, --input-file <input file>", "The input file").makeOptionMandatory())
.addOption(new Option("-o, --output-file <output file>", "The output file (if not provided, output to console)"))
.action(async (options: any) => {
try {
if (!fs.existsSync(options.configFile)) {
throw new Error(`Configuration file not found: ${options.configFile}`);
}
if (!fs.existsSync(options.inputFile)) {
throw new Error(`Input file not found: ${options.inputFile}`);
}
const api = createApi(options.implementation, options.configFile);
const document = readDocumentFromFile(options.inputFile);
const signedDocument = await api.issue(document);
if (options.outputFile) {
const outputDir = path.dirname(options.outputFile);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(options.outputFile, signedDocument.content);
console.log(`Verifiable credential written to ${options.outputFile}.`);
} else {
console.log(signedDocument);
}
} catch (error) {
console.error("Issue error:", error);
process.exit(2);
};
});
const verifyCommand = createCommandWithCommonOptions("verify", "Verify a verifiable credential")
.addOption(new Option("-i, --input-file <input file>", "The input file").makeOptionMandatory())
.action(async (options: any) => {
try {
if (!fs.existsSync(options.configFile)) {
throw new Error(`Configuration file not found: ${options.configFile}`);
}
if (!fs.existsSync(options.inputFile)) {
throw new Error(`Input file not found: ${options.inputFile}`);
}
const api = createApi(options.implementation, options.configFile);
const document = readDocumentFromFile(options.inputFile);
const publicKey = await api.retrievePublicKey();
const verificationResult = await api.verify(document, publicKey);
console.log(`Verification result: ${verificationResult}.`);
if (verificationResult !== "pass") {
process.exit(1);
}
} catch (error) {
console.error("Verify error:", error);
process.exit(2);
}
});
program
.name("vc-proto")
.description("Verifiable Credentials prototyping library command line app")
.version("0.0.0")
.addCommand(setupCommand)
.addCommand(issueCommand)
.addCommand(verifyCommand);
program.parseAsync(process.argv);