aiiinotate 0.9.1 → 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.
Files changed (55) hide show
  1. package/cli/import.js +34 -34
  2. package/cli/index.js +0 -3
  3. package/cli/migrate.js +3 -3
  4. package/cli/serve.js +1 -1
  5. package/cli/utils/fastifyClient.js +17 -5
  6. package/cli/utils/io.js +16 -11
  7. package/config/.env.template +15 -1
  8. package/docs/endpoints.md +2 -0
  9. package/eslint.config.js +11 -8
  10. package/import_anno_file.txt +10 -0
  11. package/import_manifest_file.txt +1 -0
  12. package/migrations/manageIndex.js +1 -1
  13. package/migrations/migrationScripts/20250825185706-collections.js +3 -3
  14. package/migrations/migrationScripts/20250826194832-annotations2-schema.js +2 -2
  15. package/migrations/migrationScripts/20250904080710-annotations2-indexes.js +3 -3
  16. package/migrations/migrationScripts/20251002141951-manifests2-schema.js +2 -2
  17. package/migrations/migrationScripts/20251006212110-manifests2-indexes.js +3 -3
  18. package/package.json +2 -2
  19. package/src/app.js +3 -3
  20. package/src/constants.js +11 -5
  21. package/src/data/annotations/annotations2.js +41 -35
  22. package/src/data/annotations/annotations3.js +1 -1
  23. package/src/data/annotations/routes.js +7 -7
  24. package/src/data/annotations/routes.test.js +27 -25
  25. package/src/data/collectionAbstract.js +4 -4
  26. package/src/data/manifests/manifests2.js +17 -17
  27. package/src/data/manifests/manifests2.test.js +2 -2
  28. package/src/data/manifests/routes.js +1 -1
  29. package/src/data/manifests/routes.test.js +7 -6
  30. package/src/data/routes.js +7 -7
  31. package/src/data/routes.test.js +15 -14
  32. package/src/db/index.js +2 -2
  33. package/src/fixtures/generate.js +4 -4
  34. package/src/fixtures/utils.js +1 -1
  35. package/src/schemas/index.js +88 -8
  36. package/src/schemas/schemasBase.js +5 -35
  37. package/src/schemas/schemasPresentation2.js +10 -37
  38. package/src/schemas/schemasPresentation3.js +3 -22
  39. package/src/schemas/schemasResolver.js +8 -8
  40. package/src/schemas/schemasRoutes.js +22 -53
  41. package/src/server.js +1 -1
  42. package/src/types.js +1 -0
  43. package/src/utils/iiif2Utils.js +35 -30
  44. package/src/utils/iiif2Utils.test.js +5 -4
  45. package/src/utils/logger.js +6 -6
  46. package/src/utils/routeUtils.js +5 -5
  47. package/src/utils/svg.js +13 -13
  48. package/src/utils/testUtils.js +9 -9
  49. package/src/utils/utils.js +38 -28
  50. package/wit1923_man1937.json +1 -0
  51. package/cli/utils/env.js +0 -17
  52. package/docs/env.md +0 -25
  53. /package/docs/{architecture.md → dev_architecture.md} +0 -0
  54. /package/docs/{db.md → dev_db.md} +0 -0
  55. /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 importAnnotationList(fastifyClient, fileArr) {
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 ( const [i, file] of fileArr.entries() ) {
33
+ for (let [ i, file ] of fileArr.entries()) {
34
+ i += 1
42
35
  const
43
- annotationList = JSON.parse(fileRead(file)),
44
- [statusCode, resultPromise] = await fastifyClient.importAnnotationList(annotationList),
36
+ data = JSON.parse(fileRead(file)),
37
+ [ statusCode, resultPromise ] = await importFunc(data),
45
38
  result = await resultPromise;
46
- if ( statusCode <= 299 ) {
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 filesToProcess = await parseImportInputFile(inputFile);
80
- if ( iiifVersion===2 ) {
81
- await importAnnotationList(fastifyClient, filesToProcess);
82
- } else {
83
- await importAnnotationPage(fastifyClient, filesToProcess);
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
- // exit
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 AnnotationPages to process (1 line per path)")
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 ( migrationName == null ) {
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
- * @param {object} annotationList - IIIF 2 annotationList
66
- * @returns {[number, Promise<InsertResponseType>]} - [ statusCode, response ]
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
- async importAnnotationList(annotationList) {
69
- const r = await this.injectPost("/annotations/2/createMany", annotationList);
70
- return [ r.statusCode, r.json() ];
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
- /** @returns {boolean} true if file `f` exists, false otherwise */
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 true;
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
- console.error(`io.fileOk: file does not exist or could not be read: ${f}`);
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
- // 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...");
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 {
@@ -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 to `throwOnCanvasIndexError=true` (raise an error if an annotation's target manifest can't be found) and `throwOnXywhError=true` (raise an error if you can't extract a bounding box for an annotation's target). use in controlled environments where you know precisely the structure of your annotations and where you know that your manifests are accessible through HTTP.
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 ( indexOptions.name == null ) {
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
- ( await db.listCollections().toArray() ).map(coll => coll.name);
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 ( !existingCollectionNames.includes(colName) ) {
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 ( r.ok !== 1 ) {
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 ( r.ok !== 1 ) {
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 ( const { colName, indexSpec, indexOptions } of indexes ) {
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 ( const { colName, indexSpec, indexOptions } of indexes ) {
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 ( r.ok !== 1 ) {
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 ( r.ok !== 1 ) {
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 ( const { colName, indexSpec, indexOptions } of indexes ) {
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 ( const { colName, indexSpec, indexOptions } of indexes ) {
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.9.1",
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.49.0",
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 ( ! allowedModes.includes(mode) ) {
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 ( Object.values(env_mapper).some(e => e==null) ) {
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 ( !allowedLogTargets.includes(LOG_TARGET) ) {
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
  }