Compare commits
No commits in common. "main" and "0.2.3" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.terraform
|
.terraform
|
||||||
node_modules
|
node_modules
|
||||||
/api/deploy
|
|
||||||
/web/dist
|
/web/dist
|
||||||
/dist
|
/dist
|
||||||
|
|
||||||
@ -26,4 +25,3 @@ pnpm-debug.log*
|
|||||||
.*.sw?
|
.*.sw?
|
||||||
|
|
||||||
api/hff_entry_forms_api
|
api/hff_entry_forms_api
|
||||||
api/test/runner
|
|
||||||
|
24
.lvimrc
24
.lvimrc
@ -1,24 +0,0 @@
|
|||||||
" 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
|
|
@ -1,2 +0,0 @@
|
|||||||
opentofu 1.8.1
|
|
||||||
nim 1.6.20
|
|
31
Makefile
31
Makefile
@ -1,35 +1,32 @@
|
|||||||
VERSION:=$(shell git describe --always)
|
VERSION:=$(shell git describe --always)
|
||||||
TARGET_ENV ?= dev
|
TARGET_ENV ?= dev
|
||||||
|
|
||||||
build: api-image dist/hff-entry-forms-web.${TARGET_ENV}.tar.gz
|
build: dist/hff-entry-forms-api.tar.gz dist/hff-entry-forms-web.tar.gz
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -r dist
|
-rm -r dist
|
||||||
make -C api clean
|
-rm -r web/dist
|
||||||
make -C web clean
|
-docker container prune
|
||||||
|
-docker image prune
|
||||||
test:
|
|
||||||
(cd api && nimble unittest)
|
|
||||||
|
|
||||||
update-version:
|
update-version:
|
||||||
operations/update-version.sh
|
operations/update-version.sh
|
||||||
|
|
||||||
api-image:
|
dist/hff-entry-forms-web.tar.gz:
|
||||||
make -C api build-image
|
|
||||||
|
|
||||||
dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz:
|
|
||||||
-mkdir dist
|
-mkdir dist
|
||||||
TARGET_ENV=${TARGET_ENV} make -C web build
|
TARGET_ENV=$(TARGET_ENV) make -C web build
|
||||||
tar czf dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz -C web/dist .
|
tar czf dist/hff-entry-forms-web-${VERSION}.tar.gz -C web/dist .
|
||||||
|
cp dist/hff-entry-forms-web-${VERSION}.tar.gz dist/hff-entry-forms-web.tar.gz
|
||||||
|
|
||||||
deploy-api:
|
deploy-api:
|
||||||
TARGET_ENV=${TARGET_ENV} make -C api build-image push-image publish
|
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
|
||||||
|
|
||||||
deploy-web: dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz
|
deploy-web: dist/hff-entry-forms-web.tar.gz
|
||||||
mkdir -p temp-deploy/hff-entry-forms-web-${VERSION}
|
mkdir -p temp-deploy/hff-entry-forms-web-${VERSION}
|
||||||
tar xzf dist/hff-entry-forms-web-${VERSION}.${TARGET_ENV}.tar.gz -C temp-deploy/hff-entry-forms-web-${VERSION}
|
tar xzf dist/hff-entry-forms-web-${VERSION}.tar.gz -C temp-deploy/hff-entry-forms-web-${VERSION}
|
||||||
aws s3 sync temp-deploy/hff-entry-forms-web-${VERSION} s3://forms.hopefamilyfellowship.com/${TARGET_ENV}/webroot
|
aws s3 sync temp-deploy/hff-entry-forms-web-${VERSION} s3://forms.hopefamilyfellowship.com/$(TARGET_ENV)/webroot
|
||||||
TARGET_ENV=${TARGET_ENV} operations/invalidate-cdn-cache.sh
|
TARGET_ENV=${TARGET_ENV} operations/invalidate-cdn-cache.sh
|
||||||
rm -r temp-deploy
|
rm -r temp-deploy
|
||||||
|
|
||||||
deploy: test deploy-api deploy-web
|
deploy: deploy-api deploy-web
|
||||||
|
79
README.md
79
README.md
@ -1,79 +0,0 @@
|
|||||||
# 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
|
|
||||||
```
|
|
@ -1,11 +1,12 @@
|
|||||||
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.6.10 AS build
|
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.4.8 AS build
|
||||||
|
MAINTAINER jonathan@jdbernard.com
|
||||||
|
|
||||||
COPY hff_entry_forms_api.nimble /hff_entry_forms_api/
|
COPY hff_entry_forms_api.nimble /hff_entry_forms_api/
|
||||||
COPY src /hff_entry_forms_api/src
|
COPY src /hff_entry_forms_api/src
|
||||||
WORKDIR /hff_entry_forms_api
|
WORKDIR /hff_entry_forms_api
|
||||||
RUN nimble build -y --passC:-fpermissive
|
RUN nimble build -y
|
||||||
|
|
||||||
FROM alpine:3.16.4
|
FROM alpine
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
RUN apk -v --update add --no-cache \
|
RUN apk -v --update add --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
|
31
api/Makefile
31
api/Makefile
@ -4,16 +4,7 @@ SOURCES=$(wildcard src/*.nim) $(wildcard src/hff_entry_forms_apipkg/*.nim)
|
|||||||
ECR_ACCOUNT_URL ?= 063932952339.dkr.ecr.us-west-2.amazonaws.com
|
ECR_ACCOUNT_URL ?= 063932952339.dkr.ecr.us-west-2.amazonaws.com
|
||||||
|
|
||||||
# The port on the host machine (not the container)
|
# The port on the host machine (not the container)
|
||||||
ifeq ($(TARGET_ENV),prod)
|
PORT ?= 8300
|
||||||
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.
|
# The Notion integration token.
|
||||||
AUTH_SECRET ?= 123abc
|
AUTH_SECRET ?= 123abc
|
||||||
@ -78,9 +69,6 @@ serve-docker: build-image
|
|||||||
# Utility
|
# Utility
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
clean:
|
|
||||||
-docker image rm $(ECR_ACCOUNT_URL)/hff_entry_forms_api:$(VERSION)
|
|
||||||
|
|
||||||
# Authenticate docker to the AWS private elastic container repository.
|
# Authenticate docker to the AWS private elastic container repository.
|
||||||
ecr-auth:
|
ecr-auth:
|
||||||
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin $(ECR_ACCOUNT_URL)
|
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin $(ECR_ACCOUNT_URL)
|
||||||
@ -91,20 +79,3 @@ echo-vars:
|
|||||||
"VERSION=$(VERSION)\n" \
|
"VERSION=$(VERSION)\n" \
|
||||||
"PORT=$(PORT)\n" \
|
"PORT=$(PORT)\n" \
|
||||||
"INTEGRATION_TOKEN=$(INTEGRATION_TOKEN)\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)"
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.3.3"
|
version = "0.2.3"
|
||||||
author = "Jonathan Bernard"
|
author = "Jonathan Bernard"
|
||||||
description = "Hope Family Fellowship entry forms."
|
description = "Hope Family Fellowship entry forms."
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
@ -15,11 +15,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-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/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/jdb/update-nim-package-version"
|
||||||
requires "https://git.jdb-software.com/hope-family-fellowship/notion-api-client.git"
|
|
||||||
|
|
||||||
task updateVersion, "Update the version of this package.":
|
task updateVersion, "Update the version of this package.":
|
||||||
exec "update_nim_package_version hff_entry_forms_api 'src/hff_entry_forms_apipkg/version.nim'"
|
exec "update_nim_package_version hff_entry_forms_api 'src/hff_entry_forms_apipkg/version.nim'"
|
||||||
|
|
||||||
task unittest, "Runs the unit test suite.":
|
|
||||||
exec "nim c -r test/runner"
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
[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
|
|
@ -1,5 +1,4 @@
|
|||||||
import std/[json, logging, os, sequtils, strutils, tables]
|
import cliutils, docopt, json, logging, sequtils, strutils, tables
|
||||||
import cliutils, docopt
|
|
||||||
|
|
||||||
import hff_entry_forms_apipkg/api
|
import hff_entry_forms_apipkg/api
|
||||||
import hff_entry_forms_apipkg/version
|
import hff_entry_forms_apipkg/version
|
||||||
@ -27,13 +26,12 @@ proc loadConfig(args: Table[string, docopt.Value]): HffEntryFormsApiConfig =
|
|||||||
let cfg = CombinedConfig(docopt: args, json: json)
|
let cfg = CombinedConfig(docopt: args, json: json)
|
||||||
|
|
||||||
result = HffEntryFormsApiConfig(
|
result = HffEntryFormsApiConfig(
|
||||||
debug: cfg.hasKey("debug") and cfg.getVal("debug") == "true",
|
debug: args["--debug"],
|
||||||
eventParentId: cfg.getVal("event-parent-id"),
|
eventParentId: cfg.getVal("event-parent-id"),
|
||||||
integrationToken: cfg.getVal("integration-token"),
|
integrationToken: cfg.getVal("integration-token"),
|
||||||
knownOrigins: cfg.getVal("known-origins")[1..^2].split(',').mapIt(it.strip[1..^2]),
|
knownOrigins: cfg.getVal("known-origins")[1..^2].split(',').mapIt(it[1..^2]),
|
||||||
notionApiBaseUrl: cfg.getVal("notion-api-base-url"),
|
notionApiBaseUrl: cfg.getVal("notion-api-base-url"),
|
||||||
notionVersion: cfg.getVal("notion-version"),
|
notionVersion: cfg.getVal("notion-version"),
|
||||||
notionConfigDbId: cfg.getVal("notion-config-db-id"),
|
|
||||||
port: parseInt(cfg.getVal("port", "8300")))
|
port: parseInt(cfg.getVal("port", "8300")))
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
@ -44,7 +42,7 @@ Usage:
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
-c, --config <cfgFile> Location of the config file to use (defaults to
|
-C, --config <cfgFile> Location of the config file to use (defaults to
|
||||||
hff_entry_forms_api.config.json)
|
hff_entry_forms_api.config.json)
|
||||||
|
|
||||||
-d, --debug Log debugging information.
|
-d, --debug Log debugging information.
|
||||||
@ -54,18 +52,17 @@ Options:
|
|||||||
|
|
||||||
let consoleLogger = newConsoleLogger(
|
let consoleLogger = newConsoleLogger(
|
||||||
levelThreshold=lvlInfo,
|
levelThreshold=lvlInfo,
|
||||||
fmtStr="$appname - $levelname: ",
|
fmtStr="$app - $levelname: ")
|
||||||
useStderr=true)
|
|
||||||
logging.addHandler(consoleLogger)
|
logging.addHandler(consoleLogger)
|
||||||
|
|
||||||
# Initialize our service context
|
# Initialize our service context
|
||||||
let args = docopt(doc, version = HFF_ENTRY_FORMS_API_VERSION)
|
let args = docopt(doc, version = HFF_ENTRY_FORMS_API_VERSION)
|
||||||
|
|
||||||
let cfg = loadConfig(args)
|
if args["--debug"]:
|
||||||
|
|
||||||
if cfg.debug:
|
|
||||||
consoleLogger.levelThreshold = lvlDebug
|
consoleLogger.levelThreshold = lvlDebug
|
||||||
|
|
||||||
|
let cfg = loadConfig(args)
|
||||||
|
|
||||||
if args["serve"]: start(cfg)
|
if args["serve"]: start(cfg)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
|
@ -26,7 +26,7 @@ template jsonResp(code: HttpCode,
|
|||||||
headersToSend: RawHeaders = @{:} ) =
|
headersToSend: RawHeaders = @{:} ) =
|
||||||
## Immediately send a JSON response and stop processing the request.
|
## Immediately send a JSON response and stop processing the request.
|
||||||
let reqOrigin =
|
let reqOrigin =
|
||||||
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
|
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
|
||||||
else: ""
|
else: ""
|
||||||
|
|
||||||
let corsHeaders =
|
let corsHeaders =
|
||||||
@ -34,13 +34,11 @@ template jsonResp(code: HttpCode,
|
|||||||
@{
|
@{
|
||||||
"Access-Control-Allow-Origin": reqOrigin,
|
"Access-Control-Allow-Origin": reqOrigin,
|
||||||
"Access-Control-Allow-Credentials": "true",
|
"Access-Control-Allow-Credentials": "true",
|
||||||
"Access-Control-Allow-Methods": $(reqMethod(request)),
|
"Access-Control-Allow-Methods": $(request.reqMethod),
|
||||||
"Access-Control-Allow-Headers": "Authorization,X-CSRF-TOKEN"
|
"Access-Control-Allow-Headers": "Authorization,X-CSRF-TOKEN"
|
||||||
}
|
}
|
||||||
else: @{:}
|
else: @{:}
|
||||||
|
|
||||||
#debug "Request origin: $#\nKnown origins: $#\nAdding headers:\n$#" %
|
|
||||||
# [ reqOrigin, cfg.knownOrigins.join(" | "), $corsHeaders ]
|
|
||||||
halt(
|
halt(
|
||||||
code,
|
code,
|
||||||
headersToSend & corsHeaders & @{
|
headersToSend & corsHeaders & @{
|
||||||
@ -83,7 +81,7 @@ template errorResp(err: ref ApiError): void =
|
|||||||
template optionsResp(allowedMethods: seq[HttpMethod]) =
|
template optionsResp(allowedMethods: seq[HttpMethod]) =
|
||||||
|
|
||||||
let reqOrigin =
|
let reqOrigin =
|
||||||
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
|
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
|
||||||
else: ""
|
else: ""
|
||||||
|
|
||||||
let corsHeaders =
|
let corsHeaders =
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import std/json, std/times
|
import json, times, timeutils
|
||||||
import hff_notion_api_client/utils, timeutils
|
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
EventProposal* = object
|
EventProposal* = object
|
||||||
@ -28,6 +26,14 @@ proc getOrFail(n: JsonNode, key: string): JsonNode =
|
|||||||
proc parseIso8601(n: JsonNode, key: string): DateTime =
|
proc parseIso8601(n: JsonNode, key: string): DateTime =
|
||||||
return parseIso8601(n.getOrFail(key).getStr)
|
return parseIso8601(n.getOrFail(key).getStr)
|
||||||
|
|
||||||
|
proc textProp(value: string): JsonNode =
|
||||||
|
return %*[
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": { "content": value }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
proc parseEventProposal*(n: JsonNode): EventProposal {.raises: [JsonParsingError].} =
|
proc parseEventProposal*(n: JsonNode): EventProposal {.raises: [JsonParsingError].} =
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -43,37 +49,36 @@ proc parseEventProposal*(n: JsonNode): EventProposal {.raises: [JsonParsingError
|
|||||||
except:
|
except:
|
||||||
raise newException(JsonParsingError, "Invalid EventProposal: " & getCurrentExceptionMsg())
|
raise newException(JsonParsingError, "Invalid EventProposal: " & getCurrentExceptionMsg())
|
||||||
|
|
||||||
proc toPage*(ep: EventProposal): JsonNode =
|
proc asNotionPage*(ep: EventProposal): JsonNode =
|
||||||
result = %*{
|
result = %*{
|
||||||
"properties": {
|
"properties": {
|
||||||
"Event": makeTextProp("title", ep.name),
|
"Event": { "title": textProp(ep.name) },
|
||||||
"Date": { "date": { "start": formatIso8601(ep.date) } },
|
"Date": { "date": { "start": formatIso8601(ep.date) } },
|
||||||
"Department": { "multi_select": [ { "name": ep.department } ] },
|
"Department": { "multi_select": [ { "name": ep.department } ] },
|
||||||
"Location": makeTextProp("rich_text", ep.location),
|
"Location": { "rich_text": textProp(ep.location) },
|
||||||
"Owner": makeTextProp("rich_text", ep.owner),
|
"Owner": { "rich_text": textProp(ep.owner) },
|
||||||
"State": { "select": { "name": "Proposed" } },
|
"State": { "select": { "name": "Proposed" } }
|
||||||
"Visibility": { "select": { "name": "Public" } }
|
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"object": "block",
|
"object": "block",
|
||||||
"type": "heading_2",
|
"type": "heading_2",
|
||||||
"heading_2": makeTextProp("text", "Purpose")
|
"heading_2": { "text": textProp("Purpose") }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"object": "block",
|
"object": "block",
|
||||||
"type": "paragraph",
|
"type": "paragraph",
|
||||||
"paragraph": makeTextProp("text", ep.purpose)
|
"paragraph": { "text": textProp(ep.purpose) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"object": "block",
|
"object": "block",
|
||||||
"type": "heading_2",
|
"type": "heading_2",
|
||||||
"heading_2": makeTextProp("text", "Description")
|
"heading_2": { "text": textProp("Description") }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"object": "block",
|
"object": "block",
|
||||||
"type": "paragraph",
|
"type": "paragraph",
|
||||||
"paragraph": makeTextProp("text", ep.description)
|
"paragraph": { "text": textProp(ep.description) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,28 @@
|
|||||||
import std/[json, httpclient, logging, options, sequtils, strutils]
|
import json, logging, std/httpclient, sequtils, strutils
|
||||||
import hff_notion_api_client
|
|
||||||
import hff_notion_api_client/config
|
|
||||||
|
|
||||||
import ./models, ./service
|
import ./models, ./service
|
||||||
|
|
||||||
var notionClient = none[NotionClient]()
|
proc makeHttpClient(cfg: HffEntryFormsApiConfig): HttpClient =
|
||||||
|
let headers = newHttpHeaders([
|
||||||
proc getNotionClient(cfg: HffEntryFormsApiConfig): NotionClient =
|
("Content-Type", "application/json"),
|
||||||
if notionClient.isNone:
|
("Authorization", "Bearer " & cfg.integrationToken),
|
||||||
notionClient = some(initNotionClient(NotionClientConfig(
|
("Notion-Version", cfg.notionVersion)
|
||||||
apiVersion: cfg.notionVersion,
|
], true)
|
||||||
apiBaseUrl: cfg.notionApiBaseUrl,
|
debug $headers
|
||||||
configDbId: cfg.notionConfigDbId,
|
return newHttpClient(headers = headers, )
|
||||||
integrationToken: cfg.integrationToken)))
|
|
||||||
return notionClient.get
|
|
||||||
|
|
||||||
proc getEventProposalConfig*(cfg: HffEntryFormsApiConfig): EventProposalConfig =
|
proc getEventProposalConfig*(cfg: HffEntryFormsApiConfig): EventProposalConfig =
|
||||||
let notion = getNotionClient(cfg)
|
let http = makeHttpClient(cfg)
|
||||||
|
|
||||||
var bodyJson: JsonNode
|
let apiResp = http.get(cfg.notionApiBaseUrl & "/databases/" & cfg.eventParentId)
|
||||||
try: bodyJson = notion.fetchDatabaseObject(cfg.eventParentId)
|
debug apiResp.status
|
||||||
except:
|
|
||||||
|
if not apiResp.status.startsWith("2"):
|
||||||
|
debug apiResp.body
|
||||||
raiseApiError(Http500,
|
raiseApiError(Http500,
|
||||||
"unable to read event propsal configuration from notion API")
|
"unable to read event propsal configuration from notion API")
|
||||||
|
|
||||||
|
let bodyJson = parseJson(apiResp.body)
|
||||||
let departmentOptionsJson = bodyJson{
|
let departmentOptionsJson = bodyJson{
|
||||||
"properties", "Department", "multi_select", "options"}
|
"properties", "Department", "multi_select", "options"}
|
||||||
|
|
||||||
@ -38,9 +37,13 @@ proc getEventProposalConfig*(cfg: HffEntryFormsApiConfig): EventProposalConfig =
|
|||||||
)
|
)
|
||||||
|
|
||||||
proc createProposedEvent*(cfg: HffEntryFormsApiConfig, ep: EventProposal): bool =
|
proc createProposedEvent*(cfg: HffEntryFormsApiConfig, ep: EventProposal): bool =
|
||||||
let notion = getNotionClient(cfg)
|
let http = makeHttpClient(cfg)
|
||||||
|
let epNotionPage = ep.asNotionPage
|
||||||
|
epNotionPage["parent"] = %*{ "database_id": cfg.eventParentId }
|
||||||
|
|
||||||
try:
|
let apiResp = http.post(cfg.notionApiBaseUrl & "/pages", $epNotionPage)
|
||||||
discard notion.createDbPage(cfg.eventParentId, ep)
|
|
||||||
return true
|
debug apiResp.status
|
||||||
except: return false
|
if not apiResp.status.startsWith("2"): debug apiResp.body
|
||||||
|
|
||||||
|
return apiResp.status.startsWith("2")
|
||||||
|
@ -12,7 +12,6 @@ type
|
|||||||
knownOrigins*: seq[string]
|
knownOrigins*: seq[string]
|
||||||
notionApiBaseUrl*: string
|
notionApiBaseUrl*: string
|
||||||
notionVersion*: string
|
notionVersion*: string
|
||||||
notionConfigDbId*: string
|
|
||||||
port*: int
|
port*: int
|
||||||
|
|
||||||
proc newApiError*(parent: ref Exception = nil, respCode: HttpCode, respMsg: string, msg = ""): ref ApiError =
|
proc newApiError*(parent: ref Exception = nil, respCode: HttpCode, respMsg: string, msg = ""): ref ApiError =
|
||||||
|
@ -1 +1 @@
|
|||||||
const HFF_ENTRY_FORMS_API_VERSION* = "0.3.3"
|
const HFF_ENTRY_FORMS_API_VERSION* = "0.2.3"
|
@ -1,2 +0,0 @@
|
|||||||
switch("path", "../src")
|
|
||||||
switch("verbosity", "0")
|
|
@ -1,3 +0,0 @@
|
|||||||
import std/unittest
|
|
||||||
|
|
||||||
import ./tmodels
|
|
@ -1,20 +0,0 @@
|
|||||||
import std/json, std/times, std/unittest
|
|
||||||
|
|
||||||
import hff_entry_forms_apipkg/models
|
|
||||||
|
|
||||||
suite "models":
|
|
||||||
|
|
||||||
test "toPage(EventProposal)":
|
|
||||||
let ep = EventProposal(
|
|
||||||
name: "Test Event",
|
|
||||||
description: "A test event.",
|
|
||||||
purpose: "Event example for unit testing.",
|
|
||||||
department: "Testing",
|
|
||||||
location: "Hope Family Fellowship",
|
|
||||||
owner: "Jonathan Bernard",
|
|
||||||
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-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.toPage) == expectedJson
|
|
@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
## System Components
|
## System Components
|
||||||
|
|
||||||
* DNS - managed by AWS
|
* DNS - hosted on GoDaddy
|
||||||
* API Server - hosted on `sobeck.jdb-software.com`
|
* API Server - hosted on the `ortis` ECS cluster at JDB Software
|
||||||
* API Loadbalancer - using the main load balancer for JDB Software
|
* API Loadbalancer - using the main load balancer for JDB Software
|
||||||
* Web App - Served by CloudFront from an S3 bucket managed by JDB Software
|
* Web App - Served by CloudFront from an S3 bucket managed by JDB Software
|
||||||
* Certificates - Manually created and validated for \*.HFF.com
|
* Certificates - Manually created and validated for \*.HFF.com
|
||||||
* Notion Integration - defined in Notion, token provided via the environment
|
* Notion Integration - defined in Notion, token provided via AWS secrets
|
||||||
specific config files at `/etc/hff_entry_forms/{dev,prod}.env` and passed
|
manager to the API instance running on the ortis cluster.
|
||||||
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
36
operations/opentofu/.terraform.lock.hcl
generated
@ -1,36 +0,0 @@
|
|||||||
# 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",
|
|
||||||
]
|
|
||||||
}
|
|
70
operations/terraform/deployed_env/ecs.tf
Normal file
70
operations/terraform/deployed_env/ecs.tf
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
69
operations/terraform/deployed_env/iam.tf
Normal file
69
operations/terraform/deployed_env/iam.tf
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
resource "aws_lb_target_group" "hff_entry_forms_api" {
|
resource "aws_lb_target_group" "hff_entry_forms_api" {
|
||||||
name = "${local.environment_name}-${substr(uuid(), 0, 2)}"
|
name = "${local.environment_name}-${substr(uuid(), 0, 2)}"
|
||||||
port = var.target_port
|
port = 80
|
||||||
protocol = "HTTP"
|
protocol = "HTTP"
|
||||||
target_type = "instance"
|
target_type = "instance"
|
||||||
vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id
|
vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id
|
||||||
@ -41,9 +41,3 @@ resource "aws_lb_listener_rule" "hff_entry_forms_api" {
|
|||||||
Environment = local.environment_name
|
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
|
|
||||||
}
|
|
@ -12,10 +12,6 @@ variable "ecr_repo" {
|
|||||||
description = "ECR repository information."
|
description = "ECR repository information."
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "target_port" {
|
|
||||||
description = "The port the deployed service will listen on."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "api_certificate_arn" {
|
variable "api_certificate_arn" {
|
||||||
description = "ARN of the certificate to use for the API loadbalancer."
|
description = "ARN of the certificate to use for the API loadbalancer."
|
||||||
}
|
}
|
@ -15,7 +15,6 @@ module "dev_env" {
|
|||||||
artifact_bucket = aws_s3_bucket.hff_entry_forms
|
artifact_bucket = aws_s3_bucket.hff_entry_forms
|
||||||
cloudfront_certificate_arn = var.cloudfront_certificate_arn
|
cloudfront_certificate_arn = var.cloudfront_certificate_arn
|
||||||
ecr_repo = aws_ecr_repository.hff_entry_forms_api
|
ecr_repo = aws_ecr_repository.hff_entry_forms_api
|
||||||
target_port = 6005
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module "prod_env" {
|
module "prod_env" {
|
||||||
@ -26,14 +25,11 @@ module "prod_env" {
|
|||||||
artifact_bucket = aws_s3_bucket.hff_entry_forms
|
artifact_bucket = aws_s3_bucket.hff_entry_forms
|
||||||
cloudfront_certificate_arn = var.cloudfront_certificate_arn
|
cloudfront_certificate_arn = var.cloudfront_certificate_arn
|
||||||
ecr_repo = aws_ecr_repository.hff_entry_forms_api
|
ecr_repo = aws_ecr_repository.hff_entry_forms_api
|
||||||
target_port = 6006
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data "aws_iam_policy_document" "cloudfront_access_policy" {
|
data "aws_iam_policy_document" "cloudfront_access_policy" {
|
||||||
source_policy_documents = [
|
source_json = "${module.dev_env.oai_access_policy.json}"
|
||||||
module.dev_env.oai_access_policy.json,
|
override_json = "${module.prod_env.oai_access_policy.json}"
|
||||||
module.prod_env.oai_access_policy.json
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_policy" "hff_entry_forms" {
|
resource "aws_s3_bucket_policy" "hff_entry_forms" {
|
@ -10,7 +10,7 @@ rootDir=$(git rev-parse --show-toplevel)
|
|||||||
cd "$rootDir"
|
cd "$rootDir"
|
||||||
|
|
||||||
currentBranch=$(git rev-parse --abbrev-ref HEAD)
|
currentBranch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
if [ "$currentBranch" != "main" ]; then
|
if [ "$currentBranch" != "develop" ]; then
|
||||||
printf "You are currently on the '%s' branch. Is this intended (yes/no)? " "$currentBranch"
|
printf "You are currently on the '%s' branch. Is this intended (yes/no)? " "$currentBranch"
|
||||||
read -r confirmation
|
read -r confirmation
|
||||||
|
|
||||||
@ -60,3 +60,4 @@ git commit -m "Update package version to ${newVersion}"
|
|||||||
printf ">> Tagging commit.\n"
|
printf ">> Tagging commit.\n"
|
||||||
printf "git tag -m \"Version %s\" \"%s\"\n" "$newVersion" "$newVersion"
|
printf "git tag -m \"Version %s\" \"%s\"\n" "$newVersion" "$newVersion"
|
||||||
git tag -m "Version ${newVersion}" "${newVersion}"
|
git tag -m "Version ${newVersion}" "${newVersion}"
|
||||||
|
|
||||||
|
@ -3,6 +3,3 @@ build:
|
|||||||
|
|
||||||
serve:
|
serve:
|
||||||
npm run serve
|
npm run serve
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -r dist
|
|
||||||
|
24
web/README.md
Normal file
24
web/README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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/).
|
9995
web/package-lock.json
generated
9995
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hff-entry-form-web",
|
"name": "hff-entry-form-web",
|
||||||
"version": "0.3.3",
|
"version": "0.2.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "npx servor dist",
|
"serve": "npx servor dist",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, Ref, ref } from 'vue';
|
||||||
import { logService } from '@jdbernard/logging';
|
import { logService } from '@jdbernard/logging';
|
||||||
import {
|
import {
|
||||||
default as api,
|
default as api,
|
||||||
@ -28,8 +28,8 @@ export default defineComponent({
|
|||||||
props: {},
|
props: {},
|
||||||
components: { CircleCheckIcon, CircleCrossIcon, HourGlassIcon, SpinnerIcon },
|
components: { CircleCheckIcon, CircleCrossIcon, HourGlassIcon, SpinnerIcon },
|
||||||
setup: function TheProposeEventView() {
|
setup: function TheProposeEventView() {
|
||||||
const departments = ref<{ value: string; color: string }[]>([]);
|
const departments: Ref<{ value: string; color: string }[]> = ref([]);
|
||||||
const formState = ref<FormState>('loading');
|
const formState: Ref<FormState> = ref('loading');
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
departments.value = (await api.getEventProposalConfig()).departments;
|
departments.value = (await api.getEventProposalConfig()).departments;
|
||||||
@ -62,14 +62,14 @@ export default defineComponent({
|
|||||||
if (await api.proposeEvent(formVal.event)) {
|
if (await api.proposeEvent(formVal.event)) {
|
||||||
formState.value = 'success';
|
formState.value = 'success';
|
||||||
successes.push(
|
successes.push(
|
||||||
`We've recorded the proposed details for ${formVal.event.name}.`,
|
`We've recorded the proposed details for ${formVal.event.name}.`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
formState.value = 'error';
|
formState.value = 'error';
|
||||||
errors.push(
|
errors.push(
|
||||||
'We were unable to record the proposed details for ' +
|
'We were unable to record the proposed details for ' +
|
||||||
formVal.event.name +
|
formVal.event.name +
|
||||||
". Poke Jonathan and tell him it's broken.",
|
". Poke Jonathan and tell him it's broken."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Date and time</span>
|
<span>Date and time</span>
|
||||||
<input
|
<input type="date" name="date" v-model="formVal.event.date" />
|
||||||
type="datetime-local"
|
|
||||||
name="date"
|
|
||||||
v-model="formVal.event.date"
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Department / Event Type</span>
|
<span>Department / Event Type</span>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user