Compare commits

...

17 Commits

Author SHA1 Message Date
4a9d3dab5c Create top-level README. 2024-08-13 09:09:42 -05:00
0f62d5270c operations: Update root Makefile to correctly deploy the API. 2024-08-13 08:41:22 -05:00
e8c6787a83 Update package version to 0.3.3 2024-08-12 17:36:18 -05:00
dedcf3bb70 Update package version to 0.3.3-rc1 2024-08-12 17:31:52 -05:00
7992691d94 api: Make knownOrigins parsing logic more robust. 2024-08-12 17:31:29 -05:00
f848514df1 api: Log CORS headers. 2024-08-12 16:57:52 -05:00
77a89e98aa Update package version to 0.3.2 2024-08-12 16:06:52 -05:00
27a94db3c7 web: Make the event date field aware of time as well. 2024-08-12 16:06:34 -05:00
fa6dd55ba0 api: Pass in the Notion Config DB ID (required for the new client). 2024-08-12 16:04:54 -05:00
2dda8ebd76 api: Clean up Dockerfile and Makefile. 2024-08-12 15:46:52 -05:00
789e702e7d Update package version to 0.3.2-rc2 2024-08-12 14:46:09 -05:00
f9184379b2 api: Fix file naming, add support for DEBUG env var. 2024-08-12 14:45:49 -05:00
dd384f2b53 Pin versions of local tools. 2024-08-12 13:18:37 -05:00
8cbdad0e21 operations: Expect 'main' as the default branch name. 2024-08-12 13:18:19 -05:00
b17520946e api: Update to use hff_notion_api_client instead of defunct notion_utils. 2024-08-12 13:18:02 -05:00
e90f392ef1 Update package version to 0.3.2 2024-08-12 12:15:02 -05:00
9cbc1e708a Migrate off of ECS onto sobeck.jdb-software.com. 2024-08-12 12:14:01 -05:00
35 changed files with 8623 additions and 5611 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.DS_Store
.terraform
node_modules
/api/deploy
/web/dist
/dist

24
.lvimrc Normal file
View File

@ -0,0 +1,24 @@
" The below configuration depends on global versions of some of these tools.
" All tooling can be installed with the following:
"
" asdf local nodejs latest
" npm install -g typescript prettier eslint vue-tsc
"
let g:ale_fixers = {
\ '*': ['remove_trailing_lines', 'trim_whitespace'],
\ 'javascript': ['eslint', 'prettier'],
\ 'typescript': ['prettier'],
\ 'vue': ['prettier'],
\}
let g:ale_linters = {
\ 'markdown': [],
\ 'html': ['HTMLHint', 'proselint', 'write-good'],
\ 'rst': [],
\ 'typescript': ['typescript', 'eslint'],
\ 'javascript': ['eslint'],
\ 'vue': ['vuetsc', 'prettier'],
\}
let g:ale_fix_on_save = 1
"let g:ale_linters_explicit = 1

2
.tool-versions Normal file
View File

@ -0,0 +1,2 @@
opentofu 1.8.1
nim 1.6.20

View File

@ -23,8 +23,7 @@ dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz:
tar czf dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz -C web/dist .
deploy-api:
make -C api build-image push-image
cd operations/terraform && terraform apply -target module.${TARGET_ENV}_env.aws_ecs_task_definition.hff_entry_forms_api -target module.${TARGET_ENV}_env.aws_ecs_service.hff_entry_forms_api
TARGET_ENV=${TARGET_ENV} make -C api build-image push-image publish
deploy-web: dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz
mkdir -p temp-deploy/hff-entry-forms-web-${VERSION}

79
README.md Normal file
View File

@ -0,0 +1,79 @@
# HFF Entry Forms
## Local Development
### Web
```sh
cd web
npm install
make serve & # or run in a separate terminal/pane
make build # as-needed to rebuild (not using hot-reload)
```
### API
```sh
cd api
make serve
make serve-docker # alternatively
```
## Procedures
### Version Bump
1. Ensure you know the current deployed version and the current "marked
version." Check the current deployed version by hitting the live API:
curl https://forms-api.hopefamilyfellowship.com/v1/version
and by querying ECR:
aws ecr describe-images --repository-name 063932952339.dkr.ecr.us-west-2.amazonaws.com/hff_entry_forms_api
Check the current "marked version" by looking at the latest git tag:
git tag -n
2. If necessary, bump the version using:
make update-version
This will invoke `operations/update-version.sh` which:
- updates `web/package.json`,
- updates `web/package-lock.json`,
- updates `api/src/hff_entry_forms_apipkg/version.nim`,
- updates `api/hff_entry_forms_api.nimble`,
- commits all of the above, and
- tags the commit with the new version.
#### Release Candidates (`-rcX` versions)
When we are preparing to release version a new version, we first create release
candidates. So, in preparation for releasing, for example, version 1.2.4, we
do the following:
```sh
# Development is concluded, ready to release 1.2.4
$ make update-version
Last Version: 1.2.3
New Version: 1.2.4-rc1
$ TARGET_ENV=dev make deploy
# verify successful build and deployment
# validate the release in the dev environment
$ make update-version
Last Version: 1.2.4-rc1
New Version: 1.2.4
$ TARGET_ENV=dev make deploy
# verify successful build and deployment
$ TARGET_ENV=prod make deploy
# verify successful build and deployment
# validate the release in the prod environment
```

View File

@ -1,12 +1,11 @@
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.4.8 AS build
MAINTAINER jonathan@jdbernard.com
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.6.10 AS build
COPY hff_entry_forms_api.nimble /hff_entry_forms_api/
COPY src /hff_entry_forms_api/src
WORKDIR /hff_entry_forms_api
RUN nimble build -y
RUN nimble build -y --passC:-fpermissive
FROM alpine
FROM alpine:3.16.4
EXPOSE 80
RUN apk -v --update add --no-cache \
ca-certificates \

View File

@ -4,7 +4,16 @@ SOURCES=$(wildcard src/*.nim) $(wildcard src/hff_entry_forms_apipkg/*.nim)
ECR_ACCOUNT_URL ?= 063932952339.dkr.ecr.us-west-2.amazonaws.com
# The port on the host machine (not the container)
PORT ?= 8300
ifeq ($(TARGET_ENV),prod)
PORT=6006
else
PORT=6005
endif
# The server to target when publishing the API
TARGET_SERVER ?= sobeck.jdb-software.com
TARGET_ENV ?= local
# The Notion integration token.
AUTH_SECRET ?= 123abc
@ -82,3 +91,20 @@ echo-vars:
"VERSION=$(VERSION)\n" \
"PORT=$(PORT)\n" \
"INTEGRATION_TOKEN=$(INTEGRATION_TOKEN)\n"
publish:
-rm -r deploy
-mkdir deploy
m4 \
-D "HFF_ENTRY_FORMS_API_VERSION=$(VERSION)" \
-D "TARGET_ENV=$(TARGET_ENV)" \
-D "TARGET_PORT=$(PORT)" \
hff_entry_forms_api.service \
> deploy/hff_entry_forms_api.$(TARGET_ENV).service
-ssh deployer@$(TARGET_SERVER) "docker stop hff_entry_forms_api.$(TARGET_ENV).service && sudo systemctl stop hff_entry_forms_api.$(TARGET_ENV)"
ssh deployer@$(TARGET_SERVER) "aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin $(ECR_ACCOUNT_URL) && docker pull $(ECR_ACCOUNT_URL)/hff_entry_forms_api:$(VERSION)"
scp \
deploy/hff_entry_forms_api.$(TARGET_ENV).service \
deployer@$(TARGET_SERVER):/etc/systemd/system/hff_entry_forms_api.$(TARGET_ENV).service
ssh deployer@$(TARGET_SERVER) "sudo systemctl daemon-reload"
ssh deployer@$(TARGET_SERVER) "sudo systemctl start hff_entry_forms_api.$(TARGET_ENV)"

View File

@ -1,6 +1,6 @@
# Package
version = "0.3.1"
version = "0.3.3"
author = "Jonathan Bernard"
description = "Hope Family Fellowship entry forms."
license = "GPL-3.0-or-later"
@ -16,7 +16,7 @@ requires @["docopt", "jester"]
requires "https://git.jdb-software.com/jdb/nim-cli-utils.git >= 0.6.3"
requires "https://git.jdb-software.com/jdb/nim-time-utils.git >= 0.5.0"
requires "https://git.jdb-software.com/jdb/update-nim-package-version.git"
requires "https://git.jdb-software.com/hope-family-fellowship/notion_utils.git"
requires "https://git.jdb-software.com/hope-family-fellowship/notion-api-client.git"
task updateVersion, "Update the version of this package.":
exec "update_nim_package_version hff_entry_forms_api 'src/hff_entry_forms_apipkg/version.nim'"

View File

@ -0,0 +1,16 @@
[Unit]
Description=HFF Entry Forms (TARGET_ENV)
After=network-online.target
Requires=docker.service
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/docker rm %n
ExecStart=/usr/bin/docker run --rm -p TARGET_PORT:80 --name %n \
--env-file /etc/hff_entry_forms/TARGET_ENV.env \
063932952339.dkr.ecr.us-west-2.amazonaws.com/hff_entry_forms_api:HFF_ENTRY_FORMS_API_VERSION
ExecStop=/usr/bin/docker stop --name %n
[Install]
WantedBy=default.target

View File

@ -1,4 +1,5 @@
import cliutils, docopt, json, logging, sequtils, strutils, tables
import std/[json, logging, os, sequtils, strutils, tables]
import cliutils, docopt
import hff_entry_forms_apipkg/api
import hff_entry_forms_apipkg/version
@ -26,12 +27,13 @@ proc loadConfig(args: Table[string, docopt.Value]): HffEntryFormsApiConfig =
let cfg = CombinedConfig(docopt: args, json: json)
result = HffEntryFormsApiConfig(
debug: args["--debug"],
debug: cfg.hasKey("debug") and cfg.getVal("debug") == "true",
eventParentId: cfg.getVal("event-parent-id"),
integrationToken: cfg.getVal("integration-token"),
knownOrigins: cfg.getVal("known-origins")[1..^2].split(',').mapIt(it[1..^2]),
knownOrigins: cfg.getVal("known-origins")[1..^2].split(',').mapIt(it.strip[1..^2]),
notionApiBaseUrl: cfg.getVal("notion-api-base-url"),
notionVersion: cfg.getVal("notion-version"),
notionConfigDbId: cfg.getVal("notion-config-db-id"),
port: parseInt(cfg.getVal("port", "8300")))
when isMainModule:
@ -59,11 +61,11 @@ Options:
# Initialize our service context
let args = docopt(doc, version = HFF_ENTRY_FORMS_API_VERSION)
if args["--debug"]:
consoleLogger.levelThreshold = lvlDebug
let cfg = loadConfig(args)
if cfg.debug:
consoleLogger.levelThreshold = lvlDebug
if args["serve"]: start(cfg)
except:

View File

@ -26,7 +26,7 @@ template jsonResp(code: HttpCode,
headersToSend: RawHeaders = @{:} ) =
## Immediately send a JSON response and stop processing the request.
let reqOrigin =
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
else: ""
let corsHeaders =
@ -34,11 +34,13 @@ template jsonResp(code: HttpCode,
@{
"Access-Control-Allow-Origin": reqOrigin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": $(request.reqMethod),
"Access-Control-Allow-Methods": $(reqMethod(request)),
"Access-Control-Allow-Headers": "Authorization,X-CSRF-TOKEN"
}
else: @{:}
#debug "Request origin: $#\nKnown origins: $#\nAdding headers:\n$#" %
# [ reqOrigin, cfg.knownOrigins.join(" | "), $corsHeaders ]
halt(
code,
headersToSend & corsHeaders & @{
@ -81,7 +83,7 @@ template errorResp(err: ref ApiError): void =
template optionsResp(allowedMethods: seq[HttpMethod]) =
let reqOrigin =
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
else: ""
let corsHeaders =

View File

@ -1,5 +1,5 @@
import std/json, std/times
import notion_utils, timeutils
import hff_notion_api_client/utils, timeutils
type
@ -43,7 +43,7 @@ proc parseEventProposal*(n: JsonNode): EventProposal {.raises: [JsonParsingError
except:
raise newException(JsonParsingError, "Invalid EventProposal: " & getCurrentExceptionMsg())
proc asNotionPage*(ep: EventProposal): JsonNode =
proc toPage*(ep: EventProposal): JsonNode =
result = %*{
"properties": {
"Event": makeTextProp("title", ep.name),

View File

@ -1,22 +1,29 @@
import std/json, std/logging, std/httpclient, std/sequtils, std/strutils
import notion_utils
import std/[json, httpclient, logging, options, sequtils, strutils]
import hff_notion_api_client
import hff_notion_api_client/config
import ./models, ./service
var notionClient = none[NotionClient]()
proc getNotionClient(cfg: HffEntryFormsApiConfig): NotionClient =
if notionClient.isNone:
notionClient = some(initNotionClient(NotionClientConfig(
apiVersion: cfg.notionVersion,
apiBaseUrl: cfg.notionApiBaseUrl,
configDbId: cfg.notionConfigDbId,
integrationToken: cfg.integrationToken)))
return notionClient.get
proc getEventProposalConfig*(cfg: HffEntryFormsApiConfig): EventProposalConfig =
let http = newNotionClient(
apiVersion = cfg.notionVersion,
integrationToken = cfg.integrationToken)
let notion = getNotionClient(cfg)
let apiResp = http.get(cfg.notionApiBaseUrl & "/databases/" & cfg.eventParentId)
debug apiResp.status
if not apiResp.status.startsWith("2"):
debug apiResp.body
var bodyJson: JsonNode
try: bodyJson = notion.fetchDatabaseObject(cfg.eventParentId)
except:
raiseApiError(Http500,
"unable to read event propsal configuration from notion API")
let bodyJson = parseJson(apiResp.body)
let departmentOptionsJson = bodyJson{
"properties", "Department", "multi_select", "options"}
@ -31,16 +38,9 @@ proc getEventProposalConfig*(cfg: HffEntryFormsApiConfig): EventProposalConfig =
)
proc createProposedEvent*(cfg: HffEntryFormsApiConfig, ep: EventProposal): bool =
let http = newNotionClient(
apiVersion = cfg.notionVersion,
integrationToken = cfg.integrationToken)
let notion = getNotionClient(cfg)
let epNotionPage = ep.asNotionPage
epNotionPage["parent"] = %*{ "database_id": cfg.eventParentId }
let apiResp = http.post(cfg.notionApiBaseUrl & "/pages", $epNotionPage)
debug apiResp.status
if not apiResp.status.startsWith("2"): debug apiResp.body
return apiResp.status.startsWith("2")
try:
discard notion.createDbPage(cfg.eventParentId, ep)
return true
except: return false

View File

@ -12,6 +12,7 @@ type
knownOrigins*: seq[string]
notionApiBaseUrl*: string
notionVersion*: string
notionConfigDbId*: string
port*: int
proc newApiError*(parent: ref Exception = nil, respCode: HttpCode, respMsg: string, msg = ""): ref ApiError =

View File

@ -1 +1 @@
const HFF_ENTRY_FORMS_API_VERSION* = "0.3.1"
const HFF_ENTRY_FORMS_API_VERSION* = "0.3.3"

View File

@ -4,7 +4,7 @@ import hff_entry_forms_apipkg/models
suite "models":
test "asNotionPage(EventProposal)":
test "toPage(EventProposal)":
let ep = EventProposal(
name: "Test Event",
description: "A test event.",
@ -15,6 +15,6 @@ suite "models":
date: parse("2021-10-30", "YYYY-MM-dd"),
budgetInDollars: 56)
let expectedJson = """{"properties":{"Event":{"title":[{"type":"text","text":{"content":"Test Event"}}]},"Date":{"date":{"start":"2021-10-30T00:00:00.000-05:00"}},"Department":{"multi_select":[{"name":"Testing"}]},"Location":{"rich_text":[{"type":"text","text":{"content":"Hope Family Fellowship"}}]},"Owner":{"rich_text":[{"type":"text","text":{"content":"Jonathan Bernard"}}]},"State":{"select":{"name":"Proposed"}},"Visibility":{"select":{"name":"Public"}}},"children":[{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Purpose"}}]}},{"object":"block","type":"paragraph","paragraph":{"text":[{"type":"text","text":{"content":"Event example for unit testing."}}]}},{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Description"}}]}},{"object":"block","type":"paragraph","paragraph":{"text":[{"type":"text","text":{"content":"A test event."}}]}}]}"""
let expectedJson = """{"properties":{"Event":{"title":[{"type":"text","text":{"content":"Test Event"}}]},"Date":{"date":{"start":"2021-10-30T00:00:00-05:00"}},"Department":{"multi_select":[{"name":"Testing"}]},"Location":{"rich_text":[{"type":"text","text":{"content":"Hope Family Fellowship"}}]},"Owner":{"rich_text":[{"type":"text","text":{"content":"Jonathan Bernard"}}]},"State":{"select":{"name":"Proposed"}},"Visibility":{"select":{"name":"Public"}}},"children":[{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Purpose"}}]}},{"object":"block","type":"paragraph","paragraph":{"text":[{"type":"text","text":{"content":"Event example for unit testing."}}]}},{"object":"block","type":"heading_2","heading_2":{"text":[{"type":"text","text":{"content":"Description"}}]}},{"object":"block","type":"paragraph","paragraph":{"text":[{"type":"text","text":{"content":"A test event."}}]}}]}"""
check $(ep.asNotionPage) == expectedJson
check $(ep.toPage) == expectedJson

View File

@ -2,10 +2,12 @@
## System Components
* DNS - hosted on GoDaddy
* API Server - hosted on the `ortis` ECS cluster at JDB Software
* DNS - managed by AWS
* API Server - hosted on `sobeck.jdb-software.com`
* API Loadbalancer - using the main load balancer for JDB Software
* Web App - Served by CloudFront from an S3 bucket managed by JDB Software
* Certificates - Manually created and validated for \*.HFF.com
* Notion Integration - defined in Notion, token provided via AWS secrets
manager to the API instance running on the ortis cluster.
* Notion Integration - defined in Notion, token provided via the environment
specific config files at `/etc/hff_entry_forms/{dev,prod}.env` and passed
into the docker container at runtime ad defined in the SystemD service file
(see `api/hff_entry_forms_api.service`)

36
operations/opentofu/.terraform.lock.hcl generated Normal file
View File

@ -0,0 +1,36 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/aws" {
version = "5.62.0"
hashes = [
"h1:DzXMlmL2hRPfACAbN1PUhnLDGY9Kl0vbrt05qSfGsxA=",
"zh:2cb519ce7f3cbcb88b2e93dd3b3424ad85a347fc0e7429661945da5df8a20fda",
"zh:2fc7ed911cceaa1652d1f4090eaa91e8463aba86873910bccf16601260379886",
"zh:395b32d157adeb92571a0efd230c73bbee01744782a50356fb16e8946bd63ffb",
"zh:43303d36af40a568cd40bd54dc9e8430e18c4a4d78682b459dca8c755c717a0c",
"zh:65b2c6e955deeeffb9d9cd4ed97e8c532a453ba690d0e3d88c740f9036bccc4d",
"zh:a9d09dc9daf33b16894ed7d192ceb4c402261da58cded503a3ffa1dd2373e3fb",
"zh:c5e9f8bc4397c2075b6dc62458be51b93322517affd760c161633d56b0b9a334",
"zh:db0921c091402179edd549f8aa4f12dce18aab09d4302e800c67d6ec6ff88a86",
"zh:e7d13f9c0891446d03c29e4fcd60de633f71bbf1bc9786fca47a0ee356ac979a",
"zh:f128a725dbdbd31b9ed8ea478782152339c9fab4d635485763c8da2a477fe3f6",
]
}
provider "registry.opentofu.org/hashicorp/external" {
version = "2.3.3"
hashes = [
"h1:bDJy8Mj5PMTEuxm6Wu9A9dATBL+mQDmHx8NnLzjvCcc=",
"zh:1ec36864a1872abdfd1c53ba3c6837407564ac0d86ab80bf4fdc87b41106fe68",
"zh:2117e0edbdc88f0d22fe02fe6b2cfbbbc5d5ce40f8f58e484d8d77d64dd7340f",
"zh:4bcfdacd8e2508c16e131de9072cecd359e0ade3b8c6798a049883f37a5872ea",
"zh:4da71bc601a37bf8b7413c142d43f5f28e97e531d4836ee8624f41b9fb62e250",
"zh:55b9eebac79a46f88db5615f1ee0ac4c3f9351caa4eb8542171ef5d87de60338",
"zh:74d64afaef190321f8ddf1c4a9c6489d6cf51098704a2456c1553406e8306328",
"zh:8a357e51a0ec69872fafc64da3c6a1039277d325255ef5a264b727d83995d18b",
"zh:aacd2e6c13fe19115d51cd28a40a28da017bb48c2e18dec4460d1c37506b1495",
"zh:e19c8bdf0e059341d008a50f9138c44009e9ebb3a8047a300e6bc63ed8af8ea0",
"zh:fafa9639d8b8402e35f3864c6cfb0762ec57cc365a8f383e2acf81105b1b9eea",
]
}

View File

@ -1,6 +1,6 @@
resource "aws_lb_target_group" "hff_entry_forms_api" {
name = "${local.environment_name}-${substr(uuid(), 0, 2)}"
port = 80
port = var.target_port
protocol = "HTTP"
target_type = "instance"
vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id
@ -41,3 +41,9 @@ resource "aws_lb_listener_rule" "hff_entry_forms_api" {
Environment = local.environment_name
}
}
resource "aws_lb_target_group_attachment" "hff_entry_forms_api" {
target_group_arn = aws_lb_target_group.hff_entry_forms_api.arn
target_id = data.terraform_remote_state.jdbsoft.outputs.sobeck-instance-id
port = var.target_port
}

View File

@ -12,6 +12,10 @@ variable "ecr_repo" {
description = "ECR repository information."
}
variable "target_port" {
description = "The port the deployed service will listen on."
}
variable "api_certificate_arn" {
description = "ARN of the certificate to use for the API loadbalancer."
}

View File

@ -15,6 +15,7 @@ module "dev_env" {
artifact_bucket = aws_s3_bucket.hff_entry_forms
cloudfront_certificate_arn = var.cloudfront_certificate_arn
ecr_repo = aws_ecr_repository.hff_entry_forms_api
target_port = 6005
}
module "prod_env" {
@ -25,11 +26,14 @@ module "prod_env" {
artifact_bucket = aws_s3_bucket.hff_entry_forms
cloudfront_certificate_arn = var.cloudfront_certificate_arn
ecr_repo = aws_ecr_repository.hff_entry_forms_api
target_port = 6006
}
data "aws_iam_policy_document" "cloudfront_access_policy" {
source_json = "${module.dev_env.oai_access_policy.json}"
override_json = "${module.prod_env.oai_access_policy.json}"
source_policy_documents = [
module.dev_env.oai_access_policy.json,
module.prod_env.oai_access_policy.json
]
}
resource "aws_s3_bucket_policy" "hff_entry_forms" {

View File

@ -1,70 +0,0 @@
resource "aws_secretsmanager_secret" "hff_entry_forms_api" {
name = "${local.environment_name}-Config"
tags = { Environment = local.environment_name }
}
resource "aws_ecs_task_definition" "hff_entry_forms_api" {
family = local.environment_name
network_mode = "bridge"
requires_compatibilities = ["EC2"]
execution_role_arn = aws_iam_role.ecs_task.arn
# See https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html
container_definitions = jsonencode([
{
name = local.environment_name
image = "${var.ecr_repo.repository_url}:${data.external.git_describe.result.version}"
cpu = 128
memory = 128
memoryReservation = 32
environment = [
{
name = "PORT"
value = "80"
}
]
portMappings = [
{
protocol = "tcp"
containerPort = 80
}
]
secrets = [
{
name = "INTEGRATION_TOKEN"
description = "Connection string with user credentials."
valueFrom = "${aws_secretsmanager_secret.hff_entry_forms_api.arn}:integrationToken::"
},
{
name = "KNOWN_ORIGINS"
description = "Connection string with user credentials."
valueFrom = "${aws_secretsmanager_secret.hff_entry_forms_api.arn}:knownOrigins::"
}
]
}
])
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}
resource "aws_ecs_service" "hff_entry_forms_api" {
name = local.environment_name
cluster = data.terraform_remote_state.jdbsoft.outputs.aws_ecs_cluster_ortis.id
task_definition = aws_ecs_task_definition.hff_entry_forms_api.arn
desired_count = 1
launch_type = "EC2"
load_balancer {
target_group_arn = aws_lb_target_group.hff_entry_forms_api.arn
container_name = local.environment_name
container_port = 80
}
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}

View File

@ -1,69 +0,0 @@
resource "aws_iam_role" "ecs_task" {
name = "${local.environment_name}-EcsTaskRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
inline_policy {
name = "AllowSecretsAccessForHffEntryFormsApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"kms:Decrypt"
]
Resource = [
aws_secretsmanager_secret.hff_entry_forms_api.arn
]
}
]
})
}
inline_policy {
name = "AllowAccessToEcrForHffEntryFormsApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken"
]
Resource = [ "*" ]
},
{
Effect = "Allow"
Action = [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:GetDownloadUrlForLayer"
]
Resource = [
var.ecr_repo.arn
]
}
]
})
}
tags = {
Name = "HffEntryForms-EcsTaskRole"
Environment = local.environment_name
}
}

View File

@ -10,7 +10,7 @@ rootDir=$(git rev-parse --show-toplevel)
cd "$rootDir"
currentBranch=$(git rev-parse --abbrev-ref HEAD)
if [ "$currentBranch" != "develop" ]; then
if [ "$currentBranch" != "main" ]; then
printf "You are currently on the '%s' branch. Is this intended (yes/no)? " "$currentBranch"
read -r confirmation
@ -60,4 +60,3 @@ git commit -m "Update package version to ${newVersion}"
printf ">> Tagging commit.\n"
printf "git tag -m \"Version %s\" \"%s\"\n" "$newVersion" "$newVersion"
git tag -m "Version ${newVersion}" "${newVersion}"

View File

@ -1,24 +0,0 @@
# hff-entry-form-web
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

13739
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "hff-entry-form-web",
"version": "0.3.1",
"version": "0.3.3",
"private": true,
"scripts": {
"serve": "npx servor dist",

View File

@ -1,4 +1,4 @@
import { defineComponent, Ref, ref } from 'vue';
import { defineComponent, ref } from 'vue';
import { logService } from '@jdbernard/logging';
import {
default as api,
@ -28,8 +28,8 @@ export default defineComponent({
props: {},
components: { CircleCheckIcon, CircleCrossIcon, HourGlassIcon, SpinnerIcon },
setup: function TheProposeEventView() {
const departments: Ref<{ value: string; color: string }[]> = ref([]);
const formState: Ref<FormState> = ref('loading');
const departments = ref<{ value: string; color: string }[]>([]);
const formState = ref<FormState>('loading');
setTimeout(async () => {
departments.value = (await api.getEventProposalConfig()).departments;
@ -62,14 +62,14 @@ export default defineComponent({
if (await api.proposeEvent(formVal.event)) {
formState.value = 'success';
successes.push(
`We've recorded the proposed details for ${formVal.event.name}.`
`We've recorded the proposed details for ${formVal.event.name}.`,
);
} else {
formState.value = 'error';
errors.push(
'We were unable to record the proposed details for ' +
formVal.event.name +
". Poke Jonathan and tell him it's broken."
". Poke Jonathan and tell him it's broken.",
);
}

View File

@ -18,7 +18,11 @@
</label>
<label>
<span>Date and time</span>
<input type="date" name="date" v-model="formVal.event.date" />
<input
type="datetime-local"
name="date"
v-model="formVal.event.date"
/>
</label>
<label>
<span>Department / Event Type</span>