mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'master' into 2947-refactor-the-existing-sendcoins-resolver-methode-to-distingue-between-local-transaction-and-x-transaction
This commit is contained in:
commit
5aa886121c
1
.github/workflows/lint_pr.yml
vendored
1
.github/workflows/lint_pr.yml
vendored
@ -27,6 +27,7 @@ jobs:
|
||||
frontend
|
||||
admin
|
||||
database
|
||||
dlt-database
|
||||
release
|
||||
federation
|
||||
dht
|
||||
|
||||
@ -294,7 +294,7 @@ describe('CommunityResolver', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('with several community entries', () => {
|
||||
describe('returns 2 filtered communities even with 3 existing entries', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDB()
|
||||
jest.clearAllMocks()
|
||||
@ -316,8 +316,8 @@ describe('CommunityResolver', () => {
|
||||
foreignCom1.url = 'http://stage-2.gradido.net/api'
|
||||
foreignCom1.publicKey = Buffer.from('publicKey-stage-2_Community')
|
||||
foreignCom1.privateKey = Buffer.from('privateKey-stage-2_Community')
|
||||
foreignCom1.communityUuid = 'Stage2-Com-UUID'
|
||||
foreignCom1.authenticatedAt = new Date()
|
||||
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
|
||||
// foreignCom1.authenticatedAt = new Date()
|
||||
foreignCom1.name = 'Stage-2_Community-name'
|
||||
foreignCom1.description = 'Stage-2_Community-description'
|
||||
foreignCom1.creationDate = new Date()
|
||||
@ -336,7 +336,7 @@ describe('CommunityResolver', () => {
|
||||
await DbCommunity.insert(foreignCom2)
|
||||
})
|
||||
|
||||
it('returns 3 community entries', async () => {
|
||||
it('returns 2 community entries', async () => {
|
||||
await expect(query({ query: communities })).resolves.toMatchObject({
|
||||
data: {
|
||||
communities: [
|
||||
@ -350,6 +350,7 @@ describe('CommunityResolver', () => {
|
||||
uuid: homeCom1.communityUuid,
|
||||
authenticatedAt: homeCom1.authenticatedAt?.toISOString(),
|
||||
},
|
||||
/*
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: foreignCom1.foreign,
|
||||
@ -360,6 +361,7 @@ describe('CommunityResolver', () => {
|
||||
uuid: foreignCom1.communityUuid,
|
||||
authenticatedAt: foreignCom1.authenticatedAt?.toISOString(),
|
||||
},
|
||||
*/
|
||||
{
|
||||
id: expect.any(Number),
|
||||
foreign: foreignCom2.foreign,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { IsNull, Not } from '@dbTools/typeorm'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { Resolver, Query, Authorized } from 'type-graphql'
|
||||
@ -28,6 +29,7 @@ export class CommunityResolver {
|
||||
@Query(() => [Community])
|
||||
async communities(): Promise<Community[]> {
|
||||
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||
where: { communityUuid: Not(IsNull()) }, //, authenticatedAt: Not(IsNull()) },
|
||||
order: {
|
||||
name: 'ASC',
|
||||
},
|
||||
|
||||
6
dlt-database/.env.dist
Normal file
6
dlt-database/.env.dist
Normal file
@ -0,0 +1,6 @@
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_dlt
|
||||
MIGRATIONS_TABLE=migrations
|
||||
8
dlt-database/.env.template
Normal file
8
dlt-database/.env.template
Normal file
@ -0,0 +1,8 @@
|
||||
CONFIG_VERSION=$DATABASE_CONFIG_VERSION
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=$DB_USER
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
DB_DATABASE=gradido_dlt
|
||||
MIGRATIONS_TABLE=migrations
|
||||
3
dlt-database/.eslintignore
Normal file
3
dlt-database/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
**/*.min.js
|
||||
build
|
||||
206
dlt-database/.eslintrc.js
Normal file
206
dlt-database/.eslintrc.js
Normal file
@ -0,0 +1,206 @@
|
||||
// eslint-disable-next-line import/no-commonjs, import/unambiguous
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise'],
|
||||
extends: [
|
||||
'standard',
|
||||
'eslint:recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
// 'plugin:security/recommended',
|
||||
'plugin:@eslint-community/eslint-comments/recommended',
|
||||
],
|
||||
settings: {
|
||||
'import/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||
},
|
||||
'import/resolver': {
|
||||
typescript: {
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
node: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-console': 'error',
|
||||
camelcase: 'error',
|
||||
'no-debugger': 'error',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
},
|
||||
],
|
||||
// import
|
||||
'import/export': 'error',
|
||||
'import/no-deprecated': 'error',
|
||||
'import/no-empty-named-blocks': 'error',
|
||||
// 'import/no-extraneous-dependencies': 'error',
|
||||
'import/no-mutable-exports': 'error',
|
||||
'import/no-unused-modules': 'error',
|
||||
'import/no-named-as-default': 'error',
|
||||
'import/no-named-as-default-member': 'error',
|
||||
'import/no-amd': 'error',
|
||||
'import/no-commonjs': 'error',
|
||||
'import/no-import-module-exports': 'error',
|
||||
'import/no-nodejs-modules': 'off',
|
||||
'import/unambiguous': 'error',
|
||||
'import/default': 'error',
|
||||
'import/named': 'error',
|
||||
'import/namespace': 'error',
|
||||
'import/no-absolute-path': 'error',
|
||||
// 'import/no-cycle': 'error',
|
||||
'import/no-dynamic-require': 'error',
|
||||
'import/no-internal-modules': 'off',
|
||||
'import/no-relative-packages': 'error',
|
||||
// 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
|
||||
'import/no-self-import': 'error',
|
||||
'import/no-unresolved': 'error',
|
||||
'import/no-useless-path-segments': 'error',
|
||||
'import/no-webpack-loader-syntax': 'error',
|
||||
'import/consistent-type-specifier-style': 'error',
|
||||
'import/exports-last': 'off',
|
||||
'import/extensions': 'error',
|
||||
'import/first': 'error',
|
||||
'import/group-exports': 'off',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-anonymous-default-export': 'error',
|
||||
'import/no-default-export': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'import/no-named-default': 'error',
|
||||
'import/no-namespace': 'error',
|
||||
'import/no-unassigned-import': 'error',
|
||||
// 'import/order': [
|
||||
// 'error',
|
||||
// {
|
||||
// groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||
// 'newlines-between': 'always',
|
||||
// pathGroups: [
|
||||
// {
|
||||
// pattern: '@?*/**',
|
||||
// group: 'external',
|
||||
// position: 'after',
|
||||
// },
|
||||
// {
|
||||
// pattern: '@/**',
|
||||
// group: 'external',
|
||||
// position: 'after',
|
||||
// },
|
||||
// ],
|
||||
// alphabetize: {
|
||||
// order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
|
||||
// caseInsensitive: true /* ignore case. Options: [true, false] */,
|
||||
// },
|
||||
// distinctGroup: true,
|
||||
// },
|
||||
// ],
|
||||
'import/prefer-default-export': 'off',
|
||||
// n
|
||||
'n/handle-callback-err': 'error',
|
||||
'n/no-callback-literal': 'error',
|
||||
'n/no-exports-assign': 'error',
|
||||
// 'n/no-extraneous-import': 'error',
|
||||
'n/no-extraneous-require': 'error',
|
||||
'n/no-hide-core-modules': 'error',
|
||||
'n/no-missing-import': 'off', // not compatible with typescript
|
||||
'n/no-missing-require': 'error',
|
||||
'n/no-new-require': 'error',
|
||||
'n/no-path-concat': 'error',
|
||||
// 'n/no-process-exit': 'error',
|
||||
'n/no-unpublished-bin': 'error',
|
||||
'n/no-unpublished-import': 'off', // TODO need to exclude seeds
|
||||
'n/no-unpublished-require': 'error',
|
||||
'n/no-unsupported-features': ['error', { ignores: ['modules'] }],
|
||||
'n/no-unsupported-features/es-builtins': 'error',
|
||||
'n/no-unsupported-features/es-syntax': 'error',
|
||||
'n/no-unsupported-features/node-builtins': 'error',
|
||||
'n/process-exit-as-throw': 'error',
|
||||
'n/shebang': 'error',
|
||||
'n/callback-return': 'error',
|
||||
'n/exports-style': 'error',
|
||||
'n/file-extension-in-import': 'off',
|
||||
'n/global-require': 'error',
|
||||
'n/no-mixed-requires': 'error',
|
||||
'n/no-process-env': 'error',
|
||||
'n/no-restricted-import': 'error',
|
||||
'n/no-restricted-require': 'error',
|
||||
// 'n/no-sync': 'error',
|
||||
'n/prefer-global/buffer': 'error',
|
||||
'n/prefer-global/console': 'error',
|
||||
'n/prefer-global/process': 'error',
|
||||
'n/prefer-global/text-decoder': 'error',
|
||||
'n/prefer-global/text-encoder': 'error',
|
||||
'n/prefer-global/url': 'error',
|
||||
'n/prefer-global/url-search-params': 'error',
|
||||
'n/prefer-promises/dns': 'error',
|
||||
// 'n/prefer-promises/fs': 'error',
|
||||
// promise
|
||||
// 'promise/catch-or-return': 'error',
|
||||
// 'promise/no-return-wrap': 'error',
|
||||
// 'promise/param-names': 'error',
|
||||
// 'promise/always-return': 'error',
|
||||
// 'promise/no-native': 'off',
|
||||
// 'promise/no-nesting': 'warn',
|
||||
// 'promise/no-promise-in-callback': 'warn',
|
||||
// 'promise/no-callback-in-promise': 'warn',
|
||||
// 'promise/avoid-new': 'warn',
|
||||
// 'promise/no-new-statics': 'error',
|
||||
// 'promise/no-return-in-finally': 'warn',
|
||||
// 'promise/valid-params': 'warn',
|
||||
// 'promise/prefer-await-to-callbacks': 'error',
|
||||
// 'promise/no-multiple-resolved': 'error',
|
||||
// eslint comments
|
||||
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
|
||||
'@eslint-community/eslint-comments/no-restricted-disable': 'error',
|
||||
'@eslint-community/eslint-comments/no-use': 'off',
|
||||
'@eslint-community/eslint-comments/require-description': 'off',
|
||||
},
|
||||
overrides: [
|
||||
// only for ts files
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: [
|
||||
// 'plugin:@typescript-eslint/recommended',
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
// 'plugin:@typescript-eslint/strict',
|
||||
],
|
||||
rules: {
|
||||
// allow explicitly defined dangling promises
|
||||
// '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
|
||||
'no-void': ['error', { allowAsStatement: true }],
|
||||
// ignore prefer-regexp-exec rule to allow string.match(regex)
|
||||
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||
// this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486
|
||||
'import/unambiguous': 'off',
|
||||
// this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable
|
||||
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||
},
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
// this is to properly reference the referenced project database without requirement of compiling it
|
||||
// eslint-disable-next-line camelcase
|
||||
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||
},
|
||||
},
|
||||
// we do not have testing on the database
|
||||
// {
|
||||
// files: ['*.test.ts'],
|
||||
// plugins: ['jest'],
|
||||
// rules: {
|
||||
// 'jest/no-disabled-tests': 'error',
|
||||
// 'jest/no-focused-tests': 'error',
|
||||
// 'jest/no-identical-title': 'error',
|
||||
// 'jest/prefer-to-have-length': 'error',
|
||||
// 'jest/valid-expect': 'error',
|
||||
// '@typescript-eslint/unbound-method': 'off',
|
||||
// 'jest/unbound-method': 'error',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
}
|
||||
27
dlt-database/.gitignore
vendored
Normal file
27
dlt-database/.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
build/
|
||||
.cache/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
test/unit/coverage
|
||||
|
||||
package-lock.json
|
||||
/.env
|
||||
/.env.bak
|
||||
.env.development.local
|
||||
.env.production.local
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
|
||||
# coverage folder
|
||||
|
||||
coverage/
|
||||
|
||||
*~
|
||||
9
dlt-database/.prettierrc.js
Normal file
9
dlt-database/.prettierrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
endOfLine: "auto",
|
||||
};
|
||||
130
dlt-database/Dockerfile
Normal file
130
dlt-database/Dockerfile
Normal file
@ -0,0 +1,130 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:18.7.0-alpine3.16 as base
|
||||
|
||||
# ENVs (available in production aswell, can be overwritten by commandline or env file)
|
||||
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||
ENV DOCKER_WORKDIR="/app"
|
||||
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
|
||||
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
|
||||
ENV BUILD_VERSION="0.0.0.0"
|
||||
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||
ENV BUILD_COMMIT="0000000"
|
||||
## SET NODE_ENV
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
# Labels
|
||||
LABEL org.label-schema.build-date="${BUILD_DATE}"
|
||||
LABEL org.label-schema.name="gradido:database"
|
||||
LABEL org.label-schema.description="Gradido Database Migration Service"
|
||||
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
|
||||
LABEL org.label-schema.url="https://gradido.net"
|
||||
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/database"
|
||||
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
|
||||
LABEL org.label-schema.vendor="Gradido Community"
|
||||
LABEL org.label-schema.version="${BUILD_VERSION}"
|
||||
LABEL org.label-schema.schema-version="1.0"
|
||||
LABEL maintainer="support@gradido.net"
|
||||
|
||||
# Install Additional Software
|
||||
## install: git
|
||||
#RUN apk --no-cache add git
|
||||
|
||||
## Workdir
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
##################################################################################
|
||||
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
||||
##################################################################################
|
||||
FROM base as development
|
||||
|
||||
# We don't need to copy or build anything since we gonna bind to the
|
||||
# local filesystem which will need a rebuild anyway
|
||||
|
||||
# Run command
|
||||
# (for development we need to execute npm install since the
|
||||
# node_modules are on another volume and need updating)
|
||||
CMD /bin/sh -c "yarn install"
|
||||
|
||||
##################################################################################
|
||||
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||
##################################################################################
|
||||
FROM base as build
|
||||
|
||||
# Copy everything
|
||||
COPY . .
|
||||
# npm install
|
||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||
# npm build
|
||||
RUN yarn run build
|
||||
|
||||
##################################################################################
|
||||
# TEST UP ########################################################################
|
||||
##################################################################################
|
||||
FROM build as test_up
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn install && yarn run dev_up"
|
||||
|
||||
##################################################################################
|
||||
# TEST RESET #####################################################################
|
||||
##################################################################################
|
||||
FROM build as test_reset
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn install && yarn run dev_reset"
|
||||
|
||||
##################################################################################
|
||||
# TEST DOWN ######################################################################
|
||||
##################################################################################
|
||||
FROM build as test_down
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn install && yarn run dev_down"
|
||||
|
||||
##################################################################################
|
||||
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
|
||||
##################################################################################
|
||||
FROM base as production
|
||||
|
||||
# Copy "binary"-files from build image
|
||||
COPY --from=build ${DOCKER_WORKDIR}/build ./build
|
||||
# We also copy the node_modules express and serve-static for the run script
|
||||
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
|
||||
# Copy static files
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
|
||||
# Copy package.json for script definitions (lock file should not be needed)
|
||||
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
|
||||
# Copy Mnemonic files
|
||||
COPY --from=build ${DOCKER_WORKDIR}/src/config/*.txt ./src/config/
|
||||
# Copy log folder
|
||||
COPY --from=build ${DOCKER_WORKDIR}/log ./log
|
||||
# Copy run scripts run/
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
|
||||
|
||||
##################################################################################
|
||||
# PRODUCTION UP ##################################################################
|
||||
##################################################################################
|
||||
FROM production as production_up
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run up"
|
||||
|
||||
##################################################################################
|
||||
# PRODUCTION RESET ###############################################################
|
||||
##################################################################################
|
||||
FROM production as production_reset
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run reset"
|
||||
|
||||
##################################################################################
|
||||
# PRODUCTION DOWN ################################################################
|
||||
##################################################################################
|
||||
FROM production as production_down
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run down"
|
||||
39
dlt-database/README.md
Normal file
39
dlt-database/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# database
|
||||
|
||||
## Project setup
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Upgrade migrations production
|
||||
|
||||
```bash
|
||||
yarn up
|
||||
```
|
||||
|
||||
## Upgrade migrations development
|
||||
|
||||
```bash
|
||||
yarn dev_up
|
||||
```
|
||||
|
||||
## Downgrade migrations production
|
||||
|
||||
```bash
|
||||
yarn down
|
||||
```
|
||||
|
||||
## Downgrade migrations development
|
||||
|
||||
```bash
|
||||
yarn dev_down
|
||||
```
|
||||
|
||||
## Reset database
|
||||
|
||||
```bash
|
||||
yarn dev_reset
|
||||
```
|
||||
|
||||
Runs all down migrations and after this all up migrations.
|
||||
79
dlt-database/entity/0001-init_db/Account.ts
Normal file
79
dlt-database/entity/0001-init_db/Account.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
} from 'typeorm'
|
||||
import { User } from './User'
|
||||
import { Community } from './Community'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
import { ConfirmedTransaction } from './ConfirmedTransaction'
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
@Entity('accounts')
|
||||
export class Account {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ManyToOne(() => User, (user) => user.accounts) // Assuming you have a User entity with 'accounts' relation
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User
|
||||
|
||||
// if user id is null, account belongs to community gmw or auf
|
||||
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: true })
|
||||
userId?: number
|
||||
|
||||
@Column({ name: 'derivation_index', type: 'int', unsigned: true })
|
||||
derivationIndex: number
|
||||
|
||||
@Column({ name: 'derive2_pubkey', type: 'binary', length: 32, unique: true })
|
||||
derive2Pubkey: Buffer
|
||||
|
||||
@Column({ type: 'tinyint', unsigned: true })
|
||||
type: number
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
default: 0,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
balance: Decimal
|
||||
|
||||
@Column({
|
||||
name: 'balance_date',
|
||||
type: 'datetime',
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
balanceDate: Date
|
||||
|
||||
@ManyToMany(() => Community, (community) => community.communityAccounts)
|
||||
@JoinTable({
|
||||
name: 'accounts_communities',
|
||||
joinColumn: { name: 'account_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'community_id', referencedColumnName: 'id' },
|
||||
})
|
||||
accountCommunities: Community[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.signingAccount)
|
||||
transactionRecipesSigning?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientAccount)
|
||||
transactionRecipesRecipient?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => ConfirmedTransaction, (transaction) => transaction.account)
|
||||
confirmedTransactions?: ConfirmedTransaction[]
|
||||
}
|
||||
30
dlt-database/entity/0001-init_db/AccountCommunity.ts
Normal file
30
dlt-database/entity/0001-init_db/AccountCommunity.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'
|
||||
|
||||
import { Account } from './Account'
|
||||
import { Community } from './Community'
|
||||
|
||||
@Entity('accounts_communities')
|
||||
export class AccountCommunity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.accountCommunities)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
account: Account
|
||||
|
||||
@Column({ name: 'account_id', type: 'int', unsigned: true })
|
||||
accountId: number
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.communityAccounts)
|
||||
@JoinColumn({ name: 'community_id' })
|
||||
community: Community
|
||||
|
||||
@Column({ name: 'community_id', type: 'int', unsigned: true })
|
||||
communityId: number
|
||||
|
||||
@Column({ name: 'valid_from', type: 'datetime' })
|
||||
validFrom: Date
|
||||
|
||||
@Column({ name: 'valid_to', type: 'datetime', nullable: true })
|
||||
validTo?: Date
|
||||
}
|
||||
68
dlt-database/entity/0001-init_db/Community.ts
Normal file
68
dlt-database/entity/0001-init_db/Community.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
OneToMany,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
} from 'typeorm'
|
||||
import { Account } from './Account'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'iota_topic', collation: 'utf8mb4_unicode_ci' })
|
||||
iotaTopic: string
|
||||
|
||||
@Column({ name: 'root_pubkey', type: 'binary', length: 32, unique: true })
|
||||
rootPubkey: Buffer
|
||||
|
||||
@Column({ name: 'root_privkey', type: 'binary', length: 32, nullable: true })
|
||||
rootPrivkey?: Buffer
|
||||
|
||||
@Column({ name: 'root_chaincode', type: 'binary', length: 32, nullable: true })
|
||||
rootChaincode?: Buffer
|
||||
|
||||
@Column({ type: 'tinyint', default: true })
|
||||
foreign: boolean
|
||||
|
||||
@Column({ name: 'gmw_account_id', type: 'int', unsigned: true, nullable: true })
|
||||
gmwAccountId?: number
|
||||
|
||||
@OneToOne(() => Account)
|
||||
@JoinColumn({ name: 'gmw_account_id' })
|
||||
gmwAccount?: Account
|
||||
|
||||
@Column({ name: 'auf_account_id', type: 'int', unsigned: true, nullable: true })
|
||||
aufAccountId?: number
|
||||
|
||||
@OneToOne(() => Account)
|
||||
@JoinColumn({ name: 'auf_account_id' })
|
||||
aufAccount?: Account
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP(3)' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime', nullable: true })
|
||||
confirmedAt?: Date
|
||||
|
||||
@ManyToMany(() => Account, (account) => account.accountCommunities)
|
||||
@JoinTable({
|
||||
name: 'accounts_communities',
|
||||
joinColumn: { name: 'community_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'account_id', referencedColumnName: 'id' },
|
||||
})
|
||||
communityAccounts: Account[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.senderCommunity)
|
||||
transactionRecipesSender?: TransactionRecipe[]
|
||||
|
||||
@OneToMany(() => TransactionRecipe, (recipe) => recipe.recipientCommunity)
|
||||
transactionRecipesRecipient?: TransactionRecipe[]
|
||||
}
|
||||
49
dlt-database/entity/0001-init_db/ConfirmedTransaction.ts
Normal file
49
dlt-database/entity/0001-init_db/ConfirmedTransaction.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToOne } from 'typeorm'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Account } from './Account'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
|
||||
@Entity('confirmed_transactions')
|
||||
export class ConfirmedTransaction {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@OneToOne(() => TransactionRecipe, (recipe) => recipe.confirmedTransaction)
|
||||
@JoinColumn({ name: 'transaction_recipe_id' })
|
||||
transactionRecipe: TransactionRecipe
|
||||
|
||||
@Column({ name: 'transaction_recipe_id', type: 'int', unsigned: true })
|
||||
transactionRecipeId: number
|
||||
|
||||
@Column({ type: 'bigint' })
|
||||
nr: number
|
||||
|
||||
@Column({ type: 'binary', length: 48 })
|
||||
runningHash: Buffer
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.confirmedTransactions)
|
||||
@JoinColumn({ name: 'account_id' })
|
||||
account: Account
|
||||
|
||||
@Column({ name: 'account_id', type: 'int', unsigned: true })
|
||||
accountId: number
|
||||
|
||||
@Column({
|
||||
name: 'account_balance',
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
nullable: false,
|
||||
default: 0,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
accountBalance: Decimal
|
||||
|
||||
@Column({ name: 'iota_milestone', type: 'bigint' })
|
||||
iotaMilestone: number
|
||||
|
||||
@Column({ name: 'confirmed_at', type: 'datetime' })
|
||||
confirmedAt: Date
|
||||
}
|
||||
10
dlt-database/entity/0001-init_db/InvalidTransaction.ts
Normal file
10
dlt-database/entity/0001-init_db/InvalidTransaction.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
@Entity('invalid_transactions')
|
||||
export class InvalidTransaction {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'iota_message_id', type: 'binary', length: 32 })
|
||||
iotaMessageId: Buffer
|
||||
}
|
||||
80
dlt-database/entity/0001-init_db/TransactionRecipe.ts
Normal file
80
dlt-database/entity/0001-init_db/TransactionRecipe.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { Account } from './Account'
|
||||
import { Community } from './Community'
|
||||
import { ConfirmedTransaction } from './ConfirmedTransaction'
|
||||
|
||||
@Entity('transaction_recipes')
|
||||
export class TransactionRecipe {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true, type: 'bigint' })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'iota_message_id', type: 'binary', length: 32, nullable: true })
|
||||
iotaMessageId?: Buffer
|
||||
|
||||
// if transaction has a sender than it is also the sender account
|
||||
@ManyToOne(() => Account, (account) => account.transactionRecipesSigning)
|
||||
@JoinColumn({ name: 'signing_account_id' })
|
||||
signingAccount: Account
|
||||
|
||||
@Column({ name: 'signing_account_id', type: 'int', unsigned: true })
|
||||
signingAccountId: number
|
||||
|
||||
@ManyToOne(() => Account, (account) => account.transactionRecipesRecipient)
|
||||
@JoinColumn({ name: 'recipient_account_id' })
|
||||
recipientAccount?: Account
|
||||
|
||||
@Column({ name: 'recipient_account_id', type: 'int', unsigned: true, nullable: true })
|
||||
recipientAccountId?: number
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.transactionRecipesSender)
|
||||
@JoinColumn({ name: 'sender_community_id' })
|
||||
senderCommunity: Community
|
||||
|
||||
@Column({ name: 'sender_community_id', type: 'int', unsigned: true })
|
||||
senderCommunityId: number
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.transactionRecipesRecipient)
|
||||
@JoinColumn({ name: 'recipient_community_id' })
|
||||
recipientCommunity?: Community
|
||||
|
||||
@Column({ name: 'sender_community_id', type: 'int', unsigned: true, nullable: true })
|
||||
recipientCommunityId?: number
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
nullable: true,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
amount?: Decimal
|
||||
|
||||
@Column({ type: 'tinyint' })
|
||||
type: number
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'datetime' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'body_bytes', type: 'blob' })
|
||||
bodyBytes: Buffer
|
||||
|
||||
@Column({ type: 'binary', length: 64 })
|
||||
signature: Buffer
|
||||
|
||||
@Column({ name: 'protocol_version', type: 'int', default: 1 })
|
||||
protocolVersion: number
|
||||
|
||||
@OneToOne(() => ConfirmedTransaction, (transaction) => transaction.transactionRecipe)
|
||||
confirmedTransaction?: ConfirmedTransaction
|
||||
}
|
||||
46
dlt-database/entity/0001-init_db/User.ts
Normal file
46
dlt-database/entity/0001-init_db/User.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
} from 'typeorm'
|
||||
|
||||
import { Account } from './Account'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({
|
||||
name: 'gradido_id',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
gradidoID?: string
|
||||
|
||||
@Column({ name: 'derive1_pubkey', type: 'binary', length: 32, unique: true })
|
||||
derive1Pubkey: Buffer
|
||||
|
||||
@CreateDateColumn({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'confirmed_at',
|
||||
type: 'datetime',
|
||||
nullable: true,
|
||||
})
|
||||
confirmedAt?: Date
|
||||
|
||||
@OneToMany(() => Account, (account) => account.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
accounts?: Account[]
|
||||
}
|
||||
1
dlt-database/entity/Account.ts
Normal file
1
dlt-database/entity/Account.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Account } from './0001-init_db/Account'
|
||||
1
dlt-database/entity/AccountCommunity.ts
Normal file
1
dlt-database/entity/AccountCommunity.ts
Normal file
@ -0,0 +1 @@
|
||||
export { AccountCommunity } from './0001-init_db/AccountCommunity'
|
||||
1
dlt-database/entity/Community.ts
Normal file
1
dlt-database/entity/Community.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Community } from './0001-init_db/Community'
|
||||
1
dlt-database/entity/ConfirmedTransaction.ts
Normal file
1
dlt-database/entity/ConfirmedTransaction.ts
Normal file
@ -0,0 +1 @@
|
||||
export { ConfirmedTransaction } from './0001-init_db/ConfirmedTransaction'
|
||||
1
dlt-database/entity/InvalidTransaction.ts
Normal file
1
dlt-database/entity/InvalidTransaction.ts
Normal file
@ -0,0 +1 @@
|
||||
export { InvalidTransaction } from './0001-init_db/InvalidTransaction'
|
||||
1
dlt-database/entity/TransactionRecipe.ts
Normal file
1
dlt-database/entity/TransactionRecipe.ts
Normal file
@ -0,0 +1 @@
|
||||
export { TransactionRecipe } from './0001-init_db/TransactionRecipe'
|
||||
1
dlt-database/entity/User.ts
Normal file
1
dlt-database/entity/User.ts
Normal file
@ -0,0 +1 @@
|
||||
export { User } from './0001-init_db/User'
|
||||
2
dlt-database/log/.gitignore
vendored
Normal file
2
dlt-database/log/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
130
dlt-database/migrations/0001-init_db.ts
Normal file
130
dlt-database/migrations/0001-init_db.ts
Normal file
@ -0,0 +1,130 @@
|
||||
/* FIRST MIGRATION
|
||||
*
|
||||
* This migration is special since it takes into account that
|
||||
* the database can be setup already but also may not be.
|
||||
* Therefore you will find all `CREATE TABLE` statements with
|
||||
* a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
|
||||
* downgrade function all `DROP TABLE` with a `IF EXISTS`.
|
||||
* This ensures compatibility for existing or non-existing
|
||||
* databases.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
// write upgrade logic as parameter of queryFn
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`users\` (
|
||||
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`gradido_id\` char(36) DEFAULT NULL,
|
||||
\`derive1_pubkey\` binary(32) NOT NULL,
|
||||
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
\`confirmed_at\` datetime(3) DEFAULT NULL,
|
||||
PRIMARY KEY (\`id\`),
|
||||
INDEX \`gradido_id\` (\`gradido_id\`),
|
||||
UNIQUE KEY \`pubkey\` (\`pubkey\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`accounts\` (
|
||||
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`user_id\` int(10) unsigned DEFAULT NULL,
|
||||
\`derivation_index\` int(10) unsigned NOT NULL,
|
||||
\`derive2_pubkey\` binary(32) NOT NULL,
|
||||
\`type\` tinyint unsigned NOT NULL,
|
||||
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
\`confirmed_at\` datetime(3) DEFAULT NULL,
|
||||
\`balance\` decimal(40,20) NOT NULL DEFAULT 0,
|
||||
\`balance_date\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (\`id\`),
|
||||
UNIQUE KEY \`pubkey\` (\`pubkey\`),
|
||||
FOREIGN KEY (\`user_id\`) REFERENCES users(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`communities\` (
|
||||
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`iota_topic\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
\`root_pubkey\` binary(32) NOT NULL,
|
||||
\`root_privkey\` binary(32) DEFAULT NULL,
|
||||
\`root_chaincode\` binary(32) DEFAULT NULL,
|
||||
\`foreign\` tinyint(4) NOT NULL DEFAULT true,
|
||||
\`gmw_account_id\` int(10) unsigned DEFAULT NULL,
|
||||
\`auf_account_id\` int(10) unsigned DEFAULT NULL,
|
||||
\`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
\`confirmed_at\` datetime(3) DEFAULT NULL,
|
||||
PRIMARY KEY (\`id\`),
|
||||
UNIQUE KEY \`pubkey\` (\`pubkey\`),
|
||||
FOREIGN KEY (\`gmw_account_id\`) REFERENCES accounts(id),
|
||||
FOREIGN KEY (\`auf_account_id\`) REFERENCES accounts(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`accounts_communities\` (
|
||||
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`account_id\` int(10) unsigned NOT NULL,
|
||||
\`community_id\` int(10) unsigned NOT NULL,
|
||||
\`valid_from\` datetime(3) NOT NULL,
|
||||
\`valid_to\` datetime(3) DEFAULT NULL,
|
||||
PRIMARY KEY (\`id\`),
|
||||
FOREIGN KEY (\`account_id\`) REFERENCES accounts(id),
|
||||
FOREIGN KEY (\`community_id\`) REFERENCES communities(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`transaction_recipes\` (
|
||||
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`iota_message_id\` binary(32) DEFAULT NULL,
|
||||
\`signing_account_id\` int(10) unsigned NOT NULL,
|
||||
\`recipient_account_id\` int(10) unsigned DEFAULT NULL,
|
||||
\`sender_community_id\` int(10) unsigned NOT NULL,
|
||||
\`recipient_community_id\` int(10) unsigned DEFAULT NULL,
|
||||
\`amount\` decimal(40,20) DEFAULT NULL,
|
||||
\`type\` tinyint unsigned NOT NULL,
|
||||
\`created_at\` datetime(3) NOT NULL,
|
||||
\`body_bytes\` BLOB NOT NULL,
|
||||
\`signature\` binary(64) NOT NULL,
|
||||
\`protocol_version\` int(10) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (\`id\`),
|
||||
FOREIGN KEY (\`signing_account_id\`) REFERENCES accounts(id),
|
||||
FOREIGN KEY (\`recipient_account_id\`) REFERENCES accounts(id),
|
||||
FOREIGN KEY (\`sender_community_id\`) REFERENCES communities(id),
|
||||
FOREIGN KEY (\`recipient_community_id\`) REFERENCES communities(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`confirmed_transactions\` (
|
||||
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`transaction_recipe_id\` bigint unsigned NOT NULL,
|
||||
\`nr\` bigint unsigned NOT NULL,
|
||||
\`running_hash\` binary(48) NOT NULL,
|
||||
\`account_id\` int(10) unsigned NOT NULL,
|
||||
\`account_balance\` decimal(40,20) NOT NULL DEFAULT 0,
|
||||
\`iota_milestone\` bigint NOT NULL,
|
||||
\`confirmed_at\` datetime(3) NOT NULL,
|
||||
PRIMARY KEY (\`id\`),
|
||||
FOREIGN KEY (\`transaction_recipe_id\`) REFERENCES transaction_recipes(id),
|
||||
FOREIGN KEY (\`account_id\`) REFERENCES accounts(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
|
||||
|
||||
await queryFn(`
|
||||
CREATE TABLE IF NOT EXISTS \`invalid_transactions\` (
|
||||
\`id\` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`iota_message_id\` binary(32) NOT NULL,
|
||||
PRIMARY KEY (\`id\`),
|
||||
INDEX (\`iota_message_id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
// write downgrade logic as parameter of queryFn
|
||||
await queryFn(`DROP TABLE IF EXISTS \`users\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`accounts\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`accounts_communities\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`transaction_recipes\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`confirmed_transactions\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`community\`;`)
|
||||
await queryFn(`DROP TABLE IF EXISTS \`invalid_transactions\`;`)
|
||||
}
|
||||
55
dlt-database/package.json
Normal file
55
dlt-database/package.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "gradido-database",
|
||||
"version": "1.23.2",
|
||||
"description": "Gradido Database Tool to execute database migrations",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/database",
|
||||
"author": "Ulf Gebhardt",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "tsc --build --clean",
|
||||
"up": "cross-env TZ=UTC node build/src/index.js up",
|
||||
"down": "cross-env TZ=UTC node build/src/index.js down",
|
||||
"reset": "cross-env TZ=UTC node build/src/index.js reset",
|
||||
"dev_up": "cross-env TZ=UTC ts-node src/index.ts up",
|
||||
"dev_down": "cross-env TZ=UTC ts-node src/index.ts down",
|
||||
"dev_reset": "cross-env TZ=UTC ts-node src/index.ts reset",
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^3.2.1",
|
||||
"@types/faker": "^5.5.9",
|
||||
"@types/node": "^16.10.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.4",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.7.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-security": "^1.7.1",
|
||||
"prettier": "^2.8.7",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto": "^1.0.1",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-mysql-migrate": "^1.0.2",
|
||||
"typeorm": "^0.3.16",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
39
dlt-database/src/config/index.ts
Normal file
39
dlt-database/src/config/index.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/* eslint-disable n/no-process-env */
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const constants = {
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v1.2022-08-22',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_HOST: process.env.DB_HOST || 'localhost',
|
||||
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
|
||||
DB_USER: process.env.DB_USER || 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD || '',
|
||||
DB_DATABASE: process.env.DB_DATABASE || 'gradido_dlt',
|
||||
}
|
||||
|
||||
const migrations = {
|
||||
MIGRATIONS_TABLE: process.env.MIGRATIONS_TABLE || 'migrations',
|
||||
}
|
||||
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
if (
|
||||
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
|
||||
constants.CONFIG_VERSION.CURRENT,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
|
||||
)
|
||||
}
|
||||
|
||||
export const CONFIG = { ...constants, ...database, ...migrations }
|
||||
56
dlt-database/src/index.ts
Normal file
56
dlt-database/src/index.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { createDatabase } from './prepare'
|
||||
import { CONFIG } from './config'
|
||||
|
||||
import { createPool } from 'mysql'
|
||||
import { Migration } from 'ts-mysql-migrate'
|
||||
import path from 'path'
|
||||
|
||||
const run = async (command: string) => {
|
||||
// Database actions not supported by our migration library
|
||||
await createDatabase()
|
||||
|
||||
// Initialize Migrations
|
||||
const pool = createPool({
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
user: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
})
|
||||
const migration = new Migration({
|
||||
conn: pool,
|
||||
tableName: CONFIG.MIGRATIONS_TABLE,
|
||||
silent: true,
|
||||
dir: path.join(__dirname, '..', 'migrations'),
|
||||
})
|
||||
await migration.initialize()
|
||||
|
||||
// Execute command
|
||||
switch (command) {
|
||||
case 'up':
|
||||
await migration.up() // use for upgrade script
|
||||
break
|
||||
case 'down':
|
||||
await migration.down() // use for downgrade script
|
||||
break
|
||||
case 'reset':
|
||||
// TODO protect from production
|
||||
await migration.reset()
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported command ${command}`)
|
||||
}
|
||||
|
||||
// Terminate connections gracefully
|
||||
pool.end()
|
||||
}
|
||||
|
||||
run(process.argv[2])
|
||||
.catch((err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err)
|
||||
process.exit(1)
|
||||
})
|
||||
.then(() => {
|
||||
process.exit()
|
||||
})
|
||||
22
dlt-database/src/prepare.ts
Normal file
22
dlt-database/src/prepare.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { createConnection } from 'mysql2/promise'
|
||||
|
||||
import { CONFIG } from './config'
|
||||
|
||||
export const createDatabase = async (): Promise<void> => {
|
||||
const con = await createConnection({
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
user: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
})
|
||||
|
||||
await con.connect()
|
||||
|
||||
// Create Database `gradido_dlt`
|
||||
await con.query(`
|
||||
CREATE DATABASE IF NOT EXISTS ${CONFIG.DB_DATABASE}
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
DEFAULT COLLATE utf8mb4_unicode_ci;`)
|
||||
|
||||
await con.end()
|
||||
}
|
||||
1
dlt-database/src/typeorm.ts
Normal file
1
dlt-database/src/typeorm.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from 'typeorm'
|
||||
19
dlt-database/src/typeorm/DecimalTransformer.ts
Normal file
19
dlt-database/src/typeorm/DecimalTransformer.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { ValueTransformer } from 'typeorm'
|
||||
|
||||
Decimal.set({
|
||||
precision: 25,
|
||||
rounding: Decimal.ROUND_HALF_UP,
|
||||
})
|
||||
|
||||
export const DecimalTransformer: ValueTransformer = {
|
||||
/**
|
||||
* Used to marshal Decimal when writing to the database.
|
||||
*/
|
||||
to: (decimal: Decimal | null): string | null => (decimal ? decimal.toString() : null),
|
||||
|
||||
/**
|
||||
* Used to unmarshal Decimal when reading from the database.
|
||||
*/
|
||||
from: (decimal: string | null): Decimal | null => (decimal ? new Decimal(decimal) : null),
|
||||
}
|
||||
73
dlt-database/tsconfig.json
Normal file
73
dlt-database/tsconfig.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
"declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./build", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
"composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */
|
||||
}
|
||||
2573
dlt-database/yarn.lock
Normal file
2573
dlt-database/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,12 @@ services:
|
||||
########################################################
|
||||
database:
|
||||
platform: linux/amd64
|
||||
|
||||
########################################################
|
||||
# DLT-DATABASE #############################################
|
||||
########################################################
|
||||
dlt-database:
|
||||
platform: linux/amd64
|
||||
|
||||
#########################################################
|
||||
## NGINX ################################################
|
||||
|
||||
@ -149,6 +149,28 @@ services:
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./database:/app
|
||||
|
||||
########################################################
|
||||
# DLT-DATABASE ##############################################
|
||||
########################################################
|
||||
dlt-database:
|
||||
# we always run on production here since else the service lingers
|
||||
# feel free to change this behaviour if it seems useful
|
||||
# Due to problems with the volume caching the built files
|
||||
# we changed this to test build. This keeps the service running.
|
||||
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||
image: gradido/dlt-database:local-test_up
|
||||
build:
|
||||
target: test_up
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
volumes:
|
||||
# This makes sure the docker container has its own node modules.
|
||||
# Therefore it is possible to have a different node version on the host machine
|
||||
- dlt-database_node_modules:/app/node_modules
|
||||
- dlt-database_build:/app/build
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./dlt-database:/app
|
||||
|
||||
#########################################################
|
||||
## MARIADB ##############################################
|
||||
#########################################################
|
||||
@ -203,4 +225,6 @@ volumes:
|
||||
federation_database_node_modules:
|
||||
federation_database_build:
|
||||
database_node_modules:
|
||||
database_build:
|
||||
database_build:
|
||||
dlt-database_node_modules:
|
||||
dlt-database_build:
|
||||
@ -77,6 +77,18 @@ services:
|
||||
- NODE_ENV="test"
|
||||
# restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run
|
||||
|
||||
########################################################
|
||||
# DLT-DATABASE #############################################
|
||||
########################################################
|
||||
dlt-database:
|
||||
image: gradido/dlt-database:test_up
|
||||
build:
|
||||
context: ./dlt-database
|
||||
target: test_up
|
||||
environment:
|
||||
- NODE_ENV="test"
|
||||
# restart: always # this is very dangerous, but worth a test for the delayed mariadb startup at first run
|
||||
|
||||
#########################################################
|
||||
## MARIADB ##############################################
|
||||
#########################################################
|
||||
|
||||
@ -239,6 +239,32 @@ services:
|
||||
# Application only envs
|
||||
#env_file:
|
||||
# - ./frontend/.env
|
||||
|
||||
########################################################
|
||||
# DLT-DATABASE #############################################
|
||||
########################################################
|
||||
dlt-database:
|
||||
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||
image: gradido/dlt-database:local-production_up
|
||||
build:
|
||||
context: ./dlt-database
|
||||
target: production_up
|
||||
depends_on:
|
||||
- mariadb
|
||||
networks:
|
||||
- internal-net
|
||||
- external-net # this is required to fetch the packages
|
||||
environment:
|
||||
# Envs used in Dockerfile
|
||||
# - DOCKER_WORKDIR="/app"
|
||||
- BUILD_DATE
|
||||
- BUILD_VERSION
|
||||
- BUILD_COMMIT
|
||||
- NODE_ENV="production"
|
||||
- DB_HOST=mariadb
|
||||
# Application only envs
|
||||
#env_file:
|
||||
# - ./frontend/.env
|
||||
|
||||
#########################################################
|
||||
## NGINX ################################################
|
||||
|
||||
4
docu/Concepts/DLT/database.md
Normal file
4
docu/Concepts/DLT/database.md
Normal file
@ -0,0 +1,4 @@
|
||||
# DLT Connector Database
|
||||
|
||||

|
||||
[Link zum PDF](img/dlt-diagramm.pdf)
|
||||
26
docu/Concepts/DLT/derived_keys.md
Normal file
26
docu/Concepts/DLT/derived_keys.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Key Derivation
|
||||
The DLT connector uses key derivation to derive keys for each user in the community account with a master key.
|
||||

|
||||
|
||||
## user accounts
|
||||
The Path for key derivation contain the gradido id, and derivation index of account
|
||||
Gradido ID: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16
|
||||
Derivation Index: 1
|
||||
Key derivation Path:
|
||||
```
|
||||
m/03857ac1'/9cc2'/483e'/8a91'/e5b10f5b8d16'/1
|
||||
```
|
||||
|
||||
## gmw and auf accounts
|
||||
For gmw and auf accounts two special Paths used:
|
||||
gmw account => account nr 1
|
||||
```
|
||||
m/1'
|
||||
```
|
||||
auf account => account nr 2
|
||||
```
|
||||
m/2'
|
||||
```
|
||||
|
||||
|
||||
|
||||
11057
docu/Concepts/DLT/img/dlt-diagramm.pdf
Normal file
11057
docu/Concepts/DLT/img/dlt-diagramm.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
docu/Concepts/DLT/img/dlt-diagramm.png
Normal file
BIN
docu/Concepts/DLT/img/dlt-diagramm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
67
docu/Concepts/DLT/overview.md
Normal file
67
docu/Concepts/DLT/overview.md
Normal file
@ -0,0 +1,67 @@
|
||||
# DLT-Connector Overview
|
||||
|
||||
What the DLT Connector does roughly.
|
||||
|
||||
- create transaction
|
||||
- receive transactions from iota
|
||||
- warmup, load missing transactions from iota on startup
|
||||
|
||||
## create transaction
|
||||
- called from backend with transaction details
|
||||
- sender user | signing user (in case of contribution)
|
||||
- uuid
|
||||
- account nr | default = 1
|
||||
- community uuid
|
||||
- recipient user
|
||||
- uuid
|
||||
- account nr | default = 1
|
||||
- community uuid
|
||||
- amount
|
||||
- memo
|
||||
- type
|
||||
- createdAt
|
||||
- load or create accounts
|
||||
- compose protobuf transaction
|
||||
- derive correct private key for signing account and sign transaction
|
||||
- validate transaction
|
||||
- write transaction into transaction_recipes table
|
||||
- send transaction to iota
|
||||
- update iota message id in transaction_recipes table
|
||||
- return to backend with iota message id
|
||||
|
||||
|
||||
## receive transactions from iota
|
||||
- listen on all registered community topics on iota
|
||||
- make sure we have everything from milestone
|
||||
- sort per community by iota milestone, createdAt ASC
|
||||
- per message:
|
||||
- deserialize to protobuf object
|
||||
- validate
|
||||
- if valid:
|
||||
- calculate running_hash and account_balance
|
||||
- write into confirmed_transactions
|
||||
- if invalid:
|
||||
- write into invalid_transactions
|
||||
- send request to backend with final transaction data for comparison
|
||||
- sender user | signing user (in case of contribution)
|
||||
- uuid
|
||||
- account nr
|
||||
- community uuid
|
||||
- recipient user
|
||||
- uuid
|
||||
- account nr
|
||||
- community uuid
|
||||
- amount
|
||||
- memo
|
||||
- createdAt
|
||||
- confirmedAt
|
||||
- type
|
||||
- iota message id
|
||||
- balance for createdAt
|
||||
- decay for createdAt
|
||||
|
||||
## warmup, load missing transactions from iota or Chronicle on startup
|
||||
- read all iota message ids from all registered topics
|
||||
- check if already exist
|
||||
- load details for not existing message ids
|
||||
- do for every message [receive](#receive-transactions-from-iota)
|
||||
@ -56,3 +56,23 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.community-switch > div,
|
||||
.community-switch ul.dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
.community-switch > div > button {
|
||||
border-radius: 17px;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
}
|
||||
.community-switch .dropdown-toggle::after {
|
||||
float: right;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
position: relative;
|
||||
}
|
||||
.community-switch ul li:first-child {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user