aiiinotate 0.9.0 → 0.10.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/cli/import.js +34 -34
- package/cli/index.js +0 -3
- package/cli/migrate.js +3 -3
- package/cli/serve.js +1 -1
- package/cli/utils/fastifyClient.js +17 -5
- package/cli/utils/io.js +16 -11
- package/config/.env.template +15 -1
- package/docs/endpoints.md +2 -0
- package/eslint.config.js +11 -8
- package/import_anno_file.txt +10 -0
- package/import_manifest_file.txt +1 -0
- package/migrations/manageIndex.js +1 -1
- package/migrations/migrationScripts/20250825185706-collections.js +3 -3
- package/migrations/migrationScripts/20250826194832-annotations2-schema.js +2 -2
- package/migrations/migrationScripts/20250904080710-annotations2-indexes.js +3 -3
- package/migrations/migrationScripts/20251002141951-manifests2-schema.js +2 -2
- package/migrations/migrationScripts/20251006212110-manifests2-indexes.js +3 -3
- package/package.json +2 -2
- package/src/app.js +3 -3
- package/src/constants.js +11 -5
- package/src/data/annotations/annotations2.js +54 -37
- package/src/data/annotations/annotations3.js +1 -1
- package/src/data/annotations/routes.js +8 -8
- package/src/data/annotations/routes.test.js +27 -25
- package/src/data/collectionAbstract.js +4 -4
- package/src/data/manifests/manifests2.js +17 -17
- package/src/data/manifests/manifests2.test.js +2 -2
- package/src/data/manifests/routes.js +1 -1
- package/src/data/manifests/routes.test.js +7 -6
- package/src/data/routes.js +7 -7
- package/src/data/routes.test.js +15 -14
- package/src/db/index.js +2 -2
- package/src/fixtures/generate.js +4 -4
- package/src/fixtures/utils.js +1 -1
- package/src/schemas/index.js +88 -8
- package/src/schemas/schemasBase.js +5 -35
- package/src/schemas/schemasPresentation2.js +10 -37
- package/src/schemas/schemasPresentation3.js +3 -22
- package/src/schemas/schemasResolver.js +8 -8
- package/src/schemas/schemasRoutes.js +22 -53
- package/src/server.js +1 -1
- package/src/types.js +1 -0
- package/src/utils/iiif2Utils.js +35 -30
- package/src/utils/iiif2Utils.test.js +5 -4
- package/src/utils/logger.js +6 -6
- package/src/utils/routeUtils.js +5 -5
- package/src/utils/svg.js +13 -13
- package/src/utils/testUtils.js +9 -9
- package/src/utils/utils.js +38 -28
- package/wit1923_man1937.json +1 -0
- package/cli/utils/env.js +0 -17
- package/docs/env.md +0 -25
- /package/docs/{architecture.md → dev_architecture.md} +0 -0
- /package/docs/{db.md → dev_db.md} +0 -0
- /package/docs/{notes_quirks_troubleshooting.md → dev_notes_quirks_and_troubleshooting.md} +0 -0
package/cli/import.js
CHANGED
|
@@ -19,43 +19,32 @@ const parseNumber = (x) => Number(x);
|
|
|
19
19
|
|
|
20
20
|
////////////////////////////////////////
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* @param {FastifyInstanceType} fastifyClient
|
|
24
|
-
* @param {string[]} fileArr - array of full paths to annotationLists to insert.
|
|
25
|
-
*/
|
|
26
|
-
async function importAnnotationPage(fastifyClient, fileArr) {
|
|
27
|
-
notImplementedExit(`${importAnnotationPage.name} is not implemented`)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
/**
|
|
31
23
|
* @param {FastifyClient} fastifyClient
|
|
32
24
|
* @param {string[]} fileArr - array of full paths to annotationLists to insert.
|
|
33
25
|
*/
|
|
34
|
-
async function
|
|
35
|
-
// fileArr = fileArr.slice(0,10)
|
|
26
|
+
async function importData(importFunc, datatype, fileArr) {
|
|
36
27
|
|
|
37
|
-
const pb = new ProgressBar({ desc: "importing annotations", total: fileArr.length});
|
|
38
|
-
const importErrors = [];
|
|
39
28
|
let totalImports = 0
|
|
29
|
+
const
|
|
30
|
+
pb = new ProgressBar({ desc: `importing ${datatype}s`, total: fileArr.length }),
|
|
31
|
+
importErrors = [];
|
|
40
32
|
|
|
41
|
-
for (
|
|
33
|
+
for (let [ i, file ] of fileArr.entries()) {
|
|
34
|
+
i += 1
|
|
42
35
|
const
|
|
43
|
-
|
|
44
|
-
[statusCode, resultPromise] = await
|
|
36
|
+
data = JSON.parse(fileRead(file)),
|
|
37
|
+
[ statusCode, resultPromise ] = await importFunc(data),
|
|
45
38
|
result = await resultPromise;
|
|
46
|
-
if (
|
|
39
|
+
if (statusCode <= 299) {
|
|
47
40
|
totalImports += result.insertedCount;
|
|
48
41
|
} else {
|
|
42
|
+
console.log(result);
|
|
49
43
|
importErrors.push(file);
|
|
50
44
|
}
|
|
51
45
|
pb.update(i)
|
|
52
46
|
}
|
|
53
|
-
|
|
54
|
-
if ( importErrors.length ) {
|
|
55
|
-
logger.info(`There were problems importing annotations from the following ${importErrors.length} annotation lists: ${inspectObj(importErrors, -1)}`)
|
|
56
|
-
}
|
|
57
|
-
logger.info(`Imported ${totalImports} annotations into Aiiinotate !`);
|
|
58
|
-
return
|
|
47
|
+
return [ totalImports, importErrors ]
|
|
59
48
|
}
|
|
60
49
|
|
|
61
50
|
////////////////////////////////////////
|
|
@@ -63,27 +52,33 @@ async function importAnnotationList(fastifyClient, fileArr) {
|
|
|
63
52
|
/**
|
|
64
53
|
* run the cli
|
|
65
54
|
* @param {import('commander').Command} command
|
|
55
|
+
* @param {"manifest"|"annotation"} datatype
|
|
66
56
|
* @param {object} options
|
|
67
57
|
*/
|
|
68
|
-
async function action(command, options) {
|
|
69
|
-
|
|
58
|
+
async function action(command, datatype, options) {
|
|
70
59
|
/** @type {2 | 3} */
|
|
71
60
|
const iiifVersion = options.iiifVersion;
|
|
72
61
|
/** @type {string[]} */
|
|
73
62
|
const inputFile = options.file;
|
|
74
63
|
|
|
64
|
+
if (iiifVersion===3) {
|
|
65
|
+
notImplementedExit("CLI imports for IIIF presentation V3 is not implemented");
|
|
66
|
+
}
|
|
67
|
+
|
|
75
68
|
const fastifyClient = new FastifyClient();
|
|
76
69
|
await fastifyClient.build();
|
|
70
|
+
const importFunc = fastifyClient.importData(iiifVersion, datatype);
|
|
77
71
|
|
|
78
72
|
// run
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
const fileArr = await parseImportInputFile(inputFile);
|
|
74
|
+
const [ totalImports, importErrors ] = await importData(importFunc, datatype, fileArr);
|
|
75
|
+
|
|
76
|
+
if (importErrors.length) {
|
|
77
|
+
logger.info(`There were problems importing ${datatype}s from the following ${importErrors.length} files: ${inspectObj(importErrors, -1)}`)
|
|
84
78
|
}
|
|
85
|
-
|
|
86
|
-
await fastifyClient.stop();
|
|
79
|
+
logger.info(`Imported ${totalImports} ${datatype}s into aiiinotate !`);
|
|
80
|
+
await fastifyClient.stop(); // TODO app keeps idling after that ....
|
|
81
|
+
return
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
/////////////////////////////////////////
|
|
@@ -91,21 +86,26 @@ async function action(command, options) {
|
|
|
91
86
|
/** define the cli */
|
|
92
87
|
function makeImportCommand() {
|
|
93
88
|
|
|
89
|
+
const datatypeArg =
|
|
90
|
+
new Argument("datatype", "type of data to import: manifests or annotations")
|
|
91
|
+
.choices([ "manifest", "annotation" ]);
|
|
92
|
+
|
|
94
93
|
const versionOpt =
|
|
95
94
|
new Option("-i, --iiif-version <version>", "IIIF version")
|
|
96
|
-
.choices(["2", "3"])
|
|
95
|
+
.choices([ "2", "3" ])
|
|
97
96
|
.argParser(parseNumber)
|
|
98
97
|
.makeOptionMandatory();
|
|
99
98
|
|
|
100
99
|
const filesOpt =
|
|
101
|
-
new Option("-f, --file <file>", "file containing paths to AnnotationLists or
|
|
100
|
+
new Option("-f, --file <file>", "file containing paths to AnnotationLists, AnnotationPages or Manifests to process (1 line per path)")
|
|
102
101
|
.makeOptionMandatory();
|
|
103
102
|
|
|
104
103
|
return new Command("import")
|
|
105
104
|
.description("import data into aiiinotate")
|
|
105
|
+
.addArgument(datatypeArg)
|
|
106
106
|
.addOption(filesOpt)
|
|
107
107
|
.addOption(versionOpt)
|
|
108
|
-
.action((options, command) => action(command, options))
|
|
108
|
+
.action((datatype, options, command) => action(command, datatype, options))
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
export default makeImportCommand;
|
package/cli/index.js
CHANGED
|
@@ -16,8 +16,6 @@ import { Command, Option } from "commander";
|
|
|
16
16
|
import makeImportCommand from "#cli/import.js";
|
|
17
17
|
import makeMigrateCommand from "#cli/migrate.js";
|
|
18
18
|
import makeServeCommand from "#cli/serve.js";
|
|
19
|
-
import loadEnv from "#cli/utils/env.js";
|
|
20
|
-
|
|
21
19
|
|
|
22
20
|
function makeCli() {
|
|
23
21
|
|
|
@@ -38,7 +36,6 @@ function makeCli() {
|
|
|
38
36
|
.addCommand(makeMigrateCommand());
|
|
39
37
|
|
|
40
38
|
cli.parse(process.argv);
|
|
41
|
-
return cli;
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
makeCli();
|
package/cli/migrate.js
CHANGED
|
@@ -16,7 +16,7 @@ import { execSync } from "node:child_process"
|
|
|
16
16
|
import { Command, Option, Argument } from "commander";
|
|
17
17
|
|
|
18
18
|
/** @typedef {"make"|"apply"|"revert"|"revert-all"} MigrateOpType */
|
|
19
|
-
const allowedMigrateOp = ["make", "apply", "revert", "revert-all"];
|
|
19
|
+
const allowedMigrateOp = [ "make", "apply", "revert", "revert-all" ];
|
|
20
20
|
|
|
21
21
|
const
|
|
22
22
|
// path to current dirctory
|
|
@@ -26,7 +26,7 @@ const
|
|
|
26
26
|
dirMigrationsScripts = path.resolve(dirMigrations, "migrationScripts"),
|
|
27
27
|
migrationsConfigMain = path.resolve(dirMigrations, "migrate-mongo-config-main.js"),
|
|
28
28
|
migrationsConfigTest = path.resolve(dirMigrations, "migrate-mongo-config-test.js"),
|
|
29
|
-
migrationConfigs = [migrationsConfigMain, migrationsConfigTest];
|
|
29
|
+
migrationConfigs = [ migrationsConfigMain, migrationsConfigTest ];
|
|
30
30
|
|
|
31
31
|
/** return a date in YYYYMMDDhhmmss format */
|
|
32
32
|
function formatDate(date) {
|
|
@@ -47,7 +47,7 @@ function formatDate(date) {
|
|
|
47
47
|
* @param {string} migrationName
|
|
48
48
|
*/
|
|
49
49
|
function migrateMake(migrationName) {
|
|
50
|
-
if (
|
|
50
|
+
if (migrationName == null) {
|
|
51
51
|
throw new Error(`migration name must be a string. got ${migrationName}`);
|
|
52
52
|
}
|
|
53
53
|
fs.copyFileSync(
|
package/cli/serve.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Command, Option, Argument } from "commander";
|
|
|
4
4
|
|
|
5
5
|
/** @typedef {import("#types").ServeModeType} ServeModeType */
|
|
6
6
|
|
|
7
|
-
const serveModeValues = ["dev", "prod"];
|
|
7
|
+
const serveModeValues = [ "dev", "prod" ];
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @param {import('commander').Command} command
|
|
@@ -62,12 +62,24 @@ class FastifyClient {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
66
|
-
* @
|
|
65
|
+
* function to interact with all annotation createMany + manifest create routes.
|
|
66
|
+
* @type {(iiifVersion: 2|3, datatype: "manifest"|"annotation") => (data: object) => [Number, Promise<object>] }
|
|
67
67
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
importData(iiifVersion, datatype) {
|
|
69
|
+
if (![ "2","3" ].includes(`${iiifVersion}`)) {
|
|
70
|
+
throw new Error(`fastifyClient.importData: "iiifVersion" must by 2 or 3, got "${iiifVersion}"`);
|
|
71
|
+
}
|
|
72
|
+
if (![ "manifest","annotation" ].includes(datatype)) {
|
|
73
|
+
throw new Error(`fastifyClient.importData: "datatype" must by "manifest" or "annotation", got "${datatype}"`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const routeSuffix = datatype==="manifest" ? "create" : "createMany";
|
|
77
|
+
const route = `/${datatype}s/${iiifVersion}/${routeSuffix}`;
|
|
78
|
+
|
|
79
|
+
return async (data) => {
|
|
80
|
+
const r = await this.injectPost(route, data);
|
|
81
|
+
return [ r.statusCode, r.json() ];
|
|
82
|
+
}
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
|
package/cli/utils/io.js
CHANGED
|
@@ -11,15 +11,16 @@ const cwd = process.cwd(); // directory the script is run from
|
|
|
11
11
|
*/
|
|
12
12
|
const toAbsPath = (f) => path.isAbsolute(f) ? f : path.join(cwd, f);
|
|
13
13
|
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* @returns {[string, boolean]} - [<absolute file path>, <success?>]
|
|
16
|
+
*/
|
|
15
17
|
const fileOk = (f) => {
|
|
16
18
|
f = toAbsPath(f);
|
|
17
19
|
try {
|
|
18
|
-
fs.accessSync(f, fs.constants.R_OK);
|
|
19
|
-
return
|
|
20
|
+
fs.accessSync(f, fs.constants.R_OK); // will throw if there's a problem opening.
|
|
21
|
+
return [ f, fs.lstatSync(f).isFile() ];
|
|
20
22
|
} catch (e) {
|
|
21
|
-
|
|
22
|
-
return false
|
|
23
|
+
return [ f, false ]
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -43,13 +44,17 @@ const fileRead = (f) => {
|
|
|
43
44
|
* @returns { string[] } array of absolute filepaths
|
|
44
45
|
*/
|
|
45
46
|
function fileArrayValidate (fileArr) {
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// array of [<absolute filepath>, <success opening file?>]
|
|
48
|
+
fileArr = fileArr.map(fileOk);
|
|
49
|
+
const successCount = fileArr.filter(x => x[1]).length;
|
|
50
|
+
if (successCount!==fileArr.length) {
|
|
51
|
+
console.error(
|
|
52
|
+
`io.fileArrayValidate: ${fileArr.length - successCount} files could not be accessed. exiting...`,
|
|
53
|
+
fileArr.filter(x => !x[1]).map(x => x[0])
|
|
54
|
+
);
|
|
50
55
|
process.exit(1);
|
|
51
56
|
}
|
|
52
|
-
return fileArr
|
|
57
|
+
return fileArr.map(x => x[0]);
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
/**
|
|
@@ -64,7 +69,7 @@ async function parseImportInputFile(file) {
|
|
|
64
69
|
fileRead(file)
|
|
65
70
|
.split("\n")
|
|
66
71
|
.filter(l => !l.match(/^\s*$/g));
|
|
67
|
-
return [...new Set(fileArrayValidate(fileArr))];
|
|
72
|
+
return [ ...new Set(fileArrayValidate(fileArr)) ];
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
export {
|
package/config/.env.template
CHANGED
|
@@ -18,10 +18,18 @@ AIIINOTATE_SCHEME=http
|
|
|
18
18
|
AIIINOTATE_LOG_TARGET=stdout
|
|
19
19
|
# directory to save logs to (if AIIINOTATE_LOG_TARGET enables logging to file)
|
|
20
20
|
AIIINOTATE_LOG_DIR=
|
|
21
|
+
# log level. "debug"|"info"|"warning"|"error"
|
|
22
|
+
AIIINOTATE_LOG_LEVEL=debug
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
# max number of items to display per result page
|
|
23
26
|
AIIINOTATE_PAGE_SIZE=5000
|
|
24
|
-
# "true"|"false". strict error throwing when inserting annotations. equivalent
|
|
27
|
+
# "true"|"false". strict error throwing when inserting annotations. equivalent
|
|
28
|
+
# to `throwOnCanvasIndexError=true` (raise an error if an annotation's target
|
|
29
|
+
# manifest can't be found) and `throwOnXywhError=true` (raise an error if you
|
|
30
|
+
# can't extract a bounding box for an annotation's target). use in controlled
|
|
31
|
+
# environments where you know precisely the structure of your annotations and
|
|
32
|
+
# where you know that your manifests are accessible through HTTP.
|
|
25
33
|
AIIINOTATE_STRICT_MODE=false
|
|
26
34
|
|
|
27
35
|
# IGNORE
|
|
@@ -32,3 +40,9 @@ MONGODB_CONNSTRING="mongodb://$MONGODB_HOST:$MONGODB_PORT/$MONGODB_DB"
|
|
|
32
40
|
MONGODB_DB_TEST="${MONGODB_DB}_test"
|
|
33
41
|
# IGNORE
|
|
34
42
|
MONGODB_CONNSTRING_TEST="mongodb://$MONGODB_HOST:$MONGODB_PORT/$MONGODB_DB_TEST"
|
|
43
|
+
|
|
44
|
+
# public URL root following `scheme://host`, used to build to root of the IDs of
|
|
45
|
+
# aiiinotate data. In production, set this to your domain name. in dev, set to http://localhost, or anything you want.
|
|
46
|
+
# it should be accessible through HTTP in production, but not necessarily:
|
|
47
|
+
# the app doesn't access this URL in HTTP queries when running.
|
|
48
|
+
AIIINOTATE_PUBLIC_URL="$AIIINOTATE_BASE_URL"
|
package/docs/endpoints.md
CHANGED
|
@@ -95,6 +95,8 @@ DELETE /{collection_name}/{iiif_version}/delete
|
|
|
95
95
|
- `uri`: the full URI of the annotation to delete
|
|
96
96
|
- `manifestShortId`: a manifest's identifier, to delete all annotations for a manifest
|
|
97
97
|
- `canvasUri`: the full URI to an annotation's target canvas, to delete all annotatons for the canvas
|
|
98
|
+
- `tag`: delete all annotations with a certain tag in a manifest
|
|
99
|
+
- MUST be used in conjunction with `manifestShortId` to indicate on which manifest to delete annotations by tag: `delete?tag=<your-tag>&manifestShortId=<your-manifest-id>`
|
|
98
100
|
|
|
99
101
|
#### Response
|
|
100
102
|
|
package/eslint.config.js
CHANGED
|
@@ -8,20 +8,23 @@ import stylistic from "@stylistic/eslint-plugin"
|
|
|
8
8
|
/** https://eslint.org/docs/latest/use/configure/rules */
|
|
9
9
|
export default defineConfig([
|
|
10
10
|
{
|
|
11
|
-
files: ["**/*.{js,mjs,cjs}"],
|
|
11
|
+
files: [ "**/*.{js,mjs,cjs}" ],
|
|
12
12
|
plugins: {
|
|
13
13
|
"js": js,
|
|
14
14
|
"@stylistic": stylistic
|
|
15
15
|
},
|
|
16
|
-
extends: ["js/recommended"],
|
|
17
|
-
languageOptions: { globals: {...globals.browser, ...globals.node} },
|
|
16
|
+
extends: [ "js/recommended" ],
|
|
17
|
+
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
|
18
18
|
rules: {
|
|
19
19
|
"no-unused-vars": "off",
|
|
20
|
-
"quotes": ["error", "double"],
|
|
21
|
-
"@stylistic/indent": ["error", 2],
|
|
20
|
+
"quotes": [ "error", "double" ],
|
|
21
|
+
"@stylistic/indent": [ "error", 2 ],
|
|
22
|
+
"space-in-parens": [ "error", "never" ],
|
|
23
|
+
"array-bracket-spacing": [ "error", "always", { objectsInArrays: false, arraysInArrays: false }],
|
|
24
|
+
"@stylistic/object-curly-spacing": [ "error", "always", { objectsInObjects: true, arraysInObjects: true }]
|
|
22
25
|
},
|
|
23
26
|
},
|
|
24
|
-
{ files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] },
|
|
25
|
-
{ files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] },
|
|
26
|
-
globalIgnores(["package-lock.json"]),
|
|
27
|
+
{ files: [ "**/*.json" ], plugins: { json }, language: "json/json", extends: [ "json/recommended" ] },
|
|
28
|
+
{ files: [ "**/*.css" ], plugins: { css }, language: "css/css", extends: [ "css/recommended" ] },
|
|
29
|
+
globalIgnores([ "package-lock.json" ]),
|
|
27
30
|
]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1923_man1937_anno1865.json
|
|
2
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit827_man827_anno827.json
|
|
3
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1959_man1974_anno2260.json
|
|
4
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1158_man1158_anno1158.json
|
|
5
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1992_man2007_anno1933.json
|
|
6
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1213_man1213_anno1213.json
|
|
7
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit547_pdf547_anno547.json
|
|
8
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1578_man1594_anno1555.json
|
|
9
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit321_pdf321_anno321.json
|
|
10
|
+
../sas-exporter/out_vhs/annotations_migrate_structure/wit1339_man1352_anno1343.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
./wit1923_man1937.json
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @param {CreateIndexesOptionsType} indexOptions - indexOptions.name MUST be defined !
|
|
19
19
|
*/
|
|
20
20
|
const validateIndexOptions = (indexOptions) => {
|
|
21
|
-
if (
|
|
21
|
+
if (indexOptions.name == null) {
|
|
22
22
|
throw new Error("indexOptions.name must be defined !")
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -23,11 +23,11 @@ const collectionNames = [
|
|
|
23
23
|
export const up = async (db, client) => {
|
|
24
24
|
// check if a collection exists before recreating it, otherwise you get NameSpaceExists errors.
|
|
25
25
|
const existingCollectionNames =
|
|
26
|
-
(
|
|
26
|
+
(await db.listCollections().toArray()).map(coll => coll.name);
|
|
27
27
|
|
|
28
28
|
// create a mongo collection: https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script
|
|
29
|
-
for (const colName of collectionNames
|
|
30
|
-
if (
|
|
29
|
+
for (const colName of collectionNames) {
|
|
30
|
+
if (!existingCollectionNames.includes(colName)) {
|
|
31
31
|
db.createCollection(colName);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -21,7 +21,7 @@ export const up = async (db, client) => {
|
|
|
21
21
|
},
|
|
22
22
|
r = await db.command(commandDoc);
|
|
23
23
|
|
|
24
|
-
if (
|
|
24
|
+
if (r.ok !== 1) {
|
|
25
25
|
throw new Error(`command failed with error: ${r}`);
|
|
26
26
|
}
|
|
27
27
|
};
|
|
@@ -36,7 +36,7 @@ export const down = async (db, client) => {
|
|
|
36
36
|
collMod: "annotations2",
|
|
37
37
|
validator: {}
|
|
38
38
|
});
|
|
39
|
-
if (
|
|
39
|
+
if (r.ok !== 1) {
|
|
40
40
|
throw new Error(`command failed with error: ${r}`);
|
|
41
41
|
}
|
|
42
42
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* create and manage indexes for the collection `annotations2`
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {createIndex, removeIndex} from "../manageIndex.js";
|
|
5
|
+
import { createIndex, removeIndex } from "../manageIndex.js";
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
// all filters on arrays are MultiKey index => it NOT a Sort index, but an Equality index (useful for filters)
|
|
@@ -50,7 +50,7 @@ const indexes = [
|
|
|
50
50
|
* @returns {Promise<void>}
|
|
51
51
|
*/
|
|
52
52
|
export const up = async (db, client) => {
|
|
53
|
-
for (
|
|
53
|
+
for (const { colName, indexSpec, indexOptions } of indexes) {
|
|
54
54
|
const result = await createIndex(db, colName, indexSpec, indexOptions);
|
|
55
55
|
console.log("created index:", result);
|
|
56
56
|
}
|
|
@@ -62,7 +62,7 @@ export const up = async (db, client) => {
|
|
|
62
62
|
* @returns {Promise<void>}
|
|
63
63
|
*/
|
|
64
64
|
export const down = async (db, client) => {
|
|
65
|
-
for (
|
|
65
|
+
for (const { colName, indexSpec, indexOptions } of indexes) {
|
|
66
66
|
const result = await removeIndex(db, colName, indexOptions);
|
|
67
67
|
console.log("dropped index:", result);
|
|
68
68
|
}
|
|
@@ -21,7 +21,7 @@ export const up = async (db, client) => {
|
|
|
21
21
|
r = await db.command(commandDoc);
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
if (
|
|
24
|
+
if (r.ok !== 1) {
|
|
25
25
|
throw new Error(`command failed with error: ${r}`);
|
|
26
26
|
}
|
|
27
27
|
};
|
|
@@ -36,7 +36,7 @@ export const down = async (db, client) => {
|
|
|
36
36
|
collMod: "manifests2",
|
|
37
37
|
validator: {}
|
|
38
38
|
});
|
|
39
|
-
if (
|
|
39
|
+
if (r.ok !== 1) {
|
|
40
40
|
throw new Error(`command failed with error: ${r}`);
|
|
41
41
|
}
|
|
42
42
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** create indexes for manifests2 collection */
|
|
2
2
|
|
|
3
|
-
import {createIndex, removeIndex} from "../manageIndex.js";
|
|
3
|
+
import { createIndex, removeIndex } from "../manageIndex.js";
|
|
4
4
|
|
|
5
5
|
const indexes = [
|
|
6
6
|
{
|
|
@@ -16,7 +16,7 @@ const indexes = [
|
|
|
16
16
|
* @returns {Promise<void>}
|
|
17
17
|
*/
|
|
18
18
|
export const up = async (db, client) => {
|
|
19
|
-
for (
|
|
19
|
+
for (const { colName, indexSpec, indexOptions } of indexes) {
|
|
20
20
|
const result = await createIndex(db, colName, indexSpec, indexOptions);
|
|
21
21
|
console.log("created index:", result);
|
|
22
22
|
}
|
|
@@ -28,7 +28,7 @@ export const up = async (db, client) => {
|
|
|
28
28
|
* @returns {Promise<void>}
|
|
29
29
|
*/
|
|
30
30
|
export const down = async (db, client) => {
|
|
31
|
-
for (
|
|
31
|
+
for (const { colName, indexSpec, indexOptions } of indexes) {
|
|
32
32
|
const result = await removeIndex(db, colName, indexOptions);
|
|
33
33
|
console.log("dropped index:", result);
|
|
34
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiiinotate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "a fast IIIF-compliant annotation server",
|
|
5
5
|
"main": "./cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"#annotations/*.js": "./src/data/annotations/*.js"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@dotenvx/dotenvx": "^1.
|
|
59
|
+
"@dotenvx/dotenvx": "^1.61.0",
|
|
60
60
|
"@fastify/ajv-compiler": "^4.0.2",
|
|
61
61
|
"@fastify/cors": "^11.1.0",
|
|
62
62
|
"@fastify/mongodb": "^9.0.2",
|
package/src/app.js
CHANGED
|
@@ -79,8 +79,8 @@ const defaultConfig = {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const setConfig = (mode) => {
|
|
82
|
-
const allowedModes = ["test", "default"];
|
|
83
|
-
if (
|
|
82
|
+
const allowedModes = [ "test", "default" ];
|
|
83
|
+
if (! allowedModes.includes(mode)) {
|
|
84
84
|
throw new Error(`app.build: 'mode' param expected one of ${allowedModes}, got ${mode}`)
|
|
85
85
|
}
|
|
86
86
|
return mode==="test" ? testConfig : defaultConfig;
|
|
@@ -100,7 +100,7 @@ async function build(mode="default") {
|
|
|
100
100
|
// NOTE: we allow all origins => restrict ?
|
|
101
101
|
fastify.register(cors, {
|
|
102
102
|
origin: "*",
|
|
103
|
-
methods: ["GET", "HEAD", "POST", "DELETE"]
|
|
103
|
+
methods: [ "GET", "HEAD", "POST", "DELETE" ]
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
await fastify.register(db, mongoConfig);
|
package/src/constants.js
CHANGED
|
@@ -6,11 +6,13 @@ const PAGE_SIZE = parseInt(process.env.AIIINOTATE_PAGE_SIZE);
|
|
|
6
6
|
// one of "file"|"stdout"|"stdout+file"|"off"
|
|
7
7
|
const LOG_TARGET = process.env.AIIINOTATE_LOG_TARGET;
|
|
8
8
|
const LOG_DIR = process.env.AIIINOTATE_LOG_DIR;
|
|
9
|
+
const LOG_LEVEL = process.env.AIIINOTATE_LOG_LEVEL;
|
|
9
10
|
|
|
10
11
|
const PORT = process.env.AIIINOTATE_PORT;
|
|
11
12
|
const HOST = process.env.AIIINOTATE_HOST;
|
|
12
13
|
const SCHEME = process.env.AIIINOTATE_SCHEME;
|
|
13
14
|
const BASE_URL = process.env.AIIINOTATE_BASE_URL;
|
|
15
|
+
const PUBLIC_URL = process.env.AIIINOTATE_PUBLIC_URL
|
|
14
16
|
|
|
15
17
|
const MONGODB_DB = process.env.MONGODB_DB;
|
|
16
18
|
const MONGODB_DB_TEST = process.env.MONGODB_DB_TEST;
|
|
@@ -20,36 +22,39 @@ const MONGODB_CONNSTRING_TEST = process.env.MONGODB_CONNSTRING_TEST;
|
|
|
20
22
|
// ensure that all env variables are defined.
|
|
21
23
|
const env_mapper = {
|
|
22
24
|
LOG_TARGET : LOG_TARGET,
|
|
25
|
+
LOG_LEVEL: LOG_LEVEL,
|
|
26
|
+
LOG_DIR : LOG_DIR,
|
|
23
27
|
STRICT_MODE : STRICT_MODE,
|
|
24
28
|
PAGE_SIZE : PAGE_SIZE,
|
|
25
|
-
LOG_DIR : LOG_DIR,
|
|
26
29
|
PORT : PORT,
|
|
27
30
|
HOST : HOST,
|
|
28
31
|
SCHEME : SCHEME,
|
|
29
32
|
BASE_URL : BASE_URL,
|
|
33
|
+
PUBLIC_URL : PUBLIC_URL,
|
|
30
34
|
MONGODB_DB : MONGODB_DB,
|
|
31
35
|
MONGODB_DB_TEST : MONGODB_DB_TEST,
|
|
32
36
|
MONGODB_CONNSTRING : MONGODB_CONNSTRING,
|
|
33
37
|
MONGODB_CONNSTRING_TEST : MONGODB_CONNSTRING_TEST,
|
|
34
38
|
}
|
|
35
|
-
if (
|
|
39
|
+
if (Object.values(env_mapper).some(e => e==null)) {
|
|
36
40
|
console.error("SETUP ERROR: Some environment variables are undefined ! Exiting...");
|
|
37
41
|
console.log(inspect(env_mapper));
|
|
38
42
|
process.exit(1);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
// enforce value constraints on variables
|
|
42
|
-
const allowedLogTargets = ["file", "stdout", "stdout+file", "off"];
|
|
43
|
-
if (
|
|
46
|
+
const allowedLogTargets = [ "file", "stdout", "stdout+file", "off" ];
|
|
47
|
+
if (!allowedLogTargets.includes(LOG_TARGET)) {
|
|
44
48
|
console.error(`SETUP ERROR: env variable AIIINOTATE_LOG_TARGET must be set to one of ${allowedLogTargets.map(x => "\"" + x + "\"").join(", ")}. Got "${LOG_TARGET}". Exiting...`)
|
|
45
49
|
process.exit(1);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export {
|
|
49
53
|
LOG_TARGET,
|
|
54
|
+
LOG_DIR,
|
|
55
|
+
LOG_LEVEL,
|
|
50
56
|
STRICT_MODE,
|
|
51
57
|
PAGE_SIZE,
|
|
52
|
-
LOG_DIR,
|
|
53
58
|
PORT,
|
|
54
59
|
HOST,
|
|
55
60
|
SCHEME,
|
|
@@ -58,4 +63,5 @@ export {
|
|
|
58
63
|
MONGODB_DB_TEST,
|
|
59
64
|
MONGODB_CONNSTRING,
|
|
60
65
|
MONGODB_CONNSTRING_TEST,
|
|
66
|
+
PUBLIC_URL
|
|
61
67
|
}
|