@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.
- package/LICENSE +661 -0
- package/README.md +206 -0
- package/cli/export.js +118 -0
- package/cli/import.js +111 -0
- package/cli/index.js +45 -0
- package/cli/migrate.js +131 -0
- package/cli/serve.js +29 -0
- package/cli/utils/fastifyClient.js +86 -0
- package/cli/utils/io.js +225 -0
- package/cli/utils/mongoClient.js +21 -0
- package/cli/utils/progressbar.js +86 -0
- package/cli/xywhToInt.js +99 -0
- package/config/.env.template +48 -0
- package/docker/Dockerfile +39 -0
- package/docker/README.md +5 -0
- package/docker/docker-compose.yaml +49 -0
- package/docker/docker.sh +43 -0
- package/docker/docker_aiiinotate_import.sh +62 -0
- package/docs/api.md +381 -0
- package/docs/cli.md +132 -0
- package/docs/dev_documentation/dev_architecture.md +88 -0
- package/docs/dev_documentation/dev_db.md +58 -0
- package/docs/dev_documentation/dev_iiif_compatibility.md +43 -0
- package/docs/dev_documentation/dev_notes_quirks_and_troubleshooting.md +143 -0
- 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/docs/specifications/0_w3c_open_annotations.md +332 -0
- package/docs/specifications/1_w3c_web_annotations.md +577 -0
- package/docs/specifications/2_iiif_apis.md +428 -0
- package/docs/specifications/3_iiif_annotations.md +103 -0
- package/docs/specifications/4_search_api.md +135 -0
- package/docs/specifications/5_sas.md +119 -0
- package/docs/specifications/6_mirador.md +119 -0
- package/docs/specifications/7_aikon.md +137 -0
- package/docs/specifications/include/presentation_2.0.webp +0 -0
- package/docs/specifications/include/presentation_2.0_white.png +0 -0
- package/docs/specifications/include/presentation_3.0.png +0 -0
- package/docs/specifications/include/presentation_3.0_resize.png +0 -0
- package/eslint.config.js +30 -0
- package/migrations/baseConfig.js +57 -0
- package/migrations/manageIndex.js +55 -0
- package/migrations/migrate-mongo-config-main.js +8 -0
- package/migrations/migrate-mongo-config-test.js +8 -0
- package/migrations/migrationScripts/20250825185706-collections.js +48 -0
- package/migrations/migrationScripts/20250826194832-annotations2-schema.js +42 -0
- package/migrations/migrationScripts/20250904080710-annotations2-indexes.js +69 -0
- package/migrations/migrationScripts/20251002141951-manifests2-schema.js +43 -0
- package/migrations/migrationScripts/20251006212110-manifests2-indexes.js +35 -0
- package/migrations/migrationTemplate.js +25 -0
- package/package.json +82 -0
- package/scripts/get_version.py +12 -0
- package/scripts/run.sh +36 -0
- package/scripts/setup_mongodb.sh +70 -0
- package/scripts/setup_node.sh +15 -0
- package/scripts/update_version.py +30 -0
- package/scripts/utils.sh +65 -0
- package/src/app.js +116 -0
- package/src/constants.js +73 -0
- package/src/data/annotations/annotations2.js +681 -0
- package/src/data/annotations/annotations3.js +28 -0
- package/src/data/annotations/routes.js +335 -0
- package/src/data/annotations/routes.test.js +271 -0
- package/src/data/collectionAbstract.js +283 -0
- package/src/data/index.js +29 -0
- package/src/data/manifests/manifests2.js +378 -0
- package/src/data/manifests/manifests2.test.js +53 -0
- package/src/data/manifests/manifests3.js +23 -0
- package/src/data/manifests/routes.js +122 -0
- package/src/data/manifests/routes.test.js +70 -0
- package/src/data/routes.js +181 -0
- package/src/data/routes.test.js +166 -0
- package/src/db/index.js +50 -0
- package/src/fixtures/annotations.js +41 -0
- package/src/fixtures/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
- package/src/fixtures/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
- package/src/fixtures/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
- package/src/fixtures/data/annotations2Invalid.jsonld +39 -0
- package/src/fixtures/data/annotations2SvgValid.jsonld +81 -0
- package/src/fixtures/data/annotations2Valid.jsonld +39 -0
- package/src/fixtures/data/bnf_invalid_manifest.json +2806 -0
- package/src/fixtures/data/bnf_valid_manifest.json +2817 -0
- package/src/fixtures/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
- package/src/fixtures/generate.js +181 -0
- package/src/fixtures/index.js +69 -0
- package/src/fixtures/manifests.js +14 -0
- package/src/fixtures/utils.js +37 -0
- package/src/schemas/index.js +100 -0
- package/src/schemas/schemasBase.js +19 -0
- package/src/schemas/schemasPresentation2.js +410 -0
- package/src/schemas/schemasPresentation3.js +33 -0
- package/src/schemas/schemasResolver.js +71 -0
- package/src/schemas/schemasRoutes.js +318 -0
- package/src/server.js +25 -0
- package/src/types.js +97 -0
- package/src/utils/iiif2Utils.js +332 -0
- package/src/utils/iiif2Utils.test.js +146 -0
- package/src/utils/iiif3Utils.js +0 -0
- package/src/utils/iiifUtils.js +18 -0
- package/src/utils/logger.js +119 -0
- package/src/utils/routeUtils.js +137 -0
- package/src/utils/svg.js +417 -0
- package/src/utils/testUtils.js +289 -0
- 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")
|
package/scripts/utils.sh
ADDED
|
@@ -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
|
+
|
package/src/constants.js
ADDED
|
@@ -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
|
+
}
|