119 Commits

Author SHA1 Message Date
10c9ad393d update version 2024-04-29 13:01:18 +02:00
c1df052462 ++ version zitadel 2024-04-22 16:49:11 +02:00
64b9361651 Add daisy ui and tailwind 2024-04-22 15:47:54 +02:00
Vargha Csongor
e5425b1cc8 Merge pull request #5 from vcscsvcscs/feature/add-basic-backend-functionalities
[Feature] Add backend functionalities
2024-04-22 08:39:16 +02:00
5a7e62a183 update ci to only trigger once 2024-04-22 08:38:33 +02:00
5e871cb272 fix some miss conventions 2024-04-22 08:37:21 +02:00
22ca38ad86 fix bidirectional relationships 2024-04-22 08:29:04 +02:00
3ade387d7d view family tree 2024-04-22 08:27:44 +02:00
77042ffdc5 Add verified to Person 2024-04-18 23:10:16 +02:00
5b9b6c53a6 Add ' to query 2024-04-18 23:10:00 +02:00
0b0b138c16 change errors to contain original 2024-04-18 23:09:42 +02:00
304552c2a5 Add ' to query 2024-04-18 23:09:23 +02:00
cf4b79c593 uuid to memgraph compatible 2024-04-18 23:08:52 +02:00
bc7cf7f1a6 create memgraph compatible uuid 2024-04-18 23:08:35 +02:00
e49aba7c58 fix api 2024-04-18 23:07:12 +02:00
01c6e4b0c9 Fix update person 2024-04-18 23:07:03 +02:00
564ef322e3 set uuid in a function after verify 2024-04-18 22:36:06 +02:00
d85d37eb2d Change responses 2024-04-18 22:35:48 +02:00
913042d441 add error logging and modify return 2024-04-18 22:35:32 +02:00
56607b31e5 correct typo 2024-04-18 22:35:02 +02:00
a5822913f6 create Relationship and person 2024-04-18 21:02:04 +02:00
5d03c51097 fix viewPerson query to be secure 2024-04-18 21:01:52 +02:00
5e8cdecca7 change bolt+s protocol to bolt+ssc in default for memgraph 2024-04-18 21:01:33 +02:00
40a70ecc93 Remove wrong driver close 2024-04-18 21:01:01 +02:00
12bb08d6ce fix typo and add handlers to router 2024-04-16 23:16:52 +02:00
eadfcd7afc update person 2024-04-16 23:13:16 +02:00
5d19dad30f change status code 2024-04-16 23:13:08 +02:00
7358ef5db1 delete person 2024-04-16 23:12:54 +02:00
68bd7dec11 update person to string 2024-04-16 23:00:06 +02:00
f10d8a87db verify relationship 2024-04-16 22:59:35 +02:00
72f81214be use snake case file naming convention 2024-04-16 22:45:07 +02:00
47b52d8a33 fix multi direction relationships 2024-04-16 22:27:11 +02:00
2e4cd879b2 delete relationship 2024-04-16 22:26:55 +02:00
ca67dead2b add createRelationship as a handler 2024-04-15 23:56:52 +02:00
162fe47051 create handler files 2024-04-15 23:47:25 +02:00
d49601b871 fix logging in createPerson 2024-04-15 23:46:32 +02:00
f5e95095c7 create relationship 2024-04-15 23:46:11 +02:00
c54b142b70 verify structs 2024-04-15 23:45:51 +02:00
65345e0e76 cypher injection prevention 2024-04-15 23:45:01 +02:00
a34a934bf4 move utilities to remote instead local 2024-04-15 21:13:19 +02:00
73627c7c59 Add utilities to docker files 2024-04-15 21:05:32 +02:00
33aa4945af go mod tidy 2024-04-15 20:50:46 +02:00
3b12f4798c Add Person model 2024-04-14 23:49:33 +02:00
cb6628a83c implement Get person endpoint 2024-04-14 19:21:20 +02:00
a4c1bc56f8 Fix release pipeline tagging 2024-04-14 17:13:59 +02:00
79256f2f10 Merge branch 'main' into feature/add-basic-backend-functionalities 2024-04-14 11:47:32 +02:00
cdea69736d Add init database.go 2024-04-14 11:46:46 +02:00
Vargha Csongor
ffde94d457 Merge pull request #4 from vcscsvcscs/feature/server-and-logger-setup-utilities
Move server and logger setup to utilities module
2024-04-14 11:18:27 +02:00
423e5502a7 Create http server setup utility 2024-04-14 11:16:34 +02:00
422e2f683a Create Logger setup utility 2024-04-14 11:16:17 +02:00
35f478e24c create memgraph db connection 2024-04-14 11:15:01 +02:00
a6718b2487 update docker build action 2024-04-14 01:25:56 +02:00
Vargha Csongor
317fc9067e Merge pull request #3 from vcscsvcscs/bugfix/update-go-version
update go version to 1.22.2
2024-04-14 01:12:25 +02:00
4c1b5bcce4 update go version to 1.22.2 2024-04-14 01:11:19 +02:00
Vargha Csongor
30321ba5d7 Merge pull request #1 from vcscsvcscs/feature/create-kubernetes-deployments
Feature Create kubernetes deployments
2024-04-14 00:55:29 +02:00
f42d966183 set for main revision in argocd 2024-04-14 00:54:45 +02:00
94820faf8c docker push to use linux/arm64/v8 2024-04-14 00:16:18 +02:00
8d1bb6dbfe Trial and error with busybox and designated platform 2024-04-14 00:10:48 +02:00
150d3d7b33 Change image to arm64v8 2024-04-14 00:03:14 +02:00
d004a155d7 Add default cert-issuer.yaml 2024-04-13 23:58:03 +02:00
9996b79b46 Add deployment command to readme 2024-04-13 21:14:07 +02:00
504de2fc2e Add argocd project yaml 2024-04-13 21:13:43 +02:00
36b34a161f change default cocroach db volume size 2024-04-13 21:13:17 +02:00
Vargha Csongor
87b63e40ce Merge pull request #2 from vcscsvcscs/feature/create-backend-skeleton
[Feature] Create backend skeleton
2024-04-12 13:22:06 +02:00
3e40c09f18 modify argo app names 2024-03-20 13:14:45 +01:00
ab007395bc fix deployments for backend and auth-service 2024-03-20 13:09:13 +01:00
a1e14f0a7b remove update strategy from deployment.yaml 2024-03-20 12:57:38 +01:00
2ba61b0854 fix namespace for argo deployment 2024-03-20 12:57:17 +01:00
8bf6027e42 Add argo files and kustomize 2024-03-18 22:21:29 +01:00
723c108ec4 rename hpas to use snake case for trial error 2024-03-18 20:32:00 +01:00
2e718c06f6 backend deployment 2024-03-17 16:59:32 +01:00
26c227c062 auth service deployment 2024-03-17 16:59:09 +01:00
2e720c326a remove zitadel deploy (It is done by argocd) 2024-03-17 16:15:28 +01:00
608b0e3fb2 add localhost to accepted endpoints in ca 2024-03-17 15:43:57 +01:00
a88550f8cd add dns to memegraph certificates 2024-03-17 13:03:04 +01:00
b06cfcaf69 fix type certificates 2024-03-17 12:45:41 +01:00
a78161d389 Add certificates.yaml to kustomize (memgraph) 2024-03-17 12:44:05 +01:00
0aad11b1d6 memgraph deployment 2024-03-17 12:41:50 +01:00
4199d65d6c lint 2024-03-17 11:11:46 +01:00
d533738a32 add first instance admin 2024-03-16 19:35:28 +01:00
a0233fdc2d swithc zitadel to simple subdomain 2024-03-16 15:34:01 +01:00
e89b1638d9 extend zitadel ingressroute, modify rule to be root path on domain 2024-03-16 14:59:17 +01:00
2492b94be8 zitadel ingress port 2024-03-16 14:45:09 +01:00
518f86caa4 correct zitadel db url 2024-03-16 13:55:22 +01:00
303c582217 fix zitadel values secret mount 2024-03-16 13:48:55 +01:00
cc9fbd42da fix secret mount for zitadel cert-job 2024-03-16 13:45:48 +01:00
16457111cd fix namespace issue once and for all 2024-03-16 13:40:45 +01:00
28395c2859 set namespace for zitadel kustomize 2024-03-16 13:31:41 +01:00
b7eaa44d26 put certjob to sync phase 2024-03-16 13:03:06 +01:00
ac514578f3 Add init annotations to cockroachdb 2024-03-16 13:00:20 +01:00
13f0eea33c fix zitadel deployment phases 2024-03-16 12:25:13 +01:00
91376d87de update zitadel patch 2024-03-16 12:14:31 +01:00
0226b93135 update zitadel job annotation 2024-03-16 12:10:52 +01:00
65f6200b81 add argocd annotations for deployment order 2024-03-16 11:55:44 +01:00
6e7e767f54 update argo annotation 2024-03-16 11:26:12 +01:00
30ae64e2f6 patch batch v1 2024-03-16 11:10:38 +01:00
cda79c4428 switch zitadel to cockroachDB 2024-03-16 10:52:29 +01:00
db0b28ca79 attempt to fix psql chart 2024-03-16 09:02:56 +01:00
befd578bf9 Add zitadel deployment 2024-03-15 21:37:43 +01:00
2e233abfd6 update kube actions 2024-03-11 20:39:03 +01:00
d3be181d77 fix action go dependency install 2024-03-11 19:57:14 +01:00
a230886c40 fix ci triggers 2024-03-11 19:50:57 +01:00
4bc365c4a2 test release cd 2024-03-11 19:48:35 +01:00
26b446ab54 fix docker build action context 2024-03-11 19:47:33 +01:00
a4a79a1136 rename cd pipelines 2024-03-11 19:43:07 +01:00
4764be0c2e fix kubernetes actions 2024-03-11 19:38:09 +01:00
2cb55937c9 test docker build and push 2024-03-11 19:23:09 +01:00
de64411eb0 build and push images to dockerhub 2024-03-11 00:33:09 +01:00
1ee384813b Add svelte ci 2024-03-11 00:25:21 +01:00
bb7370ae33 Add zitadel values file template from example 2024-03-10 20:52:05 +01:00
3c47578809 fix workflow references 2024-02-28 15:05:35 +01:00
23743a23b9 Attempt fixing ci 2024-02-28 15:03:54 +01:00
e2450247e7 test different workflow reference 2024-02-28 14:54:56 +01:00
3d9398e9c1 Fix ci reference 2024-02-28 14:45:26 +01:00
59b3b31b44 Create ci for subprojects 2024-02-28 13:59:22 +01:00
3a2dd1972a init go ci 2024-02-26 15:45:27 +01:00
48e7e414dd move workflows to their place 2024-02-26 15:21:20 +01:00
494278875b init ci cd yml-s 2024-02-26 14:33:32 +01:00
d0915986a6 init go dockerfiles 2024-02-26 14:26:24 +01:00
78 changed files with 4909 additions and 99 deletions

51
.github/workflows/auth-service-cd.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Release Auth service to Docker Hub and Deploy to Kubernetes
on:
push:
paths:
- "auth-service/**"
- "deployment/auth-service/**"
- ".github/workflows/auth-service-cd.yml"
jobs:
docker:
name: Build and Push Auth-service image to Docker Hub
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
id: create_image_tag
with:
script: |
if(github.ref == 'refs/heads/main') {
return 'latest';
} else if(context.issue.number) {
return "pr" + context.issue.number;
} else {
return "pr" + (
await github.rest.repos.listPullRequestsAssociatedWithCommit({
commit_sha: context.sha,
owner: context.repo.owner,
repo: context.repo.repo,
})
).data[0].number;
}
result-encoding: string
- name: Image tag
run: echo '${{steps.create_image_tag.outputs.result}}'
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
context: "{{defaultContext}}:auth-service"
tags: vcscsvcscs/gheritage-auth-service:${{steps.create_image_tag.outputs.result}}
platforms: linux/arm64/v8

20
.github/workflows/auth-service-ci.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Authentication service Continuous Integration
on:
push:
paths:
- "auth-service/**"
jobs:
lint:
uses: ./.github/workflows/go_lint.yml
with:
working-directory: 'auth-service'
build:
needs: lint
uses: ./.github/workflows/go_build.yml
with:
working-directory: 'auth-service'
test:
needs: build
uses: ./.github/workflows/go_test.yml
with:
working-directory: 'auth-service'

51
.github/workflows/backend-cd.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Release Backend service to Docker Hub and Deploy to Kubernetes
on:
push:
paths:
- "backend/**"
- "deployment/backend/**"
- ".github/workflows/backend-cd.yml"
jobs:
docker:
name: Build and Push Backend image to Docker Hub
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
id: create_image_tag
with:
script: |
if(github.ref == 'refs/heads/main') {
return 'latest';
} else if(context.issue.number) {
return "pr" + context.issue.number;
} else {
return "pr" + (
await github.rest.repos.listPullRequestsAssociatedWithCommit({
commit_sha: context.sha,
owner: context.repo.owner,
repo: context.repo.repo,
})
).data[0].number;
}
result-encoding: string
- name: Image tag
run: echo '${{steps.create_image_tag.outputs.result}}'
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
context: "{{defaultContext}}:backend"
tags: vcscsvcscs/gheritage-backend-service:${{steps.create_image_tag.outputs.result}}
platforms: linux/arm64/v8

20
.github/workflows/backend-ci.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Backend Continuous Integration
on:
push:
paths:
- "backend/**"
jobs:
lint:
uses: ./.github/workflows/go_lint.yml
with:
working-directory: 'backend'
build:
needs: lint
uses: ./.github/workflows/go_build.yml
with:
working-directory: 'backend'
test:
needs: build
uses: ./.github/workflows/go_test.yml
with:
working-directory: 'backend'

15
.github/workflows/frontend-ci.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Frontend Continuous Integration
on:
push:
paths:
- "frontend/**"
jobs:
lint:
uses: ./.github/workflows/svelte_lint.yml
with:
working-directory: 'frontend'
build:
needs: lint
uses: ./.github/workflows/svelte_build.yml
with:
working-directory: 'frontend'

29
.github/workflows/go_build.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go 1.22.x'
uses: actions/setup-go@v5
with:
go-version: '1.22.x'
- name: Display Go version
run: go version
- name: Install dependencies
run: |
cd ${{ inputs.working-directory }}
go get
- name: Build
run: |
cd ${{ inputs.working-directory }}
go build .

22
.github/workflows/go_lint.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
working-directory: ${{ inputs.working-directory }}

29
.github/workflows/go_test.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go 1.22.x'
uses: actions/setup-go@v5
with:
go-version: '1.22.x'
- name: Display Go version
run: go version
- name: Install dependencies
run: |
cd ${{ inputs.working-directory }}
go get
- name: Run tests
run: |
cd ${{ inputs.working-directory }}
go test ./...

24
.github/workflows/svelte_build.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '21.x'
- name: Install dependencies
run: |
cd ${{ inputs.working-directory }}
npm ci
- name: Build
run: |
cd ${{ inputs.working-directory }}
npm run build

24
.github/workflows/svelte_lint.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
on:
workflow_call:
inputs:
working-directory:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '21.x'
- name: Install dependencies
run: |
cd ${{ inputs.working-directory }}
npm ci
- name: Lint
run: |
cd ${{ inputs.working-directory }}
npm run lint

132
.golangci.yml Normal file
View File

@@ -0,0 +1,132 @@
linters-settings:
depguard:
rules:
logger:
deny:
# logging is allowed only by logutils.Log,
# logrus is allowed to use only in logutils package.
- pkg: "github.com/sirupsen/logrus"
desc: logging is allowed only by logutils.Log.
- pkg: "github.com/pkg/errors"
desc: Should be replaced by standard lib errors package.
- pkg: "github.com/instana/testify"
desc: It's a fork of github.com/stretchr/testify.
dupl:
threshold: 100
funlen:
lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner.
statements: 50
goconst:
min-len: 2
min-occurrences: 3
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
gocyclo:
min-complexity: 15
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
gomnd:
# don't include the "operation" and "assign"
checks:
- argument
- case
- condition
- return
ignored-numbers:
- '0'
- '1'
- '2'
- '3'
ignored-functions:
- strings.SplitN
govet:
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
enable:
- nilness
- shadow
errorlint:
asserts: false
lll:
line-length: 140
misspell:
locale: US
nolintlint:
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
revive:
rules:
- name: unexported-return
disabled: true
- name: unused-parameter
linters:
disable-all: true
enable:
- bodyclose
- depguard
- dogsled
- dupl
- errcheck
- errorlint
- exportloopref
- funlen
- gocheckcompilerdirectives
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- noctx
- nolintlint
- revive
- staticcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- whitespace
# don't enable:
# - asciicheck
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - nestif
# - prealloc
# - testpackage
# - wsl
run:
timeout: 5m

View File

@@ -23,3 +23,10 @@ The purpose of the thesis is to develop a web application that enables the commu
Users could register and the system would provide the ability to edit their own profiles, which are part of the family tree structure. The family tree would contain not only names and birth dates but also additional information such as schools, residences, workplaces, life wisdom, important knowledge, and photos. Furthermore, the application would employ protective measures, so that only those related by blood could access each other's data.
The task is highly complex, as it involves not only designing the user interface and the family tree structure but also properly implementing security layers. The development of the database system, the cloud-based server, and the CI/CD system present further challenges. The application must ensure optimal display on various devices and screen sizes, which requires additional development and design skills. The thesis details the extent to which specific challenges and solutions related to the project contribute to the success and functionality of the software.
## Deployment
To deploy all micro services use:
```bash:
kubectl apply --server-side -k .
```

13
auth-service/dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM golang:1.22.2-alpine as build
WORKDIR /app
COPY . .
RUN GOOS=linux GOARCH=arm64 go build -o auth-service
FROM arm64v8/busybox
COPY --from=build /app/auth-service /app/
CMD [ "/app/auth-service" ]

View File

@@ -1,3 +1,32 @@
module github.com/vcscsvcscs/GenerationsHeritage/auth-service
go 1.22.0
go 1.22.2
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

80
auth-service/go.sum Normal file
View File

@@ -0,0 +1,80 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb h1:fU736we2gQQRMOWP/su7sCiUFmrXTKBN0s8LG5k7bOE=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb/go.mod h1:aQlmG6BiGFmOFxzAkWTJDzm1EzdCJ4OEETXTUkWJaLk=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

13
backend/dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM golang:1.22.2-alpine as build
WORKDIR /app
COPY . .
RUN GOOS=linux GOARCH=arm64 go build -o backend
FROM busybox
COPY --from=build /app/backend /app/
CMD [ "/app/backend" ]

View File

@@ -1,6 +1,14 @@
module github.com/vcscsvcscs/GenerationsHeritage/backend
go 1.22.0
go 1.22.2
require (
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.6.0
github.com/neo4j/neo4j-go-driver/v5 v5.19.0
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb
golang.org/x/net v0.22.0
)
require (
github.com/bytedance/sonic v1.11.3 // indirect
@@ -8,7 +16,6 @@ require (
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
@@ -24,7 +31,6 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect

View File

@@ -10,6 +10,7 @@ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
@@ -17,6 +18,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -25,9 +28,11 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -43,8 +48,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/neo4j/neo4j-go-driver/v5 v5.19.0 h1:v2cB19fZQYz1xmj6EZXofFHD/+Tj16hH/OOp39uNN1I=
github.com/neo4j/neo4j-go-driver/v5 v5.19.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -56,11 +64,16 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240413225529-30321ba5d7e7 h1:6HOZdgsOt8KojDfNDOyHLwv+Chv90MECxMdP+cKKNv4=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240413225529-30321ba5d7e7/go.mod h1:8byGXK+Csy5RCmHrvdMIzS8oVuvkr9Ech2PqLrad7os=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb h1:fU736we2gQQRMOWP/su7sCiUFmrXTKBN0s8LG5k7bOE=
github.com/vcscsvcscs/GenerationsHeritage/utilities v0.0.0-20240414091827-ffde94d457cb/go.mod h1:aQlmG6BiGFmOFxzAkWTJDzm1EzdCJ4OEETXTUkWJaLk=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
@@ -74,9 +87,11 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -0,0 +1,48 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func CreatePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if err := person.Verify(); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "contains-forbidden-characters"})
return
}
rec, err := person.CreatePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "already-exists"})
return
}
c.JSON(http.StatusCreated, gin.H{"person": rec.AsMap()})
}
}

View File

@@ -0,0 +1,32 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func CreateRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := relationship.CreateRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusCreated, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -0,0 +1,32 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func CreateRelationshipAndPerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var rp memgraph.RelationshipAndPerson
if err := c.ShouldBindJSON(&rp); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := rp.CreateRelationshipAndPerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusCreated, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -0,0 +1,48 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func DeletePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if person.ID != "" {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "no person ID provided"})
return
}
err = person.DeletePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not delete person with ID provided"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "person deleted successfully"})
}
}

View File

@@ -0,0 +1,32 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func DeleteRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := relationship.DeleteRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusAccepted, gin.H{"status": "relationship deleted successfully"})
}
}

View File

@@ -0,0 +1,48 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func UpdatePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if person.ID == "" {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "No ID provided"})
return
}
rec, err := person.UpdatePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not update person with information provided"})
return
}
c.JSON(http.StatusOK, gin.H{"person": rec.AsMap()})
}
}

View File

@@ -0,0 +1,32 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
)
func VerifyRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := relationship.VerifyRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusOK, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -0,0 +1,55 @@
package handlers
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func ViewFamiliyTree(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close(ctx)
id := c.Query("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
return
}
query := `
MATCH (n:Person {ID: $person_id})-[p:Parent*1..]->(family:Person)
OPTIONAL MATCH (family)-[c:Child]->(children:Person)
WITH family, p, children, c, n
OPTIONAL MATCH (children)<-[p2:Parent]-(OtherParents:Person)
WITH family, p, children, c, OtherParents, p2,n
OPTIONAL MATCH (family)-[s:Spouse]-(spouse:Person)
RETURN family, p, children, c, OtherParents, p2, spouse, s, n`
result, err := session.Run(ctx, query, map[string]any{"person_id": id})
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
rec, err := result.Single(ctx)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not find family tree for person with id: " + id})
return
}
c.JSON(200, rec.AsMap()["n"])
}
}

View File

@@ -0,0 +1,46 @@
package handlers
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func ViewPerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close(ctx)
id := c.Query("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
return
}
result, err := session.Run(ctx, "MATCH (n:Person) WHERE n.ID = $person_id RETURN n;", map[string]any{"person_id": id})
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
rec, err := result.Single(ctx)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not find person with information provided"})
return
}
c.JSON(200, rec.AsMap()["n"])
}
}

View File

@@ -2,18 +2,16 @@ package main
import (
"context"
"errors"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
"github.com/vcscsvcscs/GenerationsHeritage/backend/handlers"
"github.com/vcscsvcscs/GenerationsHeritage/backend/memgraph"
"github.com/vcscsvcscs/GenerationsHeritage/utilities"
"github.com/vcscsvcscs/GenerationsHeritage/utilities/gin_liveness"
)
@@ -23,6 +21,9 @@ var (
key = flag.String("key", "./private/keys/key.pem", "Specify the path of TLS key")
httpsPort = flag.String("https", ":443", "Specify port for http secure hosting(example for format :443)")
httpPort = flag.String("http", ":80", "Specify port for http hosting(example for format :80)")
memgraphURI = flag.String("memgraph", "bolt+ssc://memgraph:7687", "Specify the Memgraph database URI")
memgraphUser = flag.String("memgraph-user", "", "Specify the Memgraph database user")
memgraphPass = flag.String("memgraph-pass", "", "Specify the Memgraph database password")
release = flag.Bool("release", false, "Set true to release build")
logToFile = flag.Bool("log-to-file", false, "Set true to log to file")
logToFileAndStd = flag.Bool("log-to-file-and-std", false, "Set true to log to file and std")
@@ -34,56 +35,26 @@ func main() {
if *release {
gin.SetMode(gin.ReleaseMode)
}
if *logToFileAndStd || *logToFile {
gin.DisableConsoleColor() // Disable Console Color, you don't need console color when writing the logs to file.
path := fmt.Sprintf("private/logs/%02dy_%02dm_%02dd_%02dh_%02dm_%02ds.log", time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second())
logerror1 := os.MkdirAll("private/logs/", 0755)
f, logerror2 := os.Create(path)
if logerror1 != nil || logerror2 != nil {
log.Println("Cant log to file")
} else if *logToFileAndStd {
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
} else {
gin.DefaultWriter = io.MultiWriter(f)
}
}
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.SetOutput(gin.DefaultErrorWriter)
utilities.SetupLogger(*logToFileAndStd, *logToFile)
hc := gin_liveness.New()
memgraphDriver := memgraph.InitDatabase(*memgraphURI, *memgraphUser, *memgraphPass)
router := gin.Default()
router.GET("/health", hc.HealthCheckHandler())
router.GET("/person", handlers.ViewPerson(memgraphDriver))
router.POST("/person", handlers.CreatePerson(memgraphDriver))
router.DELETE("/person", handlers.DeletePerson(memgraphDriver))
router.PUT("/person", handlers.UpdatePerson(memgraphDriver))
router.POST("/relationship", handlers.CreateRelationship(memgraphDriver))
router.DELETE("/relationship", handlers.DeleteRelationship(memgraphDriver))
router.PUT("/relationship", handlers.VerifyRelationship(memgraphDriver))
router.POST("/createRelationshipAndPerson", handlers.CreateRelationshipAndPerson(memgraphDriver))
router.GET("/familyTree", handlers.ViewFamiliyTree(memgraphDriver))
var server *http.Server
if utilities.FileExists(*cert) && utilities.FileExists(*key) {
server = &http.Server{
Addr: *httpsPort,
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
log.Println("Server starts at port ", *httpsPort)
if err := server.ListenAndServeTLS(*cert, *key); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
} else {
server = &http.Server{
Addr: *httpPort,
Handler: router,
ReadTimeout: requestTimeout * time.Second,
WriteTimeout: requestTimeout * time.Second,
}
go func() {
log.Println("Server starts at port ", *httpPort)
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
}
server := utilities.SetupHttpsServer(router, *cert, *key, *httpsPort, *httpPort, requestTimeout)
// Wait for interrupt signal to gracefully shutdown the server with some time to finish requests.
quit := make(chan os.Signal, 1)

View File

@@ -0,0 +1,34 @@
package memgraph
import (
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) CreatePerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return nil, err
}
p.ID = strings.ReplaceAll(uuid.New().String(), "-", "")
query := fmt.Sprintf("CREATE (n:Person {%s}) RETURN n;", p.ToString())
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,39 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) CreateRelationship(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return nil, err
}
query := fmt.Sprintf(`MATCH (a:Person), (b:Person) WHERE a.ID = '%s' AND b.ID = '%s'`, r.FirstPersonID, r.SecondPersonID)
if r.Direction == "->" {
query = fmt.Sprintf(`%s CREATE (a)-[r:%s {verified: false}]->(b) RETURN r;`, query, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`%s CREATE (a)<-[r:%s {verified: false}]-(b) RETURN r;`, query, r.Relationship)
} else {
query = fmt.Sprintf(`%s CREATE (a)<-[r1:%s {verified: True}]-(b) CREATE (a)-[r2:%s {verified: True}]->(b) RETURN r1, r2;`,
query, r.Relationship, r.Relationship)
}
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,45 @@
package memgraph
import (
"fmt"
"strings"
"time"
"github.com/google/uuid"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (rp *RelationshipAndPerson) CreateRelationshipAndPerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := rp.Verify(); err != nil {
return nil, err
}
rp.Person.ID = strings.ReplaceAll(uuid.New().String(), "-", "")
query := fmt.Sprintf(`MATCH (a:Person) WHERE a.ID = '%s'`, rp.Relationship.FirstPersonID)
query = fmt.Sprintf("%s CREATE (b:Person {%s})", query, rp.Person.ToString())
if rp.Relationship.Direction == "->" {
query = fmt.Sprintf(`%s CREATE (a)-[r:%s {verified: True}]->(b) RETURN r;`, query, rp.Relationship.Relationship)
} else if rp.Relationship.Direction == "<-" {
query = fmt.Sprintf(`%s CREATE (a)<-[r:%s {verified: True}]-(b) RETURN r;`, query, rp.Relationship.Relationship)
} else {
query = fmt.Sprintf(`%s CREATE (a)<-[r1:%s {verified: True}]-(b) CREATE (a)-[r2:%s {verified: True}]->(b) RETURN r1, r2, b;`,
query, rp.Relationship.Relationship, rp.Relationship.Relationship)
}
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,66 @@
package memgraph
import (
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
const dbCreateSchemaTimeout = 10 * time.Second
func createIndexes(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), dbCreateSchemaTimeout)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
indexes := []string{
`CREATE INDEX ON :Person(ID);`,
`CREATE INDEX ON :Person(Lastname);`,
`CREATE INDEX ON :Person(Firstname);`,
`CREATE INDEX ON :Person(Born);`,
`CREATE INDEX ON :Person(MothersFirstname);`,
`CREATE INDEX ON :Person(MothersLastname);`,
}
// Run index queries via implicit auto-commit transaction
for _, index := range indexes {
_, err := session.Run(ctx, index, nil)
if err != nil {
return err
}
}
return nil
}
func createConstraints(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), dbCreateSchemaTimeout)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
constraints := []string{
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.ID);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.Lastname);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.Firstname);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.Born);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.MothersFirstname);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.MothersLastname);`,
`CREATE CONSTRAINT ON (n:Person) ASSERT n.ID IS UNIQUE;`,
`CREATE CONSTRAINT ON (n:Person) ASSERT n.Lastname, n.Firstname, n.Born, n.MothersFirstname, n.MothersLastname IS UNIQUE;`,
}
// Run index queries via implicit auto-commit transaction
for _, constraint := range constraints {
_, err := session.Run(ctx, constraint, nil)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,124 @@
package memgraph
import (
"fmt"
"strings"
)
var cypherKeywords = []string{
"CREATE",
"DELETE",
"DETACH",
"DETACH DELETE",
"FOREACH",
"LOAD CSV",
"MERGE",
"MATCH",
"ON",
"OPTIONAL MATCH",
"REMOVE",
"SET",
"START",
"UNION",
"UNWIND",
"WITH",
"RETURN",
"ORDER BY",
"SKIP",
"LIMIT",
"ASC",
"DESC",
"EXISTS",
"CALL",
"USING",
"CONSTRAINT",
"DROP",
"INDEX",
"WHERE",
}
var cypherOperators = []string{
"+",
"-",
"*",
"/",
"%",
"^",
"=",
"<",
">",
"<=",
">=",
"<>",
"AND",
"OR",
"XOR",
"NOT",
"IN",
"STARTS WITH",
"ENDS WITH",
"CONTAINS",
"IS NULL",
"IS NOT NULL",
"IS UNIQUE",
"IS NODE",
"IS RELATIONSHIP",
"IS PROPERTY KEY",
"IS MAP",
"IS LIST",
"IS BOOLEAN",
"IS STRING",
"IS NUMBER",
"IS INTEGER",
"IS FLOAT",
"IS NODE",
"IS RELATIONSHIP",
"IS PATH",
"IS POINT",
"IS DATE",
"IS DURATION",
}
// cypherDelimiters contains the delimiters that need to be escaped in a string to prevent cypher injection keys are the delimiters that need to be escaped and values are the escaped delimiters
var cypherDelimiters = map[string]string{
"'": `\'`,
`"`: `\"`,
`\u0027`: `\\u0027`,
`\u0022`: "\\\\u0022",
"`": "``",
"\\u0060": "\\u0060\\u0060",
}
// VerifyString verifies if a string is valid and does not contain cypher injection
func VerifyString(s string) error {
s = strings.ToUpper(s)
for _, keyword := range cypherKeywords {
if strings.Contains(s, keyword) {
return fmt.Errorf("invalid string: %s contains cypher keyword: %s", s, keyword)
}
}
for _, operator := range cypherOperators {
if strings.Contains(s, operator) {
return fmt.Errorf("invalid string: %s contains cypher operator: %s", s, operator)
}
}
for key := range cypherDelimiters {
if strings.Contains(s, key) {
return fmt.Errorf("invalid string: %s contains cypher delimiter: %s", s, key)
}
}
return nil
}
// EscapeString escapes delimiters in a string to prevent cypher injection
func EscapeString(s string) string {
result := s
for k, v := range cypherDelimiters {
result = strings.ReplaceAll(result, k, v)
}
return result
}

View File

@@ -0,0 +1,27 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) DeletePerson(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return err
}
query := fmt.Sprintf("MATCH (n:Person {ID: '%s'}) DELETE n;", p.ID)
_, err := session.Run(ctx, query, nil)
return err
}

View File

@@ -0,0 +1,36 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) DeleteRelationship(driver neo4j.DriverWithContext) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return err
}
query := ""
if r.Direction == "->" {
query = fmt.Sprintf(`MATCH (a)-[r:%s]->(b)`, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`MATCH (a)<-[r:%s]-(b)`, r.Relationship)
} else {
query = fmt.Sprintf(`MATCH (a)-[r:%s]-(b)`, r.Relationship)
}
query = fmt.Sprintf(`%s WHERE a.ID = '%s' AND b.ID = '%s' DELETE r;`, query, r.FirstPersonID, r.SecondPersonID)
_, err := session.Run(ctx, query, nil)
return err
}

View File

@@ -0,0 +1,32 @@
package memgraph
import (
"context"
"log"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func InitDatabase(dbURI, dbUser, dbPassword string) neo4j.DriverWithContext {
driver, err := neo4j.NewDriverWithContext(dbURI, neo4j.BasicAuth(dbUser, dbPassword, ""))
if err != nil {
log.Panicln(err)
}
ctx := context.Background()
err = driver.VerifyConnectivity(ctx)
if err != nil {
log.Panicln(err)
}
if err := createIndexes(driver); err != nil {
log.Panicln(err)
}
if err := createConstraints(driver); err != nil {
log.Panicln(err)
}
return driver
}

272
backend/memgraph/model.go Normal file
View File

@@ -0,0 +1,272 @@
package memgraph
import (
"fmt"
"time"
)
var RelationshipTypes = []string{
"Parent",
"Child",
"Spouse",
"Sibling",
}
type Person struct {
ID string `json:"id"`
Firstname string `json:"first_name"`
Middlename string `json:"middle_name"`
Lastname string `json:"last_name"`
Titles []string `json:"titles"` // e.g. Jr., Sr., III
Suffixes []string `json:"suffixes"` // e.g. Ph.D., M.D.
ExtraNames []string `json:"extra_names"`
Aliases []string `json:"aliases"`
MothersFirstname string `json:"mothers_first_name"`
MothersLastname string `json:"mothers_last_name"`
Born time.Time `json:"born"`
Birthplace string `json:"birthplace"`
Residence string `json:"residence"`
Death time.Time `json:"death"`
Deathplace string `json:"deathplace"`
LifeEvents []map[string]string `json:"life_events"`
Occupations []string `json:"occupation"`
OccupationToDisplay string `json:"occupation_to_display"`
OthersSaid map[string]string `json:"others_said"`
Photos map[string]string `json:"photos"`
ProfilePicture string `json:"profile_picture"`
verified bool
}
func (p *Person) ToString() string {
result := fmt.Sprintf("ID: '%s'", p.ID)
if p.Firstname != "" {
result = fmt.Sprintf("%s, Firstname: '%s'", result, p.Firstname)
}
if p.Lastname != "" {
result = fmt.Sprintf("%s, Lastname: '%s'", result, p.Lastname)
}
if p.Middlename != "" {
result = fmt.Sprintf("%s, Middlename: '%s'", result, p.Middlename)
}
if p.MothersFirstname != "" {
result = fmt.Sprintf("%s, MothersFirstname: '%s'", result, p.MothersFirstname)
}
if p.MothersLastname != "" {
result = fmt.Sprintf("%s, MothersLastname: '%s'", result, p.MothersLastname)
}
if !p.Born.IsZero() {
result = fmt.Sprintf("%s, Born: date({year:%d, month:%d, day:%d})", result, p.Born.Year(), p.Born.Month(), p.Born.Day())
}
if !p.Death.IsZero() {
result = fmt.Sprintf("%s, Death: date({year:%d, month:%d, day:%d})", result, p.Death.Year(), p.Death.Month(), p.Death.Day())
}
if p.Birthplace != "" {
result = fmt.Sprintf("%s, Birthplace: '%s'", result, p.Birthplace)
}
if p.Residence != "" {
result = fmt.Sprintf("%s, Residence: '%s'", result, p.Residence)
}
if p.Deathplace != "" {
result = fmt.Sprintf("%s, Deathplace: '%s'", result, p.Deathplace)
}
if p.OccupationToDisplay != "" {
result = fmt.Sprintf("%s, OccupationToDisplay: '%s'", result, p.OccupationToDisplay)
}
if p.ProfilePicture != "" {
result = fmt.Sprintf("%s, ProfilePicture: '%s'", result, p.ProfilePicture)
}
if p.Titles != nil && len(p.Titles) > 0 {
result = fmt.Sprintf("%s, Titles: [", result)
for _, title := range p.Titles {
result = fmt.Sprintf("%s'%s', ", result, title)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Suffixes != nil && len(p.Suffixes) > 0 {
result = fmt.Sprintf("%s, Suffixes: [", result)
for _, suffix := range p.Suffixes {
result = fmt.Sprintf("%s'%s', ", result, suffix)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.ExtraNames != nil && len(p.ExtraNames) > 0 {
result = fmt.Sprintf("%s, ExtraNames: [", result)
for _, extraName := range p.ExtraNames {
result = fmt.Sprintf("%s'%s', ", result, extraName)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Aliases != nil && len(p.Aliases) > 0 {
result = fmt.Sprintf("%s, Aliases: [", result)
for _, alias := range p.Aliases {
result = fmt.Sprintf("%s'%s', ", result, alias)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.LifeEvents != nil && len(p.LifeEvents) > 0 {
result = fmt.Sprintf("%s, LifeEvents: [", result)
for i := 0; i < len(p.LifeEvents); i++ {
date, dok := p.LifeEvents[i]["date"]
event, eok := p.LifeEvents[i]["event"]
if dok && eok {
result = fmt.Sprintf("%s{date: '%s', event: '%s'}, ", result, date, event)
}
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.Occupations != nil && len(p.Occupations) > 0 {
result = fmt.Sprintf("%s, Occupations: [", result)
for _, occupation := range p.Occupations {
result = fmt.Sprintf("%s'%s', ", result, occupation)
}
result = fmt.Sprintf("%s]", result[:len(result)-2])
}
if p.OthersSaid != nil {
result = fmt.Sprintf("%s, OthersSaid: {", result)
for key, value := range p.OthersSaid {
result = fmt.Sprintf("%s%s: '%s', ", result, key, value)
}
result = fmt.Sprintf("%s}", result[:len(result)-2])
}
if p.Photos != nil && len(p.Photos) > 0 {
result = fmt.Sprintf("%s, Photos: {", result)
for key, value := range p.Photos {
result = fmt.Sprintf("%s%s: '%s', ", result, key, value)
}
result = fmt.Sprintf("%s}", result[:len(result)-2])
}
return result
}
// Verify checks if the person is valid and does not contain cypher injection it also escapes the delimiters contained in any of the strings
func (p *Person) Verify() error {
if p.verified {
return nil
}
if err := VerifyString(p.ID); err != nil {
return fmt.Errorf("invalid ID type %s", err)
}
p.Firstname = EscapeString(p.Firstname)
p.Middlename = EscapeString(p.Middlename)
p.Lastname = EscapeString(p.Lastname)
p.MothersFirstname = EscapeString(p.MothersFirstname)
p.MothersLastname = EscapeString(p.MothersLastname)
p.Birthplace = EscapeString(p.Birthplace)
p.Residence = EscapeString(p.Residence)
p.Deathplace = EscapeString(p.Deathplace)
p.OccupationToDisplay = EscapeString(p.OccupationToDisplay)
p.ProfilePicture = EscapeString(p.ProfilePicture)
for i, title := range p.Titles {
p.Titles[i] = EscapeString(title)
}
for i, suffix := range p.Suffixes {
p.Suffixes[i] = EscapeString(suffix)
}
for i, extraName := range p.ExtraNames {
p.ExtraNames[i] = EscapeString(extraName)
}
for i, alias := range p.Aliases {
p.Aliases[i] = EscapeString(alias)
}
for i, lifeEvent := range p.LifeEvents {
for key, value := range lifeEvent {
if key != "date" && key != "event" {
return fmt.Errorf("invalid key in life event")
}
p.LifeEvents[i][key] = EscapeString(value)
}
}
for i, occupation := range p.Occupations {
p.Occupations[i] = EscapeString(occupation)
}
for key, value := range p.OthersSaid {
if err := VerifyString(key); err != nil {
return fmt.Errorf("invalid key in others said %s", err)
}
p.OthersSaid[key] = EscapeString(value)
}
for key, value := range p.Photos {
if err := VerifyString(key); err != nil {
return fmt.Errorf("invalid key in photos %s", err)
}
p.Photos[key] = EscapeString(value)
}
p.verified = true
return nil
}
type Relationship struct {
FirstPersonID string `json:"first_person_id"`
SecondPersonID string `json:"second_person_id"`
Relationship string `json:"relationship"`
Direction string `json:"direction"`
}
// Verify checks if the relationship is valid and does not contain cypher injection
func (r *Relationship) Verify() error {
if r.Direction != "->" && r.Direction != "<-" && r.Direction != "-" {
return fmt.Errorf("invalid direction for relationship")
}
// Check if the relationship is in the list of valid relationships
found := false
for _, relationship := range RelationshipTypes {
if r.Relationship == relationship {
found = true
break
}
}
if !found {
return fmt.Errorf("invalid relationship type")
}
if err := VerifyString(r.FirstPersonID); err != nil {
return fmt.Errorf("invalid FirstPersonID type %s", err)
}
if err := VerifyString(r.SecondPersonID); err != nil {
return fmt.Errorf("invalid SecondPersonID type %s", err)
}
return nil
}
type RelationshipAndPerson struct {
Relationship Relationship `json:"relationship"`
Person Person `json:"person"`
}
func (r *RelationshipAndPerson) Verify() error {
if err := r.Relationship.Verify(); err != nil {
return err
}
if err := r.Person.Verify(); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,77 @@
package memgraph
import (
"testing"
"time"
)
func TestPerson_ToString(t *testing.T) {
tests := []struct {
name string
p *Person
want string
}{
{
name: "Test with nil values",
p: &Person{
ID: "1",
Firstname: "John",
Lastname: "Doe",
MothersFirstname: "Jane",
MothersLastname: "Doe",
Born: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Birthplace: "New York",
Residence: "New York",
Death: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Deathplace: "New York",
},
want: "ID: '1', Firstname: 'John', Lastname: 'Doe', MothersFirstname: 'Jane', MothersLastname: 'Doe', Born: date({year:2021, month:1, day:1}), Death: date({year:2021, month:1, day:1}), Birthplace: 'New York', Residence: 'New York', Deathplace: 'New York', OccupationToDisplay: '', ProfilePicture: ''",
}, {
name: "Test with All values",
p: &Person{
ID: "1",
Firstname: "John",
Lastname: "Doe",
MothersFirstname: "Jane",
MothersLastname: "Doe",
Born: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Birthplace: "New York",
Residence: "New York",
Death: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
Deathplace: "New York",
LifeEvents: []map[string]string{
{
"date": "2021-01-01",
"event": "Event 1",
},
{
"date": "2021-01-02",
"event": "Event 2",
},
},
Occupations: []string{
"Welder",
"Plumber",
},
OccupationToDisplay: "Welder",
OthersSaid: map[string]string{
"Beni": "He is a good person",
"Jani": "He is a bad person",
},
Photos: map[string]string{
"Profile": "profile.jpg",
"Family": "family.jpg",
},
ProfilePicture: "profile.jpg",
},
want: "ID: '1', Firstname: 'John', Lastname: 'Doe', MothersFirstname: 'Jane', MothersLastname: 'Doe', Born: date({year:2021, month:1, day:1}), Death: date({year:2021, month:1, day:1}), Birthplace: 'New York', Residence: 'New York', Deathplace: 'New York', OccupationToDisplay: 'Welder', ProfilePicture: 'profile.jpg', LifeEvents: [{date: '2021-01-01', event: 'Event 1'}, {date: '2021-01-02', event: 'Event 2'}], Occupations: ['Welder', 'Plumber'], OthersSaid: {Beni: 'He is a good person', Jani: 'He is a bad person'}, Photos: {Profile: 'profile.jpg', Family: 'family.jpg'}",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.p.ToString(); got != tt.want {
t.Errorf("Person.ToString() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,30 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (p *Person) UpdatePerson(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := p.Verify(); err != nil {
return nil, err
}
query := fmt.Sprintf("MATCH (n:Person {ID: '%s'}) SET n += {%s} RETURN n;", p.ID, p.ToString())
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,39 @@
package memgraph
import (
"fmt"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"golang.org/x/net/context"
)
func (r *Relationship) VerifyRelationship(driver neo4j.DriverWithContext) (*neo4j.Record, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer session.Close(ctx)
if err := r.Verify(); err != nil {
return nil, err
}
query := ""
if r.Direction == "->" {
query = fmt.Sprintf(`MATCH (a)-[r:%s]->(b)`, r.Relationship)
} else if r.Direction == "<-" {
query = fmt.Sprintf(`MATCH (a)<-[r:%s]-(b)`, r.Relationship)
} else {
query = fmt.Sprintf(`MATCH (a)-[r:%s]-(b)`, r.Relationship)
}
query = fmt.Sprintf(`%s WHERE a.ID = %s AND b.ID = %s set r.verified = true return r;`, query, r.FirstPersonID, r.SecondPersonID)
result, err := session.Run(ctx, query, nil)
if err != nil {
return nil, err
}
return result.Single(ctx)
}

View File

@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gh-auth-service
spec:
project: generations-heritage-vv
source:
repoURL: 'https://github.com/vcscsvcscs/GenerationsHeritage'
path: deployment/auth-service
targetRevision: main
kustomize:
namespace: generations-heritage
destination:
server: 'https://kubernetes.default.svc'
namespace: generations-heritage
syncPolicy:
automated:
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View File

@@ -0,0 +1,29 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: gh-auth-service-certificate
annotations:
argocd.argoproj.io/hook: PreSync
spec:
isCA: false
duration: 2160h # 90d
renewBefore: 360h # 15d
dnsNames:
- gh-auth-service.generations-heritage.svc.cluster.local
- gh-auth-service
- localhost
ipAddresses:
- 127.0.0.1
subject:
organizations:
- GenerationsHeritage
secretName: gh-auth-service-tls
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
issuerRef:
name: default-cluster-ca-issuer
kind: ClusterIssuer
group: cert-manager.io

View File

@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/instance: gh-auth-service
app.kubernetes.io/name: gh-auth-service
annotations:
argocd.argoproj.io/sync-wave: "1"
argocd.argoproj.io/hook: Synce
name: gh-auth-service
namespace: generations-heritage
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: gh-auth-service
app.kubernetes.io/name: gh-auth-service
template:
metadata:
labels:
app.kubernetes.io/instance: gh-auth-service
app.kubernetes.io/name: gh-auth-service
spec:
containers:
- image: vcscsvcscs/gheritage-auth-service:latest
imagePullPolicy: Always
name: gh-auth-service
ports:
- containerPort: 443
name: gin
securityContext:
runAsUser: 0
resources:
limits:
cpu: 250m
memory: 200Mi
requests:
cpu: 100m
memory: 50Mi
volumeMounts:
- name: gh-auth-service-certs
mountPath: /etc/gh-auth-service/ssl
volumes:
- name: gh-auth-service-certs
secret:
secretName: gh-auth-service-tls

View File

@@ -0,0 +1,26 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gh-auth-service
annotations:
argocd.argoproj.io/hook: PostSync
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gh-auth-service
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 50

View File

@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: generations-heritage
resources:
- ./certificate.yaml
- ./deployment.yaml
- ./service.yaml
- ./horizontalPodAutoScaler.yaml

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/instance: gh-auth-service
app.kubernetes.io/name: gh-auth-service
name: gh-auth-service
spec:
ports:
- name: gin
port: 443
protocol: TCP
targetPort: 443
selector:
app.kubernetes.io/instance: gh-auth-service
app.kubernetes.io/name: gh-auth-service
type: ClusterIP

View File

@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gh-backend
spec:
project: generations-heritage-vv
source:
repoURL: 'https://github.com/vcscsvcscs/GenerationsHeritage'
path: deployment/backend
targetRevision: main
kustomize:
namespace: generations-heritage
destination:
server: 'https://kubernetes.default.svc'
namespace: generations-heritage
syncPolicy:
automated:
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View File

@@ -0,0 +1,29 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: gh-backend-certificate
annotations:
argocd.argoproj.io/hook: PreSync
spec:
isCA: false
duration: 2160h # 90d
renewBefore: 360h # 15d
dnsNames:
- gh-backend.generations-heritage.svc.cluster.local
- gh-backend
- localhost
ipAddresses:
- 127.0.0.1
subject:
organizations:
- GenerationsHeritage
secretName: gh-backend-tls
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
issuerRef:
name: default-cluster-ca-issuer
kind: ClusterIssuer
group: cert-manager.io

View File

@@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/instance: gh-backend
app.kubernetes.io/name: gh-backend
annotations:
argocd.argoproj.io/sync-wave: "1"
argocd.argoproj.io/hook: Synce
name: gh-backend
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: gh-backend
app.kubernetes.io/name: gh-backend
template:
metadata:
labels:
app.kubernetes.io/instance: gh-backend
app.kubernetes.io/name: gh-backend
spec:
containers:
- image: vcscsvcscs/gheritage-backend-service:latest
imagePullPolicy: Always
name: gh-backend
ports:
- containerPort: 443
name: gin
securityContext:
runAsUser: 0
resources:
limits:
cpu: 500m
memory: 500Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: gh-backend-certs
mountPath: /etc/gh-backend/ssl
volumes:
- name: gh-backend-certs
secret:
secretName: gh-backend-tls

View File

@@ -0,0 +1,26 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gh-backend
annotations:
argocd.argoproj.io/hook: PostSync
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gh-backend
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 50

View File

@@ -0,0 +1,19 @@
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: gh-backend
annotations:
argocd.argoproj.io/hook: PostSync
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`heritagebackend.varghacsongor.hu`)
services:
- name: gh-backend
port: 443
passHostHeader: true
scheme: https
tls: {}

View File

@@ -0,0 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: generations-heritage
resources:
- ./certificate.yaml
- ./deployment.yaml
- ./service.yaml
- ./ingressRoute.yaml
- ./horizontalPodAutoScaler.yaml

View File

@@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/instance: gh-backend
app.kubernetes.io/name: gh-backend
name: gh-backend
annotations:
argocd.argoproj.io/sync-wave: "2"
argocd.argoproj.io/hook: Synce
spec:
ports:
- name: gin
port: 443
protocol: TCP
targetPort: 443
selector:
app.kubernetes.io/instance: gh-backend
app.kubernetes.io/name: gh-backend
type: ClusterIP

View File

@@ -0,0 +1,45 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: trust-manager-selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cluster-root-certificate
namespace: cert-manager
spec:
isCA: true
commonName: cluster-root-certificate-ca
secretName: cluster-root-certificate-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: trust-manager-selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: default-cluster-ca-issuer
spec:
ca:
secretName: cluster-root-certificate-ca-secret
---
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: in-cluster-trust-bundle
spec:
sources:
- useDefaultCAs: true
- secret:
name: "cluster-root-certificate-ca-secret"
key: "tls.crt"
target:
configMap:
key: "trust-bundle.pem"

View File

@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: memgraph
spec:
project: generations-heritage-vv
source:
repoURL: 'https://github.com/vcscsvcscs/GenerationsHeritage'
path: deployment/memgraph
targetRevision: main
kustomize:
namespace: generations-heritage
destination:
server: 'https://kubernetes.default.svc'
namespace: generations-heritage
syncPolicy:
automated:
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View File

@@ -0,0 +1,30 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: memraph-certificate
annotations:
argocd.argoproj.io/hook: PreSync
spec:
isCA: false
duration: 2160h # 90d
renewBefore: 360h # 15d
dnsNames:
- memgraph.generations-heritage.svc.cluster.local
- memgraph
- localhost
ipAddresses:
- 127.0.0.1
subject:
organizations:
- Memgraph
- GenerationsHeritage
secretName: memgraph-tls
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
issuerRef:
name: default-cluster-ca-issuer
kind: ClusterIssuer
group: cert-manager.io

View File

@@ -0,0 +1,33 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: generations-heritage
resources:
- ./certificates.yaml
helmCharts:
- name: memgraph
repo: https://memgraph.github.io/helm-charts
releaseName: memgraph
namespace: generations-heritage
version: 0.1.1
valuesFile: ./values.yaml
patches:
- target:
kind: StatefulSet
name: memgraph
patch: |-
- op: add
path: /spec/template/spec/containers/0/volumeMounts/-
value:
name: memgraph-certs
mountPath: /etc/memgraph/ssl
- op: add
path: /spec/template/spec/volumes/-
value:
name: memgraph-certs
secret:
secretName: memgraph-tls

View File

@@ -0,0 +1,52 @@
image:
repository: memgraph/memgraph
# Overrides the image tag whose default is v{{ .Chart.AppVersion }}
tag: ""
pullPolicy: IfNotPresent
replicaCount: 1
service:
type: ClusterIP
port: 7687
targetPort: 7687
protocol: TCP
annotations: {}
persistentVolumeClaim:
storagePVC: true
storagePVCSize: 2Gi
logPVC: true
logPVCSize: 256Mi
memgraphConfig:
- "--also-log-to-stderr=true"
- "--bolt-cert-file=/etc/memgraph/ssl/tls.crt"
- "--bolt-key-file=/etc/memgraph/ssl/tls.key"
# Annotations to add to the statefulSet
statefulSetAnnotations: {}
# Annotations to add to the Pod
podAnnotations: {}
resources:
{}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""

View File

@@ -0,0 +1,14 @@
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: generations-heritage-vv
spec:
description: Generations heritages is a project that aims to preserve the heritage of families based on bloodlines.
sourceRepos:
- '*' # Allow all repositories
destinations:
- namespace: 'generations-heritage'
server: '*'
clusterResourceWhitelist:
- group: '*'
kind: '*' # Allow all kinds

View File

@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: zitadel
spec:
project: generations-heritage-vv
source:
repoURL: 'https://github.com/vcscsvcscs/GenerationsHeritage'
path: deployment/zitadel
targetRevision: main
kustomize:
namespace: generations-heritage
destination:
server: 'https://kubernetes.default.svc'
namespace: generations-heritage
syncPolicy:
automated:
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View File

@@ -0,0 +1,113 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: zitadel-cert-creator
annotations:
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/sync-wave: "2"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-creator
rules:
- apiGroups: [ "" ]
resources: [ "secrets" ]
verbs: [ "create" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: zitadel-cert-creator
annotations:
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/sync-wave: "2"
subjects:
- kind: ServiceAccount
name: zitadel-cert-creator
roleRef:
kind: Role
name: secret-creator
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: Job
metadata:
name: create-zitadel-cert
annotations:
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/sync-wave: "2"
spec:
template:
spec:
restartPolicy: OnFailure
serviceAccountName: zitadel-cert-creator
containers:
- command:
- /bin/bash
- -ecx
- |
cockroach cert create-client \
--certs-dir /cockroach/cockroach-certs \
--ca-key /cockroach/cockroach-certs/ca.key \
--lifetime 8760h \
zitadel
export SECRET=$(cat <<EOF
{
"apiVersion": "v1",
"kind": "Secret",
"data": {
"ca.crt": "$(base64 /cockroach/cockroach-certs/ca.crt --wrap 0)",
"tls.crt": "$(base64 /cockroach/cockroach-certs/client.zitadel.crt --wrap 0)",
"tls.key": "$(base64 /cockroach/cockroach-certs/client.zitadel.key --wrap 0)"
},
"metadata": {
"name": "db-cockroachdb-zitadel-secret"
},
"type": "kubernetes.io/tls"
}
EOF
)
export APISERVER=https://kubernetes.default.svc SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace) TOKEN=$(cat ${SERVICEACCOUNT}/token) CACERT=${SERVICEACCOUNT}/ca.crt
curl \
--cacert ${CACERT} \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-X POST ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets \
--data "$(echo -n $SECRET | tr -d '\n')"
image: cockroachdb/cockroach:v23.1.8
imagePullPolicy: IfNotPresent
name: create-zitadel-cert
volumeMounts:
- mountPath: /cockroach/cockroach-certs/
name: certs
initContainers:
- command:
- /bin/sh
- -c
- cp -f /certs/* /cockroach-certs/; chmod 0400 /cockroach-certs/*.key
image: busybox
imagePullPolicy: IfNotPresent
name: copy-certs
volumeMounts:
- mountPath: /cockroach-certs/
name: certs
- mountPath: /certs/
name: certs-secret
volumes:
- emptyDir: {}
name: certs
- name: certs-secret
projected:
defaultMode: 420
sources:
- secret:
items:
- key: ca.crt
mode: 256
path: ca.crt
- key: ca.key
mode: 256
path: ca.key
name: cockroachdb-ca-secret

View File

@@ -0,0 +1,13 @@
tls:
enabled: true
annotations:
argocd.argoproj.io/hook: Sync
storage:
persistentVolume:
size: 5Gi
init:
jobAnnotations:
argocd.argoproj.io/hook: Sync

View File

@@ -0,0 +1,27 @@
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: zitadel-server
annotations:
argocd.argoproj.io/hook: PostSync
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`zitadel.varghacsongor.hu`)
priority: 10
services:
- name: zitadel
port: 8080
passHostHeader: true
- kind: Rule
match: Host(`zitadel.varghacsongor.hu`) && Headers(`Content-Type`, `application/grpc`)
priority: 11
services:
- name: zitadel
port: 8080
scheme: h2c
passHostHeader: true
tls: {}

View File

@@ -0,0 +1,58 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: generations-heritage
resources:
- ./cert-job.yaml
- ./secrets.yaml
- ./ingressRoute.yaml
helmCharts:
- name: cockroachdb
repo: https://charts.cockroachdb.com/
releaseName: cockroachdb
namespace: generations-heritage
version: 12.0.2
valuesFile: cockroach-values.yaml
- name: zitadel
repo: https://charts.zitadel.com
releaseName: zitadel
namespace: generations-heritage
version: 7.12.1
valuesFile: ./values.yaml
patches:
- target:
kind: CronJob
patch: |
- op: replace
path: /apiVersion
value: batch/v1
- target:
name: zitadel-setup
kind: Job
patch: |
- op: add
path: /metadata/annotations/argocd.argoproj.io~1sync-wave
value: 4
- target:
name: zitadel-setup
kind: Job
patch: |
- op: add
path: /metadata/annotations/argocd.argoproj.io~1hook
value: Sync
- target:
name: zitadel-init
kind: Job
patch: |
- op: add
path: /metadata/annotations/argocd.argoproj.io~1sync-wave
value: 3
- target:
name: zitadel-init
kind: Job
patch: |
- op: add
path: /metadata/annotations/argocd.argoproj.io~1hook
value: Sync

View File

@@ -0,0 +1,23 @@
apiVersion: v1
kind: Secret
metadata:
name: zitadel-masterkey
annotations:
argocd.argoproj.io/hook: PreSync
labels:
secret-generator.cs.sap.com/enabled: "true"
stringData:
masterkey: "%generate"
admin-password: "%generate"
---
apiVersion: v1
kind: Secret
metadata:
name: cockroach-auth
annotations:
argocd.argoproj.io/hook: PreSync
labels:
secret-generator.cs.sap.com/enabled: "true"
stringData:
cockroach-password: "%generate"
user-password: "%generate"

View File

@@ -0,0 +1,53 @@
zitadel:
masterkeySecretName: zitadel-masterkey
configmapConfig:
ExternalSecure: true
ExternalDomain: zitadel.varghacsongor.hu
ExternalPort: 443
TLS:
Enabled: false
Database:
Cockroach:
Host: cockroachdb-public
User:
SSL:
Mode: verify-full
Admin:
SSL:
Mode: verify-full
dbSslCaCrtSecret: cockroachdb-ca-secret
dbSslAdminCrtSecret: cockroachdb-client-secret
dbSslUserCrtSecret: db-cockroachdb-zitadel-secret
image:
repository: ghcr.io/zitadel/zitadel
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "v2.51.0"
annotations:
argocd.argoproj.io/sync-wave: "5"
argocd.argoproj.io/hook: Sync
env:
- name: ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME
value: "admin"
- name: ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD
valueFrom:
secretKeyRef:
name: zitadel-masterkey
key: admin-password
- name: ZITADEL_DATABASE_COCKROACH_USER_PASSWORD
valueFrom:
secretKeyRef:
name: cockroach-auth
key: user-password
- name: ZITADEL_DATABASE_COCKROACH_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: cockroach-auth
key: cockroach-password

39
frontend/.eslintignore Normal file
View File

@@ -0,0 +1,39 @@
/.nyc_output
/coverage
/lib
/node_modules
/*.config.js
!/.vscode
!/.github
!/.devcontainer
/prettier-playground
/tests/fixtures/rules/indent/invalid/ts
/tests/fixtures/rules/indent/invalid/ts-v5
/tests/fixtures/rules/indent/invalid/snippets01-input.svelte
/tests/fixtures/rules/indent/valid/
/tests/fixtures/rules/no-unused-class-name/valid/invalid-style01-input.svelte
/tests/fixtures/rules/no-unused-class-name/valid/unknown-lang01-input.svelte
/tests/fixtures/rules/valid-compile/invalid/ts
/tests/fixtures/rules/valid-compile/valid/babel
/tests/fixtures/rules/valid-compile/valid/ts
/tests/fixtures/rules/prefer-style-directive
/tests/fixtures/rules/@typescript-eslint
/tests/fixtures/rules/valid-compile/valid/svelte3-options-custom-element-input.svelte
/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-input.svelte
/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-input.svelte
/.svelte-kit
/svelte.config-dist.js
/build
/docs-svelte-kit/shim/eslint.mjs
/docs-svelte-kit/shim/assert.mjs
!/.*.js
/docs-svelte-kit/src/routes/*.md
/docs-svelte-kit/src/routes/**/*.md
/docs-svelte-kit/src/app.html
# JSONSchema bug?
/.devcontainer/devcontainer.json
# Parser bug?
/tests/fixtures/rules/indent/invalid/const-tag01-input.svelte
/tests/fixtures/rules/indent/invalid/const-tag01-output.svelte

19
frontend/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
extends: ['plugin:svelte/base', 'plugin:svelte/recommended', 'plugin:svelte/prettier'],
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser'
}
],
env: {
es6: true
},
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true
},
sourceType: 'module'
}
};

8
frontend/.prettierignore Normal file
View File

@@ -0,0 +1,8 @@
.svelte-kit
.type-coverage
build
/lib
.npmrc
.eslintignore
/tests/fixtures/rules/indent/valid/
.changeset

31
frontend/.prettierrc.cjs Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
module.exports = {
useTabs: true,
singleQuote: true,
trailingComma: 'none',
printWidth: 100,
plugins: ['prettier-plugin-svelte'],
overrides: [
{
files: ['.*rc'],
excludeFiles: ['.browserslistrc', '.npmrc', '.nvmrc'],
options: {
parser: 'json'
}
},
{
files: ['*.svelte'],
options: {
bracketSameLine: false
}
},
{
files: ['*.md', 'package.json', '**/package.json'],
options: {
useTabs: false,
tabWidth: 2
}
}
]
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,31 @@
{
"name": "frontend",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"svelte": "^4.2.7",
"vite": "^5.0.3"
},
"type": "module"
"name": "frontend",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"format": "npx prettier --write --plugin prettier-plugin-svelte .",
"lint": "npx eslint --ext .svelte --ext .js ."
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/eslint-parser": "^7.23.10",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"eslint": "^8.57.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-svelte": "^2.35.1",
"prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.2.2",
"svelte": "^4.2.12",
"vite": "^5.0.3"
},
"type": "module",
"dependencies": {
"svelte-eslint-parser": "^0.33.1"
}
}

View File

@@ -4,6 +4,8 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.10.2/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">

View File

@@ -1,2 +1,4 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<p>
Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation
</p>

11
kustomization.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: argocd
resources:
- ./deployment/cert-issuer.yaml
- ./deployment/project-argo.yaml
- ./deployment/auth-service-argo.yaml
- ./deployment/memgraph-argo.yaml
- ./deployment/backend-argo.yaml
- ./deployment/zitadel-argo.yaml

View File

@@ -1,6 +1,6 @@
module github.com/vcscsvcscs/GenerationsHeritage/utilities
go 1.22.1
go 1.22.2
require github.com/gin-gonic/gin v1.9.1

29
utilities/logger_setup.go Normal file
View File

@@ -0,0 +1,29 @@
package utilities
import (
"fmt"
"io"
"log"
"os"
"time"
"github.com/gin-gonic/gin"
)
func SetupLogger(logToFileAndStd bool, logToFile bool) {
if logToFileAndStd || logToFile {
gin.DisableConsoleColor() // Disable Console Color, you don't need console color when writing the logs to file.
path := fmt.Sprintf("private/logs/%02dy_%02dm_%02dd_%02dh_%02dm_%02ds.log", time.Now().Year(), time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second())
logerror1 := os.MkdirAll("private/logs/", 0755)
f, logerror2 := os.Create(path)
if logerror1 != nil || logerror2 != nil {
log.Println("Cant log to file")
} else if logToFileAndStd {
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
} else {
gin.DefaultWriter = io.MultiWriter(f)
}
}
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.SetOutput(gin.DefaultErrorWriter)
}

40
utilities/server_setup.go Normal file
View File

@@ -0,0 +1,40 @@
package utilities
import (
"errors"
"log"
"net/http"
"time"
)
func SetupHttpsServer(router http.Handler, cert, key, httpsPort, httpPort string, requestTimeout time.Duration) (server *http.Server) {
if FileExists(cert) && FileExists(key) {
server = &http.Server{
Addr: httpsPort,
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
log.Println("Server starts at port ", httpsPort)
if err := server.ListenAndServeTLS(cert, key); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
} else {
server = &http.Server{
Addr: httpPort,
Handler: router,
ReadTimeout: requestTimeout * time.Second,
WriteTimeout: requestTimeout * time.Second,
}
go func() {
log.Println("Server starts at port ", httpPort)
if err := server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatal(err)
}
}()
}
return server
}