aiiinotate 0.2.2 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -17
- package/{run.sh → _run.sh} +2 -5
- package/cli/import.js +6 -5
- package/cli/index.js +43 -10
- package/cli/migrate.js +5 -5
- package/cli/serve.js +29 -0
- package/cli/utils/env.js +17 -0
- package/cli/utils/io.js +106 -0
- package/cli/utils/mongoClient.js +18 -0
- package/docs/env.md +25 -0
- package/package.json +9 -15
- package/src/server.js +7 -4
- package/src/types.js +2 -0
- package/cli/io.js +0 -105
- package/cli/mongoClient.js +0 -11
- /package/{src/config → config}/.env.template +0 -0
package/README.md
CHANGED
|
@@ -1,61 +1,117 @@
|
|
|
1
1
|
# aiiinotate
|
|
2
2
|
|
|
3
|
-
aiiinotate is a fast and lightweight
|
|
3
|
+
aiiinotate is a fast and lightweight annotation server for IIIF. It relies on `nodejs/fastify` and `mongodb` and provides an API to read/write/update/delete IIIF annotations and index manifests.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## PROD USAGE
|
|
8
|
+
|
|
9
|
+
### Install
|
|
10
|
+
|
|
11
|
+
TODO: install mongodb
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install aiiinotate
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Usage
|
|
18
|
+
|
|
19
|
+
The base command is:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
aiiinotate --env <path-to-your-env-file> -- <command>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It will give full access to the CLI interface of Aiiinotate.
|
|
26
|
+
|
|
27
|
+
#### Run the app
|
|
28
|
+
|
|
29
|
+
0. **Setup your `.env`** file after [.env.template](./config/.env.template).
|
|
30
|
+
|
|
31
|
+
1. **Start `mongod`**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
sudo systemctl start mongod
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
2. **Create and configure the database**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
aiiinotate --env <path-to-your-env-file> -- migrate apply
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
3. **Run**
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
aiiinotate --env <path-to-your-env-file> -- serve prod
|
|
47
|
+
# or
|
|
48
|
+
aiiinotate --env <path-to-your-env-file> -- serve dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Import data
|
|
52
|
+
|
|
53
|
+
TODO
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## DEV USAGE
|
|
58
|
+
|
|
59
|
+
### Install
|
|
8
60
|
|
|
9
61
|
```bash
|
|
10
62
|
bash setup.sh
|
|
11
63
|
```
|
|
12
64
|
|
|
13
|
-
|
|
65
|
+
### Usage
|
|
14
66
|
|
|
15
|
-
|
|
67
|
+
#### First steps
|
|
16
68
|
|
|
17
|
-
|
|
69
|
+
0. **Setup your `.env`** file after [.env.template](./config/.env.template) and place it at `./config/.env`.
|
|
70
|
+
|
|
71
|
+
1. **Start `mongod`**
|
|
18
72
|
|
|
19
73
|
```bash
|
|
20
|
-
|
|
74
|
+
sudo systemctl start mongod
|
|
21
75
|
```
|
|
22
76
|
|
|
23
|
-
|
|
77
|
+
#### Run commands
|
|
78
|
+
|
|
79
|
+
- **Start the app**
|
|
24
80
|
|
|
25
81
|
```bash
|
|
26
|
-
npm
|
|
82
|
+
npm run start
|
|
27
83
|
```
|
|
28
84
|
|
|
29
|
-
Test the app
|
|
85
|
+
- **Test the app**
|
|
30
86
|
|
|
31
87
|
```bash
|
|
32
|
-
npm test
|
|
88
|
+
npm run test
|
|
33
89
|
```
|
|
34
90
|
|
|
35
|
-
Run the CLI
|
|
91
|
+
- **Run the CLI**
|
|
36
92
|
|
|
37
93
|
```bash
|
|
38
94
|
npm cli
|
|
39
95
|
```
|
|
40
96
|
|
|
41
|
-
Process migrations
|
|
97
|
+
- **Process migrations**
|
|
42
98
|
|
|
43
99
|
```bash
|
|
44
100
|
# create a new migration. NOTE: the `--` is necessary !
|
|
45
|
-
npm run migrate
|
|
101
|
+
npm run migrate make -- --migrate-name <your migration name>
|
|
46
102
|
|
|
47
103
|
# apply all pending migrations
|
|
48
|
-
npm run migrate
|
|
104
|
+
npm run migrate apply
|
|
49
105
|
|
|
50
106
|
# revert the last migration
|
|
51
|
-
npm run migrate
|
|
107
|
+
npm run migrate revert
|
|
52
108
|
|
|
53
109
|
# revert all migrations
|
|
54
|
-
npm run migrate
|
|
110
|
+
npm run migrate revert-all)
|
|
55
111
|
```
|
|
56
112
|
|
|
57
113
|
---
|
|
58
114
|
|
|
59
115
|
## License
|
|
60
116
|
|
|
61
|
-
|
|
117
|
+
GNU GPL 3.0.
|
package/{run.sh → _run.sh}
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
source "./scripts/utils.sh";
|
|
4
4
|
|
|
5
5
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
6
|
-
ENV_FILE="$SCRIPT_DIR/
|
|
6
|
+
ENV_FILE="$SCRIPT_DIR/config/.env";
|
|
7
7
|
|
|
8
8
|
print_usage() {
|
|
9
9
|
cat<<EOF
|
|
@@ -31,10 +31,7 @@ start () {
|
|
|
31
31
|
|
|
32
32
|
start_mongod
|
|
33
33
|
|
|
34
|
-
if [ "$mode" = "
|
|
35
|
-
dotenvx run -f "$ENV_FILE" -- \
|
|
36
|
-
node "$SCRIPT_DIR/scripts/setup.js";
|
|
37
|
-
elif [ "$mode" = "dev" ]; then
|
|
34
|
+
if [ "$mode" = "dev" ]; then
|
|
38
35
|
dotenvx run -f "$ENV_FILE" -- \
|
|
39
36
|
node --watch "$SCRIPT_DIR/src/server.js";
|
|
40
37
|
elif [ "$mode" = "test" ]; then
|
package/cli/import.js
CHANGED
|
@@ -2,7 +2,8 @@ import { Command, Option, Argument } from "commander";
|
|
|
2
2
|
|
|
3
3
|
import Annotations2 from "#annotations/annotations2.js";
|
|
4
4
|
import Annotations3 from "#annotations/annotations3.js";
|
|
5
|
-
import { getFilesToProcess, fileRead } from "#cli/io.js";
|
|
5
|
+
import { getFilesToProcess, fileRead } from "#cli/utils/io.js";
|
|
6
|
+
import loadMongoClient from "#cli/utils/mongoClient.js";
|
|
6
7
|
|
|
7
8
|
////////////////////////////////////////
|
|
8
9
|
|
|
@@ -70,12 +71,12 @@ async function importAnnotationList(annotations2, fileArr, iiifVersion) {
|
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
73
|
* run the cli
|
|
74
|
+
* @param {import('commander').Command} command
|
|
73
75
|
* @param {string} dataType: one of importTypes
|
|
74
76
|
* @param {object} options
|
|
75
|
-
* @param {import('commander').Command} command
|
|
76
|
-
* @param {import('mongodb').MongoClient} mongoClient
|
|
77
77
|
*/
|
|
78
|
-
async function action(
|
|
78
|
+
async function action(command, dataType, options) {
|
|
79
|
+
const mongoClient = loadMongoClient();
|
|
79
80
|
|
|
80
81
|
/** @type {2 | 3} */
|
|
81
82
|
const iiifVersion = options.iiifVersion;
|
|
@@ -86,7 +87,7 @@ async function action(mongoClient, command, dataType, options) {
|
|
|
86
87
|
|
|
87
88
|
checkAllowedImportType(iiifVersion, dataType);
|
|
88
89
|
|
|
89
|
-
const filesToProcess =
|
|
90
|
+
const filesToProcess = getFilesToProcess(files, listFiles);
|
|
90
91
|
|
|
91
92
|
const annotations2 = new Annotations2(
|
|
92
93
|
mongoClient,
|
package/cli/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* command line interface. run through the package.json.
|
|
3
5
|
* usage: npm run cli import -- [args] [opts]
|
|
@@ -7,20 +9,51 @@
|
|
|
7
9
|
* here, and then pass it down to all the other scripts.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
import { Command } from "commander";
|
|
12
|
+
import { Command, Option } from "commander";
|
|
13
|
+
|
|
14
|
+
// import dotenvx from "dotenvx";
|
|
11
15
|
|
|
12
|
-
import makeMongoClient from "#cli/mongoClient.js";
|
|
16
|
+
import makeMongoClient from "#cli/utils/mongoClient.js";
|
|
13
17
|
import makeImportCommand from "#cli/import.js";
|
|
14
18
|
import makeMigrateCommand from "#cli/migrate.js";
|
|
19
|
+
import makeServeCommand from "#cli/serve.js";
|
|
20
|
+
import loadEnv from "#cli/utils/env.js";
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function makeCli() {
|
|
24
|
+
|
|
25
|
+
const desc =
|
|
26
|
+
"Command line interface for aiiinotate.\n\n"
|
|
27
|
+
+ `All commands are accessible through this CLI: starting the app,
|
|
28
|
+
managing and running migrations, importing and exporting data.
|
|
29
|
+
Run individual commands to see command-specific help.
|
|
30
|
+
`.replace(/^\s+/gm, "");
|
|
15
31
|
|
|
16
|
-
const
|
|
32
|
+
const envFileOpt =
|
|
33
|
+
new Option("--env <env-file>", "path to .env file").makeOptionMandatory();
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
// NOTE: how do we load the env variables ? it's a bit unorthodox:
|
|
36
|
+
// - the CLI requires to use a global `--env` option with a path to the .env file.
|
|
37
|
+
// - we use the hook `preAction` that is called before any (sub-)command's `action` function is called.
|
|
38
|
+
// - in the `preAction` hook, we call `loadEnv` that will load all env files defined in the `.env` file.
|
|
39
|
+
// - this way, all env variables will be defined in the subcommand's `action` methods (and children).
|
|
40
|
+
// this is based on https://github.com/tj/commander.js/issues/563#issuecomment-520112985 but replaxes `.on` with `.action`, which works better.
|
|
41
|
+
// WARNING: this means that the env variables can't be used in (sub-)commands BEFORE `action()` has been called.
|
|
42
|
+
const cli = new Command();
|
|
43
|
+
cli
|
|
44
|
+
.name("aiiinotate")
|
|
45
|
+
.description(desc)
|
|
46
|
+
.usage("--env <path-to-your-env-file> -- <command> [options]")
|
|
47
|
+
.addOption(envFileOpt)
|
|
48
|
+
.hook("preAction", (thisCommand, actionCommand) => {
|
|
49
|
+
loadEnv(thisCommand.opts().env);
|
|
50
|
+
})
|
|
51
|
+
.addCommand(makeServeCommand())
|
|
52
|
+
.addCommand(makeImportCommand())
|
|
53
|
+
.addCommand(makeMigrateCommand());
|
|
19
54
|
|
|
20
|
-
cli
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.addCommand(makeImportCommand(mongoClient))
|
|
24
|
-
.addCommand(makeMigrateCommand(mongoClient));
|
|
55
|
+
cli.parse(process.argv);
|
|
56
|
+
return cli;
|
|
57
|
+
}
|
|
25
58
|
|
|
26
|
-
|
|
59
|
+
await makeCli();
|
package/cli/migrate.js
CHANGED
|
@@ -15,6 +15,8 @@ import { execSync } from "node:child_process"
|
|
|
15
15
|
|
|
16
16
|
import { Command, Option, Argument } from "commander";
|
|
17
17
|
|
|
18
|
+
import loadMongoClient from "#cli/utils/mongoClient.js";
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
/** @typedef {"make"|"apply"|"revert"|"revert-all"} MigrateOpType */
|
|
20
22
|
const allowedMigrateOp = ["make", "apply", "revert", "revert-all"];
|
|
@@ -81,14 +83,12 @@ function migrateRevertAll() {
|
|
|
81
83
|
|
|
82
84
|
/**
|
|
83
85
|
* run the cli
|
|
84
|
-
* @param {import('mongodb').MongoClient} mongoClient
|
|
85
86
|
* @param {import('commander').Command} command
|
|
86
87
|
* @param {MigrateOpType} mongoClient
|
|
87
88
|
* @param {object} options
|
|
88
89
|
*/
|
|
89
|
-
function action(
|
|
90
|
+
async function action(command, migrationOp, options) {
|
|
90
91
|
const { migrationName } = options;
|
|
91
|
-
console.log(">>>", migrationName, options)
|
|
92
92
|
|
|
93
93
|
switch (migrationOp) {
|
|
94
94
|
case ("make"):
|
|
@@ -106,7 +106,7 @@ function action(mongoClient, command, migrationOp, options) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function makeMigrateCommand(
|
|
109
|
+
function makeMigrateCommand() {
|
|
110
110
|
const migrationOpArg =
|
|
111
111
|
new Argument("<migration-op>", "name of migration operation").choices(allowedMigrateOp);
|
|
112
112
|
|
|
@@ -117,7 +117,7 @@ function makeMigrateCommand(mongoClient) {
|
|
|
117
117
|
.description("run database migrations")
|
|
118
118
|
.addArgument(migrationOpArg)
|
|
119
119
|
.addOption(migrationNameOpt)
|
|
120
|
-
.action((migrationOp, options, command) => action(
|
|
120
|
+
.action((migrationOp, options, command) => action(command, migrationOp, options))
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export default makeMigrateCommand;
|
package/cli/serve.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import serve from "#src/server.js";
|
|
2
|
+
|
|
3
|
+
import { Command, Option, Argument } from "commander";
|
|
4
|
+
|
|
5
|
+
/** @typedef {import("#types").RerveModeType} RerveModeType */
|
|
6
|
+
|
|
7
|
+
const serveModeValues = ["test", "dev", "prod"];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('commander').Command} command
|
|
11
|
+
* @param {RerveModeType} serveMode
|
|
12
|
+
*/
|
|
13
|
+
async function action(command, serveMode) {
|
|
14
|
+
await serve(serveMode);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeServeCommand() {
|
|
18
|
+
|
|
19
|
+
const serveModeArg =
|
|
20
|
+
new Argument("<run-mode>", "mode with which to run the app")
|
|
21
|
+
.choices(serveModeValues);
|
|
22
|
+
|
|
23
|
+
return new Command("serve")
|
|
24
|
+
.description("run Aiiinotate. <run-mode>")
|
|
25
|
+
.addArgument(serveModeArg)
|
|
26
|
+
.action((serveMode, command) => action(command, serveMode));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default makeServeCommand;
|
package/cli/utils/env.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import dotenvx from "@dotenvx/dotenvx";
|
|
2
|
+
|
|
3
|
+
import { fileOk } from "#cli/utils/io.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* check that the `--env` provided by the user exists. if it exists, load variables, otherwise exit.
|
|
7
|
+
* @param {string} envPath
|
|
8
|
+
*/
|
|
9
|
+
async function loadEnv(envPath) {
|
|
10
|
+
if ( !fileOk(envPath) ){
|
|
11
|
+
console.error(`env.loadEnv: envPath provided by '--env' not found at '${envPath}'. exiting...`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
};
|
|
14
|
+
dotenvx.config({ path: envPath })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default loadEnv
|
package/cli/utils/io.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const cwd = process.cwd(); // directory the script is run from
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* convert a filepath to absolute if it is relative.
|
|
9
|
+
* @param {string} f
|
|
10
|
+
* @returns {string}
|
|
11
|
+
*/
|
|
12
|
+
const toAbsPath = (f) => path.isAbsolute(f) ? f : path.join(cwd, f);
|
|
13
|
+
|
|
14
|
+
/** @returns {boolean} true if file `f` exists, false otherwise */
|
|
15
|
+
const fileOk = (f) => {
|
|
16
|
+
f = toAbsPath(f);
|
|
17
|
+
try {
|
|
18
|
+
fs.accessSync(f, fs.constants.R_OK);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error(`io.fileOk: file does not exist or could not be read: ${f}`);
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} f
|
|
28
|
+
* @return {string?}
|
|
29
|
+
*/
|
|
30
|
+
const fileRead = (f) => {
|
|
31
|
+
f = toAbsPath(f);
|
|
32
|
+
try {
|
|
33
|
+
return fs.readFileSync(f, { encoding: "utf8" })
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error(`io.fileRead: could not read file: ${f}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* take an input array of filepaths. convert the paths to absolute, and check that the files exist
|
|
41
|
+
* the cli exits if any of the files don't exist
|
|
42
|
+
* @param {string[]} fileArr
|
|
43
|
+
* @returns { string[] } array of absolute filepaths
|
|
44
|
+
*/
|
|
45
|
+
function fileArrayValidate (fileArr) {
|
|
46
|
+
// convert to absolute filepaths
|
|
47
|
+
const success = fileArr.every(fileOk);
|
|
48
|
+
if (!success) {
|
|
49
|
+
console.error("io.fileArrayValidate: some files could not be accessed. exiting...");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
return fileArr
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `file` is a path to a file containing paths to other files (1 file per line).
|
|
57
|
+
* validate all paths and return them as absolute paths
|
|
58
|
+
* @param {str} file
|
|
59
|
+
* @returns {string[]}
|
|
60
|
+
*/
|
|
61
|
+
async function getFilesInListFile(file) {
|
|
62
|
+
// read `file` split it by lines, remove empty lines
|
|
63
|
+
const fileArr =
|
|
64
|
+
fileRead(file)
|
|
65
|
+
.split("\n")
|
|
66
|
+
.filter(l => !l.match(/^\s*$/g));
|
|
67
|
+
return fileArrayValidate(fileArr);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* get the files to import and return them as absolute paths
|
|
72
|
+
*
|
|
73
|
+
* `fileArr` is a list of paths to either:
|
|
74
|
+
* - (fileArr=false) JSON files to import
|
|
75
|
+
* - (fileArr=true) text files containing paths to the JSONS to import
|
|
76
|
+
* => take `fileArr`, validate that all files exist, extract all filepaths
|
|
77
|
+
* from `fileArr`, and return the array of actual JSON paths to process.
|
|
78
|
+
*
|
|
79
|
+
* @param {string[]} fileArr
|
|
80
|
+
* @param {boolean} listFiles
|
|
81
|
+
* @returns {string[]} the list of existing files to process.
|
|
82
|
+
*/
|
|
83
|
+
function getFilesToProcess(fileArr, listFiles=false) {
|
|
84
|
+
let filesToProcess = fileArrayValidate(fileArr);
|
|
85
|
+
|
|
86
|
+
// if `listFile`, open the files containing paths of files to proces, and redo the same validation process for each file in a list file.
|
|
87
|
+
if ( listFiles ) {
|
|
88
|
+
let filesInListFiles = []
|
|
89
|
+
|
|
90
|
+
filesToProcess.map((theListFile) => {
|
|
91
|
+
const files = getFilesInListFile(theListFile);
|
|
92
|
+
filesInListFiles = filesInListFiles.concat(files);
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
filesToProcess = [...new Set(filesInListFiles)]; // deduplicate
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return filesToProcess
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
fileRead,
|
|
103
|
+
fileOk,
|
|
104
|
+
fileArrayValidate,
|
|
105
|
+
getFilesToProcess
|
|
106
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { MongoClient } from "mongodb";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* load a mongo client and connect it to the database. exists if there's an error
|
|
5
|
+
* @returns {import("mongodb").MongoClient}
|
|
6
|
+
*/
|
|
7
|
+
function loadMongoClient() {
|
|
8
|
+
try {
|
|
9
|
+
const client = new MongoClient(process.env.MONGODB_CONNSTRING);
|
|
10
|
+
client.db(process.env.MONGODB_DB);
|
|
11
|
+
return client;
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error(`mongoClient: could not connect to DB because of error ${err.message}`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default loadMongoClient;
|
package/docs/env.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Managing and using env variables
|
|
2
|
+
|
|
3
|
+
## In general
|
|
4
|
+
|
|
5
|
+
Env variables must be stored in a `.env` file that contains the same variables as the one in `config/.env.template`.
|
|
6
|
+
|
|
7
|
+
All commands except testing are launched through the CLI. We leverage that to load env files at CLI level, making them accessible to the app and all processes initiated from the CLI:
|
|
8
|
+
- the CLI requires a global option `--env` with a path to your env file.
|
|
9
|
+
- when defining the CLI, a lifecycle hook is used: `preAction`. After the CLI has been run, but before any action has been run, `cli.hook("preAction")` is used to load all env variables using `dotenvx`.
|
|
10
|
+
- within the subcommand's `action` hook, and the rest of app, the env variables will now be defined.
|
|
11
|
+
- this circumvents a limitation of `commander` (our CLI library) that does not allow passing global options to subcommands. It also allows to load directly all env variables at the root of the script, instead of having to redo it in each subcommand.
|
|
12
|
+
|
|
13
|
+
Testing is the only command that doesn't use the CLI. We use `dotenvx` to load the env variables and pass them to the tested functions:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
dotenvx run -f ./config/.env -- node --test --test-isolation=none
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## In dev
|
|
20
|
+
|
|
21
|
+
In dev, your `.env` file must be located at `$root/config/.env`.
|
|
22
|
+
|
|
23
|
+
## In prod
|
|
24
|
+
|
|
25
|
+
In prod, use the `--env` option to specify the path to your env variable.
|
package/package.json
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiiinotate",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "a fast IIIF-compliant annotation server",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "./cli/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"directories": {
|
|
8
8
|
"doc": "docs"
|
|
9
9
|
},
|
|
10
10
|
"bin": {
|
|
11
|
-
"
|
|
12
|
-
"run": "bash run.sh -p",
|
|
13
|
-
"aiiinotate": "npm run cli"
|
|
11
|
+
"aiiinotate": "./cli/index.js"
|
|
14
12
|
},
|
|
15
13
|
"scripts": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"test": "
|
|
20
|
-
"cli": "dotenvx run -f ./src/config/.env -- node ./cli/index.js",
|
|
14
|
+
"cli": "node ./cli/index.js --env=./config/.env",
|
|
15
|
+
"setup": "npm run cli migrate apply",
|
|
16
|
+
"start": "npm run cli serve dev",
|
|
17
|
+
"test": "dotenvx run -f ./config/.env -- node --test --test-isolation=none",
|
|
21
18
|
"lint": "npx eslint --fix",
|
|
22
|
-
"migrate
|
|
23
|
-
"migrate-apply": "npm run cli migrate apply",
|
|
24
|
-
"migrate-revert": "npm run cli migrate revert",
|
|
25
|
-
"migrate-revert-all": "npm run cli migrate revert-all"
|
|
19
|
+
"migrate": "npm run cli -- migrate"
|
|
26
20
|
},
|
|
27
21
|
"pre-commit": [
|
|
28
22
|
"lint"
|
|
@@ -50,7 +44,7 @@
|
|
|
50
44
|
"#migrations/*.js": "./migrations/*.js",
|
|
51
45
|
"#db/*.js": "./src/db/*.js",
|
|
52
46
|
"#schemas/*.js": "./src/schemas/*.js",
|
|
53
|
-
"#config/*.js": "./
|
|
47
|
+
"#config/*.js": "./config/*.js",
|
|
54
48
|
"#data/*.js": "./src/data/*.js",
|
|
55
49
|
"#utils/*.js": "./src/data/utils/*.js",
|
|
56
50
|
"#fileServer/*.js": "./src/fileServer/*.js",
|
package/src/server.js
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
import build from "#src/app.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @param {
|
|
8
|
+
* @param {import("#types").RerveModeType} serveMode
|
|
9
9
|
*/
|
|
10
|
-
async function
|
|
10
|
+
async function server (serveMode) {
|
|
11
|
+
if (["dev", "prod"].includes(serveMode)) {
|
|
12
|
+
serveMode = "default";
|
|
13
|
+
}
|
|
11
14
|
|
|
12
|
-
const fastify = await build();
|
|
15
|
+
const fastify = await build(serveMode);
|
|
13
16
|
|
|
14
17
|
try {
|
|
15
18
|
fastify.listen({ port: process.env.APP_PORT });
|
|
@@ -19,4 +22,4 @@ async function start (options) {
|
|
|
19
22
|
}
|
|
20
23
|
};
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
export default server;
|
package/src/types.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
/** @typedef {import("ajv").ValidateFunction} AjvValidateFunctionType */
|
|
17
17
|
|
|
18
|
+
/** @typedef {"test"|"dev"|"prod"} RerveModeType */
|
|
19
|
+
|
|
18
20
|
/** @typedef {"uri"|"manifestShortId"|"canvasUri"} AnnotationsDeleteKeyType */
|
|
19
21
|
|
|
20
22
|
/** @typedef {2|3} IiifPresentationVersionType */
|
package/cli/io.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
|
|
4
|
-
const cwd = process.cwd(); // directory the script is run from
|
|
5
|
-
|
|
6
|
-
/** @returns {Promise<boolean>} true if file `f` exists, false otherwise */
|
|
7
|
-
const fileOk = (f) =>
|
|
8
|
-
fs.promises.access(f, fs.constants.R_OK)
|
|
9
|
-
.then(() => true)
|
|
10
|
-
.catch(() => {
|
|
11
|
-
console.log("file does not exist or could not be read: ", f);
|
|
12
|
-
return false
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {string} f
|
|
17
|
-
* @return {Promise<string>}
|
|
18
|
-
*/
|
|
19
|
-
const fileRead = (f) =>
|
|
20
|
-
fs.promises.readFile(f, { encoding: "utf8" })
|
|
21
|
-
.then(data => data)
|
|
22
|
-
.catch(() => {
|
|
23
|
-
console.log("error reading file: ", f);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* take an input array of filepaths. convert the paths to absolute, and check that the files exist
|
|
28
|
-
* the cli exits if any of the files don't exist
|
|
29
|
-
* @param {string[]} fileArr
|
|
30
|
-
* @returns { Promise<string[]> } array of absolute filepaths
|
|
31
|
-
*/
|
|
32
|
-
async function fileArrayValidate (fileArr) {
|
|
33
|
-
// convert to absolute filepaths
|
|
34
|
-
fileArr = fileArr.map(f =>
|
|
35
|
-
path.isAbsolute(f) ? f : path.join(cwd, f));
|
|
36
|
-
|
|
37
|
-
// validate paths
|
|
38
|
-
// the `fileOk` map is wrapped in a `Promise.all` because `fileOk` returns a promise,
|
|
39
|
-
// so we `await` for all file checks to be performed before ensuring that all files have been found.
|
|
40
|
-
const success = await Promise.all(
|
|
41
|
-
fileArr.map(async (f) => await fileOk(f))
|
|
42
|
-
).then(filesExistArr =>
|
|
43
|
-
filesExistArr.every(x => x===true)
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
if (!success) {
|
|
47
|
-
console.log("\n\nERROR: some files could not be accessed. exiting...");
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
return fileArr
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* `file` is a path to a file containing paths to other files (1 file per line).
|
|
55
|
-
* validate all paths and return them as absolute paths
|
|
56
|
-
* @param {str} file
|
|
57
|
-
* @returns {Promise<string[]>}
|
|
58
|
-
*/
|
|
59
|
-
async function getFilesInListFile(file) {
|
|
60
|
-
return fileRead(file)
|
|
61
|
-
.then(content =>
|
|
62
|
-
content.split("\n").filter(l => !l.match(/^\s*$/g)) )
|
|
63
|
-
.then(fileArrayValidate);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* get the files to import and return them as absolute paths
|
|
68
|
-
*
|
|
69
|
-
* `fileArr` is a list of paths to either:
|
|
70
|
-
* - (fileArr=false) JSON files to import
|
|
71
|
-
* - (fileArr=true) text files containing paths to the JSONS to import
|
|
72
|
-
* => take `fileArr`, validate that all files exist, extract all filepaths
|
|
73
|
-
* from `fileArr`, and return the array of actual JSON paths to process.
|
|
74
|
-
*
|
|
75
|
-
* NOTE: file order is NOT PRESERVED since files are opened using async pools
|
|
76
|
-
*
|
|
77
|
-
* @param {string[]} fileArr
|
|
78
|
-
* @param {boolean} listFiles
|
|
79
|
-
* @returns {string[]}
|
|
80
|
-
*/
|
|
81
|
-
async function getFilesToProcess(fileArr, listFiles=false) {
|
|
82
|
-
let filesToProcess = await fileArrayValidate(fileArr);
|
|
83
|
-
|
|
84
|
-
// if `listFile`, open the files containing paths of files to proces, and redo the same validation process for each file in a list file.
|
|
85
|
-
if ( listFiles ) {
|
|
86
|
-
let filesInListFiles = []
|
|
87
|
-
|
|
88
|
-
await Promise.all(filesToProcess.map(async (theListFile) => {
|
|
89
|
-
const files = await getFilesInListFile(theListFile);
|
|
90
|
-
filesInListFiles = filesInListFiles.concat(files);
|
|
91
|
-
}))
|
|
92
|
-
|
|
93
|
-
filesToProcess = [...new Set(filesInListFiles)]; // deduplicate
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return filesToProcess
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
export {
|
|
101
|
-
fileRead,
|
|
102
|
-
fileOk,
|
|
103
|
-
fileArrayValidate,
|
|
104
|
-
getFilesToProcess
|
|
105
|
-
}
|
package/cli/mongoClient.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { MongoClient } from "mongodb";
|
|
2
|
-
|
|
3
|
-
export default async function() {
|
|
4
|
-
const client = new MongoClient(process.env.MONGODB_CONNSTRING);
|
|
5
|
-
try {
|
|
6
|
-
client.db(process.env.MONGODB_DB); // client.db(config.mongodbName);
|
|
7
|
-
return client;
|
|
8
|
-
} catch (err) {
|
|
9
|
-
console.log("cli/mongoClient: error connecting", err);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
File without changes
|