aiiinotate 0.11.0 → 0.13.0
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 +54 -45
- package/cli/export.js +118 -0
- package/cli/index.js +2 -0
- package/cli/utils/io.js +139 -1
- package/docker/Dockerfile +20 -8
- package/docker/README.md +3 -60
- package/docker/docker-compose.yaml +15 -4
- package/docker/docker.sh +7 -45
- package/docker/docker_aiiinotate_import.sh +62 -0
- package/docs/{endpoints.md → api.md} +3 -1
- package/docs/cli.md +35 -1
- package/docs/docker.md +98 -0
- package/docs/includes/report_benchmark_aiiinotate_2026-05-28-02:50:48_7steps.png +0 -0
- package/docs/scalability.md +34 -0
- package/package.json +1 -1
- package/src/types.js +1 -0
- package/aiiinotate_annotations_decimal_xywh.json +0 -591277
- package/import_anno_file.txt +0 -10
- package/wit1923_man1937.json +0 -1
- /package/docs/{dev_architecture.md → dev_documentation/dev_architecture.md} +0 -0
- /package/docs/{dev_db.md → dev_documentation/dev_db.md} +0 -0
- /package/docs/{dev_iiif_compatibility.md → dev_documentation/dev_iiif_compatibility.md} +0 -0
- /package/docs/{dev_notes_quirks_and_troubleshooting.md → dev_documentation/dev_notes_quirks_and_troubleshooting.md} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# aiiinotate
|
|
2
2
|
|
|
3
|
-
aiiinotate is a fast and lightweight annotation server for IIIF
|
|
3
|
+
aiiinotate is a **fast and lightweight annotation server for IIIF**.
|
|
4
|
+
|
|
5
|
+
- **aiiinotate relies on** `nodejs/fastify` and `mongodb`
|
|
6
|
+
- **provides a REST API** to read/write/update/delete IIIF annotations and index manifests.
|
|
7
|
+
- **is distributed as an NPM package**, can be used through NPM or Docker
|
|
8
|
+
- **is built for scalability and speed**: [in benchmarks](https://github.com/paulhectork/aiiinotate-benchmark) aiiinotate stores up to 10,000,000 annotations and its response times are always between $$\frac{1}{10}$$ and $$\frac{1}{100}$$ seconds
|
|
4
9
|
|
|
5
10
|
NOTE: currently, only annotations following the IIIF presentation API 2.0 and 2.1 are supported.
|
|
6
11
|
|
|
@@ -8,7 +13,7 @@ NOTE: currently, only annotations following the IIIF presentation API 2.0 and 2.
|
|
|
8
13
|
|
|
9
14
|
## API
|
|
10
15
|
|
|
11
|
-
See the [docs on the aiiinotate API](https://github.com/Aikon-platform/aiiinotate/blob/dev/docs/
|
|
16
|
+
See the [docs on the aiiinotate API](https://github.com/Aikon-platform/aiiinotate/blob/dev/docs/api.md).
|
|
12
17
|
|
|
13
18
|
---
|
|
14
19
|
|
|
@@ -16,11 +21,9 @@ See the [docs on the aiiinotate API](https://github.com/Aikon-platform/aiiinotat
|
|
|
16
21
|
|
|
17
22
|
### Install
|
|
18
23
|
|
|
19
|
-
1. **
|
|
20
|
-
- see [dev installation script for help](./scripts/setup_mongodb.sh)
|
|
21
|
-
- checkout the [official installation guide](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/#std-label-install-mdb-community-ubuntu)
|
|
24
|
+
1. **install mongodb** (see [dev installation script for help](./scripts/setup_mongodb.sh) and checkout the [official installation guide](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/#std-label-install-mdb-community-ubuntu))
|
|
22
25
|
|
|
23
|
-
2. **
|
|
26
|
+
2. **install aiiinotate**
|
|
24
27
|
```bash
|
|
25
28
|
npm install aiiinotate
|
|
26
29
|
```
|
|
@@ -33,37 +36,32 @@ Copy [`config/.env.template`](./config/.env.template) to `.env` and edit it.
|
|
|
33
36
|
|
|
34
37
|
#### Runtime env sourcing
|
|
35
38
|
|
|
36
|
-
|
|
39
|
+
`aiiinotate` runs in a subproicess and won't inherit variables from a plain bash `source` call. Use either of these instead:
|
|
40
|
+
|
|
41
|
+
1. **`dotenvx` (recommended)**:
|
|
37
42
|
|
|
38
43
|
```bash
|
|
39
|
-
|
|
40
|
-
source /path/to/.env && aiiinotate <command>
|
|
44
|
+
npx dotenvx run -f /path/to/.env -- aiiinotate <command>
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
set -a
|
|
51
|
-
source /path/to/.env
|
|
52
|
-
set +a
|
|
53
|
-
aiiinotate <command>
|
|
54
|
-
```
|
|
47
|
+
2. **manual export**:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
set -a && source /path/to/.env && set +a
|
|
51
|
+
aiiinotate <command>
|
|
52
|
+
```
|
|
55
53
|
|
|
56
54
|
For clarity, we omit env sourcing from the below commands.
|
|
57
55
|
|
|
58
56
|
### Setup the app
|
|
59
57
|
|
|
60
|
-
1. **
|
|
58
|
+
1. **start `mongod`**
|
|
61
59
|
|
|
62
60
|
```bash
|
|
63
61
|
sudo systemctl start mongod
|
|
64
62
|
```
|
|
65
63
|
|
|
66
|
-
2. **
|
|
64
|
+
2. **create and configure the database**
|
|
67
65
|
|
|
68
66
|
```bash
|
|
69
67
|
aiiinotate migrate apply
|
|
@@ -89,12 +87,20 @@ aiiinotate -- <command>
|
|
|
89
87
|
|
|
90
88
|
It will give full access to the CLI interface of Aiiinotate. Run `aiiinotate --help` for more info.
|
|
91
89
|
|
|
90
|
+
Use the CLI to:
|
|
91
|
+
- import data
|
|
92
|
+
- export data
|
|
93
|
+
- apply and manage migrations
|
|
94
|
+
|
|
92
95
|
For more information, see [the CLI docs](https://github.com/Aikon-platform/aiiinotate/blob/main/docs/cli.md).
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## DOCKER USAGE
|
|
95
100
|
|
|
96
|
-
See
|
|
101
|
+
See the docs [here](https://github.com/Aikon-platform/aiiinotate/blob/dev/docs/docker.md)
|
|
97
102
|
|
|
103
|
+
For a Mirador integration, see [the reference implementation](github.com/paulhectork/mirador-aiiinotate/tree/main) (aiiinotate + MongoDB + Mirador 4 + MAE bundled in a single `docker-compose`)
|
|
98
104
|
|
|
99
105
|
---
|
|
100
106
|
|
|
@@ -123,15 +129,15 @@ npm i
|
|
|
123
129
|
|
|
124
130
|
After installing, some setup must be done
|
|
125
131
|
|
|
126
|
-
1. **
|
|
132
|
+
1. **setup your `.env`** file after [`config/.env.template`](./config/.env.template) and place it at `./config/.env`.
|
|
127
133
|
|
|
128
|
-
2. **
|
|
134
|
+
2. **start `mongod`**
|
|
129
135
|
|
|
130
136
|
```bash
|
|
131
137
|
sudo systemctl start mongod
|
|
132
138
|
```
|
|
133
139
|
|
|
134
|
-
3. **
|
|
140
|
+
3. **configure the database**
|
|
135
141
|
|
|
136
142
|
```bash
|
|
137
143
|
npm run migrate apply
|
|
@@ -141,40 +147,33 @@ npm run migrate apply
|
|
|
141
147
|
|
|
142
148
|
Remember to have your `mongodb` service running: `sudo systemctl start mongod` !
|
|
143
149
|
|
|
144
|
-
|
|
150
|
+
#### Start the app
|
|
145
151
|
|
|
146
152
|
```bash
|
|
147
153
|
# reload enabled
|
|
148
154
|
npm run dev
|
|
149
155
|
```
|
|
150
156
|
|
|
151
|
-
|
|
157
|
+
#### Test the app
|
|
158
|
+
|
|
159
|
+
Note that the tests will probably fail if you set the env variable `AIIINOTATE_STRICT_MODE` to `true`.
|
|
152
160
|
|
|
153
161
|
```bash
|
|
154
162
|
npm run test
|
|
155
163
|
```
|
|
156
164
|
|
|
157
|
-
|
|
165
|
+
#### Run the CLI
|
|
158
166
|
|
|
167
|
+
See [the CLI docs](https://github.com/Aikon-platform/aiiinotate/blob/dev/docs/cli.md) for more info:
|
|
159
168
|
|
|
160
169
|
```bash
|
|
161
170
|
npm run cli
|
|
162
171
|
```
|
|
163
172
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
npm run migrate -- <command> <arguments?>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
- **Import data**. (see [the CLI docs](https://github.com/Aikon-platform/aiiinotate/blob/main/docs/cli.md) for more info)
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
# NOTE: the `--` is necessary !
|
|
176
|
-
npm run cli -- import <arguments>
|
|
177
|
-
```
|
|
173
|
+
Use the CLI to:
|
|
174
|
+
- import data
|
|
175
|
+
- export data
|
|
176
|
+
- apply and manage database migrations
|
|
178
177
|
|
|
179
178
|
---
|
|
180
179
|
|
|
@@ -192,6 +191,16 @@ aiiinotate is well tested: **~90% test coverage** on all files !
|
|
|
192
191
|
|
|
193
192
|
---
|
|
194
193
|
|
|
194
|
+
## Scalability
|
|
195
|
+
|
|
196
|
+
In [benchmarks](https://github.com/paulhectork/aiiinotate-benchmark), aiiinotate response times are between 1/100th and 1/10th of a second with up to 10,000,000 annotations.
|
|
197
|
+
|
|
198
|
+

|
|
199
|
+
|
|
200
|
+
See [scalability.md](./docs/scalability.md) for more information.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
195
204
|
## License
|
|
196
205
|
|
|
197
206
|
GNU GPL 3.0.
|
package/cli/export.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { Command, Option, Argument } from "commander";
|
|
4
|
+
|
|
5
|
+
import loadMongoClient from "#cli/utils/mongoClient.js";
|
|
6
|
+
import { dirOk, getCwd, writeCursorToJson } from "#cli/utils/io.js";
|
|
7
|
+
|
|
8
|
+
/** @typedef {import("#types").MongoDbType} MongoDbType */
|
|
9
|
+
/** @typedef {import("#types").MongoFindCursorType} MongoFindCursorType */
|
|
10
|
+
/** @typedef {import("fs").PathLike} PathLike */
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const exportableCollections = [ "all", "annotations2", "annotations3", "manifests2", "manifests3" ];
|
|
14
|
+
const actualCollections = exportableCollections.filter(c => c!=="all");
|
|
15
|
+
|
|
16
|
+
/** @type {(outDir: string) => string} */
|
|
17
|
+
const getOutDir = (outDir) => {
|
|
18
|
+
let success;
|
|
19
|
+
if (outDir) {
|
|
20
|
+
[ outDir, success ] = dirOk(outDir);
|
|
21
|
+
if (!success) {
|
|
22
|
+
console.error(`Error opening directory "${outDir}". Are you sure it exists ?`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
outDir = getCwd();
|
|
27
|
+
}
|
|
28
|
+
return outDir;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @type {() => (collectionName: string) => string|PathLike} */
|
|
32
|
+
const outFileNameFunc = () => {
|
|
33
|
+
// we currify this function so that, if a user exports all collections,
|
|
34
|
+
// output file nmaes share the same timestamp.
|
|
35
|
+
const timestamp = (new Date).toISOString().slice(0,-2);
|
|
36
|
+
return (outDir, collectionName) =>
|
|
37
|
+
path.join(outDir, `${timestamp}-aiiinotate-${collectionName}.json`);
|
|
38
|
+
}
|
|
39
|
+
const outFileName = outFileNameFunc();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* if `count`, returns a function to count all documents in a collection.
|
|
43
|
+
* else, returns a function to find all documents in a collection.
|
|
44
|
+
* @type {(db:MongoClient) => (count: boolean) => (collectionName: string) => MongoFindCursorType|Promise<number>}
|
|
45
|
+
*/
|
|
46
|
+
const exportCmdFunc = (db) =>
|
|
47
|
+
(count) =>
|
|
48
|
+
(collectionName) => {
|
|
49
|
+
const collection = db.collection(collectionName);
|
|
50
|
+
return count
|
|
51
|
+
? collection.countDocuments()
|
|
52
|
+
: collection.find().project({ _id: 0 })
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* export a single collection to a file
|
|
58
|
+
* @param {MongoClient} db
|
|
59
|
+
* @param {string} outDir
|
|
60
|
+
* @returns {(collctionName: string) => Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
const exportCollectionFunc = (db, outDir) => {
|
|
63
|
+
const exportCmd = exportCmdFunc(db)(false);
|
|
64
|
+
const countFunc = exportCmdFunc(db)(true);
|
|
65
|
+
|
|
66
|
+
return async (collectionName) => {
|
|
67
|
+
const out = outFileName(outDir, collectionName);
|
|
68
|
+
const collectionCursor = exportCmd(collectionName);
|
|
69
|
+
const totalCount = await countFunc(collectionName);
|
|
70
|
+
try {
|
|
71
|
+
await writeCursorToJson(out, collectionCursor, totalCount);
|
|
72
|
+
console.log(`Wrote export of collection "${collectionName}" (${totalCount} documents) to "${out}"\n`)
|
|
73
|
+
} catch(e) {
|
|
74
|
+
console.error(`Error writing export of collection "${collectionName}" to "${out}": ${e}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} collection
|
|
82
|
+
* @param {object} options
|
|
83
|
+
*/
|
|
84
|
+
async function action(collection, options) {
|
|
85
|
+
const { client, db } = loadMongoClient();
|
|
86
|
+
const outDir = getOutDir(options.output);
|
|
87
|
+
const collectionNameArray =collection === "all" ? actualCollections : [ collection ];
|
|
88
|
+
const exportCollection = exportCollectionFunc(db, outDir);
|
|
89
|
+
|
|
90
|
+
console.log(`\nExporting collection(s): ${collectionNameArray}\n`)
|
|
91
|
+
|
|
92
|
+
// fetch and write to file
|
|
93
|
+
for (const collectionName of collectionNameArray) {
|
|
94
|
+
await exportCollection(collectionName);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`\nFinished exporting collection(s): ${collectionNameArray} to "${outDir}".`)
|
|
98
|
+
await client.close();
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** define the cli */
|
|
103
|
+
function makeExportCommand() {
|
|
104
|
+
|
|
105
|
+
const collectionArg =
|
|
106
|
+
new Argument("collection", `collection to export import: one of ${exportableCollections}`)
|
|
107
|
+
.choices(exportableCollections);
|
|
108
|
+
|
|
109
|
+
const outOpt = new Option("-o, --output <directory>", "Output directory (defaults to current working directory");
|
|
110
|
+
|
|
111
|
+
return new Command("export")
|
|
112
|
+
.description("export aiiinotate data")
|
|
113
|
+
.addArgument(collectionArg)
|
|
114
|
+
.addOption(outOpt)
|
|
115
|
+
.action(async (collection, options, command) => await action(collection, options))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default makeExportCommand;
|
package/cli/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { Command, Option } from "commander";
|
|
|
14
14
|
// import dotenvx from "dotenvx";
|
|
15
15
|
|
|
16
16
|
import makeImportCommand from "#cli/import.js";
|
|
17
|
+
import makeExportCommand from "#cli/export.js";
|
|
17
18
|
import makeMigrateCommand from "#cli/migrate.js";
|
|
18
19
|
import makeServeCommand from "#cli/serve.js";
|
|
19
20
|
import makeXywhToIntCommand from "#cli/xywhToInt.js";
|
|
@@ -34,6 +35,7 @@ function makeCli() {
|
|
|
34
35
|
.description(desc)
|
|
35
36
|
.addCommand(makeServeCommand())
|
|
36
37
|
.addCommand(makeImportCommand())
|
|
38
|
+
.addCommand(makeExportCommand())
|
|
37
39
|
.addCommand(makeMigrateCommand())
|
|
38
40
|
.addCommand(makeXywhToIntCommand());
|
|
39
41
|
|
package/cli/utils/io.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
|
|
4
|
+
import ProgressBar from "#cli/utils/progressbar.js";
|
|
5
|
+
|
|
6
|
+
/** @typedef {import("fs").WriteStream} WriteStreamType */
|
|
4
7
|
|
|
5
8
|
const cwd = process.cwd(); // directory the script is run from
|
|
9
|
+
const getCwd = () => process.cwd();
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* convert a filepath to absolute if it is relative.
|
|
@@ -24,6 +28,17 @@ const fileOk = (f) => {
|
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
/** @returns {[PathLike, boolean]} */
|
|
32
|
+
const dirOk = (d) => {
|
|
33
|
+
d = toAbsPath(d);
|
|
34
|
+
try {
|
|
35
|
+
fs.accessSync(d, fs.constants.R_OK); // will throw if there's an error
|
|
36
|
+
return [ d, fs.lstatSync(d).isDirectory() ]
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return [ d, false ]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
/**
|
|
28
43
|
* @param {string} f
|
|
29
44
|
* @return {string?}
|
|
@@ -79,9 +94,132 @@ async function parseImportInputFile(file) {
|
|
|
79
94
|
return [ ...new Set(fileArrayValidate(fileArr)) ];
|
|
80
95
|
}
|
|
81
96
|
|
|
97
|
+
const fileWrite = (f, data) => {
|
|
98
|
+
try {
|
|
99
|
+
fs.writeFileSync(f, data, "utf-8");
|
|
100
|
+
} catch (e) {
|
|
101
|
+
throw new Error(`Error writing to file: ${f} because of error: ${e}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* NOTE: this will NOT work on collections and huge objects. use streamWriteJson to write from Mongo Cursors.
|
|
107
|
+
* @param {string|import("fs").PathLike} f
|
|
108
|
+
* @param {Array|object} data
|
|
109
|
+
* @returns {void}
|
|
110
|
+
*/
|
|
111
|
+
const fileWriteJson = (f, data) => {
|
|
112
|
+
data = JSON.stringify(data)
|
|
113
|
+
fileWrite(f, data);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* wrapper for writer.write(data) that respects writer backpressure
|
|
118
|
+
*
|
|
119
|
+
* we wrap the .write() in a Promise that checks on the rturn of `writer.write`
|
|
120
|
+
* and drains the stream if necessary: if `writer.write()` returns
|
|
121
|
+
* false, the write buffer is full and must be drained by the OS
|
|
122
|
+
* before continuing.
|
|
123
|
+
*
|
|
124
|
+
* this avoids:
|
|
125
|
+
* - writes that are silently dropped or reordered
|
|
126
|
+
* - corrupt/truncated JSON with no error thrown
|
|
127
|
+
*
|
|
128
|
+
* @type {(writer: WriteStreamType) => (data: string) => Promise<void> }
|
|
129
|
+
*/
|
|
130
|
+
const writeOrWait = (writer) =>
|
|
131
|
+
(data) =>
|
|
132
|
+
new Promise((resolve, reject) => {
|
|
133
|
+
const ok = writer.write(data, (error) => {
|
|
134
|
+
if (error) return reject(error);
|
|
135
|
+
});
|
|
136
|
+
if (ok) {
|
|
137
|
+
resolve(); // buffer has room, keep going
|
|
138
|
+
} else {
|
|
139
|
+
writer.once("drain", resolve); // buffer full, wait for drain
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* WriteStream.write is actually asyncrhonous => for the last line,
|
|
145
|
+
* we need to use `writer.end` to ensure everything has been written to file.
|
|
146
|
+
*
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
const endStream = (writer, data) =>
|
|
150
|
+
new Promise((resolve, reject) => {
|
|
151
|
+
writer.end(data, (error) => {
|
|
152
|
+
if (error) return reject(error);
|
|
153
|
+
resolve();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* given a file path `fp` and a FindCursor `cursor`,
|
|
159
|
+
* write all documents in `cursor` to a file.
|
|
160
|
+
*
|
|
161
|
+
* if `totalCount` is defined, print a progress bar as well.
|
|
162
|
+
* else, just print the document number and update at each iteration.
|
|
163
|
+
*
|
|
164
|
+
* necessary to use a string when `cursor` stores a lot of documents
|
|
165
|
+
* (instead of a simple file-write):
|
|
166
|
+
* - cursor.toArray() uses TONS of memory and is slow
|
|
167
|
+
* - JSON.stringify(), used to write JSONs to file, has a maximum
|
|
168
|
+
* size for arrays and will crash if stringifying huge arrays.
|
|
169
|
+
*
|
|
170
|
+
* to check that the output is a valid JSON, use:
|
|
171
|
+
* ```bash
|
|
172
|
+
* python3 -mjson.tool path/to/json > /dev/null && echo "ok" || echo "err"
|
|
173
|
+
* ````
|
|
174
|
+
*
|
|
175
|
+
* @param {string|PathLike} fp
|
|
176
|
+
* @param {MongoFindCursorType} cursor
|
|
177
|
+
* @param {number?} totalCount
|
|
178
|
+
* @returns {Promise}
|
|
179
|
+
*/
|
|
180
|
+
const writeCursorToJson = async (fp, cursor, totalCount) => {
|
|
181
|
+
const writer = fs.createWriteStream(fp);
|
|
182
|
+
const onceWriter = writeOrWait(writer);
|
|
183
|
+
|
|
184
|
+
let pb;
|
|
185
|
+
if (totalCount) {
|
|
186
|
+
pb = new ProgressBar({ desc: "writing documents to file", total: totalCount });
|
|
187
|
+
} else {
|
|
188
|
+
console.log("");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// we are writing an array => manually write the opening "[".
|
|
192
|
+
await onceWriter("[");
|
|
193
|
+
let i = 0
|
|
194
|
+
for await (const doc of cursor) {
|
|
195
|
+
i += 1;
|
|
196
|
+
// if previous items were written to file, write a "," separator.
|
|
197
|
+
if (i!=1) await onceWriter(", ");
|
|
198
|
+
if (pb) {
|
|
199
|
+
pb.update(i);
|
|
200
|
+
} else {
|
|
201
|
+
process.stdout.clearLine(0);
|
|
202
|
+
process.stdout.cursorTo(0);
|
|
203
|
+
process.stdout.write(`writing document #${i} to file`);
|
|
204
|
+
}
|
|
205
|
+
await onceWriter(JSON.stringify(doc));
|
|
206
|
+
}
|
|
207
|
+
// close the array with a "]" and end the stream.
|
|
208
|
+
await endStream(writer, "]");
|
|
209
|
+
|
|
210
|
+
if (!pb && i>0) console.log("");
|
|
211
|
+
return totalCount;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
82
215
|
export {
|
|
83
216
|
fileRead,
|
|
84
217
|
fileOk,
|
|
218
|
+
dirOk,
|
|
219
|
+
fileWrite,
|
|
220
|
+
fileWriteJson,
|
|
221
|
+
getCwd,
|
|
85
222
|
fileArrayValidate,
|
|
86
|
-
parseImportInputFile
|
|
223
|
+
parseImportInputFile,
|
|
224
|
+
writeCursorToJson
|
|
87
225
|
}
|
package/docker/Dockerfile
CHANGED
|
@@ -5,23 +5,35 @@ FROM node:23.11
|
|
|
5
5
|
ARG PORT
|
|
6
6
|
ENV PORT=${PORT}
|
|
7
7
|
|
|
8
|
+
# path to the .env file
|
|
9
|
+
ARG ENV_PATH
|
|
10
|
+
ENV ENV_PATH="$ENV_PATH"
|
|
11
|
+
|
|
8
12
|
# set up environment
|
|
13
|
+
ENV DIR=/aiiinotate
|
|
9
14
|
ENV TERM=linux
|
|
15
|
+
ENV NPM_BIN=/aiiinotate/node_modules/.bin
|
|
10
16
|
SHELL ["/bin/bash", "-c"]
|
|
11
17
|
|
|
12
18
|
# root of the app in the docker container
|
|
13
|
-
WORKDIR
|
|
19
|
+
WORKDIR ${DIR}
|
|
14
20
|
# copy the docker .env in the docker container
|
|
15
|
-
COPY
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
COPY "$ENV_PATH" ${DIR}/.env
|
|
22
|
+
|
|
23
|
+
# install iproute and curl for debug
|
|
24
|
+
RUN apt-get update
|
|
25
|
+
RUN apt install curl iproute2 -y
|
|
26
|
+
|
|
27
|
+
# create a npm package in $DIR to run aiiinotate from
|
|
28
|
+
RUN npm init -y
|
|
18
29
|
# install the app as an NPM library. we do a global install as it saves us from issues with `$PATH`.
|
|
19
|
-
RUN npm i
|
|
30
|
+
RUN npm i aiiinotate
|
|
20
31
|
|
|
21
32
|
# expose the used port
|
|
22
|
-
EXPOSE $PORT
|
|
33
|
+
EXPOSE ${PORT}
|
|
23
34
|
|
|
24
35
|
# run the migrations and start the app.
|
|
36
|
+
# ./node_modules/.bin/aiiinotate is to be able to use the `aiiinotate` cli without doing a global install of `aiiinotate`
|
|
25
37
|
# NOTE migrations must be done in CMD: they need the mongo service to be running.
|
|
26
|
-
CMD
|
|
27
|
-
|
|
38
|
+
CMD ${NPM_BIN}/dotenvx run -f ${DIR}/.env -- ${NPM_BIN}/aiiinotate migrate apply && \
|
|
39
|
+
${NPM_BIN}/dotenvx run -f ${DIR}/.env -- ${NPM_BIN}/aiiinotate serve prod;
|
package/docker/README.md
CHANGED
|
@@ -1,62 +1,5 @@
|
|
|
1
|
-
# Docker
|
|
1
|
+
# Docker setup
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This folder contains a `docker-compose` for an aiiinotate+MongoDB integration, with volume persistence and a script to import data.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Note that all `docker` commands must be run from the `docker/` directory !
|
|
8
|
-
|
|
9
|
-
1. Clone the repo and `cd` in it
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
git clone git@github.com:Aikon-platform/aiiinotate.git
|
|
13
|
-
cd aiiinotate
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
2. Create a `.env` file at `config/.env` (from the root of the project)
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
cp ./config/.env.template ./config/.env
|
|
20
|
-
vim ./config/.env # do your edits
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
3. Build the containers
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
cd docker
|
|
27
|
-
bash docker.sh build
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
4. Start the containers
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
bash docker.sh start
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Troubleshooting and useful commands
|
|
39
|
-
|
|
40
|
-
Access `mongosh` within the Mongo container:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
sudo docker exec -it docker-mongo-1 mongosh
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Check running ports in the Web container:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
sudo docker exec -it docker-web-1 ss -tnl
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
View globally installed NPM packages in the Web container:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
sudo docker exec -it docker-web-1 npm list -g --depth=0
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
CURL the Web container
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
sudo docker exec -it docker-web-1 curl http://0.0.0.0:4444 # change 4444 by your $AIIINOTATE_PORT
|
|
62
|
-
```
|
|
5
|
+
View the documentation for aiiinotate Docker setup [here](https://github.com/Aikon-platform/aiiinotate/blob/main/docs/docker.md)
|
|
@@ -9,30 +9,41 @@ services:
|
|
|
9
9
|
restart: always
|
|
10
10
|
networks:
|
|
11
11
|
- aiiinotate_network
|
|
12
|
+
volumes:
|
|
13
|
+
- mongodata:/data/db
|
|
12
14
|
environment:
|
|
13
15
|
<<: *proxy-settings
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
|
18
|
+
interval: 10s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 5
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
aiiinotate:
|
|
18
23
|
build:
|
|
19
24
|
context: ..
|
|
20
25
|
dockerfile: docker/Dockerfile
|
|
21
26
|
args:
|
|
22
27
|
PORT: ${AIIINOTATE_PORT}
|
|
28
|
+
ENV_PATH: ../docker/.env
|
|
23
29
|
# NOTE: be careful to point to docker/.env, not to config/.env, otherwise you will have very, very annoying errors.
|
|
24
30
|
env_file:
|
|
25
31
|
- ../docker/.env
|
|
26
32
|
ports:
|
|
27
33
|
- "${AIIINOTATE_PORT}:${AIIINOTATE_PORT}"
|
|
28
34
|
depends_on:
|
|
29
|
-
|
|
35
|
+
mongo:
|
|
36
|
+
condition: service_healthy
|
|
30
37
|
networks:
|
|
31
38
|
- aiiinotate_network
|
|
32
39
|
environment:
|
|
33
40
|
<<: *proxy-settings
|
|
34
41
|
restart: always
|
|
35
42
|
|
|
43
|
+
|
|
44
|
+
volumes:
|
|
45
|
+
mongodata:
|
|
46
|
+
|
|
36
47
|
networks:
|
|
37
48
|
aiiinotate_network:
|
|
38
49
|
driver: bridge
|