beavuck-time 2.1.21

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 (53) hide show
  1. package/.env +21 -0
  2. package/.env.test +30 -0
  3. package/.gitlab/issue_templates/bug.md +53 -0
  4. package/.gitlab/issue_templates/enhancement.md +53 -0
  5. package/.gitlab/merge_request_templates/bug_fix.md +9 -0
  6. package/.gitlab/merge_request_templates/enhancement.md +9 -0
  7. package/.gitlab-ci.yml +149 -0
  8. package/.nvmrc +1 -0
  9. package/.sonarlint/connectedMode.json +4 -0
  10. package/CONTRIBUTING.md +57 -0
  11. package/DOCKERHUB_OVERVIEW.md +185 -0
  12. package/Dockerfile +25 -0
  13. package/HELP.md +53 -0
  14. package/README.md +215 -0
  15. package/UNLICENSE +24 -0
  16. package/api-tests/bruno/bruno.json +9 -0
  17. package/api-tests/bruno/collection.bru +7 -0
  18. package/api-tests/bruno/environments/local.bru +8 -0
  19. package/api-tests/bruno/now/nok/nok_no_origin.bru +19 -0
  20. package/api-tests/bruno/now/nok/nok_non_recognized_referrer.bru +23 -0
  21. package/api-tests/bruno/now/nok/nok_non_trusted_origin.bru +23 -0
  22. package/api-tests/bruno/now/nok/nok_non_truted_origin_nor_referrer.bru +24 -0
  23. package/api-tests/bruno/now/ok/ok_same_origin.bru +23 -0
  24. package/api-tests/bruno/now/ok/ok_same_referrer.bru +23 -0
  25. package/api-tests/bruno/now/ok/ok_trusted_origin.bru +23 -0
  26. package/build/api.js +20 -0
  27. package/eslint.config.mjs +28 -0
  28. package/openapi/swagger.json +75 -0
  29. package/package.json +79 -0
  30. package/sonar-project.properties +9 -0
  31. package/src/__tests__/api.test.ts +98 -0
  32. package/src/__tests__/middlewares/errorHandler.test.ts +93 -0
  33. package/src/__tests__/utils/urlUtil.test.ts +53 -0
  34. package/src/api.ts +19 -0
  35. package/src/config/corsOptions.ts +63 -0
  36. package/src/config/logger.ts +38 -0
  37. package/src/controllers/nowController.ts +19 -0
  38. package/src/errors/BeavuckTimeClientError.ts +12 -0
  39. package/src/errors/BeavuckTimeServerError.ts +18 -0
  40. package/src/errors/CorsError.ts +12 -0
  41. package/src/errors/base/BeavuckTimeError.ts +12 -0
  42. package/src/middlewares/corsMiddleware.ts +32 -0
  43. package/src/middlewares/errorHandler.ts +57 -0
  44. package/src/middlewares/notFoundHandler.ts +8 -0
  45. package/src/middlewares/rateLimiter.ts +15 -0
  46. package/src/models/now.ts +14 -0
  47. package/src/routes/routes.ts +84 -0
  48. package/src/server.ts +53 -0
  49. package/src/services/nowService.ts +9 -0
  50. package/src/types/isoTimestamp.ts +8 -0
  51. package/src/utils/urlUtil.ts +29 -0
  52. package/tsconfig.json +38 -0
  53. package/tsoa.json +12 -0
package/.env ADDED
@@ -0,0 +1,21 @@
1
+ # CORS
2
+ ## URL of this API
3
+ HOST_URL=http://localhost:3000
4
+ ## To allow requests from any origin, include * (not recommended)
5
+ TRUSTED_ORIGINS=http://localhost:8477
6
+
7
+ # Server
8
+ ## Port of this API: in a container, this is the internal port -- outside of a container, this is the external port. Default is 3000
9
+ API_PORT=3000
10
+
11
+ # Rate limiting
12
+ ## Requests per minute for each IP address. If not a strictly positive, "no limit". Default is -1
13
+ RATE_LIMIT=-1
14
+
15
+ # Logging
16
+ ## Level of detail in logs: error, warn, info, http, verbose, debug, silly. Default is info
17
+ LOG_LEVEL=info
18
+ ## Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
19
+ MAX_LOG_FILES=64
20
+ ## Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
21
+ MAX_SIZE_LOG_FILES=1m
package/.env.test ADDED
@@ -0,0 +1,30 @@
1
+ #/-------------------[DOTENV_PUBLIC_KEY]--------------------/
2
+ #/ public-key encryption for .env files /
3
+ #/ [how it works](https://dotenvx.com/encryption) /
4
+ #/----------------------------------------------------------/
5
+ DOTENV_PUBLIC_KEY_TEST="02d5c6b7efe5269d1fd3626adac3338f0971e58e2555e8e27c53a7e70047273a1c"
6
+
7
+ # Just testing dotenvx here. Not used in .env. Not useful either -- nothing secret in this file at any rate.
8
+ # To change any of those values, run `dotenvx set key value` (ex: `dotenvx set HOST_URL http://localhost:3000`)
9
+
10
+ # CORS
11
+ ## URL of this API
12
+ HOST_URL=encrypted:BG5D1BHPO/UlcKh8c1s4s2YDqFQ0yIiduOKDHAsc9GNlm4/BRSPwxHNka+jJopZp++w+nfARNbSJwdgBoZvlbjujb/E8uJ2CX4erxmh1j+HBpmri2ocBa04NnJodMojPkcuxJqP3RSKfh61u+Fo1oTpCoZ8yYg==
13
+ ## To allow requests from any origin, include * (not recommended)
14
+ TRUSTED_ORIGINS=encrypted:BE1fOSnJ8rnMU+A1orA9/Ujee4ypigUywnZ9g85l74XiNxEDoOdQmAIiSjYut+y/Y5b3VkqF+vBAgP++LYCMGTPoANWHgscZ+DAzsgqeFjsFp9WTKuWXxTeauI3HzLWI3v44o67oSZ1myWk9wsvc9WWwN4plrA==
15
+
16
+ # Server
17
+ ## Port of this API: in a container, this is the internal port -- outside of a container, this is the external port. Default is 3000
18
+ API_PORT=encrypted:BGw4+UyXhxyfMzdxNjZBzTN/akEDL77i+XDqNRZA6w9ed7GYrQyYvuvEho+4VYir9T014sgLYRNeS09cR2mJV8pjyYWzHYhdXQyDeihUcNbLvIpfsZu7KH+aIeHaPx5YIRGJDSM=
19
+
20
+ # Rate limiting
21
+ ## Requests per minute for each IP address. If not a strictly positive, "no limit". Default is -1
22
+ RATE_LIMIT=encrypted:BD1uTBsjTAjd+GYlZGM0S5DVCkmiOSEouDmLQj9XztlHpP2u8KyHxByIzUed8qkJXzbcf8zvTsANzY44V91m9UZZqXeZDOQlCqmn+ZpzmPDSA3VMUPzIdUpYF9DD7rWIgxvF
23
+
24
+ # Logging
25
+ ## Level of detail in logs: error, warn, info, http, verbose, debug, silly. Default is info
26
+ LOG_LEVEL=encrypted:BGfqcxa9XJrMBk7U9QqkbfnXimot5dnz0kRroclEwtsY0JVc8VCw35idcSKpdS3jhCIxg0enNCZEbVMFMt6Q37yvYlIUqCKqk229moGTDSkQ2V/38pwnmzMS1LD/+nUH2yyFpWA=
27
+ ## Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
28
+ MAX_LOG_FILES=encrypted:BET35n9Sfhqkdahr+JCwV/1sfX5zCE2FmH4lFFOi/WUhg9Wi+DIm2NH2WqfHcYIiWcRRnqpr3f2gnXwLsmRMcSydk7gxuERgA+mn0F2ygzbjTQUw+6l3snLgHKskKTuuA1rO
29
+ ## Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
30
+ MAX_SIZE_LOG_FILES=encrypted:BJLXlbtMDxs5di0o+FRr6Im9gYs3GayVwjWomavHzCychYr9JZyOyMTrBN8YrusYoHklvEoHuGfIkUB1m8ErDEl8eJlJvaPZr30+NLPujWSrVwb1B9v8lBon2TfrDQulqSX6
@@ -0,0 +1,53 @@
1
+ ## 🗒️ Summary
2
+
3
+ (_Summarize the bug encountered concisely. "X happens"_)
4
+
5
+ ---
6
+
7
+ ## 🐛 What is the current bug behavior?
8
+
9
+ (_What actually happens. "When W, then X"_)
10
+
11
+ ---
12
+
13
+ ## 🟢 What is the expected correct behavior?
14
+
15
+ (_What you should see instead. "When W, then 42"_)
16
+
17
+ ---
18
+
19
+ ## 📋 Steps to reproduce
20
+
21
+ (_How one can reproduce the issue - this is very important. Please use a numbered list_)
22
+
23
+ 1. Step 1
24
+ 2. Step 2
25
+ 3. Step 3
26
+
27
+ ...
28
+
29
+ ---
30
+
31
+ ## 📜 Relevant logs and/or screenshots
32
+
33
+ (_Paste any relevant logs - please use code blocks (```) to format console output, logs, etc. as it's very hard to read otherwise._)
34
+
35
+ (_for example_:
36
+
37
+ ```
38
+ Error: something went wrong
39
+ ```
40
+
41
+ )
42
+
43
+ ---
44
+
45
+ ## ⛳ Possible fixes
46
+
47
+ (_Optional: If you have an idea of how to fix this, link to the code or describe the solution_)
48
+
49
+ ---
50
+
51
+ (_Automatically adds the bug label for easier tracking_)
52
+
53
+ /label ~bug
@@ -0,0 +1,53 @@
1
+ ## 🗒️ Summary
2
+
3
+ (_Summarize the enhancement you'd like to see implemented concisely. "Add X functionality"_)
4
+
5
+ ---
6
+
7
+ ## 🔍 What problem does this enhancement solve?
8
+
9
+ (_Describe the problem this enhancement would address. "Currently, Y is a limitation, and adding X would solve it"_)
10
+
11
+ ---
12
+
13
+ ## 🎯 What is the proposed solution?
14
+
15
+ (_Describe how you envision this enhancement being implemented. "Add a new method for Z"_)
16
+
17
+ ---
18
+
19
+ ## 🚀 Why is this enhancement valuable to the product?
20
+
21
+ (_Explain why this enhancement would improve the product. "This would make the process faster, reduce errors, etc."_)
22
+
23
+ ---
24
+
25
+ ## 📋 Steps to use the feature (if the enhancement is indeed a feature)
26
+
27
+ (_How will users interact with this feature? Describe the user journey or interface interactions.
28
+ For bonus points, use a user story format, and add a
29
+ [sequence diagram](https://mermaid.js.org/syntax/sequenceDiagram.html) or [flowchart](https://mermaid.js.org/syntax/flowchart.html)_)
30
+
31
+ (_For example_:
32
+
33
+ ```mermaid
34
+ sequenceDiagram
35
+ participant Client
36
+ participant API
37
+ Client->>API: GET /now (ask for time)
38
+ API->>Client: {"now": "2024-06-14T18:25:46.835Z"} (responds with time)
39
+ ```
40
+
41
+ )
42
+
43
+ ---
44
+
45
+ ## 📜 Additional details, mockups, or screenshots
46
+
47
+ (_If applicable, attach any additional context, mockups, or screenshots that help explain the enhancement_)
48
+
49
+ ---
50
+
51
+ (_Automatically adds the enhancement label for easier tracking_)
52
+
53
+ /label ~enhancement
@@ -0,0 +1,9 @@
1
+ ## 📝 Summary
2
+
3
+ (_Summarize the bug fix being implemented concisely. "By accepting this MR, you fix X issue."_)
4
+
5
+ ---
6
+
7
+ (_Automatically adds the bug label for easier tracking_)
8
+
9
+ /label ~bug
@@ -0,0 +1,9 @@
1
+ ## 📝 Summary
2
+
3
+ (_Summarize the enhancement concisely. "By accepting this MR, you add X functionality"_)
4
+
5
+ ---
6
+
7
+ (_Automatically adds the enhancement label for easier tracking_)
8
+
9
+ /label ~enhancement
package/.gitlab-ci.yml ADDED
@@ -0,0 +1,149 @@
1
+ ########~~~~-- Stages --~~~~########
2
+
3
+ stages:
4
+ - secure
5
+ - test
6
+ - update
7
+ - build
8
+ - deploy
9
+
10
+ ########~~~~-- Variables & defaults --~~~~########
11
+
12
+ variables:
13
+ IMAGE: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:lts-jod"
14
+ SCRIPT_TO_INSTALL_DEPS: "apt-get update && apt-get install -y git bash curl jq && npm install -g npm@latest && npm ci"
15
+
16
+ default:
17
+ image: $IMAGE
18
+ interruptible: true
19
+
20
+ ########~~~~-- Workflows --~~~~########
21
+
22
+ workflow:
23
+ auto_cancel:
24
+ on_new_commit: interruptible
25
+ on_job_failure: all
26
+ rules:
27
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
28
+ - if: "$CI_COMMIT_TAG"
29
+ - if: "$CI_PIPELINE_SOURCE == 'schedule'"
30
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
31
+
32
+ ########~~~~-- Includes --~~~~########
33
+
34
+ include:
35
+ - component: "$CI_SERVER_FQDN/beavuck-services/ci-cd-catalog/secret-finder/secret-finder@main"
36
+ inputs:
37
+ job_name: "find_secrets"
38
+ stage_config: "secure"
39
+
40
+ - component: "$CI_SERVER_FQDN/beavuck-services/ci-cd-catalog/command-runner/command-runner@main"
41
+ inputs:
42
+ job_name: "format"
43
+ stage_config: ".pre"
44
+ branch_name: "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"
45
+ command: "npm ci && npm run clean"
46
+ commit_message: "🎨 Format code automatically"
47
+ image: "$IMAGE"
48
+ script_to_install_deps: "$SCRIPT_TO_INSTALL_DEPS"
49
+ consecutive_command_commits_allowed: false
50
+
51
+ - component: "$CI_SERVER_FQDN/beavuck-services/ci-cd-catalog/command-runner/command-runner@main"
52
+ inputs:
53
+ job_name: "update"
54
+ stage_config: "update"
55
+ branch_name: "chore/update-dependencies"
56
+ command: "npm run update-dependencies && npm run up-patch"
57
+ commit_message: "⚙️ Update dependencies automatically"
58
+ image: "$IMAGE"
59
+ script_to_install_deps: "$SCRIPT_TO_INSTALL_DEPS"
60
+ consecutive_command_commits_allowed: true
61
+
62
+ - component: "$CI_SERVER_FQDN/beavuck-services/ci-cd-catalog/docker-publisher/docker-publisher@main"
63
+ inputs:
64
+ job_name: "publish"
65
+ stage_config: "deploy"
66
+ image_name: "beavuck/time"
67
+ publish_to_dockerhub: true
68
+
69
+ find_secrets:
70
+ rules:
71
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
72
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
73
+
74
+ update:
75
+ rules:
76
+ - if: "$CI_PIPELINE_SOURCE == 'schedule' && $IS_UPDATE == 'true'"
77
+
78
+ format:
79
+ rules:
80
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
81
+
82
+ publish:
83
+ rules:
84
+ - if: "$CI_COMMIT_TAG"
85
+ when: manual
86
+
87
+ ########~~~~-- Jobs --~~~~########
88
+
89
+ run_integration_tests:
90
+ stage: test
91
+ variables:
92
+ API_PORT: 3000
93
+ HOST_URL: http://localhost:3000
94
+ TRUSTED_ORIGINS: http://localhost:8477
95
+ before_script:
96
+ - npm install -g npm@latest
97
+ - npm ci
98
+ - npm run build
99
+ - npm start & sleep 10
100
+ script: npm run run-integration-tests
101
+ rules:
102
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
103
+ - if: "$CI_COMMIT_TAG"
104
+ needs:
105
+ - job: find_secrets
106
+ optional: true
107
+
108
+ coverage:
109
+ stage: test
110
+ artifacts:
111
+ paths: [coverage/lcov.info]
112
+ reports:
113
+ coverage_report:
114
+ coverage_format: cobertura
115
+ path: coverage/cobertura-coverage.xml
116
+ before_script: npm install -g npm@latest && npm ci
117
+ script: npm run test
118
+ rules:
119
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
120
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
121
+ - if: "$CI_COMMIT_TAG"
122
+ needs:
123
+ - job: find_secrets
124
+ optional: true
125
+
126
+ analyze:
127
+ stage: test
128
+ image:
129
+ name: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/sonarsource/sonar-scanner-cli:latest"
130
+ entrypoint: [""]
131
+ cache:
132
+ key: "${CI_JOB_NAME}"
133
+ paths: [.sonar/cache]
134
+ variables:
135
+ SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
136
+ GIT_DEPTH: 0 # Tells git to fetch all the branches of the project, required by the analysis task
137
+ script:
138
+ - git config --global --add safe.directory "/builds/$CI_PROJECT_PATH"
139
+ - LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
140
+ - if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then PROJECT_VERSION=$LATEST_TAG; else PROJECT_VERSION=$LATEST_TAG-$CI_MERGE_REQUEST_ID; fi
141
+ - sonar-scanner -Dsonar.projectVersion=$PROJECT_VERSION
142
+ rules:
143
+ - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
144
+ - if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
145
+ needs:
146
+ - job: find_secrets
147
+ optional: true
148
+ - job: coverage
149
+ artifacts: true
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ lts/jod
@@ -0,0 +1,4 @@
1
+ {
2
+ "sonarCloudOrganization": "beavuck-services",
3
+ "projectKey": "beavuck-services_time"
4
+ }
@@ -0,0 +1,57 @@
1
+ # ⏲️ Beavuck Time
2
+
3
+ Thank you for considering contributing to Beavuck Time!
4
+
5
+ ## 🈸 Opening issues
6
+
7
+ When you open an issue (be it a bug, a suggestion, or other), please follow the relevant guidelines, so that we can
8
+ help you as quickly as possible.
9
+
10
+ Any open issues that do not follow the guidelines may be closed immediately.
11
+
12
+ ### 🐞 Bug reports
13
+
14
+ Please just follow this link where we set it all up for you: [🐞 Create a bug report](https://gitlab.com/beavuck-services/time/-/issues/new?issuable_template=bug)
15
+
16
+ Or, to do it by hand : please choose the `bug` issue template when creating the issue, and follow it.
17
+
18
+ ### 💡 Suggestions, enhancement requests, etc.
19
+
20
+ Please just follow this link where we set it all up for you: [💡 Create an enhancement request](https://gitlab.com/beavuck-services/time/-/issues/new?issuable_template=enhancement)
21
+
22
+ Or, to do it by hand : please choose the `enhancement` issue template when creating the issue, and follow it.
23
+
24
+ ## 🔀 Preparing for merge requests
25
+
26
+ If you have something against TDD, now is probably the time to turn back.
27
+
28
+ You're still there! Thank you.
29
+
30
+ When you open a merge request, please make sure to follow the relevant steps, so that it can be reviewed and merged as
31
+ quickly as possible.
32
+
33
+ Any open merge requests that do not follow the guidelines may be closed immediately.
34
+
35
+ **All merge requests**
36
+
37
+ - **Commits**:
38
+ - Your MR will need to be separated into digestible, _logical commits_, with a clear message for each. To fit
39
+ with this project's conventions, a _commit message's style_ should be short, in the imperative, start with a relevant emoji,
40
+ have its first word be capitalized, and not end with a period. For example: `✏️ Fix typo in README`.
41
+ - Your MR will need to follow TDD principles:
42
+ - _First, commit failing tests_ targeting the bug or enhancement in question.
43
+ - Then, commit the code that does the thing -- the previously failing tests should now pass. (The pre-existing tests
44
+ should also still pass, obviously.)
45
+ - **Title**: Should be treated like a very important commit message, since it will appear as a commit message on the
46
+ `main` branch. See sub-parts below for specific guidelines.
47
+ - **Template**: Choose the relevant template (see sub-parts below), then follow the instructions within said template.
48
+
49
+ ### 🐛 Bug fixes
50
+
51
+ - **Title**: Should follow the convention `🐛 Fix #{{issue_number}}: {{very_short_description_in_the_imperative}}`
52
+ - **Description (Template)**: Please choose the `bug_fix` MR template when creating the MR.
53
+
54
+ ### ✨ Enhancements, new features, etc.
55
+
56
+ - **Title**: `✨ Add #{{issue_number}}: {{very_short_description_in_the_imperative}}`
57
+ - **Description (Template)**: Please choose the `enhancement` MR template when creating the MR.
@@ -0,0 +1,185 @@
1
+ # ⏲️ Beavuck Time
2
+
3
+ ## 📊 Status
4
+
5
+ [![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=beavuck-services_time)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
6
+
7
+ [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
8
+ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
9
+
10
+ [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
11
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=bugs)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
12
+
13
+ [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
14
+ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
15
+ [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
16
+
17
+ [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
18
+ [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
19
+
20
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=beavuck-services_time&metric=coverage)](https://sonarcloud.io/summary/new_code?id=beavuck-services_time)
21
+
22
+ ---
23
+
24
+ ## 💡 Why
25
+
26
+ You can't count on client devices to all be set up with the correct date and time.
27
+
28
+ But keeping track of time is usually busywork, not the business of your core APIs. And you probably don't want to flood
29
+ your own APIs, whenever you want to get an accurate timestamp for entities in your apps.
30
+
31
+ So you can set up this simple microservice, whose only job should be to answer the question: "what time is it right
32
+ now?"
33
+
34
+ ---
35
+
36
+ ## 🎯 What
37
+
38
+ Get the current time in ISO format, in UTC timezone.
39
+
40
+ This lightweight service focuses on one job.
41
+
42
+ It needs no persistence layer, is capable of handling multiple concurrent requests, and is protected by a simple
43
+ CORS config for security and performance reasons.
44
+
45
+ Dockerized for easy deployment and scaling.
46
+
47
+ ---
48
+
49
+ ## 🔍 Where
50
+
51
+ The code lives on [GitLab](https://gitlab.com/beavuck-services/time),
52
+ and the Docker image is hosted on [Docker Hub](https://hub.docker.com/r/beavuck/time)
53
+
54
+ ### 🦊 GitLab
55
+
56
+ You can find the code on GitLab, where, once you have read the [CONTRIBUTING.md](https://gitlab.com/beavuck-services/time/-/blob/main/CONTRIBUTING.md?ref_type=heads) file, you can also
57
+ create issues and merge requests.
58
+
59
+ Feel free to fork the repo and make your own changes at will, as per the [UNLICENSE](https://gitlab.com/beavuck-services/time/-/blob/main/UNLICENSE?ref_type=heads).
60
+
61
+ ### 🐳 Docker Hub
62
+
63
+ Most devs will only use Docker Hub for their purposes with this project, to use it as is as a dependency for their own
64
+ projects. On Docker Hub, while you're developing, you should use the `beavuck/time:latest` tag to always get the latest
65
+ version.
66
+
67
+ When the time comes to go to production, to protect yourself from surprise breaking changes, you should instead point to
68
+ specific minor version tags, such as `beavuck/time:2.0` : those will not get breaking changes, but they will get
69
+ security updates and bug fixes while they're active.
70
+
71
+ ---
72
+
73
+ ## ⚙️ Usage
74
+
75
+ ### 🪧 Set up (docker-compose example)
76
+
77
+ To run the service in a docker-compose environment, add this in your `docker-compose.yml`'s services section:
78
+
79
+ ```yaml
80
+ time:
81
+ image: beavuck/time:latest
82
+ ports:
83
+ - 'SOME_PORT_NUMBER:3000'
84
+ # HOST_PORT:CONTAINER_PORT (Since we are in a container, CONTAINER_PORT corresponds to the API_PORT variable below)
85
+ environment:
86
+ - HOST_URL: https://time-api.example.com
87
+ # HOST_URL: That API's URL. Essential for CORS config.
88
+ - TRUSTED_ORIGINS: https://my.app.com,https://my-other.app.com
89
+ # TRUSTED_ORIGINS: To allow requests from any origin, include * (not recommended). If empty, will only allow requests from the HOST_URL's origin. Defaults to the HOST_URL's origin
90
+ - API_PORT: 3000
91
+ # API_PORT: Optional. Internal port when in a container. Defaults to 3000
92
+ - RATE_LIMIT: 100
93
+ # RATE_LIMIT: Optional. Max allowed number of requests per minute for each IP address. If negative or 0, no limit. Defaults to no limit
94
+ - LOG_LEVEL: info
95
+ # LOG_LEVEL: Optional. Logging levels include error, warn, info, http, verbose, debug, silly. Defaults to info
96
+ - MAX_LOG_FILES: 64
97
+ # MAX_LOG_FILES: Optional. Maximum number of logs to keep. This can be a number of files or number of days. If using days, add 'd' as the suffix. Default is 64
98
+ - MAX_SIZE_LOG_FILES: 1m
99
+ # MAX_SIZE_LOG_FILES: Optional. Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. Default is 1m
100
+ ```
101
+
102
+ Here's the simple docker compose file I used to test this service locally:
103
+
104
+ ```yaml
105
+ services:
106
+ time:
107
+ image: beavuck/time:latest
108
+ ports:
109
+ - '3000:3000'
110
+ environment:
111
+ HOST_URL: http://127.0.0.1:3000
112
+ TRUSTED_ORIGINS: http://127.0.0.1:8000,http://localhost:8000
113
+ LOG_LEVEL: debug
114
+ ```
115
+
116
+ When you're ready, just run your services with:
117
+
118
+ ```shell
119
+ docker compose up -d
120
+ ```
121
+
122
+ ### ✨ Using the service
123
+
124
+ Now, when you run:
125
+
126
+ ```shell
127
+ curl --location 'http://localhost:{{SOME_PORT_NUMBER}}/now' \
128
+ --header 'Origin: {{SOME_TRUSTED_ORIGIN}}'
129
+ ```
130
+
131
+ you should expect an answer such as:
132
+
133
+ ```json
134
+ {
135
+ "now": "2024-06-15T12:35:48.022Z"
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 🛡️ CORS
142
+
143
+ This service is protected by a CORS policy, which you can configure by setting the `TRUSTED_ORIGINS` environment
144
+ variable.
145
+
146
+ When you use the API, keep in mind what roles these headers play:
147
+
148
+ | `"Origin:"` | `"Referrer:"`* | Result |
149
+ | -------------------------------------------- | ------------------------------ | ------ |
150
+ | Defined and API `TRUSTED_ORIGINS` set to `*` | Whatever | ✅ |
151
+ | Trusted | Whatever | ✅ |
152
+ | Same as this API's host | Whatever | ✅ |
153
+ | Not defined | Same as this API's host | ✅ |
154
+ | Defined and not trusted | Whatever | 🛑 |
155
+ | Not defined | Not defined | 🛑 |
156
+ | Not defined or not trusted | Different from this API's host | 🛑 |
157
+ * AKA `Referer` (sic).
158
+ ---
159
+
160
+ ## 📚 Use cases
161
+
162
+ ### Batch `POST`s
163
+
164
+ Suppose you're creating timestamped entities in your app, and you want to send them in a batch to your API. Your API
165
+ knows the current time when it gets the request, but not the creation time of each entity.
166
+
167
+ ![](https://www.mermaidchart.com/raw/d089f4b4-f7d8-4901-b257-5fcdbb258629?theme=dark&version=v0.1&format=svg)
168
+
169
+ And querying your core API for the current time for each entity is a waste of resources -- that's why you're batching
170
+ the operation in the first place.
171
+
172
+ ![](https://www.mermaidchart.com/raw/3a8e339e-151d-4e7b-aa4d-114f17d1bb72?theme=dark&version=v0.1&format=svg)
173
+
174
+ So you can use this service to get the current time whenever you need it, and use that as the creation time for each
175
+ entity.
176
+
177
+ ![](https://www.mermaidchart.com/raw/81c7ad4f-7462-4c95-8da3-1c1d3c0ac4f7?theme=dark&version=v0.1&format=svg)
178
+
179
+ ---
180
+
181
+ ## 📜 License
182
+
183
+ Have at it.
184
+
185
+ This project uses the Unlicense. See the [UNLICENSE](https://gitlab.com/beavuck-services/time/-/blob/main/UNLICENSE?ref_type=heads) file for details.
package/Dockerfile ADDED
@@ -0,0 +1,25 @@
1
+ FROM node:jod-alpine
2
+
3
+ WORKDIR /usr/src/app
4
+
5
+ COPY package*.json tsconfig.json tsoa.json ./
6
+ COPY src ./src
7
+ COPY .env ./.env
8
+
9
+ # building the app
10
+ RUN npm install --ignore-scripts -g npm@latest \
11
+ && npm ci --ignore-scripts \
12
+ && npm run build \
13
+ # managing user permissions
14
+ && apk add --no-cache shadow \
15
+ && groupadd -r appgroup \
16
+ && useradd -r -g appgroup appuser \
17
+ && chown -R appuser:appgroup /usr/src/app \
18
+ # making the image smaller by removing npm
19
+ && npm uninstall -g npm
20
+
21
+ USER appuser
22
+
23
+ EXPOSE 3000
24
+
25
+ CMD ["node", "build/server.js"]
package/HELP.md ADDED
@@ -0,0 +1,53 @@
1
+ # Beavuck Time
2
+
3
+ ## Usage
4
+
5
+ ### Set up (outside a container)
6
+
7
+ Install the dependencies, and run the service with:
8
+
9
+ ```shell
10
+ npm install
11
+ npm run go
12
+ ```
13
+
14
+ ### Managing environment variables
15
+
16
+ Environment variables are encrypted. Check out https://dotenvx.com/docs/quickstart to manage them if needed
17
+
18
+ ### Using the service
19
+
20
+ When you run:
21
+
22
+ ```shell
23
+ curl --location 'http://localhost:3000/now' \
24
+ --header 'Origin: http://localhost:8477'
25
+ ```
26
+
27
+ you should expect an answer such as:
28
+
29
+ ```json
30
+ {
31
+ "now": "2024-06-14T18:25:46.835Z"
32
+ }
33
+ ```
34
+
35
+ ### Testing
36
+
37
+ #### Node tests
38
+
39
+ To run the tests, you can use:
40
+
41
+ ```shell
42
+ npm test
43
+ ```
44
+
45
+ #### API client tests
46
+
47
+ Check out the `run-integration-tests` script in the `package.json` file. If you run the API then run the tests, you
48
+ should see the tests pass.
49
+
50
+ We write those tests using [Bruno](https://docs.usebruno.com/), because Postman is whack and bloaty and impossible to
51
+ version cleanly within a repo.
52
+
53
+ Bruno is pretty neat -- it's made by devs for devs and it has a lot to offer right now, as well as a lot of promise.