@paulhectork/aiiinotate 0.12.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 (104) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +206 -0
  3. package/cli/export.js +118 -0
  4. package/cli/import.js +111 -0
  5. package/cli/index.js +45 -0
  6. package/cli/migrate.js +131 -0
  7. package/cli/serve.js +29 -0
  8. package/cli/utils/fastifyClient.js +86 -0
  9. package/cli/utils/io.js +225 -0
  10. package/cli/utils/mongoClient.js +21 -0
  11. package/cli/utils/progressbar.js +86 -0
  12. package/cli/xywhToInt.js +99 -0
  13. package/config/.env.template +48 -0
  14. package/docker/Dockerfile +39 -0
  15. package/docker/README.md +5 -0
  16. package/docker/docker-compose.yaml +49 -0
  17. package/docker/docker.sh +43 -0
  18. package/docker/docker_aiiinotate_import.sh +62 -0
  19. package/docs/api.md +381 -0
  20. package/docs/cli.md +132 -0
  21. package/docs/dev_documentation/dev_architecture.md +88 -0
  22. package/docs/dev_documentation/dev_db.md +58 -0
  23. package/docs/dev_documentation/dev_iiif_compatibility.md +43 -0
  24. package/docs/dev_documentation/dev_notes_quirks_and_troubleshooting.md +143 -0
  25. package/docs/docker.md +98 -0
  26. package/docs/includes/report_benchmark_aiiinotate_2026-05-28-02:50:48_7steps.png +0 -0
  27. package/docs/scalability.md +34 -0
  28. package/docs/specifications/0_w3c_open_annotations.md +332 -0
  29. package/docs/specifications/1_w3c_web_annotations.md +577 -0
  30. package/docs/specifications/2_iiif_apis.md +428 -0
  31. package/docs/specifications/3_iiif_annotations.md +103 -0
  32. package/docs/specifications/4_search_api.md +135 -0
  33. package/docs/specifications/5_sas.md +119 -0
  34. package/docs/specifications/6_mirador.md +119 -0
  35. package/docs/specifications/7_aikon.md +137 -0
  36. package/docs/specifications/include/presentation_2.0.webp +0 -0
  37. package/docs/specifications/include/presentation_2.0_white.png +0 -0
  38. package/docs/specifications/include/presentation_3.0.png +0 -0
  39. package/docs/specifications/include/presentation_3.0_resize.png +0 -0
  40. package/eslint.config.js +30 -0
  41. package/migrations/baseConfig.js +57 -0
  42. package/migrations/manageIndex.js +55 -0
  43. package/migrations/migrate-mongo-config-main.js +8 -0
  44. package/migrations/migrate-mongo-config-test.js +8 -0
  45. package/migrations/migrationScripts/20250825185706-collections.js +48 -0
  46. package/migrations/migrationScripts/20250826194832-annotations2-schema.js +42 -0
  47. package/migrations/migrationScripts/20250904080710-annotations2-indexes.js +69 -0
  48. package/migrations/migrationScripts/20251002141951-manifests2-schema.js +43 -0
  49. package/migrations/migrationScripts/20251006212110-manifests2-indexes.js +35 -0
  50. package/migrations/migrationTemplate.js +25 -0
  51. package/package.json +82 -0
  52. package/scripts/get_version.py +12 -0
  53. package/scripts/run.sh +36 -0
  54. package/scripts/setup_mongodb.sh +70 -0
  55. package/scripts/setup_node.sh +15 -0
  56. package/scripts/update_version.py +30 -0
  57. package/scripts/utils.sh +65 -0
  58. package/src/app.js +116 -0
  59. package/src/constants.js +73 -0
  60. package/src/data/annotations/annotations2.js +681 -0
  61. package/src/data/annotations/annotations3.js +28 -0
  62. package/src/data/annotations/routes.js +335 -0
  63. package/src/data/annotations/routes.test.js +271 -0
  64. package/src/data/collectionAbstract.js +283 -0
  65. package/src/data/index.js +29 -0
  66. package/src/data/manifests/manifests2.js +378 -0
  67. package/src/data/manifests/manifests2.test.js +53 -0
  68. package/src/data/manifests/manifests3.js +23 -0
  69. package/src/data/manifests/routes.js +122 -0
  70. package/src/data/manifests/routes.test.js +70 -0
  71. package/src/data/routes.js +181 -0
  72. package/src/data/routes.test.js +166 -0
  73. package/src/db/index.js +50 -0
  74. package/src/fixtures/annotations.js +41 -0
  75. package/src/fixtures/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
  76. package/src/fixtures/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
  77. package/src/fixtures/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
  78. package/src/fixtures/data/annotations2Invalid.jsonld +39 -0
  79. package/src/fixtures/data/annotations2SvgValid.jsonld +81 -0
  80. package/src/fixtures/data/annotations2Valid.jsonld +39 -0
  81. package/src/fixtures/data/bnf_invalid_manifest.json +2806 -0
  82. package/src/fixtures/data/bnf_valid_manifest.json +2817 -0
  83. package/src/fixtures/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
  84. package/src/fixtures/generate.js +181 -0
  85. package/src/fixtures/index.js +69 -0
  86. package/src/fixtures/manifests.js +14 -0
  87. package/src/fixtures/utils.js +37 -0
  88. package/src/schemas/index.js +100 -0
  89. package/src/schemas/schemasBase.js +19 -0
  90. package/src/schemas/schemasPresentation2.js +410 -0
  91. package/src/schemas/schemasPresentation3.js +33 -0
  92. package/src/schemas/schemasResolver.js +71 -0
  93. package/src/schemas/schemasRoutes.js +318 -0
  94. package/src/server.js +25 -0
  95. package/src/types.js +97 -0
  96. package/src/utils/iiif2Utils.js +332 -0
  97. package/src/utils/iiif2Utils.test.js +146 -0
  98. package/src/utils/iiif3Utils.js +0 -0
  99. package/src/utils/iiifUtils.js +18 -0
  100. package/src/utils/logger.js +119 -0
  101. package/src/utils/routeUtils.js +137 -0
  102. package/src/utils/svg.js +417 -0
  103. package/src/utils/testUtils.js +289 -0
  104. package/src/utils/utils.js +403 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * create and manage indexes for the collection `annotations2`
3
+ */
4
+
5
+ import { createIndex, removeIndex } from "../manageIndex.js";
6
+
7
+
8
+ // all filters on arrays are MultiKey index => it NOT a Sort index, but an Equality index (useful for filters)
9
+ const indexes = [
10
+ {
11
+ colName: "annotations2",
12
+ indexSpec: { "@id": 1 },
13
+ indexOptions: { name: "annotationIdIndex" }
14
+ },
15
+ {
16
+ colName: "annotations2",
17
+ indexSpec: { "on.full": 1 },
18
+ indexOptions: { name: "canvasIdIndex" }
19
+ },
20
+ {
21
+ colName: "annotations2",
22
+ indexSpec: { "on.manifestUri": 1 },
23
+ indexOptions: { name: "manifestIdIndex" }
24
+ },
25
+ {
26
+ colName: "annotations2",
27
+ indexSpec: { "on.manifestShortId": 1 },
28
+ indexOptions: { name: "manifestShortIdIndex" }
29
+ },
30
+ {
31
+ colName: "annotations2",
32
+ indexSpec: { "on.canvasIdx": 1 },
33
+ indexOptions: { name: "canvasIdxIndex" }
34
+ },
35
+ {
36
+ colName: "annotations2",
37
+ indexSpec: { "on.resource.@id": 1 },
38
+ indexOptions: { name: "resourceIdIndex" }
39
+ },
40
+ {
41
+ colName: "annotations2",
42
+ indexSpec: { "on.resource.chars": 1 },
43
+ indexOptions: { name: "resourceCharsIndex" }
44
+ }
45
+ ]
46
+
47
+ /**
48
+ * @param {import('mongodb').Db} db
49
+ * @param {import('mongodb').MongoClient} client
50
+ * @returns {Promise<void>}
51
+ */
52
+ export const up = async (db, client) => {
53
+ for (const { colName, indexSpec, indexOptions } of indexes) {
54
+ const result = await createIndex(db, colName, indexSpec, indexOptions);
55
+ console.log("created index:", result);
56
+ }
57
+ };
58
+
59
+ /**
60
+ * @param {import('mongodb').Db} db
61
+ * @param {import('mongodb').MongoClient} client
62
+ * @returns {Promise<void>}
63
+ */
64
+ export const down = async (db, client) => {
65
+ for (const { colName, indexSpec, indexOptions } of indexes) {
66
+ const result = await removeIndex(db, colName, indexOptions);
67
+ console.log("dropped index:", result);
68
+ }
69
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * define the validation schema for collection `manifests2`.
3
+ */
4
+
5
+ import build from "#src/app.js"
6
+
7
+ /**
8
+ * @param db {import('mongodb').Db}
9
+ * @param client {import('mongodb').MongoClient}
10
+ * @returns {Promise<void>}
11
+ */
12
+ export const up = async (db, client) => {
13
+ const
14
+ fastify = await build(),
15
+ fastifySchema = fastify.schemasPresentation2.getSchema("manifestMongo"),
16
+ schema = fastify.schemasResolver(fastifySchema),
17
+ commandDoc = {
18
+ collMod: "manifests2",
19
+ validator: { $jsonSchema: schema },
20
+ },
21
+ r = await db.command(commandDoc);
22
+
23
+
24
+ if (r.ok !== 1) {
25
+ throw new Error(`command failed with error: ${r}`);
26
+ }
27
+ };
28
+
29
+ /**
30
+ * @param db {import('mongodb').Db}
31
+ * @param client {import('mongodb').MongoClient}
32
+ * @returns {Promise<void>}
33
+ */
34
+ export const down = async (db, client) => {
35
+ const r = await db.command({
36
+ collMod: "manifests2",
37
+ validator: {}
38
+ });
39
+ if (r.ok !== 1) {
40
+ throw new Error(`command failed with error: ${r}`);
41
+ }
42
+ };
43
+
@@ -0,0 +1,35 @@
1
+ /** create indexes for manifests2 collection */
2
+
3
+ import { createIndex, removeIndex } from "../manageIndex.js";
4
+
5
+ const indexes = [
6
+ {
7
+ colName: "manifests2",
8
+ indexSpec: { "@id": 1 },
9
+ indexOptions: { name: "manifestIdIndex", unique: true }
10
+ },
11
+ ]
12
+
13
+ /**
14
+ * @param {import('mongodb').Db} db
15
+ * @param {import('mongodb').MongoClient} client
16
+ * @returns {Promise<void>}
17
+ */
18
+ export const up = async (db, client) => {
19
+ for (const { colName, indexSpec, indexOptions } of indexes) {
20
+ const result = await createIndex(db, colName, indexSpec, indexOptions);
21
+ console.log("created index:", result);
22
+ }
23
+ };
24
+
25
+ /**
26
+ * @param {import('mongodb').Db} db
27
+ * @param {import('mongodb').MongoClient} client
28
+ * @returns {Promise<void>}
29
+ */
30
+ export const down = async (db, client) => {
31
+ for (const { colName, indexSpec, indexOptions } of indexes) {
32
+ const result = await removeIndex(db, colName, indexOptions);
33
+ console.log("dropped index:", result);
34
+ }
35
+ };
@@ -0,0 +1,25 @@
1
+ // template migration file generated by migrate-mongo
2
+
3
+ /**
4
+ * @param {import('mongodb').Db} db
5
+ * @param {import('mongodb').MongoClient} client
6
+ * @returns {Promise<void>}
7
+ */
8
+ export const up = async (db, client) => {
9
+ // TODO write your migration here.
10
+ // See https://github.com/seppevs/migrate-mongo/#creating-a-new-migration-script
11
+ // Example:
12
+ // await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: true}});
13
+ };
14
+
15
+ /**
16
+ * @param {import('mongodb').Db} db
17
+ * @param {import('mongodb').MongoClient} client
18
+ * @returns {Promise<void>}
19
+ */
20
+ export const down = async (db, client) => {
21
+ // TODO write the statements to rollback your migration (if possible)
22
+ // Example:
23
+ // await db.collection('albums').updateOne({artist: 'The Beatles'}, {$set: {blacklisted: false}});
24
+ };
25
+
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@paulhectork/aiiinotate",
3
+ "version": "0.12.0",
4
+ "description": "a fast IIIF-compliant annotation server",
5
+ "main": "./cli/index.js",
6
+ "type": "module",
7
+ "directories": {
8
+ "doc": "docs"
9
+ },
10
+ "bin": {
11
+ "aiiinotate": "./cli/index.js"
12
+ },
13
+ "scripts": {
14
+ "setup": "sudo systemctl start mongod && npm run cli migrate apply",
15
+ "cli": "bash ./scripts/run.sh cli ./config/.env",
16
+ "dev": "bash ./scripts/run.sh dev ./config/.env",
17
+ "test": "bash ./scripts/run.sh test ./config/.env",
18
+ "lint": "npx eslint --fix",
19
+ "migrate": "npm run cli -- migrate",
20
+ "update_version": "python3 scripts/update_version.py",
21
+ "get_version": "python3 scripts/get_version.py"
22
+ },
23
+ "pre-commit": [
24
+ "lint"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/Aikon-platform/aiiinotate.git"
29
+ },
30
+ "keywords": [
31
+ "annotation-server",
32
+ "iiif",
33
+ "annotation",
34
+ "international-image-interoperability-framework"
35
+ ],
36
+ "author": "Aikon",
37
+ "license": "MIT",
38
+ "bugs": {
39
+ "url": "https://github.com/Aikon-platform/aiiinotate/issues"
40
+ },
41
+ "homepage": "https://github.com/Aikon-platform/aiiinotate#readme",
42
+ "imports": {
43
+ "#types": "./src/types.js",
44
+ "#constants": "./src/constants.js",
45
+ "#cli/*.js": "./cli/*.js",
46
+ "#src/*.js": "./src/*.js",
47
+ "#migrations/*.js": "./migrations/*.js",
48
+ "#db/*.js": "./src/db/*.js",
49
+ "#schemas/*.js": "./src/schemas/*.js",
50
+ "#config/*.js": "./config/*.js",
51
+ "#data/*.js": "./src/data/*.js",
52
+ "#utils/*.js": "./src/utils/*.js",
53
+ "#fixtures/*.js": "./src/fixtures/*.js",
54
+ "#fixtures/*.json": "./src/fixtures/*.json",
55
+ "#manifests/*.js": "./src/data/manifests/*.js",
56
+ "#annotations/*.js": "./src/data/annotations/*.js"
57
+ },
58
+ "dependencies": {
59
+ "@dotenvx/dotenvx": "^1.61.0",
60
+ "@fastify/ajv-compiler": "^4.0.2",
61
+ "@fastify/cors": "^11.1.0",
62
+ "@fastify/mongodb": "^9.0.2",
63
+ "commander": "^14.0.0",
64
+ "fastify": "^5.5.0",
65
+ "migrate-mongo": "^12.1.3",
66
+ "mongodb": "^6.18.0",
67
+ "pino": "^10.3.1",
68
+ "pino-pretty": "^13.1.3",
69
+ "svg-path-bbox": "^2.1.0",
70
+ "uuid": "^11.1.0"
71
+ },
72
+ "devDependencies": {
73
+ "@eslint/css": "^0.10.0",
74
+ "@eslint/js": "^9.33.0",
75
+ "@eslint/json": "^0.13.1",
76
+ "@fastify/pre-commit": "^2.2.1",
77
+ "@stylistic/eslint-plugin": "^5.2.3",
78
+ "eslint": "^9.33.0",
79
+ "globals": "^16.3.0",
80
+ "nodemon": "^3.1.11"
81
+ }
82
+ }
@@ -0,0 +1,12 @@
1
+ import re
2
+ import sys
3
+ import json
4
+ import shutil
5
+ import pathlib
6
+
7
+ curdir = pathlib.Path(__file__).parent.resolve()
8
+ pkg_file = curdir.parent.joinpath("package.json").resolve()
9
+
10
+ with open(pkg_file, mode="r") as fh:
11
+ data = json.load(fh)
12
+ print(f"\naiiinotate current version: {data['version']}\n")
package/scripts/run.sh ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+
3
+ SCRIPT="$1";
4
+ ENV_PATH="$2";
5
+ shift 2;
6
+ EXTRA_ARGS=("$@");
7
+
8
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd );
9
+ ROOT_DIR=$(realpath "$SCRIPT_DIR"/..);
10
+ DOTENVX_BIN="$ROOT_DIR/node_modules/.bin/dotenvx";
11
+
12
+ # verify ENV_PATH and convert it to an absolute path if needed
13
+ if [[ ! -f "$ENV_PATH" ]];
14
+ then echo "Env path '$ENV_PATH' not found ! Exiting..." && exit 1;
15
+ fi;
16
+ if [[ ! "$ENV_PATH" = /* ]];
17
+ then ENV_PATH="$PWD"/"$ENV_PATH";
18
+ fi;
19
+
20
+ cd "$ROOT_DIR"; # necessary because node needs to access package.json
21
+ sudo systemctl start mongod;
22
+ case "$SCRIPT" in
23
+ cli)
24
+ "$DOTENVX_BIN" run -f "$ENV_PATH" -- node "$ROOT_DIR"/cli/index.js "${EXTRA_ARGS[@]}";
25
+ ;;
26
+ dev)
27
+ nodemon --watch ./src --exec "bash -c '$DOTENVX_BIN run -f $ENV_PATH -- node $ROOT_DIR/cli/index.js serve dev'";
28
+ ;;
29
+ test)
30
+ "$DOTENVX_BIN" run -f "$ENV_PATH" -- node --test --test-isolation=none --experimental-test-coverage
31
+ ;;
32
+ *)
33
+ echo "Unknown run mode: $SCRIPT. Exiting...";
34
+ exit 1;
35
+ ;;
36
+ esac
@@ -0,0 +1,70 @@
1
+ #!/bin/env bash
2
+
3
+ set -e
4
+
5
+ # mongo install guide: https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/#std-label-install-mdb-community-ubuntu
6
+
7
+ #NOTE only the mongodb installation is done here. for database creation, see `setup_mongodb_populate.sh`
8
+
9
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
10
+ source "$SCRIPT_DIR/utils.sh"
11
+
12
+ # float arithmetic comparison is not supported by bash and we need to use `bc`
13
+ # usage: if float_comparison "a >= b"; then... ; fi
14
+ float_comparison () {
15
+ expr="$1"
16
+ (( $(echo "$expr" |bc -l) ));
17
+ }
18
+
19
+ install_mongodb_ubuntu () {
20
+
21
+ sudo apt-get install gnupg curl
22
+
23
+ curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
24
+ sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \
25
+ --dearmor
26
+
27
+ # assert we have an 86 64 architecture
28
+ if [ "$(arch)" != "x86_64" ];
29
+ then echo "MongoDB only supports x86_64 architectures (yours is $(arch)). exiting..."; exit 1
30
+ fi;
31
+
32
+ # fetch the release name. Mongo only supports LTS versions, so if the user's Ubuntu version is not LTS, we get the name of the last LTS released before the user's version.
33
+ source "/etc/lsb-release"
34
+ if float_comparison "$DISTRIB_RELEASE >= 24.04";
35
+ then DISTRIB="noble";
36
+ elif float_comparison "$DISTRIB_RELEASE >= 22.04";
37
+ then DISTRIB="jammy";
38
+ elif float_comparison "$DISTRIB_RELEASE >= 20.04";
39
+ then DISTRIB="focal";
40
+ else echo "Your Ubuntu version ($DISTRIB_RELEASE) is not supported by MongoDB 8.0"; exit 1;
41
+ fi;
42
+
43
+ # create list file
44
+ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu $DISTRIB/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
45
+
46
+ sudo apt-get update
47
+
48
+ sudo apt-get install -y mongodb-org
49
+ }
50
+
51
+ install_mongodb_mac () {
52
+ xcode-select --install;
53
+ brew tap mongodb/brew;
54
+ brew update;
55
+ brew install mongodb-community@8.0;
56
+ }
57
+
58
+ if ! command -v mongod ; then
59
+ echo_title "INSTALL MONGODB"
60
+
61
+ if [ "$OS" = "Linux" ]; then
62
+ install_mongodb_ubuntu;
63
+ sudo systemctl start mongod;
64
+ elif [ "$OS" = "Mac" ]; then
65
+ install_mongodb_mac;
66
+ brew services start mongodb-community@8.0;
67
+ else echo "Unsupported OS: $OS"; exit 1;
68
+ fi;
69
+
70
+ fi;
@@ -0,0 +1,15 @@
1
+ #!/bin/env bash
2
+
3
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4
+ source "$SCRIPT_DIR/utils.sh";
5
+
6
+ if ! command -v npm &> /dev/null; then
7
+ echo_title "INSTALL NVM & NODE";
8
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
9
+ nvm install node
10
+ npm install -g webpack webpack-cli
11
+ fi
12
+
13
+ echo_title "SETUP FASTIFY APP";
14
+ cd "$ROOT_DIR";
15
+ npm i;
@@ -0,0 +1,30 @@
1
+ import re
2
+ import sys
3
+ import json
4
+ import shutil
5
+ import pathlib
6
+
7
+ curdir = pathlib.Path(__file__).parent.resolve()
8
+ pkg_file = curdir.parent.joinpath("package.json").resolve()
9
+ pkg_lock_file = curdir.parent.joinpath("package-lock.json").resolve()
10
+
11
+ usage = "\nupdate_version.py: CLI to update NPM version.\nUSAGE: \n\tpython3 update_version.py [VERSION]\n"
12
+
13
+ if len(sys.argv) != 2:
14
+ print(usage)
15
+ exit()
16
+
17
+ version = sys.argv[1]
18
+ if not re.search(r"^\d+\.\d+\.[^\s]+$", version):
19
+ print(r"argument VERSION must match '^\d+\.\d+\.[^\s]+$' (i.e. '0.2.1', '1.3.4fix'). exiting...")
20
+ exit()
21
+
22
+ for fp in [pkg_file, pkg_lock_file]:
23
+ shutil.copy2(fp, f"{fp}.bak")
24
+ with open(fp, mode="r") as fh:
25
+ data = json.load(fh)
26
+ data["version"] = version
27
+ with open(fp, mode="w") as fh:
28
+ json.dump(data, fh, indent=2)
29
+
30
+ print(f"\nUpdated NPM package to version: {version}.\n")
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bash
2
+
3
+ color_echo() {
4
+ Color_Off="\033[0m"
5
+ Red="\033[1;91m" # Red
6
+ Green="\033[1;92m" # Green
7
+ Yellow="\033[1;93m" # Yellow
8
+ Blue="\033[1;94m" # Blue
9
+ Purple="\033[1;95m" # Purple
10
+ Cyan="\033[1;96m" # Cyan
11
+
12
+ case "$1" in
13
+ "green") echo -e "$Green$2$Color_Off";;
14
+ "red") echo -e "$Red$2$Color_Off";;
15
+ "blue") echo -e "$Blue$2$Color_Off";;
16
+ "yellow") echo -e "$Yellow$2$Color_Off";;
17
+ "purple") echo -e "$Purple$2$Color_Off";;
18
+ "cyan") echo -e "$Cyan$2$Color_Off";;
19
+ *) echo "$2";;
20
+ esac
21
+ }
22
+
23
+ echo_title(){
24
+ sep_line="========================================"
25
+ len_title=${#1}
26
+
27
+ if [ "$len_title" -gt 40 ]; then
28
+ sep_line=$(printf "%0.s=" $(seq 1 $len_title))
29
+ title="$1"
30
+ else
31
+ diff=$((38 - len_title))
32
+ half_diff=$((diff / 2))
33
+ sep=$(printf "%0.s=" $(seq 1 $half_diff))
34
+
35
+ if [ $((diff % 2)) -ne 0 ]; then
36
+ title="$sep $1 $sep="
37
+ else
38
+ title="$sep $1 $sep"
39
+ fi
40
+ fi
41
+
42
+ color_echo purple "\n\n$sep_line\n$title\n$sep_line"
43
+ }
44
+
45
+ get_os() {
46
+ uname_out="$(uname -s)"
47
+ case "${uname_out}" in
48
+ Linux*) os=Linux;;
49
+ Darwin*) os=Mac;;
50
+ CYGWIN*) os=Cygwin;;
51
+ MINGW*) os=MinGw;;
52
+ MSYS_NT*) os=Git;;
53
+ *) os="UNKNOWN:${unameOut}"
54
+ esac
55
+ echo "${os}"
56
+ }
57
+ export OS
58
+ OS=$(get_os)
59
+
60
+ # float arithmetic comparison is not supported by bash and we need to use `bc`
61
+ # usage: if float_comparison "a >= b"; then... ; fi
62
+ float_comparison () {
63
+ expr="$1"
64
+ (( $(echo "$expr" |bc -l) ));
65
+ }
package/src/app.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * build a fastify app
3
+ */
4
+
5
+ import Fastify from "fastify";
6
+ import cors from "@fastify/cors";
7
+ // import swagger from "@fastify/swagger";
8
+
9
+ import fixtures from "#fixtures/index.js";
10
+ import schemas from "#schemas/index.js";
11
+ import data from "#data/index.js";
12
+ import db from "#db/index.js";
13
+ import logger from "#utils/logger.js";
14
+
15
+ /** @typedef {import("#types").FastifyInstanceType} FastifyInstanceType */
16
+
17
+ //NOTE: couldn´t get fastify/swagger to work for now...
18
+ // const swaggerConfig = {
19
+ // openapi: {
20
+ // openapi: "3.0.0",
21
+ // info: {
22
+ // title: "Aiiinotate",
23
+ // description: "A fast and lightweight IIIF annotations server",
24
+ // version: "1.0.0",
25
+ // },
26
+ // servers: [
27
+ // {
28
+ // url: process.env.AIIINOTATE_BASE_URL,
29
+ // description: "Aiiinotate URL"
30
+ // }
31
+ // ],
32
+ // // tags: [],
33
+ // // components: {},
34
+ // // externalDocs: {
35
+ // // url: 'https://swagger.io',
36
+ // // description: 'Find more info here'
37
+ // // }
38
+ // },
39
+ //
40
+ // // swagger: {
41
+ // // info: {
42
+ // // title: "Aiiinotate",
43
+ // // description: "A fast and lightweight IIIF annotations server",
44
+ // // version: "1.0.0",
45
+ // // },
46
+ // // externalDocs: {
47
+ // // url: 'https://swagger.io',
48
+ // // description: 'Find more info here'
49
+ // // },
50
+ // // host: process.env.AIIINOTATE_BASE_URL.replace(/^http(s)?\:\/\//g, ""), // process.env.AIIINOTATE_BASE_URL,
51
+ // // schemes: [ "http", "https" ],
52
+ // // consumes: ['application/json'],
53
+ // // produces: ['application/json'],
54
+ // // tags: [ Object ],
55
+ // // },
56
+ // hideUntagged: false,
57
+ // exposeRoute: true,
58
+ // }
59
+
60
+ const fastifyConfigCommon = {
61
+ bodyLimit: 100 * 1048576 // 100 MiB
62
+ }
63
+
64
+ const testConfig = {
65
+ fastify: {
66
+ ...fastifyConfigCommon
67
+ },
68
+ mongo: {
69
+ test: true,
70
+ }
71
+ }
72
+
73
+ const defaultConfig = {
74
+ fastify: {
75
+ loggerInstance: logger.pinoLogger,
76
+ ...fastifyConfigCommon
77
+ },
78
+ mongo: { }
79
+ }
80
+
81
+ const setConfig = (mode) => {
82
+ const allowedModes = [ "test", "default" ];
83
+ if (! allowedModes.includes(mode)) {
84
+ throw new Error(`app.build: 'mode' param expected one of ${allowedModes}, got ${mode}`)
85
+ }
86
+ return mode==="test" ? testConfig : defaultConfig;
87
+ }
88
+
89
+ /**
90
+ * @param {"test"|"default"} mode
91
+ * @returns {Promise<FastifyInstanceType>}
92
+ */
93
+ async function build(mode="default") {
94
+ const
95
+ config = setConfig(mode),
96
+ mongoConfig = config.mongo,
97
+ fastifyConfig = config.fastify,
98
+ fastify = Fastify(fastifyConfig);
99
+
100
+ // NOTE: we allow all origins => restrict ?
101
+ fastify.register(cors, {
102
+ origin: "*",
103
+ methods: [ "GET", "HEAD", "POST", "DELETE" ]
104
+ });
105
+
106
+ await fastify.register(db, mongoConfig);
107
+ await fastify.register(fixtures);
108
+ fastify.register(schemas);
109
+ fastify.register(data);
110
+ await fastify.ready();
111
+
112
+ return fastify
113
+ }
114
+
115
+ export default build;
116
+
@@ -0,0 +1,73 @@
1
+ import { inspect } from "node:util";
2
+
3
+ const STRICT_MODE = process.env.AIIINOTATE_STRICT_MODE?.toLowerCase() === "true";
4
+ const PAGE_SIZE = parseInt(process.env.AIIINOTATE_PAGE_SIZE);
5
+
6
+ // one of "file"|"stdout"|"stdout+file"|"off"
7
+ const LOG_TARGET = process.env.AIIINOTATE_LOG_TARGET;
8
+ const LOG_DIR = process.env.AIIINOTATE_LOG_DIR;
9
+ const LOG_LEVEL = process.env.AIIINOTATE_LOG_LEVEL;
10
+
11
+ const PORT = process.env.AIIINOTATE_PORT;
12
+ const HOST = process.env.AIIINOTATE_HOST;
13
+ const SCHEME = process.env.AIIINOTATE_SCHEME;
14
+ const BASE_URL = process.env.AIIINOTATE_BASE_URL;
15
+ const PUBLIC_URL = process.env.AIIINOTATE_PUBLIC_URL
16
+
17
+ const MONGODB_DB = process.env.MONGODB_DB;
18
+ const MONGODB_DB_TEST = process.env.MONGODB_DB_TEST;
19
+ const MONGODB_CONNSTRING = process.env.MONGODB_CONNSTRING;
20
+ const MONGODB_CONNSTRING_TEST = process.env.MONGODB_CONNSTRING_TEST;
21
+
22
+ // ensure that all env variables are defined.
23
+ const env_mapper = {
24
+ LOG_TARGET : LOG_TARGET,
25
+ LOG_LEVEL: LOG_LEVEL,
26
+ LOG_DIR : LOG_DIR,
27
+ STRICT_MODE : STRICT_MODE,
28
+ PAGE_SIZE : PAGE_SIZE,
29
+ PORT : PORT,
30
+ HOST : HOST,
31
+ SCHEME : SCHEME,
32
+ BASE_URL : BASE_URL,
33
+ PUBLIC_URL : PUBLIC_URL,
34
+ MONGODB_DB : MONGODB_DB,
35
+ MONGODB_DB_TEST : MONGODB_DB_TEST,
36
+ MONGODB_CONNSTRING : MONGODB_CONNSTRING,
37
+ MONGODB_CONNSTRING_TEST : MONGODB_CONNSTRING_TEST,
38
+ }
39
+ if (Object.values(env_mapper).some(e => e==null)) {
40
+ console.error("SETUP ERROR: Some environment variables are undefined ! Exiting...");
41
+ console.log(inspect(env_mapper));
42
+ process.exit(1);
43
+ }
44
+
45
+ const allowedLogLevels = [ "trace", "debug", "info", "warn", "error", "fatal" ];
46
+ if (!allowedLogLevels.includes(LOG_LEVEL)) {
47
+ console.error(`SETUP ERROR: env variable AIIINOTATE_LOG_TARGET must be set to one of ${allowedLogLevels.map(x => "\"" + x + "\"").join(", ")}. Got "${LOG_LEVEL}". Exiting...`)
48
+ process.exit(1);
49
+ }
50
+
51
+ // enforce value constraints on variables
52
+ const allowedLogTargets = [ "file", "stdout", "stdout+file", "off" ];
53
+ if (!allowedLogTargets.includes(LOG_TARGET)) {
54
+ console.error(`SETUP ERROR: env variable AIIINOTATE_LOG_TARGET must be set to one of ${allowedLogTargets.map(x => "\"" + x + "\"").join(", ")}. Got "${LOG_TARGET}". Exiting...`)
55
+ process.exit(1);
56
+ }
57
+
58
+ export {
59
+ LOG_TARGET,
60
+ LOG_DIR,
61
+ LOG_LEVEL,
62
+ STRICT_MODE,
63
+ PAGE_SIZE,
64
+ PORT,
65
+ HOST,
66
+ SCHEME,
67
+ BASE_URL,
68
+ MONGODB_DB,
69
+ MONGODB_DB_TEST,
70
+ MONGODB_CONNSTRING,
71
+ MONGODB_CONNSTRING_TEST,
72
+ PUBLIC_URL
73
+ }