Compare commits
579 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55d06640b5 | |||
| 3874a8da21 | |||
| 6a3ecd5b09 | |||
| afcd4f7262 | |||
| b0ae731fa6 | |||
| 0b678fe69a | |||
| 4cecc78a74 | |||
| 92273d2fc6 | |||
| 04ec749c3c | |||
| 165377816c | |||
| 729904e1c3 | |||
| 6bc4fa0a82 | |||
| f12403672b | |||
| 97daff8d4f | |||
| a32451fd7f | |||
| ca14770971 | |||
| 35ac0695c7 | |||
| 589b614a53 | |||
| 64ea3e05d8 | |||
| 5d0e115438 | |||
| 9f35c1eded | |||
| ff07346fe9 | |||
| 22fffc2f8c | |||
| 205363dd42 | |||
| d2297b882f | |||
| 48bf8dbc5b | |||
| 9585c760d5 | |||
| 8c2cd0aa4d | |||
| 2e680b04c9 | |||
| e2b81f7b57 | |||
| c38b277887 | |||
| a1f6304b22 | |||
| 09d7f56efc | |||
| 9221f3fc96 | |||
| 6b30a0aebc | |||
| 158563f387 | |||
| b0fc5752e2 | |||
| 28903615ed | |||
| caa83c0432 | |||
| 045f4a42db | |||
| 3275bc9cae | |||
| 6d14eaefe1 | |||
| 03274725be | |||
| 0951006063 | |||
| 616bfc6a2a | |||
| c0bfe429ee | |||
| c1d90485a1 | |||
| 6c30527684 | |||
| 4ac751f492 | |||
| de7b137257 | |||
| 0bf689fa8d | |||
| f31ef1649f | |||
| c66bc62c41 | |||
| 9ee59215c8 | |||
| a991adecaf | |||
| 601b1d5c89 | |||
| 8b593883a7 | |||
| bb549c9a89 | |||
| 9f2005622a | |||
| 4238bde13a | |||
| 2d4ce1aac0 | |||
| 37269abde2 | |||
| 54038eabd4 | |||
| 57922e3071 | |||
| ad36dfd448 | |||
| 6e334d2efb | |||
| d3dc1401fd | |||
| 0f59c9911d | |||
| 0ebfd2bc76 | |||
| 734625c1e3 | |||
| eaaea8a64f | |||
| c026ab1777 | |||
| 2c7193efea | |||
| 21f5ef469b | |||
| e7f9eb6e06 | |||
| 9db9c382ab | |||
| b2ba626cde | |||
| 85d93c4f4b | |||
| 09f6dd8d82 | |||
| 2aa6df48c6 | |||
| 2a16260b05 | |||
| c28158b041 | |||
| 661199850c | |||
| 67b69a45cc | |||
| 579322578b | |||
| 0de65042b0 | |||
| f9643f8651 | |||
| fc9ac4c40e | |||
| 84843066f2 | |||
| af3d5c4654 | |||
| 676f3daf50 | |||
| 54970015cb | |||
| 2b49430530 | |||
| feea5f3518 | |||
| 04e580b40f | |||
| f07a3ba97d | |||
| f44e3a81ab | |||
| 77d3bd019e | |||
| 72725d5ab4 | |||
| aa717ed1fe | |||
| 292b49ba79 | |||
| c600eb5d5a | |||
| 6d2f788fa2 | |||
| b8acff3e7c | |||
| b8bdb03fc0 | |||
| 7257abefb4 | |||
| 3095613a76 | |||
| db12b64b3a | |||
| a081edde25 | |||
| 5496c4a10a | |||
| c968ded99a | |||
| 4224a833b4 | |||
| df470f1a5e | |||
| ed0100a82c | |||
| 0ad72e8334 | |||
| 50ee5d1f49 | |||
| 94283a8da2 | |||
| 86e9a3e838 | |||
| d77b9ef7c9 | |||
| e29afa3155 | |||
| 7376fbe7a1 | |||
| 04e98e1c39 | |||
| ddca6e7ec9 | |||
| 1ac12ac668 | |||
| 399936e3ab | |||
| 3d086992dc | |||
| 62ded1290c | |||
| 33cbdfbd13 | |||
| f5ce6ed4bc | |||
| eb9a2ac2e0 | |||
| 8bed529d05 | |||
| 41a8b8007a | |||
| 141dc843f3 | |||
| 902825404e | |||
| 749eaaab30 | |||
| 8f5767b992 | |||
| 715f0c5853 | |||
| a960fd3d56 | |||
| 17b8ac6d0b | |||
| 4ac78fe4d1 | |||
| 957bcf790f | |||
| 66f0b38008 | |||
| f6a2246aab | |||
| fa3e941069 | |||
| 9bb049f746 | |||
| c41f2a4d65 | |||
| 06ff9f2499 | |||
| f91f9fcc94 | |||
| 9a626b0d4f | |||
| ac2adaf4ba | |||
| 3ab198615b | |||
| 7c2831098c | |||
| 2454e22ea2 | |||
| b1fb40ca61 | |||
| e403e938c3 | |||
| 952ba1f1ea | |||
| a25690c2d3 | |||
| 6b6b9c61d7 | |||
| fcc5e522a7 | |||
| c33c0487cf | |||
| 1753a6c247 | |||
| 195f513416 | |||
| c6d38bb3d7 | |||
| 9b2fba9600 | |||
| c88f6501fa | |||
| c511346160 | |||
| 0b02daac1d | |||
| 2390c085e4 | |||
| 698d94feed | |||
| a9a6d39127 | |||
| 395bd31898 | |||
| 7e24492ce8 | |||
| 19d3d80013 | |||
| 8c7875d7ea | |||
| 110ce0d4c6 | |||
| ff8c57fdab | |||
| 54ccdc57bf | |||
| 2463f06ba1 | |||
| 88d5b1f98f | |||
| 43522e9c81 | |||
| 6e798f739f | |||
| 5b5f1280af | |||
| 2188e8dd78 | |||
| 8659e9ea37 | |||
| 4cff481d61 | |||
| 6da910d8fb | |||
| cc08ad46e3 | |||
| 5b52c1adbb | |||
| a20958a89b | |||
| dea36d4b80 | |||
| 6cb7e4caf7 | |||
| 6e41668b25 | |||
| d3e1acddc5 | |||
| a9c511eb2e | |||
| ffef33a9fc | |||
| 982917ddbb | |||
| 07406a50bb | |||
| 78f325e127 | |||
| e1bb97a1db | |||
| 831952806d | |||
| 4c57b7a009 | |||
| 683188f67a | |||
| 848e5271ab | |||
| 7ca5614c44 | |||
| 70fc781a03 | |||
| aafdbab781 | |||
| 5dd0f7ea10 | |||
| 9393d9105c | |||
| 2c3856be3c | |||
| dc746a51a5 | |||
| 0f2c9354f0 | |||
| af00032ee9 | |||
| 5d7e685dc4 | |||
| bfcb79c02b | |||
| ebee6273b0 | |||
| b45900e5bc | |||
| 9f0657683a | |||
| 35b8d8ca25 | |||
| 8f7095ce19 | |||
| d9c8dd20e9 | |||
| 8dcc462a30 | |||
| b561948030 | |||
| 2cbd8684cf | |||
| 107c9fce94 | |||
| 4c18b3e059 | |||
| 9960ec4d58 | |||
| 2a09e048e4 | |||
| 9711f3ba72 | |||
| 0622d2b81b | |||
| a372d1fb60 | |||
| 99e55e730e | |||
| 57d1e915e6 | |||
| 96ad6228bd | |||
| 678f9fbe87 | |||
| 714933df56 | |||
| f0ef9ad51e | |||
| 0255213934 | |||
| 0ad92a999c | |||
| b06456d573 | |||
| 6f1fc2c9b4 | |||
| 44a1982d87 | |||
| 6b0cf5aa96 | |||
| 2cfac7a772 | |||
| 99aaf35e0b | |||
| 942ed1fb55 | |||
| 25f83a98e3 | |||
| ed4040f2ec | |||
| 41034de676 | |||
| 2db0f1a6c8 | |||
| 6b06cc7ef5 | |||
| 2bf26212af | |||
| 9d273c172d | |||
| 6ae3bc82bb | |||
| c782002274 | |||
| cd0906d041 | |||
| f936ecca33 | |||
| 8794e3bc53 | |||
| 4503e2a222 | |||
| 015725f88c | |||
| 39451f0e37 | |||
| 2bb0b7faa5 | |||
| 8db7c3769a | |||
| d4e32de882 | |||
| db75dea9ee | |||
| 489bba9c4b | |||
| 4accc49d60 | |||
| 5a47cd5216 | |||
| 439608cf27 | |||
| f9df7a1b5a | |||
| 78c801994a | |||
| 882fd68cf9 | |||
| 3433b73edf | |||
| a235d6a8cd | |||
| d76e88af21 | |||
| 3f6c6c443a | |||
| 13c6d406cf | |||
| 19fea4e761 | |||
| c84c96dcd1 | |||
| cdd8ccc2d4 | |||
| 09210d5d40 | |||
| 8f44a26037 | |||
| 5e986b2d04 | |||
| 298c0922cb | |||
| dc127ea6a3 | |||
| 522ed94c32 | |||
| 6edf66a599 | |||
| 475ec24528 | |||
| b555af0df7 | |||
| f90430e544 | |||
| 4ccb75818c | |||
| c5c9ed24c3 | |||
| 386aa0adc1 | |||
| 0b26b75699 | |||
| d013f67c70 | |||
| dc7c0e61fd | |||
| 89bd041d29 | |||
| ac730d6086 | |||
| 427eaed544 | |||
| 00f783c0b6 | |||
| 8e1e53d55e | |||
| 5c9a5c13b8 | |||
| c1f03a8e75 | |||
| 1affa83620 | |||
| 2aa8180113 | |||
| e6001d83a4 | |||
| 8550c8fde9 | |||
| 48d9a09307 | |||
| 9d0b874488 | |||
| 062a13b2c2 | |||
| 937f8723ed | |||
| 0c5cabbd79 | |||
| 95d8f710d8 | |||
| bb9b8b34e5 | |||
| 54c1164bd7 | |||
| 89c1158d95 | |||
| 9cae189941 | |||
| 53a31cd4c4 | |||
| d72a615481 | |||
| 8dcffa80a8 | |||
| 7cf53acd18 | |||
| 748cf68055 | |||
| 9cd22bdc06 | |||
| 5830d4b91c | |||
| b42f7f566b | |||
| db8223ca98 | |||
| 9403afc392 | |||
| 2b2d3b9517 | |||
| 3eebdfcdb3 | |||
| f7766bc3d4 | |||
| f2614abbdd | |||
| 58824ea879 | |||
| 9ef7a18847 | |||
| 58c0167696 | |||
| 9adfd286f9 | |||
| 4e8f530fbb | |||
| 3e694b0772 | |||
| 2fd5b04d4d | |||
| 4688b9a9c9 | |||
| 153e0035ba | |||
| 88b6ecc557 | |||
| 9df55874a6 | |||
| 87d4ab827a | |||
| 318166f23a | |||
| cc3f712659 | |||
| ee399d8a08 | |||
| 96c233d5c5 | |||
| 652b3e1954 | |||
| a8b76d5537 | |||
| e041b70cdd | |||
| 63bf912b3e | |||
| 0cbe7b1655 | |||
| 7bbec29c5b | |||
| 7cec7dbac8 | |||
| 09a19b5f42 | |||
| 8fe765c097 | |||
| b11e8e07c4 | |||
| f72763306d | |||
| e3d1a476e2 | |||
| f0bc86d42f | |||
| a62e806175 | |||
| 5bcf5bf93e | |||
| 4f35ba0931 | |||
| 2bcdfe778a | |||
| c89da1d0f7 | |||
| 46a1eda029 | |||
| 1c39819112 | |||
| 0efe617c03 | |||
| 10df947efe | |||
| fb7790ba4a | |||
| a9338ed822 | |||
| eaab8cdd93 | |||
| edfcd0dc6e | |||
| 838b56089b | |||
| 178810f908 | |||
| 69f5aca853 | |||
| 8e6aece9ae | |||
| 59b883ff7f | |||
| 548d34fbf4 | |||
| bb6345ccfa | |||
| d59a10f718 | |||
| aab1c5419a | |||
| 9b83785b95 | |||
| 099b710eb1 | |||
| a5424afc38 | |||
| 626325066d | |||
| 37195f6008 | |||
| fcbc68cefe | |||
| 9241953e31 | |||
| 651a912498 | |||
| a05f6fb6b5 | |||
| d6440d31f2 | |||
| 88d5dc2f17 | |||
| a17ad85858 | |||
| 4b49c1f30f | |||
| a9e36b9a59 | |||
| 80429bbfb8 | |||
| f39e20d7a7 | |||
| a03bac5d74 | |||
| 1aff09598a | |||
| 4036a71ee1 | |||
| a966be2f46 | |||
| a0b3bc1cab | |||
| eb2e2b0a26 | |||
| 55ad7b2e81 | |||
| dbcd2897a4 | |||
| 68a6d1c166 | |||
| fe82ec6fc2 | |||
| 5f2819a961 | |||
| 812bfc7cf5 | |||
| d164cafd33 | |||
| 3ada4183d9 | |||
| fa68621b41 | |||
| 4f2b9d39da | |||
| fd01c9269f | |||
| 82d150e53a | |||
| 251f3fe2da | |||
| 4f0e1e6b3d | |||
| a5dbf5d4b7 | |||
| 38baf77c30 | |||
| bfb8b03fc9 | |||
| 2dd38d9b03 | |||
| 307c64bc1e | |||
| 782e3a85f9 | |||
| 3bae6e749a | |||
| 530ef6b83e | |||
| 5b334eb2d5 | |||
| 28dc2e425a | |||
| 83b72e7403 | |||
| 01e1f65ffe | |||
| 9b15f888e6 | |||
| 171b8afa8e | |||
| 27f2f9f13a | |||
| 2c3983cead | |||
| bebd043d58 | |||
| a1c828fe62 | |||
| dfb885f38d | |||
| 702c095544 | |||
| b3e886d444 | |||
| 46d85e92cd | |||
| 0d84f2857f | |||
| ac6c80db90 | |||
| e608e85d56 | |||
| 58104a9a4d | |||
| 8d68dcabb5 | |||
| a5e5389d6c | |||
| 8de862c82f | |||
| 5141303ee1 | |||
| 3d95680cbc | |||
| 78663b873c | |||
| 0a106026dd | |||
| 999ca6274c | |||
| 36b9177069 | |||
| 8cf7bf859b | |||
| 2e54b62f60 | |||
| df46069d92 | |||
| e31014dde4 | |||
| f9a14581e1 | |||
| 95ec005894 | |||
| 73d271b8bc | |||
| 0c3b56e44a | |||
| 736f340979 | |||
| 49e62d35c3 | |||
| 810bce7495 | |||
| aff876aa05 | |||
| 9511644ce6 | |||
| 21d73e5f69 | |||
| d62f3fb936 | |||
| 0a011b6075 | |||
| 4e561dc764 | |||
| a55256ad82 | |||
| ab26ef64a5 | |||
| 6d1610eee0 | |||
| 2ba143d6ea | |||
| bd542ac308 | |||
| e71ffd1a77 | |||
| 1ac968f63c | |||
| dc4f62a085 | |||
| ad91d4f1ce | |||
| 8d4c7512ab | |||
| 12d5837526 | |||
| c82d2abab4 | |||
| 26cb717a08 | |||
| 0b1cc0ef5b | |||
| 05dc9138b4 | |||
| a7dcacb26c | |||
| 3ac3f871e4 | |||
| b180c0bbe6 | |||
| e4987f3bde | |||
| ee2690c2cc | |||
| 4cad26f793 | |||
| 84f3d5fec5 | |||
| bb3939d570 | |||
| b303f708f5 | |||
| d39550e090 | |||
| 22d956f38a | |||
| dab7728138 | |||
| 4038b6bc51 | |||
| bcaf3a246c | |||
| 4fb115fcbc | |||
| bfd4b2b6de | |||
| c0cd3fc5c2 | |||
| f95c4393d2 | |||
| e2eb5fabcc | |||
| 7275e8ff0d | |||
| accfc3df12 | |||
| 35392483d9 | |||
| 9770851fd4 | |||
| e013a6f087 | |||
| 1ec9ff20b1 | |||
| 9b0dea80c9 | |||
| eea1ea7ed0 | |||
| 85cd46bfc7 | |||
| a7ca394864 | |||
| e178a0795a | |||
| e8b0470ceb | |||
| c08abfdfdf | |||
| b1c765eb51 | |||
| 4b0f7d45e8 | |||
| 9e6271b1dc | |||
| a96eb31dc9 | |||
| 221a7809b6 | |||
| 8c33243c90 | |||
| c4b07b98aa | |||
| 1fda80a86b | |||
| 1287c729f2 | |||
| c069faa6f4 | |||
| 286fd91b2b | |||
| 33250d2f3d | |||
| 44ca940ca3 | |||
| 3b0ef7a96d | |||
| dfb6c593e4 | |||
| 853b01e2ca | |||
| 5a924fa382 | |||
| d4985a024d | |||
| 2797266de6 | |||
| b476cc91ca | |||
| 9e3aa19a09 | |||
| 7443e8a532 | |||
| 1d6dbf63c0 | |||
| e17687f80d | |||
| 27f4e14a4e | |||
| 8d5de98218 | |||
| dbf5c0a5bd | |||
| c1422f789a | |||
| 613af9399a | |||
| 34a6108bb2 | |||
| 66a5508abe | |||
| 3a273ea64f | |||
| 6f88e6ef26 | |||
| fd7905833e | |||
| 1977e436d6 | |||
| 0dfb3d00e9 | |||
| 785ec9bdb1 | |||
| eeccc4fd49 | |||
| efea9a7c37 | |||
| 04eafd6705 | |||
| 73b554aa48 | |||
| d3ddcf4c20 | |||
| ed9ea2f1d3 | |||
| 7eae3e9923 | |||
| 03d95033d7 | |||
| 9a79606565 | |||
| c5a101aad2 | |||
| 2dbf4d652d | |||
| 7364380312 | |||
| b7fe70aba3 | |||
| bca9982c57 | |||
| 7c16435010 | |||
| a79def625c | |||
| 8d5f804a60 | |||
| 0167381f0d | |||
| 4e2c4b39bb | |||
| 7bfc84abc8 | |||
| 5ac6c64079 | |||
| e7c4261b86 | |||
| 163b75e81b | |||
| 949132ef5a | |||
| 5617d31ed8 | |||
| 832d865397 |
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
'root': true,
|
||||||
|
'env': {
|
||||||
|
'node': true
|
||||||
|
},
|
||||||
|
'extends': [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/vue3-essential'
|
||||||
|
],
|
||||||
|
'rules': {
|
||||||
|
'vue/no-use-v-if-with-v-for': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
name: Docker Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ secrets.DOCKER_REPO }}/mayswind/ezbookkeeping
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=raw,value=latest
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Set up the environment
|
||||||
|
run: |
|
||||||
|
cat >> docker/custom-backend-pre-setup.sh <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
${{ vars.CUSTOM_BACKEND_PRE_SETUP }}
|
||||||
|
EOF
|
||||||
|
cat >> docker/custom-frontend-pre-setup.sh <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
${{ vars.CUSTOM_FRONTEND_PRE_SETUP }}
|
||||||
|
EOF
|
||||||
|
chmod +x docker/custom-backend-pre-setup.sh
|
||||||
|
chmod +x docker/custom-frontend-pre-setup.sh
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
file: Dockerfile
|
||||||
|
context: .
|
||||||
|
platforms: ${{ vars.BUILD_RELEASE_PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
RELEASE_BUILD=1
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
name: Docker Snapshot
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v4
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ secrets.DOCKER_REPO }}/mayswind/ezbookkeeping
|
||||||
|
tags: |
|
||||||
|
type=raw,value=SNAPSHOT-{{date 'YYYYMMDD'}}
|
||||||
|
type=raw,value=latest-snapshot
|
||||||
|
type=sha,format=short,prefix=SNAPSHOT-
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Set up the environment
|
||||||
|
run: |
|
||||||
|
sed -i 's#FROM #FROM ${{ secrets.DOCKER_REPO }}/mirrors/#g' Dockerfile
|
||||||
|
cat >> docker/custom-backend-pre-setup.sh <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
${{ vars.CUSTOM_BACKEND_PRE_SETUP }}
|
||||||
|
EOF
|
||||||
|
cat >> docker/custom-frontend-pre-setup.sh <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
${{ vars.CUSTOM_FRONTEND_PRE_SETUP }}
|
||||||
|
EOF
|
||||||
|
chmod +x docker/custom-backend-pre-setup.sh
|
||||||
|
chmod +x docker/custom-frontend-pre-setup.sh
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
file: Dockerfile
|
||||||
|
context: .
|
||||||
|
platforms: ${{ vars.BUILD_SNAPSHOT_PLATFORMS }}
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -9,35 +9,44 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
uses: actions/checkout@v3
|
||||||
uses: actions/checkout@v2
|
|
||||||
-
|
- name: Docker meta
|
||||||
name: Set up docker tag
|
id: meta
|
||||||
id: vars
|
uses: docker/metadata-action@v4
|
||||||
run: echo ::set-output name=RELEASE_TAG::${GITHUB_REF/refs\/tags\/v/}
|
with:
|
||||||
-
|
images: |
|
||||||
name: Set up QEMU
|
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping
|
||||||
uses: docker/setup-qemu-action@v1
|
tags: |
|
||||||
-
|
type=semver,pattern={{version}}
|
||||||
name: Set up Docker Buildx
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
uses: docker/setup-buildx-action@v1
|
type=raw,value=latest
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
- name: Set up QEMU
|
||||||
uses: docker/login-action@v1
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
-
|
|
||||||
name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64/v8
|
||||||
|
linux/arm/v7
|
||||||
|
linux/arm/v6
|
||||||
push: true
|
push: true
|
||||||
build-args: |
|
build-args: |
|
||||||
RELEASE_BUILD=1
|
RELEASE_BUILD=1
|
||||||
tags: |
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping:${{ steps.vars.outputs.RELEASE_TAG }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping:latest
|
|
||||||
@@ -9,33 +9,41 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
uses: actions/checkout@v3
|
||||||
uses: actions/checkout@v2
|
|
||||||
-
|
- name: Docker meta
|
||||||
name: Set up docker tag
|
id: meta
|
||||||
id: vars
|
uses: docker/metadata-action@v4
|
||||||
run: echo ::set-output name=BUILD_DATE::$(date '+%Y%m%d')
|
with:
|
||||||
-
|
images: |
|
||||||
name: Set up QEMU
|
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping
|
||||||
uses: docker/setup-qemu-action@v1
|
tags: |
|
||||||
-
|
type=raw,value=SNAPSHOT-{{date 'YYYYMMDD'}}
|
||||||
name: Set up Docker Buildx
|
type=raw,value=latest-snapshot
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
-
|
- name: Set up QEMU
|
||||||
name: Login to DockerHub
|
uses: docker/setup-qemu-action@v2
|
||||||
uses: docker/login-action@v1
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
-
|
|
||||||
name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64/v8
|
||||||
|
linux/arm/v7
|
||||||
|
linux/arm/v6
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping:SNAPSHOT-${{ steps.vars.outputs.BUILD_DATE }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
${{ secrets.DOCKER_USERNAME }}/ezbookkeeping:latest-snapshot
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
name: Docker Snapshot
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
file: Dockerfile
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: false
|
||||||
+4
-3
@@ -1,5 +1,5 @@
|
|||||||
# Build backend binary file
|
# Build backend binary file
|
||||||
FROM golang:1.16.5-alpine3.13 AS be-builder
|
FROM golang:1.20.8-alpine3.17 AS be-builder
|
||||||
ARG RELEASE_BUILD
|
ARG RELEASE_BUILD
|
||||||
ENV RELEASE_BUILD=$RELEASE_BUILD
|
ENV RELEASE_BUILD=$RELEASE_BUILD
|
||||||
WORKDIR /go/src/github.com/mayswind/ezbookkeeping
|
WORKDIR /go/src/github.com/mayswind/ezbookkeeping
|
||||||
@@ -9,7 +9,7 @@ RUN apk add git gcc g++ libc-dev
|
|||||||
RUN ./build.sh backend
|
RUN ./build.sh backend
|
||||||
|
|
||||||
# Build frontend files
|
# Build frontend files
|
||||||
FROM node:14.17.0-alpine3.13 AS fe-builder
|
FROM node:18.17.1-alpine3.17 AS fe-builder
|
||||||
ARG RELEASE_BUILD
|
ARG RELEASE_BUILD
|
||||||
ENV RELEASE_BUILD=$RELEASE_BUILD
|
ENV RELEASE_BUILD=$RELEASE_BUILD
|
||||||
WORKDIR /go/src/github.com/mayswind/ezbookkeeping
|
WORKDIR /go/src/github.com/mayswind/ezbookkeeping
|
||||||
@@ -19,7 +19,7 @@ RUN apk add git
|
|||||||
RUN ./build.sh frontend
|
RUN ./build.sh frontend
|
||||||
|
|
||||||
# Package docker image
|
# Package docker image
|
||||||
FROM alpine:3.13.5
|
FROM alpine:3.17.5
|
||||||
LABEL maintainer="MaysWind <i@mayswind.net>"
|
LABEL maintainer="MaysWind <i@mayswind.net>"
|
||||||
RUN addgroup -S -g 1000 ezbookkeeping && adduser -S -G ezbookkeeping -u 1000 ezbookkeeping
|
RUN addgroup -S -g 1000 ezbookkeeping && adduser -S -G ezbookkeeping -u 1000 ezbookkeeping
|
||||||
RUN apk --no-cache add tzdata
|
RUN apk --no-cache add tzdata
|
||||||
@@ -32,6 +32,7 @@ WORKDIR /ezbookkeeping
|
|||||||
COPY --from=be-builder --chown=1000:1000 /go/src/github.com/mayswind/ezbookkeeping/ezbookkeeping /ezbookkeeping/ezbookkeeping
|
COPY --from=be-builder --chown=1000:1000 /go/src/github.com/mayswind/ezbookkeeping/ezbookkeeping /ezbookkeeping/ezbookkeeping
|
||||||
COPY --from=fe-builder --chown=1000:1000 /go/src/github.com/mayswind/ezbookkeeping/dist /ezbookkeeping/public
|
COPY --from=fe-builder --chown=1000:1000 /go/src/github.com/mayswind/ezbookkeeping/dist /ezbookkeeping/public
|
||||||
COPY --chown=1000:1000 conf /ezbookkeeping/conf
|
COPY --chown=1000:1000 conf /ezbookkeeping/conf
|
||||||
|
COPY --chown=1000:1000 templates /ezbookkeeping/templates
|
||||||
COPY --chown=1000:1000 LICENSE /ezbookkeeping/LICENSE
|
COPY --chown=1000:1000 LICENSE /ezbookkeeping/LICENSE
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020-2021 MaysWind (i@mayswind.net)
|
Copyright (c) 2020-2023 MaysWind (i@mayswind.net)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ezBookkeeping
|
# ezBookkeeping
|
||||||
[](https://github.com/mayswind/ezbookkeeping/blob/master/LICENSE)
|
[](https://github.com/mayswind/ezbookkeeping/blob/master/LICENSE)
|
||||||
[](https://github.com/mayswind/ezbookkeeping/actions)
|
[](https://github.com/mayswind/ezbookkeeping/actions)
|
||||||
[](https://goreportcard.com/report/github.com/mayswind/ezbookkeeping)
|
[](https://goreportcard.com/report/github.com/mayswind/ezbookkeeping)
|
||||||
[](https://hub.docker.com/r/mayswind/ezbookkeeping)
|
[](https://hub.docker.com/r/mayswind/ezbookkeeping)
|
||||||
[](https://github.com/mayswind/ezbookkeeping/releases)
|
[](https://github.com/mayswind/ezbookkeeping/releases)
|
||||||
@@ -13,12 +13,14 @@ ezBookkeeping is a lightweight personal bookkeeping app hosted by yourself. It c
|
|||||||
2. Lightweight & Fast
|
2. Lightweight & Fast
|
||||||
3. Easy to install
|
3. Easy to install
|
||||||
* Docker support
|
* Docker support
|
||||||
* Multiple database support (sqlite, mysql, etc.)
|
* Multiple database support (SQLite, MySQL, PostgreSQL, etc.)
|
||||||
* Multiple os & architecture support (Windows, macOS, Linux & x86, amd64, ARM)
|
* Multiple operation system & hardware support (Windows, macOS, Linux & x86, amd64, ARM)
|
||||||
4. User-friendly interface
|
4. User-friendly interface
|
||||||
|
* Both desktop and mobile UI
|
||||||
* Close to native app experience (for mobile device)
|
* Close to native app experience (for mobile device)
|
||||||
* Two-level account & two-level category support
|
* Two-level account & two-level category support
|
||||||
* Plentiful preset categories
|
* Plentiful preset categories
|
||||||
|
* Geographic location and map support
|
||||||
* Searching & filtering history records
|
* Searching & filtering history records
|
||||||
* Data statistics
|
* Data statistics
|
||||||
* Dark theme
|
* Dark theme
|
||||||
@@ -26,12 +28,15 @@ ezBookkeeping is a lightweight personal bookkeeping app hosted by yourself. It c
|
|||||||
6. Multiple timezone support
|
6. Multiple timezone support
|
||||||
7. Multi-language support
|
7. Multi-language support
|
||||||
8. Two-factor authentication
|
8. Two-factor authentication
|
||||||
9. Application lock (WebAuthn support)
|
9. Application lock (PIN code / WebAuthn)
|
||||||
10. Data export
|
10. Data export
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
### Mobile Device
|
### Desktop Version
|
||||||
[](https://raw.githubusercontent.com/wiki/mayswind/ezbookkeeping/img/en.png)
|
[](https://raw.githubusercontent.com/wiki/mayswind/ezbookkeeping/img/desktop/en.png)
|
||||||
|
|
||||||
|
### Mobile Version
|
||||||
|
[](https://raw.githubusercontent.com/wiki/mayswind/ezbookkeeping/img/mobile/en.png)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
### Ship with docker
|
### Ship with docker
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
'@vue/cli-plugin-babel/preset'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
TYPE=""
|
TYPE=""
|
||||||
|
NO_LINT="0"
|
||||||
|
NO_TEST="0"
|
||||||
RELEASE=${RELEASE_BUILD:-"0"}
|
RELEASE=${RELEASE_BUILD:-"0"}
|
||||||
RELEASE_TYPE="unknown"
|
RELEASE_TYPE="unknown"
|
||||||
VERSION=""
|
VERSION=""
|
||||||
@@ -31,16 +33,18 @@ Usage:
|
|||||||
build.sh type [options]
|
build.sh type [options]
|
||||||
|
|
||||||
Types:
|
Types:
|
||||||
backend Build backend binary file
|
backend Build backend binary file
|
||||||
frontend Build frontend files
|
frontend Build frontend files
|
||||||
package Build package archive
|
package Build package archive
|
||||||
docker Build docker image
|
docker Build docker image
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-r, --release Build release (The script will use environment variable "RELEASE_BUILD" to detect whether this is release building by default)
|
-r, --release Build release (The script will use environment variable "RELEASE_BUILD" to detect whether this is release building by default)
|
||||||
-o, --output Package file name (For "package" type only)
|
-o, --output <filename> Package file name (For "package" type only)
|
||||||
-t, --tag Docker tag (For "docker" type only)
|
-t, --tag Docker tag (For "docker" type only)
|
||||||
-h, --help Show help
|
--no-lint Do not execute lint check before building
|
||||||
|
--no-test Do not execute unit testing before building
|
||||||
|
-h, --help Show help
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +67,12 @@ parse_args() {
|
|||||||
DOCKER_TAG="$2"
|
DOCKER_TAG="$2"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
--no-lint)
|
||||||
|
NO_LINT="1"
|
||||||
|
;;
|
||||||
|
--no-test)
|
||||||
|
NO_TEST="1"
|
||||||
|
;;
|
||||||
--help | -h)
|
--help | -h)
|
||||||
show_help
|
show_help
|
||||||
exit 0
|
exit 0
|
||||||
@@ -111,6 +121,30 @@ set_build_parameters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_backend() {
|
build_backend() {
|
||||||
|
echo "Pulling backend dependencies..."
|
||||||
|
go get .
|
||||||
|
|
||||||
|
if [ "$NO_LINT" = "0" ]; then
|
||||||
|
echo "Executing backend lint checking..."
|
||||||
|
go vet -v ./...
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo_red "Error: Failed to pass lint checking"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$NO_TEST" = "0" ]; then
|
||||||
|
echo "Executing backend unit testing..."
|
||||||
|
go clean -cache
|
||||||
|
go test ./... -v
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo_red "Error: Failed to pass unit testing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
backend_build_extra_arguments="-X main.Version=$VERSION"
|
backend_build_extra_arguments="-X main.Version=$VERSION"
|
||||||
backend_build_extra_arguments="$backend_build_extra_arguments -X main.CommitHash=$COMMIT_HASH"
|
backend_build_extra_arguments="$backend_build_extra_arguments -X main.CommitHash=$COMMIT_HASH"
|
||||||
|
|
||||||
@@ -125,17 +159,26 @@ build_backend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_frontend() {
|
build_frontend() {
|
||||||
frontend_build_arguments="";
|
|
||||||
|
|
||||||
if [ "$RELEASE" = "0" ]; then
|
|
||||||
frontend_build_arguments="--buildUnixTime=$BUILD_UNIXTIME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Pulling frontend dependencies..."
|
echo "Pulling frontend dependencies..."
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
if [ "$NO_LINT" = "0" ]; then
|
||||||
|
echo "Executing frontend lint checking..."
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo_red "Error: Failed to pass lint checking"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Building frontend files ($RELEASE_TYPE)..."
|
echo "Building frontend files ($RELEASE_TYPE)..."
|
||||||
npm run build -- "$frontend_build_arguments"
|
|
||||||
|
if [ "$RELEASE" = "0" ]; then
|
||||||
|
buildUnixTime=$BUILD_UNIXTIME npm run build
|
||||||
|
else
|
||||||
|
npm run build
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
build_package() {
|
build_package() {
|
||||||
@@ -158,6 +201,8 @@ build_package() {
|
|||||||
|
|
||||||
rm -rf package
|
rm -rf package
|
||||||
mkdir package
|
mkdir package
|
||||||
|
mkdir package/data
|
||||||
|
mkdir package/log
|
||||||
cp ezbookkeeping package/
|
cp ezbookkeeping package/
|
||||||
cp -R dist package/public
|
cp -R dist package/public
|
||||||
cp -R conf package/conf
|
cp -R conf package/conf
|
||||||
|
|||||||
+44
-11
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/exchangerates"
|
"github.com/mayswind/ezbookkeeping/pkg/exchangerates"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/mail"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
||||||
@@ -17,29 +18,40 @@ import (
|
|||||||
func initializeSystem(c *cli.Context) (*settings.Config, error) {
|
func initializeSystem(c *cli.Context) (*settings.Config, error) {
|
||||||
var err error
|
var err error
|
||||||
configFilePath := c.String("conf-path")
|
configFilePath := c.String("conf-path")
|
||||||
|
isDisableBootLog := c.Bool("no-boot-log")
|
||||||
|
|
||||||
if configFilePath != "" {
|
if configFilePath != "" {
|
||||||
if _, err = os.Stat(configFilePath); err != nil {
|
if _, err = os.Stat(configFilePath); err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] cannot load configuration from custom config path %s, because file not exists", configFilePath)
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] cannot load configuration from custom config path %s, because file not exists", configFilePath)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.BootInfof("[initializer.initializeSystem] will loading configuration from custom config path %s", configFilePath)
|
if !isDisableBootLog {
|
||||||
|
log.BootInfof("[initializer.initializeSystem] will loading configuration from custom config path %s", configFilePath)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
configFilePath, err = settings.GetDefaultConfigFilePath()
|
configFilePath, err = settings.GetDefaultConfigFilePath()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] cannot get default configuration path, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] cannot get default configuration path, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.BootInfof("[initializer.initializeSystem] will load configuration from default config path %s", configFilePath)
|
if !isDisableBootLog {
|
||||||
|
log.BootInfof("[initializer.initializeSystem] will load configuration from default config path %s", configFilePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := settings.LoadConfiguration(configFilePath)
|
config, err := settings.LoadConfiguration(configFilePath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] cannot load configuration, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] cannot load configuration, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,33 +60,53 @@ func initializeSystem(c *cli.Context) (*settings.Config, error) {
|
|||||||
err = datastore.InitializeDataStore(config)
|
err = datastore.InitializeDataStore(config)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] initializes data store failed, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] initializes data store failed, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = log.SetLoggerConfiguration(config)
|
err = log.SetLoggerConfiguration(config, isDisableBootLog)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] sets logger configuration failed, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] sets logger configuration failed, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = uuid.InitializeUuidGenerator(config)
|
err = uuid.InitializeUuidGenerator(config)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] initializes uuid generator failed, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] initializes uuid generator failed, because %s", err.Error())
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mail.InitializeMailer(config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] initializes mailer failed, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = exchangerates.InitializeExchangeRatesDataSource(config)
|
err = exchangerates.InitializeExchangeRatesDataSource(config)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[initializer.initializeSystem] initializes exchange rates data source failed, because %s", err.Error())
|
if !isDisableBootLog {
|
||||||
|
log.BootErrorf("[initializer.initializeSystem] initializes exchange rates data source failed, because %s", err.Error())
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgJson, _ := json.Marshal(getConfigWithoutSensitiveData(config))
|
cfgJson, _ := json.Marshal(getConfigWithoutSensitiveData(config))
|
||||||
log.BootInfof("[initializer.initializeSystem] has loaded configuration %s", cfgJson)
|
|
||||||
|
if !isDisableBootLog {
|
||||||
|
log.BootInfof("[initializer.initializeSystem] has loaded configuration %s", cfgJson)
|
||||||
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
@@ -88,6 +120,7 @@ func getConfigWithoutSensitiveData(config *settings.Config) *settings.Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clonedConfig.DatabaseConfig.DatabasePassword = "****"
|
clonedConfig.DatabaseConfig.DatabasePassword = "****"
|
||||||
|
clonedConfig.SMTPConfig.SMTPPasswd = "****"
|
||||||
clonedConfig.SecretKey = "****"
|
clonedConfig.SecretKey = "****"
|
||||||
|
|
||||||
return clonedConfig
|
return clonedConfig
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecurityUtils represents the security command
|
||||||
|
var SecurityUtils = &cli.Command{
|
||||||
|
Name: "security",
|
||||||
|
Usage: "ezBookkeeping security utilities",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "gen-secret-key",
|
||||||
|
Usage: "Generate a random secret key",
|
||||||
|
Action: genSecretKey,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "length",
|
||||||
|
Aliases: []string{"l"},
|
||||||
|
Required: false,
|
||||||
|
DefaultText: "32",
|
||||||
|
Usage: "The length of secret key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func genSecretKey(c *cli.Context) error {
|
||||||
|
length := c.Int("length")
|
||||||
|
|
||||||
|
if length <= 0 {
|
||||||
|
length = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
secretKey, err := utils.GetRandomNumberOrLetter(length)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[Secret Key] %s\n", secretKey)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+206
-2
@@ -86,6 +86,71 @@ var UserData = &cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "user-enable",
|
||||||
|
Usage: "Enable specified user",
|
||||||
|
Action: enableUser,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "user-disable",
|
||||||
|
Usage: "Disable specified user",
|
||||||
|
Action: disableUser,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "user-resend-verify-email",
|
||||||
|
Usage: "Resend user verify email",
|
||||||
|
Action: resendUserVerifyEmail,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "user-set-email-verified",
|
||||||
|
Usage: "Set user email address verified",
|
||||||
|
Action: setUserEmailVerified,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "user-set-email-unverified",
|
||||||
|
Usage: "Set user email address unverified",
|
||||||
|
Action: setUserEmailUnverified,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "user-delete",
|
Name: "user-delete",
|
||||||
Usage: "Delete specified user",
|
Usage: "Delete specified user",
|
||||||
@@ -138,6 +203,19 @@ var UserData = &cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "send-password-reset-mail",
|
||||||
|
Usage: "Send password reset mail",
|
||||||
|
Action: sendPasswordResetMail,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Specific user name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "transaction-check",
|
Name: "transaction-check",
|
||||||
Usage: "Check whether user all transactions and accounts are correct",
|
Usage: "Check whether user all transactions and accounts are correct",
|
||||||
@@ -239,6 +317,126 @@ func modifyUserPassword(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendPasswordResetMail(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.SendPasswordResetMail(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.sendPasswordResetMail] error occurs when sending password reset email")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.sendPasswordResetMail] a password reset email for user \"%s\" has been sent", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableUser(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.EnableUser(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.enableUser] error occurs when setting user enabled")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.enableUser] user \"%s\" has been set enabled", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableUser(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.DisableUser(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.disableUser] error occurs when setting user disabled")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.disableUser] user \"%s\" has been set disabled", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resendUserVerifyEmail(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.ResendVerifyEmail(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.resendUserVerifyEmail] error occurs when resending user verify email")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.resendUserVerifyEmail] verify email for user \"%s\" has been resent", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUserEmailVerified(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.SetUserEmailVerified(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.setUserEmailVerified] error occurs when setting user email address verified")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.setUserEmailVerified] user \"%s\" email address has been set verified", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUserEmailUnverified(c *cli.Context) error {
|
||||||
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username := c.String("username")
|
||||||
|
err = clis.UserData.SetUserEmailUnverified(c, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.setUserEmailUnverified] error occurs when setting user email address unverified")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.BootInfof("[user_data.setUserEmailUnverified] user \"%s\" email address has been set unverified", username)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func deleteUser(c *cli.Context) error {
|
func deleteUser(c *cli.Context) error {
|
||||||
_, err := initializeSystem(c)
|
_, err := initializeSystem(c)
|
||||||
|
|
||||||
@@ -398,9 +596,15 @@ func printUserInfo(user *models.User) {
|
|||||||
fmt.Printf("[Nickname] %s\n", user.Nickname)
|
fmt.Printf("[Nickname] %s\n", user.Nickname)
|
||||||
fmt.Printf("[Password] %s\n", user.Password)
|
fmt.Printf("[Password] %s\n", user.Password)
|
||||||
fmt.Printf("[Salt] %s\n", user.Salt)
|
fmt.Printf("[Salt] %s\n", user.Salt)
|
||||||
|
fmt.Printf("[DefaultAccountId] %d\n", user.DefaultAccountId)
|
||||||
|
fmt.Printf("[TransactionEditScope] %s (%d)\n", user.TransactionEditScope, user.TransactionEditScope)
|
||||||
|
fmt.Printf("[Language] %s\n", user.Language)
|
||||||
fmt.Printf("[DefaultCurrency] %s\n", user.DefaultCurrency)
|
fmt.Printf("[DefaultCurrency] %s\n", user.DefaultCurrency)
|
||||||
fmt.Printf("[FirstDayOfWeek] %s\n", user.FirstDayOfWeek)
|
fmt.Printf("[FirstDayOfWeek] %s (%d)\n", user.FirstDayOfWeek, user.FirstDayOfWeek)
|
||||||
fmt.Printf("[TransactionEditScope] %s\n", user.TransactionEditScope)
|
fmt.Printf("[LongDateFormat] %s (%d)\n", user.LongDateFormat, user.LongDateFormat)
|
||||||
|
fmt.Printf("[ShortDateFormat] %s (%d)\n", user.ShortDateFormat, user.ShortDateFormat)
|
||||||
|
fmt.Printf("[LongTimeFormat] %s (%d)\n", user.LongTimeFormat, user.LongTimeFormat)
|
||||||
|
fmt.Printf("[ShortTimeFormat] %s (%d)\n", user.ShortTimeFormat, user.ShortTimeFormat)
|
||||||
fmt.Printf("[Deleted] %t\n", user.Deleted)
|
fmt.Printf("[Deleted] %t\n", user.Deleted)
|
||||||
fmt.Printf("[EmailVerified] %t\n", user.EmailVerified)
|
fmt.Printf("[EmailVerified] %t\n", user.EmailVerified)
|
||||||
fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime)
|
fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime)
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/mail"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/requestid"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Utilities represents the utilities command
|
||||||
|
var Utilities = &cli.Command{
|
||||||
|
Name: "utility",
|
||||||
|
Usage: "ezBookkeeping utilities",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "parse-default-request-id",
|
||||||
|
Usage: "Parse a request id which is generated by default request generator and show the details",
|
||||||
|
Action: parseRequestId,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "id",
|
||||||
|
Required: true,
|
||||||
|
Usage: "Request ID",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "send-test-mail",
|
||||||
|
Usage: "Send an email to specified e-mail address",
|
||||||
|
Action: sendTestMail,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "to",
|
||||||
|
Required: true,
|
||||||
|
Usage: "To e-mail address",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRequestId(c *cli.Context) error {
|
||||||
|
config, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = requestid.InitializeRequestIdGenerator(config)
|
||||||
|
defaultGenerator, err := requestid.NewDefaultRequestIdGenerator(config)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestId := c.String("id")
|
||||||
|
requestIdInfo, err := defaultGenerator.ParseRequestIdInfo(requestId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newRequestId := defaultGenerator.GenerateRequestId(net.IPv4zero.String())
|
||||||
|
newRequestIdInfo, err := defaultGenerator.ParseRequestIdInfo(newRequestId)
|
||||||
|
printRequestIdInfo(requestId, requestIdInfo, newRequestIdInfo)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendTestMail(c *cli.Context) error {
|
||||||
|
config, err := initializeSystem(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.EnableSMTP || mail.Container.Current == nil {
|
||||||
|
return errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
toAddress := c.String("to")
|
||||||
|
|
||||||
|
err = mail.Container.Current.SendMail(&mail.MailMessage{
|
||||||
|
To: toAddress,
|
||||||
|
Subject: "ezBookkeeping test e-mail",
|
||||||
|
Body: "This is a test e-mail",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Test e-mail has been sent")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printRequestIdInfo(requestId string, requestIdInfo *requestid.RequestIdInfo, newRequestIdInfo *requestid.RequestIdInfo) {
|
||||||
|
fmt.Printf("[RequestId] %s\n", requestId)
|
||||||
|
fmt.Printf("[ServerUniqId] %d (Current Server %d)\n", requestIdInfo.ServerUniqId, newRequestIdInfo.ServerUniqId)
|
||||||
|
fmt.Printf("[InstanceUniqId] %d (Current Server %d)\n", requestIdInfo.InstanceUniqId, newRequestIdInfo.InstanceUniqId)
|
||||||
|
|
||||||
|
displayTime, err := utils.ParseFromElapsedSeconds(int(requestIdInfo.SecondsElapsedToday))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("[SecondsElapsedToday] %d (%s)\n", requestIdInfo.SecondsElapsedToday, displayTime)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("[SecondsElapsedToday] %d\n", requestIdInfo.SecondsElapsedToday)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[RandomNumber] %d\n", requestIdInfo.RandomNumber)
|
||||||
|
fmt.Printf("[RequestSeqId] %d\n", requestIdInfo.RequestSeqId)
|
||||||
|
fmt.Printf("[IsClientIpv6] %t\n", requestIdInfo.IsClientIpv6)
|
||||||
|
|
||||||
|
if requestIdInfo.IsClientIpv6 {
|
||||||
|
fmt.Printf("[ClientIpv6Hash] %d\n", requestIdInfo.ClientIp)
|
||||||
|
} else {
|
||||||
|
ip := make(net.IP, 4)
|
||||||
|
binary.BigEndian.PutUint32(ip, requestIdInfo.ClientIp)
|
||||||
|
fmt.Printf("[ClientIpv4] %s\n", ip.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
+127
-14
@@ -3,7 +3,10 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cache"
|
||||||
|
"github.com/gin-contrib/cache/persistence"
|
||||||
"github.com/gin-contrib/gzip"
|
"github.com/gin-contrib/gzip"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
@@ -71,6 +74,8 @@ func startWebServer(c *cli.Context) error {
|
|||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workboxFileNames := utils.ListFileNamesWithPrefixAndSuffix(config.StaticRootPath, "workbox-", ".js")
|
||||||
|
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
router.Use(bindMiddleware(middlewares.Recovery))
|
router.Use(bindMiddleware(middlewares.Recovery))
|
||||||
|
|
||||||
@@ -110,13 +115,16 @@ func startWebServer(c *cli.Context) error {
|
|||||||
router.Static("/mobile/css", filepath.Join(config.StaticRootPath, "css"))
|
router.Static("/mobile/css", filepath.Join(config.StaticRootPath, "css"))
|
||||||
router.Static("/mobile/img", filepath.Join(config.StaticRootPath, "img"))
|
router.Static("/mobile/img", filepath.Join(config.StaticRootPath, "img"))
|
||||||
router.Static("/mobile/fonts", filepath.Join(config.StaticRootPath, "fonts"))
|
router.Static("/mobile/fonts", filepath.Join(config.StaticRootPath, "fonts"))
|
||||||
router.Static("/mobile/sw", filepath.Join(config.StaticRootPath, "sw"))
|
|
||||||
router.StaticFile("/mobile/favicon.ico", filepath.Join(config.StaticRootPath, "favicon.ico"))
|
router.StaticFile("/mobile/favicon.ico", filepath.Join(config.StaticRootPath, "favicon.ico"))
|
||||||
router.StaticFile("/mobile/favicon.png", filepath.Join(config.StaticRootPath, "favicon.png"))
|
router.StaticFile("/mobile/favicon.png", filepath.Join(config.StaticRootPath, "favicon.png"))
|
||||||
router.StaticFile("/mobile/touchicon.png", filepath.Join(config.StaticRootPath, "touchicon.png"))
|
router.StaticFile("/mobile/touchicon.png", filepath.Join(config.StaticRootPath, "touchicon.png"))
|
||||||
router.StaticFile("/mobile/manifest.json", filepath.Join(config.StaticRootPath, "manifest.json"))
|
router.StaticFile("/mobile/manifest.json", filepath.Join(config.StaticRootPath, "manifest.json"))
|
||||||
router.StaticFile("/mobile/sw.js", filepath.Join(config.StaticRootPath, "sw.js"))
|
router.StaticFile("/mobile/sw.js", filepath.Join(config.StaticRootPath, "sw.js"))
|
||||||
|
|
||||||
|
for i := 0; i < len(workboxFileNames); i++ {
|
||||||
|
router.StaticFile("/mobile/"+workboxFileNames[i], filepath.Join(config.StaticRootPath, workboxFileNames[i]))
|
||||||
|
}
|
||||||
|
|
||||||
desktopEntryRoute := router.Group("/desktop")
|
desktopEntryRoute := router.Group("/desktop")
|
||||||
desktopEntryRoute.Use(bindMiddleware(middlewares.ServerSettingsCookie(config)))
|
desktopEntryRoute.Use(bindMiddleware(middlewares.ServerSettingsCookie(config)))
|
||||||
{
|
{
|
||||||
@@ -130,37 +138,90 @@ func startWebServer(c *cli.Context) error {
|
|||||||
router.StaticFile("/desktop/favicon.png", filepath.Join(config.StaticRootPath, "favicon.png"))
|
router.StaticFile("/desktop/favicon.png", filepath.Join(config.StaticRootPath, "favicon.png"))
|
||||||
router.StaticFile("/desktop/touchicon.png", filepath.Join(config.StaticRootPath, "touchicon.png"))
|
router.StaticFile("/desktop/touchicon.png", filepath.Join(config.StaticRootPath, "touchicon.png"))
|
||||||
router.StaticFile("/desktop/manifest.json", filepath.Join(config.StaticRootPath, "manifest.json"))
|
router.StaticFile("/desktop/manifest.json", filepath.Join(config.StaticRootPath, "manifest.json"))
|
||||||
|
router.StaticFile("/desktop/sw.js", filepath.Join(config.StaticRootPath, "sw.js"))
|
||||||
|
|
||||||
|
for i := 0; i < len(workboxFileNames); i++ {
|
||||||
|
router.StaticFile("/desktop/"+workboxFileNames[i], filepath.Join(config.StaticRootPath, workboxFileNames[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
router.GET("/healthz.json", bindApi(api.Healths.HealthStatusHandler))
|
||||||
|
|
||||||
|
if config.Mode == settings.MODE_DEVELOPMENT {
|
||||||
|
devRoute := router.Group("/dev")
|
||||||
|
devRoute.GET("/cookies", bindMiddleware(middlewares.ServerSettingsCookie(config)))
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyRoute := router.Group("/proxy")
|
||||||
|
proxyRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByQueryString))
|
||||||
|
{
|
||||||
|
if config.EnableMapDataFetchProxy {
|
||||||
|
if config.MapProvider == settings.OpenStreetMapProvider ||
|
||||||
|
config.MapProvider == settings.OpenStreetMapHumanitarianStyleProvider ||
|
||||||
|
config.MapProvider == settings.OpenTopoMapProvider ||
|
||||||
|
config.MapProvider == settings.OPNVKarteMapProvider ||
|
||||||
|
config.MapProvider == settings.CyclOSMMapProvider ||
|
||||||
|
config.MapProvider == settings.TomTomMapProvider {
|
||||||
|
proxyRoute.GET("/map/tile/:zoomLevel/:coordinateX/:fileName", bindProxy(api.MapImages.MapTileImageProxyHandler))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationInternalProxyMethod {
|
||||||
|
amapApiProxyRoute := router.Group("/_AMapService")
|
||||||
|
amapApiProxyRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByCookie))
|
||||||
|
{
|
||||||
|
amapApiProxyRoute.GET("/*action", bindProxy(api.AmapApis.AmapApiProxyHandler))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qrCodeRoute := router.Group("/qrcode")
|
||||||
|
qrCodeRoute.Use(bindMiddleware(middlewares.RequestId(config)))
|
||||||
|
{
|
||||||
|
qrCodeCacheStore := persistence.NewInMemoryStore(time.Minute)
|
||||||
|
qrCodeRoute.GET("/mobile_url.png", bindCachedPngImage(api.QrCodes.MobileUrlQrCodeHandler, qrCodeCacheStore))
|
||||||
|
}
|
||||||
|
|
||||||
apiRoute := router.Group("/api")
|
apiRoute := router.Group("/api")
|
||||||
|
|
||||||
apiRoute.Use(bindMiddleware(middlewares.RequestId(config)))
|
apiRoute.Use(bindMiddleware(middlewares.RequestId(config)))
|
||||||
apiRoute.Use(bindMiddleware(middlewares.RequestLog))
|
apiRoute.Use(bindMiddleware(middlewares.RequestLog))
|
||||||
{
|
{
|
||||||
apiRoute.POST("/authorize.json", bindApi(api.Authorizations.AuthorizeHandler))
|
apiRoute.POST("/authorize.json", bindApiWithTokenUpdate(api.Authorizations.AuthorizeHandler, config))
|
||||||
|
|
||||||
if config.EnableTwoFactor {
|
if config.EnableTwoFactor {
|
||||||
twoFactorRoute := apiRoute.Group("/2fa")
|
twoFactorRoute := apiRoute.Group("/2fa")
|
||||||
twoFactorRoute.Use(bindMiddleware(middlewares.JWTTwoFactorAuthorization))
|
twoFactorRoute.Use(bindMiddleware(middlewares.JWTTwoFactorAuthorization))
|
||||||
{
|
{
|
||||||
twoFactorRoute.POST("/authorize.json", bindApi(api.Authorizations.TwoFactorAuthorizeHandler))
|
twoFactorRoute.POST("/authorize.json", bindApiWithTokenUpdate(api.Authorizations.TwoFactorAuthorizeHandler, config))
|
||||||
twoFactorRoute.POST("/recovery.json", bindApi(api.Authorizations.TwoFactorAuthorizeByRecoveryCodeHandler))
|
twoFactorRoute.POST("/recovery.json", bindApiWithTokenUpdate(api.Authorizations.TwoFactorAuthorizeByRecoveryCodeHandler, config))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.EnableUserRegister {
|
if config.EnableUserRegister {
|
||||||
apiRoute.POST("/register.json", bindApi(api.Users.UserRegisterHandler))
|
apiRoute.POST("/register.json", bindApiWithTokenUpdate(api.Users.UserRegisterHandler, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.EnableDataExport {
|
if config.EnableUserVerifyEmail {
|
||||||
dataRoute := apiRoute.Group("/data")
|
apiRoute.POST("/verify_email/resend.json", bindApi(api.Users.UserSendVerifyEmailByUnloginUserHandler))
|
||||||
dataRoute.Use(bindMiddleware(middlewares.HeaderInQueryString))
|
|
||||||
dataRoute.Use(bindMiddleware(middlewares.JWTAuthorizationByQueryString))
|
emailVerifyRoute := apiRoute.Group("/verify_email")
|
||||||
|
emailVerifyRoute.Use(bindMiddleware(middlewares.JWTEmailVerifyAuthorization))
|
||||||
{
|
{
|
||||||
dataRoute.GET("/export.csv", bindCsv(api.DataManagements.ExportDataHandler))
|
emailVerifyRoute.POST("/by_token.json", bindApi(api.Users.UserEmailVerifyHandler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apiRoute.GET("/logout.json", bindApi(api.Tokens.TokenRevokeCurrentHandler))
|
if config.EnableUserForgetPassword {
|
||||||
|
apiRoute.POST("/forget_password/request.json", bindApi(api.ForgetPasswords.UserForgetPasswordRequestHandler))
|
||||||
|
|
||||||
|
resetPasswordRoute := apiRoute.Group("/forget_password/reset")
|
||||||
|
resetPasswordRoute.Use(bindMiddleware(middlewares.JWTResetPasswordAuthorization))
|
||||||
|
{
|
||||||
|
resetPasswordRoute.POST("/by_token.json", bindApi(api.ForgetPasswords.UserResetPasswordHandler))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiRoute.GET("/logout.json", bindApiWithTokenUpdate(api.Tokens.TokenRevokeCurrentHandler, config))
|
||||||
|
|
||||||
apiV1Route := apiRoute.Group("/v1")
|
apiV1Route := apiRoute.Group("/v1")
|
||||||
apiV1Route.Use(bindMiddleware(middlewares.JWTAuthorization))
|
apiV1Route.Use(bindMiddleware(middlewares.JWTAuthorization))
|
||||||
@@ -169,24 +230,33 @@ func startWebServer(c *cli.Context) error {
|
|||||||
apiV1Route.GET("/tokens/list.json", bindApi(api.Tokens.TokenListHandler))
|
apiV1Route.GET("/tokens/list.json", bindApi(api.Tokens.TokenListHandler))
|
||||||
apiV1Route.POST("/tokens/revoke.json", bindApi(api.Tokens.TokenRevokeHandler))
|
apiV1Route.POST("/tokens/revoke.json", bindApi(api.Tokens.TokenRevokeHandler))
|
||||||
apiV1Route.POST("/tokens/revoke_all.json", bindApi(api.Tokens.TokenRevokeAllHandler))
|
apiV1Route.POST("/tokens/revoke_all.json", bindApi(api.Tokens.TokenRevokeAllHandler))
|
||||||
apiV1Route.POST("/tokens/refresh.json", bindApi(api.Tokens.TokenRefreshHandler))
|
apiV1Route.POST("/tokens/refresh.json", bindApiWithTokenUpdate(api.Tokens.TokenRefreshHandler, config))
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
apiV1Route.GET("/users/profile/get.json", bindApi(api.Users.UserProfileHandler))
|
apiV1Route.GET("/users/profile/get.json", bindApi(api.Users.UserProfileHandler))
|
||||||
apiV1Route.POST("/users/profile/update.json", bindApi(api.Users.UserUpdateProfileHandler))
|
apiV1Route.POST("/users/profile/update.json", bindApiWithTokenUpdate(api.Users.UserUpdateProfileHandler, config))
|
||||||
|
|
||||||
|
if config.EnableUserVerifyEmail {
|
||||||
|
apiV1Route.POST("/users/verify_email/resend.json", bindApi(api.Users.UserSendVerifyEmailByLoginedUserHandler))
|
||||||
|
}
|
||||||
|
|
||||||
// Two Factor Authorization
|
// Two Factor Authorization
|
||||||
if config.EnableTwoFactor {
|
if config.EnableTwoFactor {
|
||||||
apiV1Route.GET("/users/2fa/status.json", bindApi(api.TwoFactorAuthorizations.TwoFactorStatusHandler))
|
apiV1Route.GET("/users/2fa/status.json", bindApi(api.TwoFactorAuthorizations.TwoFactorStatusHandler))
|
||||||
apiV1Route.POST("/users/2fa/enable/request.json", bindApi(api.TwoFactorAuthorizations.TwoFactorEnableRequestHandler))
|
apiV1Route.POST("/users/2fa/enable/request.json", bindApi(api.TwoFactorAuthorizations.TwoFactorEnableRequestHandler))
|
||||||
apiV1Route.POST("/users/2fa/enable/confirm.json", bindApi(api.TwoFactorAuthorizations.TwoFactorEnableConfirmHandler))
|
apiV1Route.POST("/users/2fa/enable/confirm.json", bindApiWithTokenUpdate(api.TwoFactorAuthorizations.TwoFactorEnableConfirmHandler, config))
|
||||||
apiV1Route.POST("/users/2fa/disable.json", bindApi(api.TwoFactorAuthorizations.TwoFactorDisableHandler))
|
apiV1Route.POST("/users/2fa/disable.json", bindApi(api.TwoFactorAuthorizations.TwoFactorDisableHandler))
|
||||||
apiV1Route.POST("/users/2fa/recovery/regenerate.json", bindApi(api.TwoFactorAuthorizations.TwoFactorRecoveryCodeRegenerateHandler))
|
apiV1Route.POST("/users/2fa/recovery/regenerate.json", bindApi(api.TwoFactorAuthorizations.TwoFactorRecoveryCodeRegenerateHandler))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
|
apiV1Route.GET("/data/statistics.json", bindApi(api.DataManagements.DataStatisticsHandler))
|
||||||
apiV1Route.POST("/data/clear.json", bindApi(api.DataManagements.ClearDataHandler))
|
apiV1Route.POST("/data/clear.json", bindApi(api.DataManagements.ClearDataHandler))
|
||||||
|
|
||||||
|
if config.EnableDataExport {
|
||||||
|
apiV1Route.GET("/data/export.csv", bindCsv(api.DataManagements.ExportDataHandler))
|
||||||
|
}
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
apiV1Route.GET("/accounts/list.json", bindApi(api.Accounts.AccountListHandler))
|
apiV1Route.GET("/accounts/list.json", bindApi(api.Accounts.AccountListHandler))
|
||||||
apiV1Route.GET("/accounts/get.json", bindApi(api.Accounts.AccountGetHandler))
|
apiV1Route.GET("/accounts/get.json", bindApi(api.Accounts.AccountGetHandler))
|
||||||
@@ -274,6 +344,23 @@ func bindApi(fn core.ApiHandlerFunc) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func bindApiWithTokenUpdate(fn core.ApiHandlerFunc, config *settings.Config) gin.HandlerFunc {
|
||||||
|
return func(ginCtx *gin.Context) {
|
||||||
|
c := core.WrapContext(ginCtx)
|
||||||
|
result, err := fn(c)
|
||||||
|
|
||||||
|
if err == nil && config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationInternalProxyMethod {
|
||||||
|
middlewares.AmapApiProxyAuthCookie(c, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintJsonErrorResult(c, err)
|
||||||
|
} else {
|
||||||
|
utils.PrintJsonSuccessResult(c, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func bindCsv(fn core.DataHandlerFunc) gin.HandlerFunc {
|
func bindCsv(fn core.DataHandlerFunc) gin.HandlerFunc {
|
||||||
return func(ginCtx *gin.Context) {
|
return func(ginCtx *gin.Context) {
|
||||||
c := core.WrapContext(ginCtx)
|
c := core.WrapContext(ginCtx)
|
||||||
@@ -286,3 +373,29 @@ func bindCsv(fn core.DataHandlerFunc) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func bindCachedPngImage(fn core.DataHandlerFunc, store persistence.CacheStore) gin.HandlerFunc {
|
||||||
|
return cache.CachePage(store, time.Minute, func(ginCtx *gin.Context) {
|
||||||
|
c := core.WrapContext(ginCtx)
|
||||||
|
result, _, err := fn(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintDataErrorResult(c, "text/text", err)
|
||||||
|
} else {
|
||||||
|
utils.PrintDataSuccessResult(c, "img/png", "", result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindProxy(fn core.ProxyHandlerFunc) gin.HandlerFunc {
|
||||||
|
return func(ginCtx *gin.Context) {
|
||||||
|
c := core.WrapContext(ginCtx)
|
||||||
|
proxy, err := fn(c)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintDataErrorResult(c, "text/text", err)
|
||||||
|
} else {
|
||||||
|
proxy.ServeHTTP(c.Writer, c.Request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+100
-13
@@ -28,13 +28,13 @@ cert_key_file =
|
|||||||
# Unix socket path, for "socket" only
|
# Unix socket path, for "socket" only
|
||||||
unix_socket =
|
unix_socket =
|
||||||
|
|
||||||
# Static file root path (relative or absolute)
|
# Static file root path (relative or absolute path)
|
||||||
static_root_path = public
|
static_root_path = public
|
||||||
|
|
||||||
# Enable GZip
|
# Enable GZip
|
||||||
enable_gzip = false
|
enable_gzip = false
|
||||||
|
|
||||||
# Set to true to log each request and execution times
|
# Set to true to log each request and execution time
|
||||||
log_request = true
|
log_request = true
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
@@ -50,24 +50,37 @@ passwd =
|
|||||||
# For "postgres" only, Either "disable", "require" or "verify-full"
|
# For "postgres" only, Either "disable", "require" or "verify-full"
|
||||||
ssl_mode = disable
|
ssl_mode = disable
|
||||||
|
|
||||||
# For "sqlite3" only, absolute path of db file
|
# For "sqlite3" only, db file path (relative or absolute path)
|
||||||
db_path = data/ezbookkeeping.db
|
db_path = data/ezbookkeeping.db
|
||||||
|
|
||||||
# Max idle connection number, default is 2
|
# Max idle connection number (0 - 65535, 0 means no idle connections are retained), default is 2
|
||||||
max_idle_conn = 2
|
max_idle_conn = 2
|
||||||
|
|
||||||
# Max opened connection number, default is 0 (unlimited)
|
# Max opened connection number (0 - 65535), default is 0 (unlimited)
|
||||||
max_open_conn = 0
|
max_open_conn = 0
|
||||||
|
|
||||||
# Max connection lifetime (seconds), default is 14400 (4 hours)
|
# Max connection lifetime (0 - 4294967295 seconds), default is 14400 (4 hours)
|
||||||
conn_max_lifetime = 14400
|
conn_max_lifetime = 14400
|
||||||
|
|
||||||
# Set to true to log each sql statement and execution times
|
# Set to true to log each sql statement and execution time
|
||||||
log_query = false
|
log_query = false
|
||||||
|
|
||||||
# Set to true to automatically update database structure when starting web server
|
# Set to true to automatically update database structure when starting web server
|
||||||
auto_update_database = true
|
auto_update_database = true
|
||||||
|
|
||||||
|
[mail]
|
||||||
|
# Set to true to enable sending mail by SMTP server
|
||||||
|
enable_smtp = false
|
||||||
|
|
||||||
|
# SMTP Server connection configuration
|
||||||
|
smtp_host = 127.0.0.1:25
|
||||||
|
smtp_user =
|
||||||
|
smtp_passwd =
|
||||||
|
smtp_skip_tls_verify = false
|
||||||
|
|
||||||
|
# Mail from address. This can be just an email address, or the "Name" <user@domain.com> format.
|
||||||
|
from_address =
|
||||||
|
|
||||||
[log]
|
[log]
|
||||||
# Either "console", "file", default is "console"
|
# Either "console", "file", default is "console"
|
||||||
# Use space to separate multiple modes, e.g. "console file"
|
# Use space to separate multiple modes, e.g. "console file"
|
||||||
@@ -76,14 +89,14 @@ mode = console file
|
|||||||
# Either "debug", "info", "warn", "error", default is "info"
|
# Either "debug", "info", "warn", "error", default is "info"
|
||||||
level = info
|
level = info
|
||||||
|
|
||||||
# For "file" only, absolute path of log file
|
# For "file" only, log file path (relative or absolute path)
|
||||||
log_path = log/ezbookkeeping.log
|
log_path = log/ezbookkeeping.log
|
||||||
|
|
||||||
[uuid]
|
[uuid]
|
||||||
# Uuid generator type, supports "internal" currently
|
# Uuid generator type, supports "internal" currently
|
||||||
generator_type = internal
|
generator_type = internal
|
||||||
|
|
||||||
# For "internal" only, each server must have unique id
|
# For "internal" only, each server must have unique id (0 - 255)
|
||||||
server_id = 0
|
server_id = 0
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
@@ -93,12 +106,18 @@ secret_key =
|
|||||||
# Set to true to enable two factor authorization
|
# Set to true to enable two factor authorization
|
||||||
enable_two_factor = true
|
enable_two_factor = true
|
||||||
|
|
||||||
# Token expired seconds, default is 2592000 (30 days)
|
# Token expired seconds (0 - 4294967295), default is 2592000 (30 days)
|
||||||
token_expired_time = 2592000
|
token_expired_time = 2592000
|
||||||
|
|
||||||
# Temporary token expired seconds, default is 300 (5 minutes)
|
# Temporary token expired seconds (0 - 4294967295), default is 300 (5 minutes)
|
||||||
temporary_token_expired_time = 300
|
temporary_token_expired_time = 300
|
||||||
|
|
||||||
|
# Email verify token expired seconds (0 - 4294967295), default is 3600 (60 minutes)
|
||||||
|
email_verify_token_expired_time = 3600
|
||||||
|
|
||||||
|
# Password reset token expired seconds (0 - 4294967295), default is 3600 (60 minutes)
|
||||||
|
password_reset_token_expired_time = 3600
|
||||||
|
|
||||||
# Add X-Request-Id header to response to track user request or error, default is true
|
# Add X-Request-Id header to response to track user request or error, default is true
|
||||||
request_id_header = true
|
request_id_header = true
|
||||||
|
|
||||||
@@ -106,13 +125,81 @@ request_id_header = true
|
|||||||
# Set to true to allow users to register account by themselves
|
# Set to true to allow users to register account by themselves
|
||||||
enable_register = true
|
enable_register = true
|
||||||
|
|
||||||
|
# Set to true to allow users to verify email address
|
||||||
|
enable_email_verify = false
|
||||||
|
|
||||||
|
# Set to true to require email must be verified when login
|
||||||
|
enable_force_email_verify = false
|
||||||
|
|
||||||
|
# Set to true to allow users to reset password
|
||||||
|
enable_forget_password = true
|
||||||
|
|
||||||
|
# Set to true to require email must be verified when use forget password
|
||||||
|
forget_password_require_email_verify = false
|
||||||
|
|
||||||
|
# User avatar provider, supports the following types:
|
||||||
|
# "gravatar": https://gravatar.com
|
||||||
|
# Leave blank if you want to disable user avatar
|
||||||
|
avatar_provider =
|
||||||
|
|
||||||
[data]
|
[data]
|
||||||
# Set to true to allow users to export their data
|
# Set to true to allow users to export their data
|
||||||
enable_export = true
|
enable_export = true
|
||||||
|
|
||||||
|
[map]
|
||||||
|
# Map provider, supports the following types:
|
||||||
|
# "openstreetmap": https://www.openstreetmap.org
|
||||||
|
# "openstreetmap_humanitarian": http://map.hotosm.org
|
||||||
|
# "opentopomap": https://opentopomap.org
|
||||||
|
# "opnvkarte": https://publictransportmap.org
|
||||||
|
# "cyclosm": https://www.cyclosm.org
|
||||||
|
# "tomtom": https://www.tomtom.com
|
||||||
|
# "googlemap": https://map.google.com
|
||||||
|
# "baidumap": https://map.baidu.com
|
||||||
|
# "amap": https://amap.com
|
||||||
|
# Leave blank if you want to disable map
|
||||||
|
map_provider = openstreetmap
|
||||||
|
|
||||||
|
# Set to true to use the ezbookkeeping server to proxy map data requests, for "openstreetmap", "openstreetmap_humanitarian", "opentopomap", "opnvkarte", "cyclosm" or "tomtom"
|
||||||
|
map_data_fetch_proxy = false
|
||||||
|
|
||||||
|
# For "tomtom" only, TomTom map API key, please visit https://developer.tomtom.com/how-to-get-tomtom-api-key
|
||||||
|
tomtom_map_api_key =
|
||||||
|
|
||||||
|
# For "googlemap" only, Google map JavaScript API key, please visit https://developers.google.com/maps/get-started for more information
|
||||||
|
google_map_api_key =
|
||||||
|
|
||||||
|
# For "baidumap" only, Baidu map JavaScript API application key, please visit https://lbsyun.baidu.com/index.php?title=jspopular3.0/guide/getkey for more information
|
||||||
|
baidu_map_ak =
|
||||||
|
|
||||||
|
# For "amap" only, Amap JavaScript API application key, please visit https://lbs.amap.com/api/javascript-api/guide/abc/prepare for more information
|
||||||
|
amap_application_key =
|
||||||
|
|
||||||
|
# For "amap" only, Amap JavaScript API security verification method, supports the following methods:
|
||||||
|
# "internal_proxy": use the internal proxy to request amap api with amap application secret (default)
|
||||||
|
# "external_proxy": use an external proxy to request amap api (amap application secret should be set by external proxy)
|
||||||
|
# "plain_text": append amap application secret to frontend request directly (insecurity for public network)
|
||||||
|
# Please visit https://developer.amap.com/api/jsapi-v2/guide/abc/load for more information
|
||||||
|
amap_security_verification_method = plain_text
|
||||||
|
|
||||||
|
# For "amap" only, Amap JavaScript API application secret, this setting must be provided when "amap_security_verification_method" is set to "internal_proxy" or "plain_text", please visit https://lbs.amap.com/api/javascript-api/guide/abc/prepare for more information
|
||||||
|
amap_application_secret =
|
||||||
|
|
||||||
|
# For "amap" only, Amap JavaScript API external proxy url, this setting must be provided when "amap_security_verification_method" is set to "external_proxy"
|
||||||
|
amap_api_external_proxy_url =
|
||||||
|
|
||||||
[exchange_rates]
|
[exchange_rates]
|
||||||
# Exchange rates data source, supports "euro_central_bank", "bank_of_canada", "reserve_bank_of_australia", "czech_national_bank", "national_bank_of_poland" currently
|
# Exchange rates data source, supports the following types:
|
||||||
|
# "euro_central_bank"
|
||||||
|
# "bank_of_canada"
|
||||||
|
# "reserve_bank_of_australia",
|
||||||
|
# "czech_national_bank"
|
||||||
|
# "national_bank_of_poland"
|
||||||
|
# "monetary_authority_of_singapore"
|
||||||
data_source = euro_central_bank
|
data_source = euro_central_bank
|
||||||
|
|
||||||
# Requesting exchange rates data timeout (milliseconds), default is 10000 (10 seconds)
|
# Requesting exchange rates data timeout (0 - 4294967295 milliseconds), default is 10000 (10 seconds)
|
||||||
request_timeout = 10000
|
request_timeout = 10000
|
||||||
|
|
||||||
|
# Set to true skip tls verification when request exchange rates data
|
||||||
|
skip_tls_verify = false
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/cmd"
|
"github.com/mayswind/ezbookkeeping/cmd"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,6 +25,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
settings.Version = Version
|
||||||
|
settings.CommitHash = CommitHash
|
||||||
|
|
||||||
app := &cli.App{
|
app := &cli.App{
|
||||||
Name: "ezBookkeeping",
|
Name: "ezBookkeeping",
|
||||||
Usage: "A lightweight personal bookkeeping app hosted by yourself.",
|
Usage: "A lightweight personal bookkeeping app hosted by yourself.",
|
||||||
@@ -32,12 +36,18 @@ func main() {
|
|||||||
cmd.WebServer,
|
cmd.WebServer,
|
||||||
cmd.Database,
|
cmd.Database,
|
||||||
cmd.UserData,
|
cmd.UserData,
|
||||||
|
cmd.SecurityUtils,
|
||||||
|
cmd.Utilities,
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "conf-path",
|
Name: "conf-path",
|
||||||
Usage: "Custom config `FILE` path",
|
Usage: "Custom config `FILE` path",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "no-boot-log",
|
||||||
|
Usage: "Disable boot log",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,65 @@
|
|||||||
module github.com/mayswind/ezbookkeeping
|
module github.com/mayswind/ezbookkeeping
|
||||||
|
|
||||||
go 1.14
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/boombuler/barcode v1.0.1
|
||||||
github.com/gin-contrib/gzip v0.0.3
|
github.com/gin-contrib/cache v1.2.0
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-contrib/gzip v0.0.6
|
||||||
github.com/go-playground/validator/v10 v10.4.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-playground/validator/v10 v10.15.1
|
||||||
github.com/lib/pq v1.8.0
|
github.com/go-sql-driver/mysql v1.7.1
|
||||||
github.com/mattn/go-sqlite3 v1.14.4
|
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/lib/pq v1.10.9
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
github.com/urfave/cli/v2 v2.25.7
|
||||||
gopkg.in/ini.v1 v1.62.0
|
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||||
xorm.io/xorm v1.0.5
|
golang.org/x/crypto v0.12.0
|
||||||
|
gopkg.in/ini.v1 v1.67.0
|
||||||
|
gopkg.in/mail.v2 v2.3.1
|
||||||
|
xorm.io/xorm v1.3.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
||||||
|
github.com/buger/jsonparser v1.1.1 // indirect
|
||||||
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gomodule/redigo v1.8.9 // 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/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/memcachier/mc/v3 v3.0.3 // 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/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
|
golang.org/x/net v0.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
|
golang.org/x/text v0.12.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
xorm.io/builder v0.3.12 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,141 +1,778 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||||
|
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||||
|
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||||
|
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
|
||||||
|
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
|
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||||
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
|
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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
|
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||||
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gin-contrib/gzip v0.0.3/go.mod h1:YxxswVZIqOvcHEQpsSn+QF5guQtO1dCfy0shBPy4jFc=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gin-contrib/cache v1.2.0 h1:WA+AJR4kmHDTaLLShCHo/IeWVmmGRZ3Lsr3JQ46tFlE=
|
||||||
|
github.com/gin-contrib/cache v1.2.0/go.mod h1:2KkFL8PSnPF3Tt5E2Jpc3HWuBAUKqGZnClCFMm0tXQI=
|
||||||
|
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||||
|
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
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-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||||
|
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
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/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||||
|
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||||
|
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
|
||||||
|
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
|
||||||
|
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
|
||||||
|
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
|
||||||
|
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
|
||||||
|
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
|
||||||
|
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
|
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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/memcachier/mc/v3 v3.0.3 h1:qii+lDiPKi36O4Xg+HVKwHu6Oq+Gt17b+uEiA0Drwv4=
|
||||||
|
github.com/memcachier/mc/v3 v3.0.3/go.mod h1:GzjocBahcXPxt2cmqzknrgqCOmMxiSzhVKPOe90Tpug=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||||
|
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||||
|
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||||
|
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||||
|
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
|
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||||
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
|
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
|
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
|
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
|
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
|
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/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||||
|
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||||
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
|
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62 h1:pyecQtsPmlkCsMkYhT5iZ+sUXuwee+OvfuJjinEA3ko=
|
||||||
|
github.com/robfig/go-cache v0.0.0-20130306151617-9fc39e0dbf62/go.mod h1:65XQgovT59RWatovFwnwocoUxiI/eENTnOY5GK3STuY=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
|
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||||
|
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||||
|
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
|
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||||
|
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
|
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
|
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
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.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
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/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
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/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||||
|
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
xorm.io/xorm v1.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||||
|
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
|
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
|
||||||
|
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||||
|
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||||
|
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
|
||||||
|
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
|
||||||
|
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
|
||||||
|
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
|
||||||
|
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
|
||||||
|
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
|
||||||
|
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
|
||||||
|
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
|
||||||
|
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
|
||||||
|
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
|
||||||
|
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
|
||||||
|
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
|
||||||
|
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
|
||||||
|
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
|
||||||
|
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
|
||||||
|
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
|
||||||
|
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
|
||||||
|
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
|
||||||
|
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
|
||||||
|
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
|
||||||
|
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
|
||||||
|
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
|
||||||
|
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
|
||||||
|
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
|
||||||
|
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
|
||||||
|
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
|
||||||
|
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
|
||||||
|
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
|
||||||
|
modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4=
|
||||||
|
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
|
||||||
|
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
|
||||||
|
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
|
||||||
|
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
|
||||||
|
modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8=
|
||||||
|
modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
|
||||||
|
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
|
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||||
|
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||||
|
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
|
||||||
|
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
|
||||||
|
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
|
||||||
|
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
|
||||||
|
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
|
||||||
|
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
|
||||||
|
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
|
||||||
|
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
|
||||||
|
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
|
||||||
|
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
|
||||||
|
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
|
||||||
|
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
|
||||||
|
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
|
||||||
|
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
|
||||||
|
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
|
||||||
|
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
|
||||||
|
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
|
||||||
|
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
|
||||||
|
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
|
||||||
|
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
|
||||||
|
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
|
||||||
|
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
|
||||||
|
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
|
||||||
|
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
|
||||||
|
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
|
||||||
|
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
|
||||||
|
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
|
||||||
|
modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
|
||||||
|
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
|
||||||
|
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
|
||||||
|
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
|
||||||
|
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
|
||||||
|
modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE=
|
||||||
|
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
|
||||||
|
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||||
|
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
|
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
|
||||||
|
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
|
||||||
|
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
|
||||||
|
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
|
||||||
|
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE=
|
||||||
|
modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
|
||||||
|
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
|
||||||
|
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||||
|
modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
|
||||||
|
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
|
||||||
|
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||||
|
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
|
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
|
||||||
|
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
|
xorm.io/xorm v1.3.2 h1:uTRRKF2jYzbZ5nsofXVUx6ncMaek+SHjWYtCXyZo1oM=
|
||||||
|
xorm.io/xorm v1.3.2/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
|
||||||
|
|||||||
Generated
+8162
-12525
File diff suppressed because it is too large
Load Diff
+41
-44
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ezbookkeeping",
|
"name": "ezbookkeeping",
|
||||||
"version": "0.1.0",
|
"version": "0.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -12,56 +12,53 @@
|
|||||||
"url": "https://github.com/mayswind/ezbookkeeping/issues"
|
"url": "https://github.com/mayswind/ezbookkeeping/issues"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "cross-env NODE_ENV=development vite",
|
||||||
"build": "vue-cli-service build",
|
"build": "cross-env NODE_ENV=production vite build",
|
||||||
"lint": "vue-cli-service lint"
|
"serve:dist": "vite preview",
|
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"@mdi/js": "^7.2.96",
|
||||||
|
"@vuepic/vue-datepicker": "^5.4.0",
|
||||||
|
"axios": "^1.4.0",
|
||||||
"cbor-js": "^0.1.0",
|
"cbor-js": "^0.1.0",
|
||||||
"core-js": "^3.6.5",
|
"clipboard": "^2.0.11",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.1.1",
|
||||||
"framework7": "^5.7.14",
|
"dom7": "^4.0.6",
|
||||||
"framework7-icons": "^3.0.1",
|
"echarts": "^5.4.3",
|
||||||
"framework7-vue": "^5.7.14",
|
"framework7": "^8.3.0",
|
||||||
"js-cookie": "^2.2.1",
|
"framework7-icons": "^5.0.5",
|
||||||
|
"framework7-vue": "^8.3.0",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"line-awesome": "^1.3.0",
|
"line-awesome": "^1.3.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.4",
|
||||||
"moment-timezone": "^0.5.33",
|
"moment-timezone": "^0.5.43",
|
||||||
|
"pinia": "^2.1.6",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"ua-parser-js": "^0.7.28",
|
"skeleton-elements": "^4.0.1",
|
||||||
"vue": "^2.6.12",
|
"swiper": "^10.2.0",
|
||||||
"vue-clipboard2": "^0.3.1",
|
"ua-parser-js": "^1.0.35",
|
||||||
"vue-i18n": "^8.24.3",
|
"vue": "^3.3.4",
|
||||||
"vue-pincode-input": "^0.4.0",
|
"vue-echarts": "^6.6.1",
|
||||||
"vuex": "^3.6.2"
|
"vue-i18n": "^9.2.2",
|
||||||
|
"vue-router": "^4.2.4",
|
||||||
|
"vue3-perfect-scrollbar": "^1.6.1",
|
||||||
|
"vuedraggable": "^4.1.0",
|
||||||
|
"vuetify": "^3.3.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.5.11",
|
"@vitejs/plugin-vue": "^4.3.1",
|
||||||
"@vue/cli-plugin-eslint": "^4.5.11",
|
"@vue/compiler-sfc": "^3.3.4",
|
||||||
"@vue/cli-plugin-pwa": "^4.5.11",
|
"cross-env": "^7.0.3",
|
||||||
"@vue/cli-service": "^4.5.11",
|
"eslint": "^8.47.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"babel-plugin-component": "^1.1.1",
|
"git-rev-sync": "^3.0.2",
|
||||||
"eslint": "^6.7.2",
|
"postcss-preset-env": "^9.1.1",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"sass": "^1.66.1",
|
||||||
"git-revision-webpack-plugin": "^3.0.6",
|
"vite": "^4.4.9",
|
||||||
"moment-locales-webpack-plugin": "^1.2.0",
|
"vite-plugin-pwa": "^0.16.4",
|
||||||
"vue-template-compiler": "^2.6.12"
|
"vite-plugin-vuetify": "^1.0.2"
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:vue/essential",
|
|
||||||
"eslint:recommended"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"parser": "babel-eslint"
|
|
||||||
},
|
|
||||||
"rules": {}
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
|||||||
+29
-22
@@ -24,7 +24,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AccountListHandler returns accounts list of current user
|
// AccountListHandler returns accounts list of current user
|
||||||
func (a *AccountsApi) AccountListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountListReq models.AccountListRequest
|
var accountListReq models.AccountListRequest
|
||||||
err := c.ShouldBindQuery(&accountListReq)
|
err := c.ShouldBindQuery(&accountListReq)
|
||||||
|
|
||||||
@@ -34,11 +34,11 @@ func (a *AccountsApi) AccountListHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
accounts, err := a.accounts.GetAllAccountsByUid(uid)
|
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountListHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountListHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
userAllAccountResps := make([]*models.AccountInfoResponse, len(accounts))
|
userAllAccountResps := make([]*models.AccountInfoResponse, len(accounts))
|
||||||
@@ -84,7 +84,7 @@ func (a *AccountsApi) AccountListHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountGetHandler returns one specific account of current user
|
// AccountGetHandler returns one specific account of current user
|
||||||
func (a *AccountsApi) AccountGetHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountGetHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountGetReq models.AccountGetRequest
|
var accountGetReq models.AccountGetRequest
|
||||||
err := c.ShouldBindQuery(&accountGetReq)
|
err := c.ShouldBindQuery(&accountGetReq)
|
||||||
|
|
||||||
@@ -94,11 +94,11 @@ func (a *AccountsApi) AccountGetHandler(c *core.Context) (interface{}, *errs.Err
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(uid, accountGetReq.Id)
|
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(c, uid, accountGetReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountGetHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", accountGetReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountGetHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", accountGetReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountRespMap := make(map[int64]*models.AccountInfoResponse)
|
accountRespMap := make(map[int64]*models.AccountInfoResponse)
|
||||||
@@ -127,7 +127,7 @@ func (a *AccountsApi) AccountGetHandler(c *core.Context) (interface{}, *errs.Err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountCreateHandler saves a new account by request parameters for current user
|
// AccountCreateHandler saves a new account by request parameters for current user
|
||||||
func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountCreateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountCreateReq models.AccountCreateRequest
|
var accountCreateReq models.AccountCreateRequest
|
||||||
err := c.ShouldBindJSON(&accountCreateReq)
|
err := c.ShouldBindJSON(&accountCreateReq)
|
||||||
|
|
||||||
@@ -136,6 +136,13 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.
|
|||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utcOffset, err := c.GetClientTimezoneOffset()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] cannot get client timezone offset, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
if accountCreateReq.Type == models.ACCOUNT_TYPE_SINGLE_ACCOUNT {
|
if accountCreateReq.Type == models.ACCOUNT_TYPE_SINGLE_ACCOUNT {
|
||||||
if len(accountCreateReq.SubAccounts) > 0 {
|
if len(accountCreateReq.SubAccounts) > 0 {
|
||||||
log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account cannot have any sub accounts")
|
log.WarnfWithRequestId(c, "[accounts.AccountCreateHandler] account cannot have any sub accounts")
|
||||||
@@ -186,17 +193,17 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
maxOrderId, err := a.accounts.GetMaxDisplayOrder(uid, accountCreateReq.Category)
|
maxOrderId, err := a.accounts.GetMaxDisplayOrder(c, uid, accountCreateReq.Category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
mainAccount := a.createNewAccountModel(uid, &accountCreateReq, maxOrderId+1)
|
mainAccount := a.createNewAccountModel(uid, &accountCreateReq, maxOrderId+1)
|
||||||
childrenAccounts := a.createSubAccountModels(uid, &accountCreateReq)
|
childrenAccounts := a.createSubAccountModels(uid, &accountCreateReq)
|
||||||
|
|
||||||
err = a.accounts.CreateAccounts(mainAccount, childrenAccounts)
|
err = a.accounts.CreateAccounts(c, mainAccount, childrenAccounts, utcOffset)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountCreateHandler] failed to create account \"id:%d\" for user \"uid:%d\", because %s", mainAccount.AccountId, uid, err.Error())
|
||||||
@@ -219,7 +226,7 @@ func (a *AccountsApi) AccountCreateHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountModifyHandler saves an existed account by request parameters for current user
|
// AccountModifyHandler saves an existed account by request parameters for current user
|
||||||
func (a *AccountsApi) AccountModifyHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountModifyHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountModifyReq models.AccountModifyRequest
|
var accountModifyReq models.AccountModifyRequest
|
||||||
err := c.ShouldBindJSON(&accountModifyReq)
|
err := c.ShouldBindJSON(&accountModifyReq)
|
||||||
|
|
||||||
@@ -229,11 +236,11 @@ func (a *AccountsApi) AccountModifyHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(uid, accountModifyReq.Id)
|
accountAndSubAccounts, err := a.accounts.GetAccountAndSubAccountsByAccountId(c, uid, accountModifyReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountModifyHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountModifyHandler] failed to get account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountMap := a.accounts.GetAccountMapByList(accountAndSubAccounts)
|
accountMap := a.accounts.GetAccountMapByList(accountAndSubAccounts)
|
||||||
@@ -275,7 +282,7 @@ func (a *AccountsApi) AccountModifyHandler(c *core.Context) (interface{}, *errs.
|
|||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.accounts.ModifyAccounts(uid, toUpdateAccounts)
|
err = a.accounts.ModifyAccounts(c, uid, toUpdateAccounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountModifyHandler] failed to update account \"id:%d\" for user \"uid:%d\", because %s", accountModifyReq.Id, uid, err.Error())
|
||||||
@@ -325,7 +332,7 @@ func (a *AccountsApi) AccountModifyHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountHideHandler hides an existed account by request parameters for current user
|
// AccountHideHandler hides an existed account by request parameters for current user
|
||||||
func (a *AccountsApi) AccountHideHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountHideHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountHideReq models.AccountHideRequest
|
var accountHideReq models.AccountHideRequest
|
||||||
err := c.ShouldBindJSON(&accountHideReq)
|
err := c.ShouldBindJSON(&accountHideReq)
|
||||||
|
|
||||||
@@ -335,7 +342,7 @@ func (a *AccountsApi) AccountHideHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.accounts.HideAccount(uid, []int64{accountHideReq.Id}, accountHideReq.Hidden)
|
err = a.accounts.HideAccount(c, uid, []int64{accountHideReq.Id}, accountHideReq.Hidden)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountHideHandler] failed to hide account \"id:%d\" for user \"uid:%d\", because %s", accountHideReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountHideHandler] failed to hide account \"id:%d\" for user \"uid:%d\", because %s", accountHideReq.Id, uid, err.Error())
|
||||||
@@ -347,7 +354,7 @@ func (a *AccountsApi) AccountHideHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountMoveHandler moves display order of existed accounts by request parameters for current user
|
// AccountMoveHandler moves display order of existed accounts by request parameters for current user
|
||||||
func (a *AccountsApi) AccountMoveHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountMoveHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountMoveReq models.AccountMoveRequest
|
var accountMoveReq models.AccountMoveRequest
|
||||||
err := c.ShouldBindJSON(&accountMoveReq)
|
err := c.ShouldBindJSON(&accountMoveReq)
|
||||||
|
|
||||||
@@ -370,7 +377,7 @@ func (a *AccountsApi) AccountMoveHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
accounts[i] = account
|
accounts[i] = account
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.accounts.ModifyAccountDisplayOrders(uid, accounts)
|
err = a.accounts.ModifyAccountDisplayOrders(c, uid, accounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountMoveHandler] failed to move accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountMoveHandler] failed to move accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -382,7 +389,7 @@ func (a *AccountsApi) AccountMoveHandler(c *core.Context) (interface{}, *errs.Er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AccountDeleteHandler deletes an existed account by request parameters for current user
|
// AccountDeleteHandler deletes an existed account by request parameters for current user
|
||||||
func (a *AccountsApi) AccountDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AccountsApi) AccountDeleteHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var accountDeleteReq models.AccountDeleteRequest
|
var accountDeleteReq models.AccountDeleteRequest
|
||||||
err := c.ShouldBindJSON(&accountDeleteReq)
|
err := c.ShouldBindJSON(&accountDeleteReq)
|
||||||
|
|
||||||
@@ -392,7 +399,7 @@ func (a *AccountsApi) AccountDeleteHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.accounts.DeleteAccount(uid, accountDeleteReq.Id)
|
err = a.accounts.DeleteAccount(c, uid, accountDeleteReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[accounts.AccountDeleteHandler] failed to delete account \"id:%d\" for user \"uid:%d\", because %s", accountDeleteReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[accounts.AccountDeleteHandler] failed to delete account \"id:%d\" for user \"uid:%d\", because %s", accountDeleteReq.Id, uid, err.Error())
|
||||||
@@ -403,7 +410,7 @@ func (a *AccountsApi) AccountDeleteHandler(c *core.Context) (interface{}, *errs.
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AccountsApi) createNewAccountModel(uid int64, accountCreateReq *models.AccountCreateRequest, order int) *models.Account {
|
func (a *AccountsApi) createNewAccountModel(uid int64, accountCreateReq *models.AccountCreateRequest, order int32) *models.Account {
|
||||||
return &models.Account{
|
return &models.Account{
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
Name: accountCreateReq.Name,
|
Name: accountCreateReq.Name,
|
||||||
@@ -425,7 +432,7 @@ func (a *AccountsApi) createSubAccountModels(uid int64, accountCreateReq *models
|
|||||||
|
|
||||||
childrenAccounts := make([]*models.Account, len(accountCreateReq.SubAccounts))
|
childrenAccounts := make([]*models.Account, len(accountCreateReq.SubAccounts))
|
||||||
|
|
||||||
for i := 0; i < len(accountCreateReq.SubAccounts); i++ {
|
for i := int32(0); i < int32(len(accountCreateReq.SubAccounts)); i++ {
|
||||||
childrenAccounts[i] = a.createNewAccountModel(uid, accountCreateReq.SubAccounts[i], i+1)
|
childrenAccounts[i] = a.createNewAccountModel(uid, accountCreateReq.SubAccounts[i], i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const amapCustomMapStylesUrl = "https://webapi.amap.com/v4/map/styles"
|
||||||
|
const amapOverseasMapUrl = "https://fmap01.amap.com/v3/vectormap"
|
||||||
|
const amapRestApiUrl = "https://restapi.amap.com/"
|
||||||
|
|
||||||
|
// AmapApiProxy represents amap api proxy
|
||||||
|
type AmapApiProxy struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a amap api proxy singleton instance
|
||||||
|
var (
|
||||||
|
AmapApis = &AmapApiProxy{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// AmapApiProxyHandler returns amap api response
|
||||||
|
func (p *AmapApiProxy) AmapApiProxyHandler(c *core.Context) (*httputil.ReverseProxy, *errs.Error) {
|
||||||
|
var targetUrl string
|
||||||
|
|
||||||
|
if strings.HasPrefix(c.Request.RequestURI, "/_AMapService/v4/map/styles") {
|
||||||
|
targetUrl = amapCustomMapStylesUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/v4/map/styles")
|
||||||
|
} else if strings.HasPrefix(c.Request.RequestURI, "/_AMapService/v3/vectormap") {
|
||||||
|
targetUrl = amapOverseasMapUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/v3/vectormap")
|
||||||
|
} else {
|
||||||
|
targetUrl = amapRestApiUrl + strings.TrimPrefix(c.Request.URL.Path, "/_AMapService/")
|
||||||
|
}
|
||||||
|
|
||||||
|
director := func(req *http.Request) {
|
||||||
|
targetRawUrl := fmt.Sprintf("%s?%s&jscode=%s", targetUrl, req.URL.RawQuery, settings.Container.Current.AmapApplicationSecret)
|
||||||
|
targetUrl, _ := url.Parse(targetRawUrl)
|
||||||
|
|
||||||
|
oldCookies := req.Cookies()
|
||||||
|
req.Header.Del("Cookie")
|
||||||
|
|
||||||
|
for i := 0; i < len(oldCookies); i++ {
|
||||||
|
if strings.HasPrefix(oldCookies[i].Name, "ebk_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
req.AddCookie(oldCookies[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
req.URL = targetUrl
|
||||||
|
req.RequestURI = req.URL.RequestURI()
|
||||||
|
req.Host = targetUrl.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httputil.ReverseProxy{Director: director}, nil
|
||||||
|
}
|
||||||
+65
-17
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/services"
|
"github.com/mayswind/ezbookkeeping/pkg/services"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthorizationsApi represents authorization api
|
// AuthorizationsApi represents authorization api
|
||||||
@@ -27,7 +28,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AuthorizeHandler verifies and authorizes current login request
|
// AuthorizeHandler verifies and authorizes current login request
|
||||||
func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var credential models.UserLoginRequest
|
var credential models.UserLoginRequest
|
||||||
err := c.ShouldBindJSON(&credential)
|
err := c.ShouldBindJSON(&credential)
|
||||||
|
|
||||||
@@ -36,14 +37,35 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
|||||||
return nil, errs.ErrLoginNameOrPasswordInvalid
|
return nil, errs.ErrLoginNameOrPasswordInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.users.GetUserByUsernameOrEmailAndPassword(credential.LoginName, credential.Password)
|
user, err := a.users.GetUserByUsernameOrEmailAndPassword(c, credential.LoginName, credential.Password)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because %s", credential.LoginName, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because %s", credential.LoginName, err.Error())
|
||||||
return nil, errs.ErrLoginNameOrPasswordWrong
|
return nil, errs.ErrLoginNameOrPasswordWrong
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.users.UpdateUserLastLoginTime(user.Uid)
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because user is disabled", credential.LoginName)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified {
|
||||||
|
hasValidEmailVerifyToken, err := a.tokens.ExistsValidTokenByType(c, user.Uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] failed check whether user \"uid:%d\" has valid verify email token, because %s", user.Uid, err.Error())
|
||||||
|
hasValidEmailVerifyToken = false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because user has not verified email", credential.LoginName)
|
||||||
|
|
||||||
|
return nil, errs.NewErrorWithContext(errs.ErrEmailIsNotVerified, map[string]any{
|
||||||
|
"email": user.Email,
|
||||||
|
"hasValidEmailVerifyToken": hasValidEmailVerifyToken,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.users.UpdateUserLastLoginTime(c, user.Uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to update last login time for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to update last login time for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -52,7 +74,7 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
|||||||
twoFactorEnable := a.tokens.CurrentConfig().EnableTwoFactor
|
twoFactorEnable := a.tokens.CurrentConfig().EnableTwoFactor
|
||||||
|
|
||||||
if twoFactorEnable {
|
if twoFactorEnable {
|
||||||
twoFactorEnable, err = a.twoFactorAuthorizations.ExistsTwoFactorSetting(user.Uid)
|
twoFactorEnable, err = a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, user.Uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to check two factor setting for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to check two factor setting for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -64,9 +86,9 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
|||||||
var claims *core.UserTokenClaims
|
var claims *core.UserTokenClaims
|
||||||
|
|
||||||
if twoFactorEnable {
|
if twoFactorEnable {
|
||||||
token, claims, err = a.tokens.CreateRequire2FAToken(user, c)
|
token, claims, err = a.tokens.CreateRequire2FAToken(c, user)
|
||||||
} else {
|
} else {
|
||||||
token, claims, err = a.tokens.CreateToken(user, c)
|
token, claims, err = a.tokens.CreateToken(c, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -74,6 +96,10 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
|||||||
return nil, errs.ErrTokenGenerating
|
return nil, errs.ErrTokenGenerating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !twoFactorEnable {
|
||||||
|
c.SetTextualToken(token)
|
||||||
|
}
|
||||||
|
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[authorizations.AuthorizeHandler] user \"uid:%d\" has logined, token type is %d, token will be expired at %d", user.Uid, claims.Type, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[authorizations.AuthorizeHandler] user \"uid:%d\" has logined, token type is %d, token will be expired at %d", user.Uid, claims.Type, claims.ExpiresAt)
|
||||||
@@ -83,7 +109,7 @@ func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorAuthorizeHandler verifies and authorizes current 2fa login by passcode
|
// TwoFactorAuthorizeHandler verifies and authorizes current 2fa login by passcode
|
||||||
func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var credential models.TwoFactorLoginRequest
|
var credential models.TwoFactorLoginRequest
|
||||||
err := c.ShouldBindJSON(&credential)
|
err := c.ShouldBindJSON(&credential)
|
||||||
|
|
||||||
@@ -93,7 +119,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(uid)
|
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -105,27 +131,38 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interfac
|
|||||||
return nil, errs.ErrPasscodeInvalid
|
return nil, errs.ErrPasscodeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
|
||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has not verified email", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsNotVerified
|
||||||
|
}
|
||||||
|
|
||||||
oldTokenClaims := c.GetTokenClaims()
|
oldTokenClaims := c.GetTokenClaims()
|
||||||
err = a.tokens.DeleteTokenByClaims(oldTokenClaims)
|
err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
return nil, errs.ErrTokenGenerating
|
return nil, errs.ErrTokenGenerating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has authorized two factor via passcode, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has authorized two factor via passcode, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
@@ -135,7 +172,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorAuthorizeByRecoveryCodeHandler verifies and authorizes current 2fa login by recovery code
|
// TwoFactorAuthorizeByRecoveryCodeHandler verifies and authorizes current 2fa login by recovery code
|
||||||
func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var credential models.TwoFactorRecoveryCodeLoginRequest
|
var credential models.TwoFactorRecoveryCodeLoginRequest
|
||||||
err := c.ShouldBindJSON(&credential)
|
err := c.ShouldBindJSON(&credential)
|
||||||
|
|
||||||
@@ -145,7 +182,7 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -156,14 +193,24 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont
|
|||||||
return nil, errs.ErrTwoFactorIsNotEnabled
|
return nil, errs.ErrTwoFactorIsNotEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
|
||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.GetAndUseUserTwoFactorRecoveryCode(uid, credential.RecoveryCode, user.Salt)
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.EnableUserForceVerifyEmail && !user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has not verified email", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsNotVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.twoFactorAuthorizations.GetAndUseUserTwoFactorRecoveryCode(c, uid, credential.RecoveryCode, user.Salt)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor recovery code for user \"uid:%d\", because %s", uid, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor recovery code for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -171,19 +218,20 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldTokenClaims := c.GetTokenClaims()
|
oldTokenClaims := c.GetTokenClaims()
|
||||||
err = a.tokens.DeleteTokenByClaims(oldTokenClaims)
|
err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
return nil, errs.ErrTokenGenerating
|
return nil, errs.ErrTokenGenerating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt)
|
||||||
|
|||||||
+55
-14
@@ -57,7 +57,7 @@ func (a *DataManagementsApi) ExportDataHandler(c *core.Context) ([]byte, string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -67,28 +67,28 @@ func (a *DataManagementsApi) ExportDataHandler(c *core.Context) ([]byte, string,
|
|||||||
return nil, "", errs.ErrUserNotFound
|
return nil, "", errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts, err := a.accounts.GetAllAccountsByUid(uid)
|
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
categories, err := a.categories.GetAllCategoriesByUid(uid, 0, -1)
|
categories, err := a.categories.GetAllCategoriesByUid(c, uid, 0, -1)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, err := a.tags.GetAllTagsByUid(uid)
|
tags, err := a.tags.GetAllTagsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, "", errs.ErrOperationFailed
|
return nil, "", errs.ErrOperationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
tagIndexs, err := a.tags.GetAllTagIdsOfAllTransactions(uid)
|
tagIndexs, err := a.tags.GetAllTagIdsOfAllTransactions(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get tag index for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get tag index for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -99,7 +99,7 @@ func (a *DataManagementsApi) ExportDataHandler(c *core.Context) ([]byte, string,
|
|||||||
categoryMap := a.categories.GetCategoryMapByList(categories)
|
categoryMap := a.categories.GetCategoryMapByList(categories)
|
||||||
tagMap := a.tags.GetTagMapByList(tags)
|
tagMap := a.tags.GetTagMapByList(tags)
|
||||||
|
|
||||||
allTransactions, err := a.transactions.GetAllTransactions(uid, pageCountForDataExport, true)
|
allTransactions, err := a.transactions.GetAllTransactions(c, uid, pageCountForDataExport, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to all transactions user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to all transactions user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -118,8 +118,49 @@ func (a *DataManagementsApi) ExportDataHandler(c *core.Context) ([]byte, string,
|
|||||||
return result, fileName, nil
|
return result, fileName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataStatisticsHandler returns user data statistics
|
||||||
|
func (a *DataManagementsApi) DataStatisticsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
totalAccountCount, err := a.accounts.GetTotalAccountCountByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[data_managements.DataStatisticsHandler] failed to get total account count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTransactionCategoryCount, err := a.categories.GetTotalCategoryCountByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[data_managements.DataStatisticsHandler] failed to get total transaction category count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTransactionTagCount, err := a.tags.GetTotalTagCountByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[data_managements.DataStatisticsHandler] failed to get total transaction tag count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
totalTransactionCount, err := a.transactions.GetTotalTransactionCountByUid(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[data_managements.DataStatisticsHandler] failed to get total transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStatisticsResp := &models.DataStatisticsResponse{
|
||||||
|
TotalAccountCount: totalAccountCount,
|
||||||
|
TotalTransactionCategoryCount: totalTransactionCategoryCount,
|
||||||
|
TotalTransactionTagCount: totalTransactionTagCount,
|
||||||
|
TotalTransactionCount: totalTransactionCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataStatisticsResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ClearDataHandler deletes all user data
|
// ClearDataHandler deletes all user data
|
||||||
func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var clearDataReq models.ClearDataRequest
|
var clearDataReq models.ClearDataRequest
|
||||||
err := c.ShouldBindJSON(&clearDataReq)
|
err := c.ShouldBindJSON(&clearDataReq)
|
||||||
|
|
||||||
@@ -129,7 +170,7 @@ func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -143,25 +184,25 @@ func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (interface{}, *er
|
|||||||
return nil, errs.ErrUserPasswordWrong
|
return nil, errs.ErrUserPasswordWrong
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.transactions.DeleteAllTransactions(uid)
|
err = a.transactions.DeleteAllTransactions(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transactions, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transactions, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.categories.DeleteAllCategories(uid)
|
err = a.categories.DeleteAllCategories(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transaction categories, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transaction categories, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.tags.DeleteAllTags(uid)
|
err = a.tags.DeleteAllTags(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transaction tags, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[data_managements.ClearDataHandler] failed to delete all transaction tags, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[data_managements.ClearDataHandler] user \"uid:%d\" has cleared all data", uid)
|
log.InfofWithRequestId(c, "[data_managements.ClearDataHandler] user \"uid:%d\" has cleared all data", uid)
|
||||||
|
|||||||
+2
-2
@@ -14,11 +14,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ApiNotFound returns api not found error
|
// ApiNotFound returns api not found error
|
||||||
func (a *DefaultApi) ApiNotFound(c *core.Context) (interface{}, *errs.Error) {
|
func (a *DefaultApi) ApiNotFound(c *core.Context) (any, *errs.Error) {
|
||||||
return nil, errs.ErrApiNotFound
|
return nil, errs.ErrApiNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodNotAllowed returns method not allowed error
|
// MethodNotAllowed returns method not allowed error
|
||||||
func (a *DefaultApi) MethodNotAllowed(c *core.Context) (interface{}, *errs.Error) {
|
func (a *DefaultApi) MethodNotAllowed(c *core.Context) (any, *errs.Error) {
|
||||||
return nil, errs.ErrMethodNotAllowed
|
return nil, errs.ErrMethodNotAllowed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,7 +24,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// LatestExchangeRateHandler returns latest exchange rate data
|
// LatestExchangeRateHandler returns latest exchange rate data
|
||||||
func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
dataSource := exchangerates.Container.Current
|
dataSource := exchangerates.Container.Current
|
||||||
|
|
||||||
if dataSource == nil {
|
if dataSource == nil {
|
||||||
@@ -32,8 +33,17 @@ func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (interface
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
|
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
|
||||||
|
if settings.Container.Current.ExchangeRatesSkipTLSVerify {
|
||||||
|
transport.TLSClientConfig = &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: time.Duration(settings.Container.Current.ExchangeRatesRequestTimeout) * time.Millisecond,
|
Transport: transport,
|
||||||
|
Timeout: time.Duration(settings.Container.Current.ExchangeRatesRequestTimeout) * time.Millisecond,
|
||||||
}
|
}
|
||||||
|
|
||||||
urls := dataSource.GetRequestUrls()
|
urls := dataSource.GetRequestUrls()
|
||||||
@@ -53,7 +63,7 @@ func (a *ExchangeRatesApi) LatestExchangeRateHandler(c *core.Context) (interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
exchangeRateResp, err := dataSource.Parse(c, body)
|
exchangeRateResp, err := dataSource.Parse(c, body)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/services"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ForgetPasswordsApi represents user forget password api
|
||||||
|
type ForgetPasswordsApi struct {
|
||||||
|
users *services.UserService
|
||||||
|
tokens *services.TokenService
|
||||||
|
forgetPasswords *services.ForgetPasswordService
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a user api singleton instance
|
||||||
|
var (
|
||||||
|
ForgetPasswords = &ForgetPasswordsApi{
|
||||||
|
users: services.Users,
|
||||||
|
tokens: services.Tokens,
|
||||||
|
forgetPasswords: services.ForgetPasswords,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserForgetPasswordRequestHandler generates password reset link and send user an email with this link
|
||||||
|
func (a *ForgetPasswordsApi) UserForgetPasswordRequestHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
var request models.ForgetPasswordRequest
|
||||||
|
err := c.ShouldBindJSON(&request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.ErrEmailIsEmptyOrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := a.users.GetUserByEmail(c, request.Email)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.ErrorfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] user \"uid:%d\" has not verified email", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsNotVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.Container.Current.EnableSMTP {
|
||||||
|
return nil, errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _, err := a.tokens.CreatePasswordResetToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return nil, errs.ErrTokenGenerating
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = a.forgetPasswords.SendPasswordResetEmail(c, user, token, c.GetClientLocale())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserForgetPasswordRequestHandler] cannot send email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserResetPasswordHandler resets user password by request parameters
|
||||||
|
func (a *ForgetPasswordsApi) UserResetPasswordHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
var request models.PasswordResetRequest
|
||||||
|
err := c.ShouldBindJSON(&request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.ErrorfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] user \"uid:%d\" has not verified email", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsNotVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Email != request.Email {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] request email not equals the user email")
|
||||||
|
return nil, errs.ErrEmptyIsInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.users.IsPasswordEqualsUserPassword(request.Password, user) {
|
||||||
|
oldTokenClaims := c.GetTokenClaims()
|
||||||
|
err = a.tokens.DeleteTokenByClaims(c, oldTokenClaims)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] failed to revoke password reset token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrNewPasswordEqualsOldInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
userNew := &models.User{
|
||||||
|
Uid: user.Uid,
|
||||||
|
Salt: user.Salt,
|
||||||
|
Password: request.Password,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = a.users.UpdateUser(c, userNew, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
err = a.tokens.DeleteTokensBeforeTime(c, uid, now)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.InfofWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
|
||||||
|
} else {
|
||||||
|
log.WarnfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthsApi represents health api
|
||||||
|
type HealthsApi struct{}
|
||||||
|
|
||||||
|
// Initialize a healths api singleton instance
|
||||||
|
var (
|
||||||
|
Healths = &HealthsApi{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthStatusHandler returns the health status of current service
|
||||||
|
func (a *HealthsApi) HealthStatusHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
result := make(map[string]string)
|
||||||
|
|
||||||
|
result["version"] = settings.Version
|
||||||
|
result["commit"] = settings.CommitHash
|
||||||
|
result["status"] = "ok"
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const openStreetMapTileImageUrlFormat = "https://tile.openstreetmap.org/%s/%s/%s" // https://tile.openstreetmap.org/{z}/{x}/{y}.png
|
||||||
|
const openStreetMapHumanitarianStyleTileImageUrlFormat = "https://a.tile.openstreetmap.fr/hot/%s/%s/%s" // https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png
|
||||||
|
const openTopoMapTileImageUrlFormat = "https://tile.opentopomap.org/%s/%s/%s" // https://tile.opentopomap.org/{z}/{x}/{y}.png
|
||||||
|
const opnvKarteMapTileImageUrlFormat = "https://tileserver.memomaps.de/tilegen/%s/%s/%s" // https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png
|
||||||
|
const cyclOSMMapTileImageUrlFormat = "https://a.tile-cyclosm.openstreetmap.fr/cyclosm/%s/%s/%s" // https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png
|
||||||
|
const tomtomMapTileImageUrlFormat = "https://api.tomtom.com/map/1/tile/basic/main/%s/%s/%s" // https://api.tomtom.com/map/{versionNumber}/tile/{layer}/{style}/{z}/{x}/{y}.png?key={key}&language={language}
|
||||||
|
|
||||||
|
// MapImageProxy represents map image proxy
|
||||||
|
type MapImageProxy struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a map image proxy singleton instance
|
||||||
|
var (
|
||||||
|
MapImages = &MapImageProxy{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MapTileImageProxyHandler returns map tile image
|
||||||
|
func (p *MapImageProxy) MapTileImageProxyHandler(c *core.Context) (*httputil.ReverseProxy, *errs.Error) {
|
||||||
|
mapProvider := strings.Replace(c.Query("provider"), "-", "_", -1)
|
||||||
|
targetUrl := ""
|
||||||
|
|
||||||
|
if mapProvider == settings.OpenStreetMapProvider {
|
||||||
|
targetUrl = openStreetMapTileImageUrlFormat
|
||||||
|
} else if mapProvider == settings.OpenStreetMapHumanitarianStyleProvider {
|
||||||
|
targetUrl = openStreetMapHumanitarianStyleTileImageUrlFormat
|
||||||
|
} else if mapProvider == settings.OpenTopoMapProvider {
|
||||||
|
targetUrl = openTopoMapTileImageUrlFormat
|
||||||
|
} else if mapProvider == settings.OPNVKarteMapProvider {
|
||||||
|
targetUrl = opnvKarteMapTileImageUrlFormat
|
||||||
|
} else if mapProvider == settings.CyclOSMMapProvider {
|
||||||
|
targetUrl = cyclOSMMapTileImageUrlFormat
|
||||||
|
} else if mapProvider == settings.TomTomMapProvider {
|
||||||
|
targetUrl = tomtomMapTileImageUrlFormat + "?key=" + settings.Container.Current.TomTomMapAPIKey
|
||||||
|
language := c.Query("language")
|
||||||
|
|
||||||
|
if language != "" {
|
||||||
|
targetUrl = targetUrl + "&language=" + language
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, errs.ErrParameterInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
director := func(req *http.Request) {
|
||||||
|
zoomLevel := c.Param("zoomLevel")
|
||||||
|
coordinateX := c.Param("coordinateX")
|
||||||
|
fileName := c.Param("fileName")
|
||||||
|
|
||||||
|
imageRawUrl := fmt.Sprintf(targetUrl, zoomLevel, coordinateX, fileName)
|
||||||
|
imageUrl, _ := url.Parse(imageRawUrl)
|
||||||
|
|
||||||
|
req.URL = imageUrl
|
||||||
|
req.RequestURI = req.URL.RequestURI()
|
||||||
|
req.Host = imageUrl.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httputil.ReverseProxy{Director: director}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
|
"github.com/boombuler/barcode"
|
||||||
|
"github.com/boombuler/barcode/qr"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
qrCodeDefaultWidth int = 320
|
||||||
|
qrCodeDefaultHeight int = 320
|
||||||
|
)
|
||||||
|
|
||||||
|
// QrCodesApi represents qrcode generator api
|
||||||
|
type QrCodesApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a qrcode generator api singleton instance
|
||||||
|
var (
|
||||||
|
QrCodes = &QrCodesApi{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// MobileUrlQrCodeHandler returns a mobile url qr code image
|
||||||
|
func (a *QrCodesApi) MobileUrlQrCodeHandler(c *core.Context) ([]byte, string, *errs.Error) {
|
||||||
|
fullUrl := settings.Container.Current.RootUrl + "mobile"
|
||||||
|
data, err := a.generateUrlQrCode(c, fullUrl)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *QrCodesApi) generateUrlQrCode(c *core.Context, url string) ([]byte, *errs.Error) {
|
||||||
|
qrCodeImg, _ := qr.Encode(url, qr.M, qr.Auto)
|
||||||
|
qrCodeImg, _ = barcode.Scale(qrCodeImg, qrCodeDefaultWidth, qrCodeDefaultHeight)
|
||||||
|
imgData := &bytes.Buffer{}
|
||||||
|
|
||||||
|
if err := png.Encode(imgData, qrCodeImg); err != nil {
|
||||||
|
return nil, errs.ErrOperationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
return imgData.Bytes(), nil
|
||||||
|
}
|
||||||
+22
-28
@@ -26,13 +26,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TokenListHandler returns available token list of current user
|
// TokenListHandler returns available token list of current user
|
||||||
func (a *TokensApi) TokenListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
tokens, err := a.tokens.GetAllUnexpiredNormalTokensByUid(uid)
|
tokens, err := a.tokens.GetAllUnexpiredNormalTokensByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[tokens.TokenListHandler] failed to get all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[tokens.TokenListHandler] failed to get all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenResps := make(models.TokenInfoResponseSlice, len(tokens))
|
tokenResps := make(models.TokenInfoResponseSlice, len(tokens))
|
||||||
@@ -48,7 +48,7 @@ func (a *TokensApi) TokenListHandler(c *core.Context) (interface{}, *errs.Error)
|
|||||||
ExpiredAt: token.ExpiredUnixTime,
|
ExpiredAt: token.ExpiredUnixTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.Int64ToString(token.Uid) == claims.Id && utils.Int64ToString(token.UserTokenId) == claims.UserTokenId && token.CreatedUnixTime == claims.IssuedAt {
|
if token.Uid == claims.Uid && utils.Int64ToString(token.UserTokenId) == claims.UserTokenId && token.CreatedUnixTime == claims.IssuedAt {
|
||||||
tokenResp.IsCurrent = true
|
tokenResp.IsCurrent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,18 +61,11 @@ func (a *TokensApi) TokenListHandler(c *core.Context) (interface{}, *errs.Error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TokenRevokeCurrentHandler revokes current token of current user
|
// TokenRevokeCurrentHandler revokes current token of current user
|
||||||
func (a *TokensApi) TokenRevokeCurrentHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenRevokeCurrentHandler(c *core.Context) (any, *errs.Error) {
|
||||||
_, claims, err := a.tokens.ParseToken(c)
|
_, claims, err := a.tokens.ParseTokenByHeader(c)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
return nil, errs.Or(err, errs.NewIncompleteOrIncorrectSubmissionError(err))
|
||||||
}
|
|
||||||
|
|
||||||
uid, err := utils.StringToInt64(claims.Id)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.TokenRevokeCurrentHandler] parse user id failed, because %s", err.Error())
|
|
||||||
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
||||||
@@ -83,25 +76,25 @@ func (a *TokensApi) TokenRevokeCurrentHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenRecord := &models.TokenRecord{
|
tokenRecord := &models.TokenRecord{
|
||||||
Uid: uid,
|
Uid: claims.Uid,
|
||||||
UserTokenId: userTokenId,
|
UserTokenId: userTokenId,
|
||||||
CreatedUnixTime: claims.IssuedAt,
|
CreatedUnixTime: claims.IssuedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenId := a.tokens.GenerateTokenId(tokenRecord)
|
tokenId := a.tokens.GenerateTokenId(tokenRecord)
|
||||||
err = a.tokens.DeleteToken(tokenRecord)
|
err = a.tokens.DeleteToken(c, tokenRecord)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[token.TokenRevokeCurrentHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", tokenId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[token.TokenRevokeCurrentHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", tokenId, claims.Uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[token.TokenRevokeCurrentHandler] user \"uid:%d\" has revoked token \"id:%s\"", uid, tokenId)
|
log.InfofWithRequestId(c, "[token.TokenRevokeCurrentHandler] user \"uid:%d\" has revoked token \"id:%s\"", claims.Uid, tokenId)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenRevokeHandler revokes specific token of current user
|
// TokenRevokeHandler revokes specific token of current user
|
||||||
func (a *TokensApi) TokenRevokeHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenRevokeHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tokenRevokeReq models.TokenRevokeRequest
|
var tokenRevokeReq models.TokenRevokeRequest
|
||||||
err := c.ShouldBindJSON(&tokenRevokeReq)
|
err := c.ShouldBindJSON(&tokenRevokeReq)
|
||||||
|
|
||||||
@@ -127,7 +120,7 @@ func (a *TokensApi) TokenRevokeHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
return nil, errs.ErrInvalidTokenId
|
return nil, errs.ErrInvalidTokenId
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.tokens.DeleteToken(tokenRecord)
|
err = a.tokens.DeleteToken(c, tokenRecord)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[token.TokenRevokeHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", tokenRevokeReq.TokenId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[token.TokenRevokeHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", tokenRevokeReq.TokenId, uid, err.Error())
|
||||||
@@ -139,13 +132,13 @@ func (a *TokensApi) TokenRevokeHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TokenRevokeAllHandler revokes all tokens of current user except current token
|
// TokenRevokeAllHandler revokes all tokens of current user except current token
|
||||||
func (a *TokensApi) TokenRevokeAllHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenRevokeAllHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
tokens, err := a.tokens.GetAllTokensByUid(uid)
|
tokens, err := a.tokens.GetAllTokensByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[tokens.TokenRevokeAllHandler] failed to get all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[tokens.TokenRevokeAllHandler] failed to get all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
claims := c.GetTokenClaims()
|
claims := c.GetTokenClaims()
|
||||||
@@ -154,7 +147,7 @@ func (a *TokensApi) TokenRevokeAllHandler(c *core.Context) (interface{}, *errs.E
|
|||||||
for i := 0; i < len(tokens); i++ {
|
for i := 0; i < len(tokens); i++ {
|
||||||
token := tokens[i]
|
token := tokens[i]
|
||||||
|
|
||||||
if utils.Int64ToString(token.Uid) == claims.Id && utils.Int64ToString(token.UserTokenId) == claims.UserTokenId && token.CreatedUnixTime == claims.IssuedAt {
|
if token.Uid == claims.Uid && utils.Int64ToString(token.UserTokenId) == claims.UserTokenId && token.CreatedUnixTime == claims.IssuedAt {
|
||||||
currentTokenIndex = i
|
currentTokenIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -162,7 +155,7 @@ func (a *TokensApi) TokenRevokeAllHandler(c *core.Context) (interface{}, *errs.E
|
|||||||
|
|
||||||
tokens = append(tokens[:currentTokenIndex], tokens[currentTokenIndex+1:]...)
|
tokens = append(tokens[:currentTokenIndex], tokens[currentTokenIndex+1:]...)
|
||||||
|
|
||||||
err = a.tokens.DeleteTokens(uid, tokens)
|
err = a.tokens.DeleteTokens(c, uid, tokens)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[token.TokenRevokeAllHandler] failed to revoke all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[token.TokenRevokeAllHandler] failed to revoke all tokens for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -174,16 +167,16 @@ func (a *TokensApi) TokenRevokeAllHandler(c *core.Context) (interface{}, *errs.E
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TokenRefreshHandler refresh current token of current user
|
// TokenRefreshHandler refresh current token of current user
|
||||||
func (a *TokensApi) TokenRefreshHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenRefreshHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[token.TokenRefreshHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
|
log.WarnfWithRequestId(c, "[token.TokenRefreshHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
|
||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[token.TokenRefreshHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[token.TokenRefreshHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -198,6 +191,7 @@ func (a *TokensApi) TokenRefreshHandler(c *core.Context) (interface{}, *errs.Err
|
|||||||
CreatedUnixTime: oldTokenClaims.IssuedAt,
|
CreatedUnixTime: oldTokenClaims.IssuedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[token.TokenRefreshHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[token.TokenRefreshHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package api
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
@@ -23,7 +25,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CategoryListHandler returns transaction category list of current user
|
// CategoryListHandler returns transaction category list of current user
|
||||||
func (a *TransactionCategoriesApi) CategoryListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryListReq models.TransactionCategoryListRequest
|
var categoryListReq models.TransactionCategoryListRequest
|
||||||
err := c.ShouldBindQuery(&categoryListReq)
|
err := c.ShouldBindQuery(&categoryListReq)
|
||||||
|
|
||||||
@@ -33,18 +35,18 @@ func (a *TransactionCategoriesApi) CategoryListHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
categories, err := a.categories.GetAllCategoriesByUid(uid, categoryListReq.Type, categoryListReq.ParentId)
|
categories, err := a.categories.GetAllCategoriesByUid(c, uid, categoryListReq.Type, categoryListReq.ParentId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryListHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryListHandler] failed to get categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.getTransactionCategoryListByTypeResponse(categories, categoryListReq.ParentId)
|
return a.getTransactionCategoryListByTypeResponse(categories, categoryListReq.ParentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CategoryGetHandler returns one specific transaction category of current user
|
// CategoryGetHandler returns one specific transaction category of current user
|
||||||
func (a *TransactionCategoriesApi) CategoryGetHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryGetHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryGetReq models.TransactionCategoryGetRequest
|
var categoryGetReq models.TransactionCategoryGetRequest
|
||||||
err := c.ShouldBindQuery(&categoryGetReq)
|
err := c.ShouldBindQuery(&categoryGetReq)
|
||||||
|
|
||||||
@@ -54,11 +56,11 @@ func (a *TransactionCategoriesApi) CategoryGetHandler(c *core.Context) (interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
category, err := a.categories.GetCategoryByCategoryId(uid, categoryGetReq.Id)
|
category, err := a.categories.GetCategoryByCategoryId(c, uid, categoryGetReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryGetHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryGetReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryGetHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryGetReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
categoryResp := category.ToTransactionCategoryInfoResponse()
|
categoryResp := category.ToTransactionCategoryInfoResponse()
|
||||||
@@ -67,7 +69,7 @@ func (a *TransactionCategoriesApi) CategoryGetHandler(c *core.Context) (interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CategoryCreateHandler saves a new transaction category by request parameters for current user
|
// CategoryCreateHandler saves a new transaction category by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryCreateReq models.TransactionCategoryCreateRequest
|
var categoryCreateReq models.TransactionCategoryCreateRequest
|
||||||
err := c.ShouldBindJSON(&categoryCreateReq)
|
err := c.ShouldBindJSON(&categoryCreateReq)
|
||||||
|
|
||||||
@@ -84,7 +86,7 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (inter
|
|||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
if categoryCreateReq.ParentId > 0 {
|
if categoryCreateReq.ParentId > 0 {
|
||||||
parentCategory, err := a.categories.GetCategoryByCategoryId(uid, categoryCreateReq.ParentId)
|
parentCategory, err := a.categories.GetCategoryByCategoryId(c, uid, categoryCreateReq.ParentId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get parent category \"id:%d\" for user \"uid:%d\", because %s", categoryCreateReq.ParentId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get parent category \"id:%d\" for user \"uid:%d\", because %s", categoryCreateReq.ParentId, uid, err.Error())
|
||||||
@@ -102,22 +104,22 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxOrderId int
|
var maxOrderId int32
|
||||||
|
|
||||||
if categoryCreateReq.ParentId <= 0 {
|
if categoryCreateReq.ParentId <= 0 {
|
||||||
maxOrderId, err = a.categories.GetMaxDisplayOrder(uid, categoryCreateReq.Type)
|
maxOrderId, err = a.categories.GetMaxDisplayOrder(c, uid, categoryCreateReq.Type)
|
||||||
} else {
|
} else {
|
||||||
maxOrderId, err = a.categories.GetMaxSubCategoryDisplayOrder(uid, categoryCreateReq.Type, categoryCreateReq.ParentId)
|
maxOrderId, err = a.categories.GetMaxSubCategoryDisplayOrder(c, uid, categoryCreateReq.Type, categoryCreateReq.ParentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
category := a.createNewCategoryModel(uid, &categoryCreateReq, maxOrderId+1)
|
category := a.createNewCategoryModel(uid, &categoryCreateReq, maxOrderId+1)
|
||||||
|
|
||||||
err = a.categories.CreateCategory(category)
|
err = a.categories.CreateCategory(c, category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to create category \"id:%d\" for user \"uid:%d\", because %s", category.CategoryId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateHandler] failed to create category \"id:%d\" for user \"uid:%d\", because %s", category.CategoryId, uid, err.Error())
|
||||||
@@ -132,9 +134,9 @@ func (a *TransactionCategoriesApi) CategoryCreateHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CategoryCreateBatchHandler saves some new transaction category by request parameters for current user
|
// CategoryCreateBatchHandler saves some new transaction category by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryCreateBatchHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryCreateBatchHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryCreateBatchReq models.TransactionCategoryCreateBatchRequest
|
var categoryCreateBatchReq models.TransactionCategoryCreateBatchRequest
|
||||||
err := c.ShouldBindJSON(&categoryCreateBatchReq)
|
err := c.ShouldBindBodyWith(&categoryCreateBatchReq, binding.JSON)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] parse request failed, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] parse request failed, because %s", err.Error())
|
||||||
@@ -143,58 +145,17 @@ func (a *TransactionCategoriesApi) CategoryCreateBatchHandler(c *core.Context) (
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
categoryTypeMaxOrderMap := make(map[models.TransactionCategoryType]int)
|
categories, err := a.createBatchCategories(c, uid, &categoryCreateBatchReq)
|
||||||
categoriesMap := make(map[*models.TransactionCategory][]*models.TransactionCategory)
|
|
||||||
categoriesMap[nil] = make([]*models.TransactionCategory, len(categoryCreateBatchReq.Categories))
|
|
||||||
totalCount := 0
|
|
||||||
|
|
||||||
for i := 0; i < len(categoryCreateBatchReq.Categories); i++ {
|
|
||||||
categoryCreateReq := categoryCreateBatchReq.Categories[i]
|
|
||||||
var maxOrderId, exists = categoryTypeMaxOrderMap[categoryCreateReq.Type]
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
maxOrderId, err = a.categories.GetMaxDisplayOrder(uid, categoryCreateReq.Type)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
|
||||||
return nil, errs.ErrOperationFailed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
category := a.createNewCategoryModel(uid, &models.TransactionCategoryCreateRequest{
|
|
||||||
Name: categoryCreateReq.Name,
|
|
||||||
Type: categoryCreateReq.Type,
|
|
||||||
Icon: categoryCreateReq.Icon,
|
|
||||||
Color: categoryCreateReq.Color,
|
|
||||||
}, maxOrderId+1)
|
|
||||||
|
|
||||||
categoriesMap[category] = make([]*models.TransactionCategory, len(categoryCreateReq.SubCategories))
|
|
||||||
|
|
||||||
for j := 0; j < len(categoryCreateReq.SubCategories); j++ {
|
|
||||||
subCategory := a.createNewCategoryModel(uid, categoryCreateReq.SubCategories[j], j+1)
|
|
||||||
categoriesMap[category][j] = subCategory
|
|
||||||
totalCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
categoriesMap[nil][i] = category
|
|
||||||
categoryTypeMaxOrderMap[categoryCreateReq.Type] = maxOrderId + 1
|
|
||||||
totalCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
categories, err := a.categories.CreateCategories(uid, categoriesMap)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] failed to create categories for user \"uid:%d\", because %s", uid, err.Error())
|
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] user \"uid:%d\" has created categoroies successfully", uid)
|
|
||||||
|
|
||||||
return a.getTransactionCategoryListByTypeResponse(categories, 0)
|
return a.getTransactionCategoryListByTypeResponse(categories, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CategoryModifyHandler saves an existed transaction category by request parameters for current user
|
// CategoryModifyHandler saves an existed transaction category by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryModifyReq models.TransactionCategoryModifyRequest
|
var categoryModifyReq models.TransactionCategoryModifyRequest
|
||||||
err := c.ShouldBindJSON(&categoryModifyReq)
|
err := c.ShouldBindJSON(&categoryModifyReq)
|
||||||
|
|
||||||
@@ -204,11 +165,11 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
category, err := a.categories.GetCategoryByCategoryId(uid, categoryModifyReq.Id)
|
category, err := a.categories.GetCategoryByCategoryId(c, uid, categoryModifyReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to get category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
newCategory := &models.TransactionCategory{
|
newCategory := &models.TransactionCategory{
|
||||||
@@ -229,7 +190,7 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (inter
|
|||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.categories.ModifyCategory(newCategory)
|
err = a.categories.ModifyCategory(c, newCategory)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to update category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryModifyHandler] failed to update category \"id:%d\" for user \"uid:%d\", because %s", categoryModifyReq.Id, uid, err.Error())
|
||||||
@@ -247,7 +208,7 @@ func (a *TransactionCategoriesApi) CategoryModifyHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CategoryHideHandler hides an existed transaction category by request parameters for current user
|
// CategoryHideHandler hides an existed transaction category by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryHideHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryHideHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryHideReq models.TransactionCategoryHideRequest
|
var categoryHideReq models.TransactionCategoryHideRequest
|
||||||
err := c.ShouldBindJSON(&categoryHideReq)
|
err := c.ShouldBindJSON(&categoryHideReq)
|
||||||
|
|
||||||
@@ -257,7 +218,7 @@ func (a *TransactionCategoriesApi) CategoryHideHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.categories.HideCategory(uid, []int64{categoryHideReq.Id}, categoryHideReq.Hidden)
|
err = a.categories.HideCategory(c, uid, []int64{categoryHideReq.Id}, categoryHideReq.Hidden)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryHideHandler] failed to hide category \"id:%d\" for user \"uid:%d\", because %s", categoryHideReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryHideHandler] failed to hide category \"id:%d\" for user \"uid:%d\", because %s", categoryHideReq.Id, uid, err.Error())
|
||||||
@@ -269,7 +230,7 @@ func (a *TransactionCategoriesApi) CategoryHideHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CategoryMoveHandler moves display order of existed transaction categories by request parameters for current user
|
// CategoryMoveHandler moves display order of existed transaction categories by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryMoveHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryMoveHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryMoveReq models.TransactionCategoryMoveRequest
|
var categoryMoveReq models.TransactionCategoryMoveRequest
|
||||||
err := c.ShouldBindJSON(&categoryMoveReq)
|
err := c.ShouldBindJSON(&categoryMoveReq)
|
||||||
|
|
||||||
@@ -292,7 +253,7 @@ func (a *TransactionCategoriesApi) CategoryMoveHandler(c *core.Context) (interfa
|
|||||||
categories[i] = category
|
categories[i] = category
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.categories.ModifyCategoryDisplayOrders(uid, categories)
|
err = a.categories.ModifyCategoryDisplayOrders(c, uid, categories)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryMoveHandler] failed to move categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryMoveHandler] failed to move categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -304,7 +265,7 @@ func (a *TransactionCategoriesApi) CategoryMoveHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CategoryDeleteHandler deletes an existed transaction category by request parameters for current user
|
// CategoryDeleteHandler deletes an existed transaction category by request parameters for current user
|
||||||
func (a *TransactionCategoriesApi) CategoryDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionCategoriesApi) CategoryDeleteHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var categoryDeleteReq models.TransactionCategoryDeleteRequest
|
var categoryDeleteReq models.TransactionCategoryDeleteRequest
|
||||||
err := c.ShouldBindJSON(&categoryDeleteReq)
|
err := c.ShouldBindJSON(&categoryDeleteReq)
|
||||||
|
|
||||||
@@ -314,7 +275,7 @@ func (a *TransactionCategoriesApi) CategoryDeleteHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.categories.DeleteCategory(uid, categoryDeleteReq.Id)
|
err = a.categories.DeleteCategory(c, uid, categoryDeleteReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryDeleteHandler] failed to delete category \"id:%d\" for user \"uid:%d\", because %s", categoryDeleteReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryDeleteHandler] failed to delete category \"id:%d\" for user \"uid:%d\", because %s", categoryDeleteReq.Id, uid, err.Error())
|
||||||
@@ -325,7 +286,59 @@ func (a *TransactionCategoriesApi) CategoryDeleteHandler(c *core.Context) (inter
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TransactionCategoriesApi) createNewCategoryModel(uid int64, categoryCreateReq *models.TransactionCategoryCreateRequest, order int) *models.TransactionCategory {
|
func (a *TransactionCategoriesApi) createBatchCategories(c *core.Context, uid int64, categoryCreateBatchReq *models.TransactionCategoryCreateBatchRequest) ([]*models.TransactionCategory, error) {
|
||||||
|
var err error
|
||||||
|
categoryTypeMaxOrderMap := make(map[models.TransactionCategoryType]int32)
|
||||||
|
categoriesMap := make(map[*models.TransactionCategory][]*models.TransactionCategory)
|
||||||
|
categoriesMap[nil] = make([]*models.TransactionCategory, len(categoryCreateBatchReq.Categories))
|
||||||
|
totalCount := 0
|
||||||
|
|
||||||
|
for i := 0; i < len(categoryCreateBatchReq.Categories); i++ {
|
||||||
|
categoryCreateReq := categoryCreateBatchReq.Categories[i]
|
||||||
|
var maxOrderId, exists = categoryTypeMaxOrderMap[categoryCreateReq.Type]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
maxOrderId, err = a.categories.GetMaxDisplayOrder(c, uid, categoryCreateReq.Type)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transaction_categories.CategoryCreateBatchHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
category := a.createNewCategoryModel(uid, &models.TransactionCategoryCreateRequest{
|
||||||
|
Name: categoryCreateReq.Name,
|
||||||
|
Type: categoryCreateReq.Type,
|
||||||
|
Icon: categoryCreateReq.Icon,
|
||||||
|
Color: categoryCreateReq.Color,
|
||||||
|
}, maxOrderId+1)
|
||||||
|
|
||||||
|
categoriesMap[category] = make([]*models.TransactionCategory, len(categoryCreateReq.SubCategories))
|
||||||
|
|
||||||
|
for j := int32(0); j < int32(len(categoryCreateReq.SubCategories)); j++ {
|
||||||
|
subCategory := a.createNewCategoryModel(uid, categoryCreateReq.SubCategories[j], j+1)
|
||||||
|
categoriesMap[category][j] = subCategory
|
||||||
|
totalCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
categoriesMap[nil][i] = category
|
||||||
|
categoryTypeMaxOrderMap[categoryCreateReq.Type] = maxOrderId + 1
|
||||||
|
totalCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
categories, err := a.categories.CreateCategories(c, uid, categoriesMap)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transaction_categories.createBatchCategories] failed to create categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.InfofWithRequestId(c, "[transaction_categories.createBatchCategories] user \"uid:%d\" has created categories successfully", uid)
|
||||||
|
|
||||||
|
return categories, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *TransactionCategoriesApi) createNewCategoryModel(uid int64, categoryCreateReq *models.TransactionCategoryCreateRequest, order int32) *models.TransactionCategory {
|
||||||
return &models.TransactionCategory{
|
return &models.TransactionCategory{
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
Name: categoryCreateReq.Name,
|
Name: categoryCreateReq.Name,
|
||||||
|
|||||||
+21
-21
@@ -23,13 +23,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TagListHandler returns transaction tag list of current user
|
// TagListHandler returns transaction tag list of current user
|
||||||
func (a *TransactionTagsApi) TagListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
tags, err := a.tags.GetAllTagsByUid(uid)
|
tags, err := a.tags.GetAllTagsByUid(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagListHandler] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagListHandler] failed to get tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
tagResps := make(models.TransactionTagInfoResponseSlice, len(tags))
|
tagResps := make(models.TransactionTagInfoResponseSlice, len(tags))
|
||||||
@@ -44,7 +44,7 @@ func (a *TransactionTagsApi) TagListHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagGetHandler returns one specific transaction tag of current user
|
// TagGetHandler returns one specific transaction tag of current user
|
||||||
func (a *TransactionTagsApi) TagGetHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagGetHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagGetReq models.TransactionTagGetRequest
|
var tagGetReq models.TransactionTagGetRequest
|
||||||
err := c.ShouldBindQuery(&tagGetReq)
|
err := c.ShouldBindQuery(&tagGetReq)
|
||||||
|
|
||||||
@@ -54,11 +54,11 @@ func (a *TransactionTagsApi) TagGetHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
tag, err := a.tags.GetTagByTagId(uid, tagGetReq.Id)
|
tag, err := a.tags.GetTagByTagId(c, uid, tagGetReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagGetHandler] failed to get tag \"id:%d\" for user \"uid:%d\", because %s", tagGetReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagGetHandler] failed to get tag \"id:%d\" for user \"uid:%d\", because %s", tagGetReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
tagResp := tag.ToTransactionTagInfoResponse()
|
tagResp := tag.ToTransactionTagInfoResponse()
|
||||||
@@ -67,7 +67,7 @@ func (a *TransactionTagsApi) TagGetHandler(c *core.Context) (interface{}, *errs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagCreateHandler saves a new transaction tag by request parameters for current user
|
// TagCreateHandler saves a new transaction tag by request parameters for current user
|
||||||
func (a *TransactionTagsApi) TagCreateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagCreateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagCreateReq models.TransactionTagCreateRequest
|
var tagCreateReq models.TransactionTagCreateRequest
|
||||||
err := c.ShouldBindJSON(&tagCreateReq)
|
err := c.ShouldBindJSON(&tagCreateReq)
|
||||||
|
|
||||||
@@ -78,16 +78,16 @@ func (a *TransactionTagsApi) TagCreateHandler(c *core.Context) (interface{}, *er
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
maxOrderId, err := a.tags.GetMaxDisplayOrder(uid)
|
maxOrderId, err := a.tags.GetMaxDisplayOrder(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagCreateHandler] failed to get max display order for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := a.createNewTagModel(uid, &tagCreateReq, maxOrderId+1)
|
tag := a.createNewTagModel(uid, &tagCreateReq, maxOrderId+1)
|
||||||
|
|
||||||
err = a.tags.CreateTag(tag)
|
err = a.tags.CreateTag(c, tag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagCreateHandler] failed to create tag \"id:%d\" for user \"uid:%d\", because %s", tag.TagId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagCreateHandler] failed to create tag \"id:%d\" for user \"uid:%d\", because %s", tag.TagId, uid, err.Error())
|
||||||
@@ -102,7 +102,7 @@ func (a *TransactionTagsApi) TagCreateHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagModifyHandler saves an existed transaction tag by request parameters for current user
|
// TagModifyHandler saves an existed transaction tag by request parameters for current user
|
||||||
func (a *TransactionTagsApi) TagModifyHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagModifyHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagModifyReq models.TransactionTagModifyRequest
|
var tagModifyReq models.TransactionTagModifyRequest
|
||||||
err := c.ShouldBindJSON(&tagModifyReq)
|
err := c.ShouldBindJSON(&tagModifyReq)
|
||||||
|
|
||||||
@@ -112,11 +112,11 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
tag, err := a.tags.GetTagByTagId(uid, tagModifyReq.Id)
|
tag, err := a.tags.GetTagByTagId(c, uid, tagModifyReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagModifyHandler] failed to get tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagModifyHandler] failed to get tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTag := &models.TransactionTag{
|
newTag := &models.TransactionTag{
|
||||||
@@ -129,7 +129,7 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.Context) (interface{}, *er
|
|||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.tags.ModifyTag(newTag)
|
err = a.tags.ModifyTag(c, newTag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagModifyHandler] failed to update tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagModifyHandler] failed to update tag \"id:%d\" for user \"uid:%d\", because %s", tagModifyReq.Id, uid, err.Error())
|
||||||
@@ -145,7 +145,7 @@ func (a *TransactionTagsApi) TagModifyHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagHideHandler hides an transaction tag by request parameters for current user
|
// TagHideHandler hides an transaction tag by request parameters for current user
|
||||||
func (a *TransactionTagsApi) TagHideHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagHideHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagHideReq models.TransactionTagHideRequest
|
var tagHideReq models.TransactionTagHideRequest
|
||||||
err := c.ShouldBindJSON(&tagHideReq)
|
err := c.ShouldBindJSON(&tagHideReq)
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ func (a *TransactionTagsApi) TagHideHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.tags.HideTag(uid, []int64{tagHideReq.Id}, tagHideReq.Hidden)
|
err = a.tags.HideTag(c, uid, []int64{tagHideReq.Id}, tagHideReq.Hidden)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.CategoryHideHandler] failed to hide tag \"id:%d\" for user \"uid:%d\", because %s", tagHideReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.CategoryHideHandler] failed to hide tag \"id:%d\" for user \"uid:%d\", because %s", tagHideReq.Id, uid, err.Error())
|
||||||
@@ -167,7 +167,7 @@ func (a *TransactionTagsApi) TagHideHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagMoveHandler moves display order of existed transaction tags by request parameters for current user
|
// TagMoveHandler moves display order of existed transaction tags by request parameters for current user
|
||||||
func (a *TransactionTagsApi) TagMoveHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagMoveHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagMoveReq models.TransactionTagMoveRequest
|
var tagMoveReq models.TransactionTagMoveRequest
|
||||||
err := c.ShouldBindJSON(&tagMoveReq)
|
err := c.ShouldBindJSON(&tagMoveReq)
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ func (a *TransactionTagsApi) TagMoveHandler(c *core.Context) (interface{}, *errs
|
|||||||
tags[i] = tag
|
tags[i] = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.tags.ModifyTagDisplayOrders(uid, tags)
|
err = a.tags.ModifyTagDisplayOrders(c, uid, tags)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.CategoryMoveHandler] failed to move tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.CategoryMoveHandler] failed to move tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -202,7 +202,7 @@ func (a *TransactionTagsApi) TagMoveHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TagDeleteHandler deletes an existed transaction tag by request parameters for current user
|
// TagDeleteHandler deletes an existed transaction tag by request parameters for current user
|
||||||
func (a *TransactionTagsApi) TagDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionTagsApi) TagDeleteHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var tagDeleteReq models.TransactionTagDeleteRequest
|
var tagDeleteReq models.TransactionTagDeleteRequest
|
||||||
err := c.ShouldBindJSON(&tagDeleteReq)
|
err := c.ShouldBindJSON(&tagDeleteReq)
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@ func (a *TransactionTagsApi) TagDeleteHandler(c *core.Context) (interface{}, *er
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
err = a.tags.DeleteTag(uid, tagDeleteReq.Id)
|
err = a.tags.DeleteTag(c, uid, tagDeleteReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transaction_tags.TagDeleteHandler] failed to delete tag \"id:%d\" for user \"uid:%d\", because %s", tagDeleteReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transaction_tags.TagDeleteHandler] failed to delete tag \"id:%d\" for user \"uid:%d\", because %s", tagDeleteReq.Id, uid, err.Error())
|
||||||
@@ -223,7 +223,7 @@ func (a *TransactionTagsApi) TagDeleteHandler(c *core.Context) (interface{}, *er
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TransactionTagsApi) createNewTagModel(uid int64, tagCreateReq *models.TransactionTagCreateRequest, order int) *models.TransactionTag {
|
func (a *TransactionTagsApi) createNewTagModel(uid int64, tagCreateReq *models.TransactionTagCreateRequest, order int32) *models.TransactionTag {
|
||||||
return &models.TransactionTag{
|
return &models.TransactionTag{
|
||||||
Uid: uid,
|
Uid: uid,
|
||||||
Name: tagCreateReq.Name,
|
Name: tagCreateReq.Name,
|
||||||
|
|||||||
+163
-77
@@ -4,6 +4,8 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
@@ -35,7 +37,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TransactionCountHandler returns transaction total count of current user
|
// TransactionCountHandler returns transaction total count of current user
|
||||||
func (a *TransactionsApi) TransactionCountHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionCountHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionCountReq models.TransactionCountRequest
|
var transactionCountReq models.TransactionCountRequest
|
||||||
err := c.ShouldBindQuery(&transactionCountReq)
|
err := c.ShouldBindQuery(&transactionCountReq)
|
||||||
|
|
||||||
@@ -46,14 +48,26 @@ func (a *TransactionsApi) TransactionCountHandler(c *core.Context) (interface{},
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
allCategoryIds, err := a.getCategoryAndSubCategoryIds(transactionCountReq.CategoryId, uid)
|
allAccountIds, err := a.getAccountOrSubAccountIds(c, transactionCountReq.AccountId, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionCountHandler] get account error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
allCategoryIds, err := a.getCategoryOrSubCategoryIds(c, transactionCountReq.CategoryId, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[transactions.TransactionCountHandler] get transaction category error, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[transactions.TransactionCountHandler] get transaction category error, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCount, err := a.transactions.GetTransactionCount(uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, transactionCountReq.AccountId, transactionCountReq.Keyword)
|
totalCount, err := a.transactions.GetTransactionCount(c, uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, allAccountIds, transactionCountReq.Keyword)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transactions.TransactionCountHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
countResp := &models.TransactionCountResponse{
|
countResp := &models.TransactionCountResponse{
|
||||||
TotalCount: totalCount,
|
TotalCount: totalCount,
|
||||||
@@ -63,7 +77,7 @@ func (a *TransactionsApi) TransactionCountHandler(c *core.Context) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionListHandler returns transaction list of current user
|
// TransactionListHandler returns transaction list of current user
|
||||||
func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionListReq models.TransactionListByMaxTimeRequest
|
var transactionListReq models.TransactionListByMaxTimeRequest
|
||||||
err := c.ShouldBindQuery(&transactionListReq)
|
err := c.ShouldBindQuery(&transactionListReq)
|
||||||
|
|
||||||
@@ -80,7 +94,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -90,24 +104,42 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{},
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
allCategoryIds, err := a.getCategoryAndSubCategoryIds(transactionListReq.CategoryId, uid)
|
allAccountIds, err := a.getAccountOrSubAccountIds(c, transactionListReq.AccountId, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionListHandler] get account error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
allCategoryIds, err := a.getCategoryOrSubCategoryIds(c, transactionListReq.CategoryId, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[transactions.TransactionListHandler] get transaction category error, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[transactions.TransactionListHandler] get transaction category error, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions, err := a.transactions.GetTransactionsByMaxTime(uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, transactionListReq.AccountId, transactionListReq.Keyword, transactionListReq.Count+1, true)
|
var totalCount int64
|
||||||
|
|
||||||
|
if transactionListReq.WithCount {
|
||||||
|
totalCount, err = a.transactions.GetTransactionCount(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, transactionListReq.Keyword)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions, err := a.transactions.GetTransactionsByMaxTime(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, true, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMore := false
|
hasMore := false
|
||||||
var nextTimeSequenceId *int64
|
var nextTimeSequenceId *int64
|
||||||
|
|
||||||
if len(transactions) > transactionListReq.Count {
|
if len(transactions) > int(transactionListReq.Count) {
|
||||||
hasMore = true
|
hasMore = true
|
||||||
nextTimeSequenceId = &transactions[transactionListReq.Count].TransactionTime
|
nextTimeSequenceId = &transactions[transactionListReq.Count].TransactionTime
|
||||||
transactions = transactions[:transactionListReq.Count]
|
transactions = transactions[:transactionListReq.Count]
|
||||||
@@ -128,11 +160,15 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{},
|
|||||||
transactionResps.NextTimeSequenceId = nextTimeSequenceId
|
transactionResps.NextTimeSequenceId = nextTimeSequenceId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if transactionListReq.WithCount {
|
||||||
|
transactionResps.TotalCount = &totalCount
|
||||||
|
}
|
||||||
|
|
||||||
return transactionResps, nil
|
return transactionResps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionMonthListHandler returns transaction list of current user by month
|
// TransactionMonthListHandler returns all transaction list of current user by month
|
||||||
func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionListReq models.TransactionListInMonthByPageRequest
|
var transactionListReq models.TransactionListInMonthByPageRequest
|
||||||
err := c.ShouldBindQuery(&transactionListReq)
|
err := c.ShouldBindQuery(&transactionListReq)
|
||||||
|
|
||||||
@@ -149,7 +185,7 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -159,25 +195,25 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
allCategoryIds, err := a.getCategoryAndSubCategoryIds(transactionListReq.CategoryId, uid)
|
allAccountIds, err := a.getAccountOrSubAccountIds(c, transactionListReq.AccountId, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionMonthListHandler] get account error, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
allCategoryIds, err := a.getCategoryOrSubCategoryIds(c, transactionListReq.CategoryId, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[transactions.TransactionMonthListHandler] get transaction category error, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[transactions.TransactionMonthListHandler] get transaction category error, because %s", err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions, err := a.transactions.GetTransactionsInMonthByPage(uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, transactionListReq.AccountId, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, utcOffset)
|
transactions, err := a.transactions.GetTransactionsInMonthByPage(c, uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, allAccountIds, transactionListReq.Keyword)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
|
||||||
|
|
||||||
totalCount, err := a.transactions.GetMonthTransactionCount(uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, transactionListReq.AccountId, transactionListReq.Keyword, utcOffset)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transaction count in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
|
||||||
return nil, errs.ErrOperationFailed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionResult, err := a.getTransactionListResult(c, user, transactions, utcOffset, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
transactionResult, err := a.getTransactionListResult(c, user, transactions, utcOffset, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag)
|
||||||
@@ -189,14 +225,14 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac
|
|||||||
|
|
||||||
transactionResps := &models.TransactionInfoPageWrapperResponse2{
|
transactionResps := &models.TransactionInfoPageWrapperResponse2{
|
||||||
Items: transactionResult,
|
Items: transactionResult,
|
||||||
TotalCount: totalCount,
|
TotalCount: int64(transactionResult.Len()),
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactionResps, nil
|
return transactionResps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionStatisticsHandler returns transaction statistics of current user
|
// TransactionStatisticsHandler returns transaction statistics of current user
|
||||||
func (a *TransactionsApi) TransactionStatisticsHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionStatisticsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var statisticReq models.TransactionStatisticRequest
|
var statisticReq models.TransactionStatisticRequest
|
||||||
err := c.ShouldBindQuery(&statisticReq)
|
err := c.ShouldBindQuery(&statisticReq)
|
||||||
|
|
||||||
@@ -206,7 +242,12 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(uid, statisticReq.StartTime, statisticReq.EndTime)
|
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
statisticResp := &models.TransactionStatisticResponse{
|
statisticResp := &models.TransactionStatisticResponse{
|
||||||
StartTime: statisticReq.StartTime,
|
StartTime: statisticReq.StartTime,
|
||||||
@@ -228,7 +269,7 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.Context) (interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionAmountsHandler returns transaction amounts of current user
|
// TransactionAmountsHandler returns transaction amounts of current user
|
||||||
func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionAmountsReq models.TransactionAmountsRequest
|
var transactionAmountsReq models.TransactionAmountsRequest
|
||||||
err := c.ShouldBindQuery(&transactionAmountsReq)
|
err := c.ShouldBindQuery(&transactionAmountsReq)
|
||||||
|
|
||||||
@@ -249,31 +290,31 @@ func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (interface{
|
|||||||
return nil, errs.ErrQueryItemsEmpty
|
return nil, errs.ErrQueryItemsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(requestItems) > 5 {
|
if len(requestItems) > 20 {
|
||||||
log.WarnfWithRequestId(c, "[transactions.TransactionAmountsHandler] parse request failed, because there are too many items")
|
log.WarnfWithRequestId(c, "[transactions.TransactionAmountsHandler] parse request failed, because there are too many items")
|
||||||
return nil, errs.ErrQueryItemsTooMuch
|
return nil, errs.ErrQueryItemsTooMuch
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
accounts, err := a.accounts.GetAllAccountsByUid(uid)
|
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
accountMap := a.accounts.GetAccountMapByList(accounts)
|
accountMap := a.accounts.GetAccountMapByList(accounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionAmountsHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionAmountsHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
amountsResp := make(map[string]*models.TransactionAmountsResponseItem)
|
amountsResp := orderedmap.New[string, *models.TransactionAmountsResponseItem]()
|
||||||
|
|
||||||
for i := 0; i < len(requestItems); i++ {
|
for i := 0; i < len(requestItems); i++ {
|
||||||
requestItem := requestItems[i]
|
requestItem := requestItems[i]
|
||||||
|
|
||||||
incomeAmounts, expenseAmounts, err := a.transactions.GetAccountsTotalIncomeAndExpense(uid, requestItem.StartTime, requestItem.EndTime)
|
incomeAmounts, expenseAmounts, err := a.transactions.GetAccountsTotalIncomeAndExpense(c, uid, requestItem.StartTime, requestItem.EndTime)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionAmountsHandler] failed to get transaction amounts item for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionAmountsHandler] failed to get transaction amounts item for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
amountsMap := make(map[string]*models.TransactionAmountsResponseItemAmountInfo)
|
amountsMap := make(map[string]*models.TransactionAmountsResponseItemAmountInfo)
|
||||||
@@ -322,24 +363,26 @@ func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (interface{
|
|||||||
amountsMap[account.Currency] = totalAmounts
|
amountsMap[account.Currency] = totalAmounts
|
||||||
}
|
}
|
||||||
|
|
||||||
allTotalAmounts := make([]*models.TransactionAmountsResponseItemAmountInfo, 0)
|
allTotalAmounts := make(models.TransactionAmountsResponseItemAmountInfoSlice, 0)
|
||||||
|
|
||||||
for _, totalAmounts := range amountsMap {
|
for _, totalAmounts := range amountsMap {
|
||||||
allTotalAmounts = append(allTotalAmounts, totalAmounts)
|
allTotalAmounts = append(allTotalAmounts, totalAmounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
amountsResp[requestItem.Name] = &models.TransactionAmountsResponseItem{
|
sort.Sort(allTotalAmounts)
|
||||||
|
|
||||||
|
amountsResp.Set(requestItem.Name, &models.TransactionAmountsResponseItem{
|
||||||
StartTime: requestItem.StartTime,
|
StartTime: requestItem.StartTime,
|
||||||
EndTime: requestItem.EndTime,
|
EndTime: requestItem.EndTime,
|
||||||
Amounts: allTotalAmounts,
|
Amounts: allTotalAmounts,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return amountsResp, nil
|
return amountsResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionMonthAmountsHandler returns every month transaction amounts of current user
|
// TransactionMonthAmountsHandler returns every month transaction amounts of current user
|
||||||
func (a *TransactionsApi) TransactionMonthAmountsHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionMonthAmountsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionAmountsReq models.TransactionMonthAmountsRequest
|
var transactionAmountsReq models.TransactionMonthAmountsRequest
|
||||||
err := c.ShouldBindQuery(&transactionAmountsReq)
|
err := c.ShouldBindQuery(&transactionAmountsReq)
|
||||||
|
|
||||||
@@ -364,15 +407,21 @@ func (a *TransactionsApi) TransactionMonthAmountsHandler(c *core.Context) (inter
|
|||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
accounts, err := a.accounts.GetAllAccountsByUid(uid)
|
accounts, err := a.accounts.GetAllAccountsByUid(c, uid)
|
||||||
accountMap := a.accounts.GetAccountMapByList(accounts)
|
accountMap := a.accounts.GetAccountMapByList(accounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthAmountsHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthAmountsHandler] failed to get all accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalAmounts, err := a.transactions.GetAccountsMonthTotalIncomeAndExpense(c, uid, startTime, endTime, pageCountForLoadTransactionAmounts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transactions.TransactionMonthAmountsHandler] failed to get accounts month total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalAmounts, err := a.transactions.GetAccountsMonthTotalIncomeAndExpense(uid, startTime, endTime, pageCountForLoadTransactionAmounts)
|
|
||||||
amountsMap := make(map[string]map[string]*models.TransactionAmountsResponseItemAmountInfo)
|
amountsMap := make(map[string]map[string]*models.TransactionAmountsResponseItemAmountInfo)
|
||||||
|
|
||||||
for yearMonth, monthAccountsAmounts := range totalAmounts {
|
for yearMonth, monthAccountsAmounts := range totalAmounts {
|
||||||
@@ -426,12 +475,14 @@ func (a *TransactionsApi) TransactionMonthAmountsHandler(c *core.Context) (inter
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
amounts := make([]*models.TransactionAmountsResponseItemAmountInfo, 0, len(monthTotalAmounts))
|
amounts := make(models.TransactionAmountsResponseItemAmountInfoSlice, 0, len(monthTotalAmounts))
|
||||||
|
|
||||||
for _, monthTotalAmount := range monthTotalAmounts {
|
for _, monthTotalAmount := range monthTotalAmounts {
|
||||||
amounts = append(amounts, monthTotalAmount)
|
amounts = append(amounts, monthTotalAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Sort(amounts)
|
||||||
|
|
||||||
amountsResp = append(amountsResp, &models.TransactionMonthAmountsResponseItem{
|
amountsResp = append(amountsResp, &models.TransactionMonthAmountsResponseItem{
|
||||||
Year: year,
|
Year: year,
|
||||||
Month: month,
|
Month: month,
|
||||||
@@ -445,7 +496,7 @@ func (a *TransactionsApi) TransactionMonthAmountsHandler(c *core.Context) (inter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionGetHandler returns one specific transaction of current user
|
// TransactionGetHandler returns one specific transaction of current user
|
||||||
func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionGetReq models.TransactionGetRequest
|
var transactionGetReq models.TransactionGetRequest
|
||||||
err := c.ShouldBindQuery(&transactionGetReq)
|
err := c.ShouldBindQuery(&transactionGetReq)
|
||||||
|
|
||||||
@@ -462,7 +513,7 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -472,15 +523,15 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction, err := a.transactions.GetTransactionByTransactionId(uid, transactionGetReq.Id)
|
transaction, err := a.transactions.GetTransactionByTransactionId(c, uid, transactionGetReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionGetReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionGetReq.Id, uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId)
|
transaction = a.transactions.GetRelatedTransferTransaction(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountIds := make([]int64, 0, 2)
|
accountIds := make([]int64, 0, 2)
|
||||||
@@ -491,7 +542,7 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *
|
|||||||
accountIds = utils.ToUniqueInt64Slice(accountIds)
|
accountIds = utils.ToUniqueInt64Slice(accountIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountMap, err := a.accounts.GetAccountsByAccountIds(uid, accountIds)
|
accountMap, err := a.accounts.GetAccountsByAccountIds(c, uid, accountIds)
|
||||||
|
|
||||||
if _, exists := accountMap[transaction.AccountId]; !exists {
|
if _, exists := accountMap[transaction.AccountId]; !exists {
|
||||||
log.WarnfWithRequestId(c, "[transactions.TransactionGetHandler] account of transaction \"id:%d\" does not exist for user \"uid:%d\"", transaction.TransactionId, uid)
|
log.WarnfWithRequestId(c, "[transactions.TransactionGetHandler] account of transaction \"id:%d\" does not exist for user \"uid:%d\"", transaction.TransactionId, uid)
|
||||||
@@ -505,31 +556,31 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, []int64{transaction.TransactionId})
|
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(c, uid, []int64{transaction.TransactionId})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
var category *models.TransactionCategory
|
var category *models.TransactionCategory
|
||||||
var tagMap map[int64]*models.TransactionTag
|
var tagMap map[int64]*models.TransactionTag
|
||||||
|
|
||||||
if !transactionGetReq.TrimCategory {
|
if !transactionGetReq.TrimCategory {
|
||||||
category, err = a.transactionCategories.GetCategoryByCategoryId(uid, transaction.CategoryId)
|
category, err = a.transactionCategories.GetCategoryByCategoryId(c, uid, transaction.CategoryId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions category for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions category for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !transactionGetReq.TrimTag {
|
if !transactionGetReq.TrimTag {
|
||||||
tagMap, err = a.transactionTags.GetTagsByTagIds(uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds)))
|
tagMap, err = a.transactionTags.GetTagsByTagIds(c, uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionGetHandler] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +612,7 @@ func (a *TransactionsApi) TransactionGetHandler(c *core.Context) (interface{}, *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionCreateHandler saves a new transaction by request parameters for current user
|
// TransactionCreateHandler saves a new transaction by request parameters for current user
|
||||||
func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionCreateReq models.TransactionCreateRequest
|
var transactionCreateReq models.TransactionCreateRequest
|
||||||
err := c.ShouldBindJSON(&transactionCreateReq)
|
err := c.ShouldBindJSON(&transactionCreateReq)
|
||||||
|
|
||||||
@@ -601,7 +652,7 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -611,14 +662,14 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction := a.createNewTransactionModel(uid, &transactionCreateReq)
|
transaction := a.createNewTransactionModel(uid, &transactionCreateReq, c.ClientIP())
|
||||||
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transactionCreateReq.UtcOffset)
|
transactionEditable := user.CanEditTransactionByTransactionTime(transaction.TransactionTime, transactionCreateReq.UtcOffset)
|
||||||
|
|
||||||
if !transactionEditable {
|
if !transactionEditable {
|
||||||
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotCreateTransactionWithThisTransactionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.transactions.CreateTransaction(transaction, tagIds)
|
err = a.transactions.CreateTransaction(c, transaction, tagIds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionCreateHandler] failed to create transaction \"id:%d\" for user \"uid:%d\", because %s", transaction.TransactionId, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionCreateHandler] failed to create transaction \"id:%d\" for user \"uid:%d\", because %s", transaction.TransactionId, uid, err.Error())
|
||||||
@@ -633,7 +684,7 @@ func (a *TransactionsApi) TransactionCreateHandler(c *core.Context) (interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionModifyHandler saves an existed transaction by request parameters for current user
|
// TransactionModifyHandler saves an existed transaction by request parameters for current user
|
||||||
func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionModifyReq models.TransactionModifyRequest
|
var transactionModifyReq models.TransactionModifyRequest
|
||||||
err := c.ShouldBindJSON(&transactionModifyReq)
|
err := c.ShouldBindJSON(&transactionModifyReq)
|
||||||
|
|
||||||
@@ -650,7 +701,7 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -660,7 +711,7 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction, err := a.transactions.GetTransactionByTransactionId(uid, transactionModifyReq.Id)
|
transaction, err := a.transactions.GetTransactionByTransactionId(c, uid, transactionModifyReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionModifyReq.Id, uid, err.Error())
|
||||||
@@ -672,11 +723,11 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrTransactionTypeInvalid
|
return nil, errs.ErrTransactionTypeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, []int64{transaction.TransactionId})
|
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(c, uid, []int64{transaction.TransactionId})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.ErrOperationFailed
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
transactionTagIds := allTransactionTagIds[transaction.TransactionId]
|
||||||
@@ -702,6 +753,11 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
newTransaction.RelatedAccountAmount = transactionModifyReq.DestinationAmount
|
newTransaction.RelatedAccountAmount = transactionModifyReq.DestinationAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if transactionModifyReq.GeoLocation != nil {
|
||||||
|
newTransaction.GeoLongitude = transactionModifyReq.GeoLocation.Longitude
|
||||||
|
newTransaction.GeoLatitude = transactionModifyReq.GeoLocation.Latitude
|
||||||
|
}
|
||||||
|
|
||||||
if newTransaction.CategoryId == transaction.CategoryId &&
|
if newTransaction.CategoryId == transaction.CategoryId &&
|
||||||
utils.GetUnixTimeFromTransactionTime(newTransaction.TransactionTime) == utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime) &&
|
utils.GetUnixTimeFromTransactionTime(newTransaction.TransactionTime) == utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime) &&
|
||||||
newTransaction.TimezoneUtcOffset == transaction.TimezoneUtcOffset &&
|
newTransaction.TimezoneUtcOffset == transaction.TimezoneUtcOffset &&
|
||||||
@@ -711,6 +767,8 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
(transaction.Type != models.TRANSACTION_DB_TYPE_TRANSFER_OUT || newTransaction.RelatedAccountAmount == transaction.RelatedAccountAmount) &&
|
(transaction.Type != models.TRANSACTION_DB_TYPE_TRANSFER_OUT || newTransaction.RelatedAccountAmount == transaction.RelatedAccountAmount) &&
|
||||||
newTransaction.HideAmount == transaction.HideAmount &&
|
newTransaction.HideAmount == transaction.HideAmount &&
|
||||||
newTransaction.Comment == transaction.Comment &&
|
newTransaction.Comment == transaction.Comment &&
|
||||||
|
newTransaction.GeoLongitude == transaction.GeoLongitude &&
|
||||||
|
newTransaction.GeoLatitude == transaction.GeoLatitude &&
|
||||||
utils.Int64SliceEquals(tagIds, transactionTagIds) {
|
utils.Int64SliceEquals(tagIds, transactionTagIds) {
|
||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
@@ -730,7 +788,7 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrCannotModifyTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotModifyTransactionWithThisTransactionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.transactions.ModifyTransaction(newTransaction, addTransactionTagIds, removeTransactionTagIds)
|
err = a.transactions.ModifyTransaction(c, newTransaction, addTransactionTagIds, removeTransactionTagIds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to update transaction \"id:%d\" for user \"uid:%d\", because %s", transactionModifyReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionModifyHandler] failed to update transaction \"id:%d\" for user \"uid:%d\", because %s", transactionModifyReq.Id, uid, err.Error())
|
||||||
@@ -746,7 +804,7 @@ func (a *TransactionsApi) TransactionModifyHandler(c *core.Context) (interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TransactionDeleteHandler deletes an existed transaction by request parameters for current user
|
// TransactionDeleteHandler deletes an existed transaction by request parameters for current user
|
||||||
func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionDeleteReq models.TransactionDeleteRequest
|
var transactionDeleteReq models.TransactionDeleteRequest
|
||||||
err := c.ShouldBindJSON(&transactionDeleteReq)
|
err := c.ShouldBindJSON(&transactionDeleteReq)
|
||||||
|
|
||||||
@@ -763,7 +821,7 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{}
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -773,7 +831,7 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction, err := a.transactions.GetTransactionByTransactionId(uid, transactionDeleteReq.Id)
|
transaction, err := a.transactions.GetTransactionByTransactionId(c, uid, transactionDeleteReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionDeleteHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionDeleteReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionDeleteHandler] failed to get transaction \"id:%d\" for user \"uid:%d\", because %s", transactionDeleteReq.Id, uid, err.Error())
|
||||||
@@ -791,7 +849,7 @@ func (a *TransactionsApi) TransactionDeleteHandler(c *core.Context) (interface{}
|
|||||||
return nil, errs.ErrCannotDeleteTransactionWithThisTransactionTime
|
return nil, errs.ErrCannotDeleteTransactionWithThisTransactionTime
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.transactions.DeleteTransaction(uid, transactionDeleteReq.Id)
|
err = a.transactions.DeleteTransaction(c, uid, transactionDeleteReq.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.TransactionDeleteHandler] failed to delete transaction \"id:%d\" for user \"uid:%d\", because %s", transactionDeleteReq.Id, uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.TransactionDeleteHandler] failed to delete transaction \"id:%d\" for user \"uid:%d\", because %s", transactionDeleteReq.Id, uid, err.Error())
|
||||||
@@ -826,11 +884,33 @@ func (a *TransactionsApi) filterTransactions(c *core.Context, uid int64, transac
|
|||||||
return finalTransactions
|
return finalTransactions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TransactionsApi) getCategoryAndSubCategoryIds(categoryId int64, uid int64) ([]int64, error) {
|
func (a *TransactionsApi) getAccountOrSubAccountIds(c *core.Context, accountId int64, uid int64) ([]int64, error) {
|
||||||
|
var allAccountIds []int64
|
||||||
|
|
||||||
|
if accountId > 0 {
|
||||||
|
allSubAccounts, err := a.accounts.GetSubAccountsByAccountId(c, uid, accountId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allSubAccounts) > 0 {
|
||||||
|
for i := 0; i < len(allSubAccounts); i++ {
|
||||||
|
allAccountIds = append(allAccountIds, allSubAccounts[i].AccountId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
allAccountIds = append(allAccountIds, accountId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allAccountIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *TransactionsApi) getCategoryOrSubCategoryIds(c *core.Context, categoryId int64, uid int64) ([]int64, error) {
|
||||||
var allCategoryIds []int64
|
var allCategoryIds []int64
|
||||||
|
|
||||||
if categoryId > 0 {
|
if categoryId > 0 {
|
||||||
allSubCategories, err := a.transactionCategories.GetAllCategoriesByUid(uid, 0, categoryId)
|
allSubCategories, err := a.transactionCategories.GetAllCategoriesByUid(c, uid, 0, categoryId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -897,7 +977,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
categoryIds = append(categoryIds, transactions[i].CategoryId)
|
categoryIds = append(categoryIds, transactions[i].CategoryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
allAccounts, err := a.accounts.GetAccountsByAccountIds(uid, utils.ToUniqueInt64Slice(accountIds))
|
allAccounts, err := a.accounts.GetAccountsByAccountIds(c, uid, utils.ToUniqueInt64Slice(accountIds))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -906,7 +986,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
|
|
||||||
transactions = a.filterTransactions(c, uid, transactions, allAccounts)
|
transactions = a.filterTransactions(c, uid, transactions, allAccounts)
|
||||||
|
|
||||||
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, transactionIds)
|
allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(c, uid, transactionIds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -917,7 +997,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
var tagMap map[int64]*models.TransactionTag
|
var tagMap map[int64]*models.TransactionTag
|
||||||
|
|
||||||
if !trimCategory {
|
if !trimCategory {
|
||||||
categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(uid, utils.ToUniqueInt64Slice(categoryIds))
|
categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(c, uid, utils.ToUniqueInt64Slice(categoryIds))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -926,7 +1006,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !trimTag {
|
if !trimTag {
|
||||||
tagMap, err = a.transactionTags.GetTagsByTagIds(uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds)))
|
tagMap, err = a.transactionTags.GetTagsByTagIds(c, uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -940,7 +1020,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
transaction := transactions[i]
|
transaction := transactions[i]
|
||||||
|
|
||||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId)
|
transaction = a.transactions.GetRelatedTransferTransaction(transaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId])
|
transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId])
|
||||||
@@ -973,7 +1053,7 @@ func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TransactionsApi) createNewTransactionModel(uid int64, transactionCreateReq *models.TransactionCreateRequest) *models.Transaction {
|
func (a *TransactionsApi) createNewTransactionModel(uid int64, transactionCreateReq *models.TransactionCreateRequest, clientIp string) *models.Transaction {
|
||||||
var transactionDbType models.TransactionDbType
|
var transactionDbType models.TransactionDbType
|
||||||
|
|
||||||
if transactionCreateReq.Type == models.TRANSACTION_TYPE_MODIFY_BALANCE {
|
if transactionCreateReq.Type == models.TRANSACTION_TYPE_MODIFY_BALANCE {
|
||||||
@@ -996,6 +1076,7 @@ func (a *TransactionsApi) createNewTransactionModel(uid int64, transactionCreate
|
|||||||
Amount: transactionCreateReq.SourceAmount,
|
Amount: transactionCreateReq.SourceAmount,
|
||||||
HideAmount: transactionCreateReq.HideAmount,
|
HideAmount: transactionCreateReq.HideAmount,
|
||||||
Comment: transactionCreateReq.Comment,
|
Comment: transactionCreateReq.Comment,
|
||||||
|
CreatedIp: clientIp,
|
||||||
}
|
}
|
||||||
|
|
||||||
if transactionCreateReq.Type == models.TRANSACTION_TYPE_TRANSFER {
|
if transactionCreateReq.Type == models.TRANSACTION_TYPE_TRANSFER {
|
||||||
@@ -1003,5 +1084,10 @@ func (a *TransactionsApi) createNewTransactionModel(uid int64, transactionCreate
|
|||||||
transaction.RelatedAccountAmount = transactionCreateReq.DestinationAmount
|
transaction.RelatedAccountAmount = transactionCreateReq.DestinationAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if transactionCreateReq.GeoLocation != nil {
|
||||||
|
transaction.GeoLongitude = transactionCreateReq.GeoLocation.Longitude
|
||||||
|
transaction.GeoLatitude = transactionCreateReq.GeoLocation.Latitude
|
||||||
|
}
|
||||||
|
|
||||||
return transaction
|
return transaction
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TwoFactorStatusHandler returns 2fa status of current user
|
// TwoFactorStatusHandler returns 2fa status of current user
|
||||||
func (a *TwoFactorAuthorizationsApi) TwoFactorStatusHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TwoFactorAuthorizationsApi) TwoFactorStatusHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(uid)
|
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(c, uid)
|
||||||
|
|
||||||
if err == errs.ErrTwoFactorIsNotEnabled {
|
if err == errs.ErrTwoFactorIsNotEnabled {
|
||||||
statusResp := &models.TwoFactorStatusResponse{
|
statusResp := &models.TwoFactorStatusResponse{
|
||||||
@@ -58,9 +58,9 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorStatusHandler(c *core.Context) (in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorEnableRequestHandler returns a new 2fa secret and qr code for current user to set 2fa and verify passcode next
|
// TwoFactorEnableRequestHandler returns a new 2fa secret and qr code for current user to set 2fa and verify passcode next
|
||||||
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Context) (any, *errs.Error) {
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
enabled, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
enabled, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to check two factor setting, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to check two factor setting, because %s", err.Error())
|
||||||
@@ -71,7 +71,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Conte
|
|||||||
return nil, errs.ErrTwoFactorAlreadyEnabled
|
return nil, errs.ErrTwoFactorAlreadyEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -81,7 +81,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Conte
|
|||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := a.twoFactorAuthorizations.GenerateTwoFactorSecret(user)
|
key, err := a.twoFactorAuthorizations.GenerateTwoFactorSecret(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two factor secret, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two factor secret, because %s", err.Error())
|
||||||
@@ -110,7 +110,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorEnableConfirmHandler enables 2fa for current user
|
// TwoFactorEnableConfirmHandler enables 2fa for current user
|
||||||
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var confirmReq models.TwoFactorEnableConfirmRequest
|
var confirmReq models.TwoFactorEnableConfirmRequest
|
||||||
err := c.ShouldBindJSON(&confirmReq)
|
err := c.ShouldBindJSON(&confirmReq)
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
exists, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
exists, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to check two factor setting, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to check two factor setting, because %s", err.Error())
|
||||||
@@ -131,7 +131,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
return nil, errs.ErrTwoFactorAlreadyEnabled
|
return nil, errs.ErrTwoFactorAlreadyEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -158,14 +158,14 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(uid, recoveryCodes, user.Salt)
|
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(c, uid, recoveryCodes, user.Salt)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.CreateTwoFactorSetting(twoFactorSetting)
|
err = a.twoFactorAuthorizations.CreateTwoFactorSetting(c, twoFactorSetting)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor setting for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -175,7 +175,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" has enabled two factor authorization", uid)
|
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" has enabled two factor authorization", uid)
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
err = a.tokens.DeleteTokensBeforeTime(uid, now)
|
err = a.tokens.DeleteTokensBeforeTime(c, uid, now)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
|
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
|
||||||
@@ -183,7 +183,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -195,6 +195,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
return confirmResp, nil
|
return confirmResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
@@ -208,7 +209,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorDisableHandler disables 2fa for current user
|
// TwoFactorDisableHandler disables 2fa for current user
|
||||||
func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var disableReq models.TwoFactorDisableRequest
|
var disableReq models.TwoFactorDisableRequest
|
||||||
err := c.ShouldBindJSON(&disableReq)
|
err := c.ShouldBindJSON(&disableReq)
|
||||||
|
|
||||||
@@ -218,7 +219,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (i
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -232,7 +233,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (i
|
|||||||
return nil, errs.ErrUserPasswordWrong
|
return nil, errs.ErrUserPasswordWrong
|
||||||
}
|
}
|
||||||
|
|
||||||
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to check two factor setting, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to check two factor setting, because %s", err.Error())
|
||||||
@@ -243,14 +244,14 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (i
|
|||||||
return nil, errs.ErrTwoFactorIsNotEnabled
|
return nil, errs.ErrTwoFactorIsNotEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.DeleteTwoFactorRecoveryCodes(uid)
|
err = a.twoFactorAuthorizations.DeleteTwoFactorRecoveryCodes(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor recovery codes for user \"uid:%d\"", uid)
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor recovery codes for user \"uid:%d\"", uid)
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.DeleteTwoFactorSetting(uid)
|
err = a.twoFactorAuthorizations.DeleteTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor setting for user \"uid:%d\"", uid)
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor setting for user \"uid:%d\"", uid)
|
||||||
@@ -263,7 +264,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorRecoveryCodeRegenerateHandler returns new 2fa recovery codes and revokes old recovery codes for current user
|
// TwoFactorRecoveryCodeRegenerateHandler returns new 2fa recovery codes and revokes old recovery codes for current user
|
||||||
func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var regenerateReq models.TwoFactorRegenerateRecoveryCodeRequest
|
var regenerateReq models.TwoFactorRegenerateRecoveryCodeRequest
|
||||||
err := c.ShouldBindJSON(®enerateReq)
|
err := c.ShouldBindJSON(®enerateReq)
|
||||||
|
|
||||||
@@ -273,7 +274,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *c
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -287,7 +288,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *c
|
|||||||
return nil, errs.ErrUserPasswordWrong
|
return nil, errs.ErrUserPasswordWrong
|
||||||
}
|
}
|
||||||
|
|
||||||
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to check two factor setting, because %s", err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to check two factor setting, because %s", err.Error())
|
||||||
@@ -305,7 +306,7 @@ func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *c
|
|||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(uid, recoveryCodes, user.Salt)
|
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(c, uid, recoveryCodes, user.Salt)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
|
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
|||||||
+305
-23
@@ -4,6 +4,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
@@ -15,26 +17,28 @@ import (
|
|||||||
|
|
||||||
// UsersApi represents user api
|
// UsersApi represents user api
|
||||||
type UsersApi struct {
|
type UsersApi struct {
|
||||||
users *services.UserService
|
users *services.UserService
|
||||||
tokens *services.TokenService
|
tokens *services.TokenService
|
||||||
|
accounts *services.AccountService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a user api singleton instance
|
// Initialize a user api singleton instance
|
||||||
var (
|
var (
|
||||||
Users = &UsersApi{
|
Users = &UsersApi{
|
||||||
users: services.Users,
|
users: services.Users,
|
||||||
tokens: services.Tokens,
|
tokens: services.Tokens,
|
||||||
|
accounts: services.Accounts,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserRegisterHandler saves a new user by request parameters
|
// UserRegisterHandler saves a new user by request parameters
|
||||||
func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *UsersApi) UserRegisterHandler(c *core.Context) (any, *errs.Error) {
|
||||||
if !settings.Container.Current.EnableUserRegister {
|
if !settings.Container.Current.EnableUserRegister {
|
||||||
return nil, errs.ErrUserRegistrationNotAllowed
|
return nil, errs.ErrUserRegistrationNotAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
var userRegisterReq models.UserRegisterRequest
|
var userRegisterReq models.UserRegisterRequest
|
||||||
err := c.ShouldBindJSON(&userRegisterReq)
|
err := c.ShouldBindBodyWith(&userRegisterReq, binding.JSON)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] parse request failed, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] parse request failed, because %s", err.Error())
|
||||||
@@ -55,12 +59,13 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
Email: userRegisterReq.Email,
|
Email: userRegisterReq.Email,
|
||||||
Nickname: userRegisterReq.Nickname,
|
Nickname: userRegisterReq.Nickname,
|
||||||
Password: userRegisterReq.Password,
|
Password: userRegisterReq.Password,
|
||||||
|
Language: userRegisterReq.Language,
|
||||||
DefaultCurrency: userRegisterReq.DefaultCurrency,
|
DefaultCurrency: userRegisterReq.DefaultCurrency,
|
||||||
FirstDayOfWeek: userRegisterReq.FirstDayOfWeek,
|
FirstDayOfWeek: userRegisterReq.FirstDayOfWeek,
|
||||||
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
|
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.users.CreateUser(user)
|
err = a.users.CreateUser(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[users.UserRegisterHandler] failed to create user \"%s\", because %s", user.Username, err.Error())
|
log.ErrorfWithRequestId(c, "[users.UserRegisterHandler] failed to create user \"%s\", because %s", user.Username, err.Error())
|
||||||
@@ -69,12 +74,46 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
|
|
||||||
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"%s\" has registered successfully, uid is %d", user.Username, user.Uid)
|
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"%s\" has registered successfully, uid is %d", user.Username, user.Uid)
|
||||||
|
|
||||||
authResp := &models.AuthResponse{
|
presetCategoriesSaved := false
|
||||||
Need2FA: false,
|
|
||||||
User: user.ToUserBasicInfo(),
|
if len(userRegisterReq.Categories) > 0 {
|
||||||
|
_, err = TransactionCategories.createBatchCategories(c, user.Uid, &userRegisterReq.TransactionCategoryCreateBatchRequest)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
presetCategoriesSaved = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
authResp := &models.RegisterResponse{
|
||||||
|
AuthResponse: models.AuthResponse{
|
||||||
|
Need2FA: false,
|
||||||
|
User: user.ToUserBasicInfo(),
|
||||||
|
},
|
||||||
|
NeedVerifyEmail: settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableUserForceVerifyEmail,
|
||||||
|
PresetCategoriesSaved: presetCategoriesSaved,
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableSMTP {
|
||||||
|
token, _, err := a.tokens.CreateEmailVerifyToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserRegisterHandler] failed to create email verify token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
err = a.users.SendVerifyEmail(user, token, c.GetClientLocale())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] cannot send verify email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.EnableUserForceVerifyEmail {
|
||||||
|
return authResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -82,6 +121,7 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
authResp.Token = token
|
authResp.Token = token
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"uid:%d\" has logined, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"uid:%d\" has logined, token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
@@ -89,10 +129,72 @@ func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Erro
|
|||||||
return authResp, nil
|
return authResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserProfileHandler returns user profile of current user
|
// UserEmailVerifyHandler sets user email address verified
|
||||||
func (a *UsersApi) UserProfileHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *UsersApi) UserEmailVerifyHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
var userVerifyEmailReq models.UserVerifyEmailRequest
|
||||||
|
err := c.ShouldBindJSON(&userVerifyEmailReq)
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserEmailVerifyHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserEmailVerifyHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserEmailVerifyHandler] user \"uid:%d\" email has been verified", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.users.SetUserEmailVerified(c, user.Username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserEmailVerifyHandler] failed to update user \"uid:%d\" email address verified, because %s", user.Uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.tokens.DeleteTokensByType(c, uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.InfofWithRequestId(c, "[users.UserEmailVerifyHandler] revoke old email verify tokens for user \"uid:%d\"", user.Uid)
|
||||||
|
} else {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserEmailVerifyHandler] failed to revoke old email verify tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &models.UserVerifyEmailResponse{}
|
||||||
|
|
||||||
|
if userVerifyEmailReq.RequestNewToken {
|
||||||
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserEmailVerifyHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.NewToken = token
|
||||||
|
resp.User = user.ToUserBasicInfo()
|
||||||
|
c.SetTextualToken(token)
|
||||||
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
|
log.InfofWithRequestId(c, "[users.UserEmailVerifyHandler] user \"uid:%d\" token created, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserProfileHandler returns user profile of current user
|
||||||
|
func (a *UsersApi) UserProfileHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -107,7 +209,7 @@ func (a *UsersApi) UserProfileHandler(c *core.Context) (interface{}, *errs.Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UserUpdateProfileHandler saves user profile by request parameters for current user
|
// UserUpdateProfileHandler saves user profile by request parameters for current user
|
||||||
func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var userUpdateReq models.UserProfileUpdateRequest
|
var userUpdateReq models.UserProfileUpdateRequest
|
||||||
err := c.ShouldBindJSON(&userUpdateReq)
|
err := c.ShouldBindJSON(&userUpdateReq)
|
||||||
|
|
||||||
@@ -117,7 +219,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
user, err := a.users.GetUserById(uid)
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.IsCustomError(err) {
|
if !errs.IsCustomError(err) {
|
||||||
@@ -159,6 +261,35 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
anythingUpdate = true
|
anythingUpdate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userUpdateReq.DefaultAccountId > 0 && userUpdateReq.DefaultAccountId != user.DefaultAccountId {
|
||||||
|
accounts, err := a.accounts.GetAccountsByAccountIds(c, uid, []int64{userUpdateReq.DefaultAccountId})
|
||||||
|
|
||||||
|
if err != nil || len(accounts) < 1 {
|
||||||
|
return nil, errs.Or(err, errs.ErrUserDefaultAccountIsInvalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
user.DefaultAccountId = userUpdateReq.DefaultAccountId
|
||||||
|
userNew.DefaultAccountId = userUpdateReq.DefaultAccountId
|
||||||
|
anythingUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUpdateReq.TransactionEditScope != nil && *userUpdateReq.TransactionEditScope != user.TransactionEditScope {
|
||||||
|
user.TransactionEditScope = *userUpdateReq.TransactionEditScope
|
||||||
|
userNew.TransactionEditScope = *userUpdateReq.TransactionEditScope
|
||||||
|
anythingUpdate = true
|
||||||
|
} else {
|
||||||
|
userNew.TransactionEditScope = models.TRANSACTION_EDIT_SCOPE_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyUserLanguage := false
|
||||||
|
|
||||||
|
if userUpdateReq.Language != user.Language {
|
||||||
|
user.Language = userUpdateReq.Language
|
||||||
|
userNew.Language = userUpdateReq.Language
|
||||||
|
modifyUserLanguage = true
|
||||||
|
anythingUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
if userUpdateReq.DefaultCurrency != "" && userUpdateReq.DefaultCurrency != user.DefaultCurrency {
|
if userUpdateReq.DefaultCurrency != "" && userUpdateReq.DefaultCurrency != user.DefaultCurrency {
|
||||||
user.DefaultCurrency = userUpdateReq.DefaultCurrency
|
user.DefaultCurrency = userUpdateReq.DefaultCurrency
|
||||||
userNew.DefaultCurrency = userUpdateReq.DefaultCurrency
|
userNew.DefaultCurrency = userUpdateReq.DefaultCurrency
|
||||||
@@ -173,34 +304,84 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
userNew.FirstDayOfWeek = models.WEEKDAY_INVALID
|
userNew.FirstDayOfWeek = models.WEEKDAY_INVALID
|
||||||
}
|
}
|
||||||
|
|
||||||
if userUpdateReq.TransactionEditScope != nil && *userUpdateReq.TransactionEditScope != user.TransactionEditScope {
|
if userUpdateReq.LongDateFormat != nil && *userUpdateReq.LongDateFormat != user.LongDateFormat {
|
||||||
user.TransactionEditScope = *userUpdateReq.TransactionEditScope
|
user.LongDateFormat = *userUpdateReq.LongDateFormat
|
||||||
userNew.TransactionEditScope = *userUpdateReq.TransactionEditScope
|
userNew.LongDateFormat = *userUpdateReq.LongDateFormat
|
||||||
anythingUpdate = true
|
anythingUpdate = true
|
||||||
} else {
|
} else {
|
||||||
userNew.TransactionEditScope = models.TRANSACTION_EDIT_SCOPE_INVALID
|
userNew.LongDateFormat = models.LONG_DATE_FORMAT_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUpdateReq.ShortDateFormat != nil && *userUpdateReq.ShortDateFormat != user.ShortDateFormat {
|
||||||
|
user.ShortDateFormat = *userUpdateReq.ShortDateFormat
|
||||||
|
userNew.ShortDateFormat = *userUpdateReq.ShortDateFormat
|
||||||
|
anythingUpdate = true
|
||||||
|
} else {
|
||||||
|
userNew.ShortDateFormat = models.SHORT_DATE_FORMAT_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUpdateReq.LongTimeFormat != nil && *userUpdateReq.LongTimeFormat != user.LongTimeFormat {
|
||||||
|
user.LongTimeFormat = *userUpdateReq.LongTimeFormat
|
||||||
|
userNew.LongTimeFormat = *userUpdateReq.LongTimeFormat
|
||||||
|
anythingUpdate = true
|
||||||
|
} else {
|
||||||
|
userNew.LongTimeFormat = models.LONG_TIME_FORMAT_INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUpdateReq.ShortTimeFormat != nil && *userUpdateReq.ShortTimeFormat != user.ShortTimeFormat {
|
||||||
|
user.ShortTimeFormat = *userUpdateReq.ShortTimeFormat
|
||||||
|
userNew.ShortTimeFormat = *userUpdateReq.ShortTimeFormat
|
||||||
|
anythingUpdate = true
|
||||||
|
} else {
|
||||||
|
userNew.ShortTimeFormat = models.SHORT_TIME_FORMAT_INVALID
|
||||||
}
|
}
|
||||||
|
|
||||||
if !anythingUpdate {
|
if !anythingUpdate {
|
||||||
return nil, errs.ErrNothingWillBeUpdated
|
return nil, errs.ErrNothingWillBeUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
keyProfileUpdated, err := a.users.UpdateUser(userNew)
|
keyProfileUpdated, emailSetToUnverified, err := a.users.UpdateUser(c, userNew, modifyUserLanguage)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
return nil, errs.Or(err, errs.ErrOperationFailed)
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if emailSetToUnverified {
|
||||||
|
user.EmailVerified = false
|
||||||
|
}
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid)
|
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid)
|
||||||
|
|
||||||
resp := &models.UserProfileUpdateResponse{
|
resp := &models.UserProfileUpdateResponse{
|
||||||
User: user.ToUserBasicInfo(),
|
User: user.ToUserBasicInfo(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if emailSetToUnverified && settings.Container.Current.EnableUserVerifyEmail && settings.Container.Current.EnableSMTP {
|
||||||
|
err = a.tokens.DeleteTokensByType(c, uid, core.USER_TOKEN_TYPE_EMAIL_VERIFY)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to revoke old email verify tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
} else {
|
||||||
|
token, _, err := a.tokens.CreateEmailVerifyToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to create email verify token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
} else {
|
||||||
|
go func() {
|
||||||
|
err = a.users.SendVerifyEmail(user, token, c.GetClientLocale())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] cannot send verify email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if keyProfileUpdated {
|
if keyProfileUpdated {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
err = a.tokens.DeleteTokensBeforeTime(uid, now)
|
err = a.tokens.DeleteTokensBeforeTime(c, uid, now)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
|
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
|
||||||
@@ -208,7 +389,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
token, claims, err := a.tokens.CreateToken(user, c)
|
token, claims, err := a.tokens.CreateToken(c, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
@@ -216,6 +397,7 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp.NewToken = token
|
resp.NewToken = token
|
||||||
|
c.SetTextualToken(token)
|
||||||
c.SetTokenClaims(claims)
|
c.SetTokenClaims(claims)
|
||||||
|
|
||||||
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
|
||||||
@@ -225,3 +407,103 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserSendVerifyEmailByUnloginUserHandler sends unlogin user verify email
|
||||||
|
func (a *UsersApi) UserSendVerifyEmailByUnloginUserHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
if !settings.Container.Current.EnableUserVerifyEmail {
|
||||||
|
return nil, errs.ErrEmailValidationNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
var userResendVerifyEmailReq models.UserResendVerifyEmailRequest
|
||||||
|
err := c.ShouldBindJSON(&userResendVerifyEmailReq)
|
||||||
|
|
||||||
|
user, err := a.users.GetUserByEmail(c, userResendVerifyEmailReq.Email)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.users.IsPasswordEqualsUserPassword(userResendVerifyEmailReq.Password, user) {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] request password not equals to the user password")
|
||||||
|
return nil, errs.ErrUserPasswordWrong
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Disabled {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] user \"uid:%d\" is disabled", user.Uid)
|
||||||
|
return nil, errs.ErrUserIsDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] user \"uid:%d\" email has been verified", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.Container.Current.EnableSMTP {
|
||||||
|
return nil, errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _, err := a.tokens.CreateEmailVerifyToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return nil, errs.ErrTokenGenerating
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = a.users.SendVerifyEmail(user, token, c.GetClientLocale())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByUnloginUserHandler] cannot send email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserSendVerifyEmailByLoginedUserHandler sends logined user verify email
|
||||||
|
func (a *UsersApi) UserSendVerifyEmailByLoginedUserHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
if !settings.Container.Current.EnableUserVerifyEmail {
|
||||||
|
return nil, errs.ErrEmailValidationNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
user, err := a.users.GetUserById(c, uid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !errs.IsCustomError(err) {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserSendVerifyEmailByLoginedUserHandler] failed to get user, because %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.EmailVerified {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByLoginedUserHandler] user \"uid:%d\" email has been verified", user.Uid)
|
||||||
|
return nil, errs.ErrEmailIsVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.Container.Current.EnableSMTP {
|
||||||
|
return nil, errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _, err := a.tokens.CreateEmailVerifyToken(c, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[users.UserSendVerifyEmailByLoginedUserHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return nil, errs.ErrTokenGenerating
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err = a.users.SendVerifyEmail(user, token, c.GetClientLocale())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[users.UserSendVerifyEmailByLoginedUserHandler] cannot send email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|||||||
+164
-17
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/services"
|
"github.com/mayswind/ezbookkeeping/pkg/services"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/validators"
|
"github.com/mayswind/ezbookkeeping/pkg/validators"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ type UserDataCli struct {
|
|||||||
users *services.UserService
|
users *services.UserService
|
||||||
twoFactorAuthorizations *services.TwoFactorAuthorizationService
|
twoFactorAuthorizations *services.TwoFactorAuthorizationService
|
||||||
tokens *services.TokenService
|
tokens *services.TokenService
|
||||||
|
forgetPasswords *services.ForgetPasswordService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize an user data cli singleton instance
|
// Initialize an user data cli singleton instance
|
||||||
@@ -39,6 +41,7 @@ var (
|
|||||||
users: services.Users,
|
users: services.Users,
|
||||||
twoFactorAuthorizations: services.TwoFactorAuthorizations,
|
twoFactorAuthorizations: services.TwoFactorAuthorizations,
|
||||||
tokens: services.Tokens,
|
tokens: services.Tokens,
|
||||||
|
forgetPasswords: services.ForgetPasswords,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,7 +87,7 @@ func (l *UserDataCli) AddNewUser(c *cli.Context, username string, email string,
|
|||||||
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
|
TransactionEditScope: models.TRANSACTION_EDIT_SCOPE_ALL,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := l.users.CreateUser(user)
|
err := l.users.CreateUser(nil, user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.AddNewUser] failed to create user \"%s\", because %s", user.Username, err.Error())
|
log.BootErrorf("[user_data.AddNewUser] failed to create user \"%s\", because %s", user.Username, err.Error())
|
||||||
@@ -103,7 +106,7 @@ func (l *UserDataCli) GetUserByUsername(c *cli.Context, username string) (*model
|
|||||||
return nil, errs.ErrUsernameIsEmpty
|
return nil, errs.ErrUsernameIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := l.users.GetUserByUsername(username)
|
user, err := l.users.GetUserByUsername(nil, username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.GetUserByUsername] failed to get user by user name \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.GetUserByUsername] failed to get user by user name \"%s\", because %s", username, err.Error())
|
||||||
@@ -125,7 +128,7 @@ func (l *UserDataCli) ModifyUserPassword(c *cli.Context, username string, passwo
|
|||||||
return errs.ErrPasswordIsEmpty
|
return errs.ErrPasswordIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := l.users.GetUserByUsername(username)
|
user, err := l.users.GetUserByUsername(nil, username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.ModifyUserPassword] failed to get user by user name \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.ModifyUserPassword] failed to get user by user name \"%s\", because %s", username, err.Error())
|
||||||
@@ -142,7 +145,7 @@ func (l *UserDataCli) ModifyUserPassword(c *cli.Context, username string, passwo
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = l.users.UpdateUser(userNew)
|
_, _, err = l.users.UpdateUser(nil, userNew, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.ModifyUserPassword] failed to update user \"%s\" password, because %s", user.Username, err.Error())
|
log.BootErrorf("[user_data.ModifyUserPassword] failed to update user \"%s\" password, because %s", user.Username, err.Error())
|
||||||
@@ -150,7 +153,7 @@ func (l *UserDataCli) ModifyUserPassword(c *cli.Context, username string, passwo
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
err = l.tokens.DeleteTokensBeforeTime(user.Uid, now)
|
err = l.tokens.DeleteTokensBeforeTime(nil, user.Uid, now)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.BootInfof("[user_data.ModifyUserPassword] revoke old tokens before unix time \"%d\" for user \"%s\"", now, user.Username)
|
log.BootInfof("[user_data.ModifyUserPassword] revoke old tokens before unix time \"%d\" for user \"%s\"", now, user.Username)
|
||||||
@@ -161,6 +164,150 @@ func (l *UserDataCli) ModifyUserPassword(c *cli.Context, username string, passwo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPasswordResetMail sends an email with password reset link
|
||||||
|
func (l *UserDataCli) SendPasswordResetMail(c *cli.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.SendPasswordResetMail] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := l.users.GetUserByUsername(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.SendPasswordResetMail] failed to get user by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.Container.Current.ForgetPasswordRequireVerifyEmail && !user.EmailVerified {
|
||||||
|
log.BootWarnf("[user_data.SendPasswordResetMail] user \"uid:%d\" has not verified email", user.Uid)
|
||||||
|
return errs.ErrEmailIsNotVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _, err := l.tokens.CreatePasswordResetToken(nil, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.SendPasswordResetMail] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.forgetPasswords.SendPasswordResetEmail(nil, user, token, "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootWarnf("[user_data.SendPasswordResetMail] cannot send email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableUser sets user enabled according to the specified user name
|
||||||
|
func (l *UserDataCli) EnableUser(c *cli.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.EnableUser] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.users.EnableUser(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.EnableUser] failed to set user enabled by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableUser sets user disabled according to the specified user name
|
||||||
|
func (l *UserDataCli) DisableUser(c *cli.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.DisableUser] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.users.DisableUser(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.DisableUser] failed to set user disabled by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResendVerifyEmail resends an email with account activation link
|
||||||
|
func (l *UserDataCli) ResendVerifyEmail(c *cli.Context, username string) error {
|
||||||
|
if !settings.Container.Current.EnableUserVerifyEmail {
|
||||||
|
return errs.ErrEmailValidationNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.ResendVerifyEmail] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := l.users.GetUserByUsername(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.ResendVerifyEmail] failed to get user by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.EmailVerified {
|
||||||
|
log.BootWarnf("[user_data.ResendVerifyEmail] user \"uid:%d\" email has been verified", user.Uid)
|
||||||
|
return errs.ErrEmailIsVerified
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _, err := l.tokens.CreateEmailVerifyToken(nil, user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.ResendVerifyEmail] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
|
||||||
|
return errs.ErrTokenGenerating
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.users.SendVerifyEmail(user, token, "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.ResendVerifyEmail] cannot send email to \"%s\", because %s", user.Email, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserEmailVerified sets user email address verified
|
||||||
|
func (l *UserDataCli) SetUserEmailVerified(c *cli.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.SetUserEmailVerified] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.users.SetUserEmailVerified(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.SetUserEmailVerified] failed to set user email address verified by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserEmailUnverified sets user email address unverified
|
||||||
|
func (l *UserDataCli) SetUserEmailUnverified(c *cli.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
log.BootErrorf("[user_data.SetUserEmailUnverified] user name is empty")
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.users.SetUserEmailUnverified(nil, username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.BootErrorf("[user_data.SetUserEmailUnverified] failed to set user email address unverified by user name \"%s\", because %s", username, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteUser deletes user according to the specified user name
|
// DeleteUser deletes user according to the specified user name
|
||||||
func (l *UserDataCli) DeleteUser(c *cli.Context, username string) error {
|
func (l *UserDataCli) DeleteUser(c *cli.Context, username string) error {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
@@ -168,7 +315,7 @@ func (l *UserDataCli) DeleteUser(c *cli.Context, username string) error {
|
|||||||
return errs.ErrUsernameIsEmpty
|
return errs.ErrUsernameIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
err := l.users.DeleteUser(username)
|
err := l.users.DeleteUser(nil, username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.DeleteUser] failed to delete user by user name \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.DeleteUser] failed to delete user by user name \"%s\", because %s", username, err.Error())
|
||||||
@@ -192,7 +339,7 @@ func (l *UserDataCli) ListUserTokens(c *cli.Context, username string) ([]*models
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens, err := l.tokens.GetAllUnexpiredNormalTokensByUid(uid)
|
tokens, err := l.tokens.GetAllUnexpiredNormalTokensByUid(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.ListUserTokens] failed to get tokens of user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.ListUserTokens] failed to get tokens of user \"%s\", because %s", username, err.Error())
|
||||||
@@ -217,7 +364,7 @@ func (l *UserDataCli) ClearUserTokens(c *cli.Context, username string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
err = l.tokens.DeleteTokensBeforeTime(uid, now)
|
err = l.tokens.DeleteTokensBeforeTime(nil, uid, now)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.ClearUserTokens] failed to delete tokens of user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.ClearUserTokens] failed to delete tokens of user \"%s\", because %s", username, err.Error())
|
||||||
@@ -241,7 +388,7 @@ func (l *UserDataCli) DisableUserTwoFactorAuthorization(c *cli.Context, username
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
enableTwoFactor, err := l.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
|
enableTwoFactor, err := l.twoFactorAuthorizations.ExistsTwoFactorSetting(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to check two factor setting, because %s", err.Error())
|
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to check two factor setting, because %s", err.Error())
|
||||||
@@ -252,14 +399,14 @@ func (l *UserDataCli) DisableUserTwoFactorAuthorization(c *cli.Context, username
|
|||||||
return errs.ErrTwoFactorIsNotEnabled
|
return errs.ErrTwoFactorIsNotEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
err = l.twoFactorAuthorizations.DeleteTwoFactorRecoveryCodes(uid)
|
err = l.twoFactorAuthorizations.DeleteTwoFactorRecoveryCodes(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to delete two factor recovery codes for user \"%s\"", username)
|
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to delete two factor recovery codes for user \"%s\"", username)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = l.twoFactorAuthorizations.DeleteTwoFactorSetting(uid)
|
err = l.twoFactorAuthorizations.DeleteTwoFactorSetting(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to delete two factor setting for user \"%s\"", username)
|
log.BootErrorf("[user_data.DisableUserTwoFactorAuthorization] failed to delete two factor setting for user \"%s\"", username)
|
||||||
@@ -298,7 +445,7 @@ func (l *UserDataCli) CheckTransactionAndAccount(c *cli.Context, username string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allTransactions, err := l.transactions.GetAllTransactions(uid, pageCountForGettingTransactions, false)
|
allTransactions, err := l.transactions.GetAllTransactions(nil, uid, pageCountForGettingTransactions, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.CheckTransactionAndAccount] failed to all transactions for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.CheckTransactionAndAccount] failed to all transactions for user \"%s\", because %s", username, err.Error())
|
||||||
@@ -410,7 +557,7 @@ func (l *UserDataCli) ExportTransaction(c *cli.Context, username string) ([]byte
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
allTransactions, err := l.transactions.GetAllTransactions(uid, pageCountForDataExport, true)
|
allTransactions, err := l.transactions.GetAllTransactions(nil, uid, pageCountForDataExport, true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.ExportTransaction] failed to all transactions for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.ExportTransaction] failed to all transactions for user \"%s\", because %s", username, err.Error())
|
||||||
@@ -444,7 +591,7 @@ func (l *UserDataCli) getUserEssentialData(uid int64, username string) (accountM
|
|||||||
return nil, nil, nil, nil, errs.ErrUserIdInvalid
|
return nil, nil, nil, nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts, err := l.accounts.GetAllAccountsByUid(uid)
|
accounts, err := l.accounts.GetAllAccountsByUid(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.getUserEssentialData] failed to get accounts for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.getUserEssentialData] failed to get accounts for user \"%s\", because %s", username, err.Error())
|
||||||
@@ -453,7 +600,7 @@ func (l *UserDataCli) getUserEssentialData(uid int64, username string) (accountM
|
|||||||
|
|
||||||
accountMap = l.accounts.GetAccountMapByList(accounts)
|
accountMap = l.accounts.GetAccountMapByList(accounts)
|
||||||
|
|
||||||
categories, err := l.categories.GetAllCategoriesByUid(uid, 0, -1)
|
categories, err := l.categories.GetAllCategoriesByUid(nil, uid, 0, -1)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.getUserEssentialData] failed to get categories for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.getUserEssentialData] failed to get categories for user \"%s\", because %s", username, err.Error())
|
||||||
@@ -462,7 +609,7 @@ func (l *UserDataCli) getUserEssentialData(uid int64, username string) (accountM
|
|||||||
|
|
||||||
categoryMap = l.categories.GetCategoryMapByList(categories)
|
categoryMap = l.categories.GetCategoryMapByList(categories)
|
||||||
|
|
||||||
tags, err := l.tags.GetAllTagsByUid(uid)
|
tags, err := l.tags.GetAllTagsByUid(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.getUserEssentialData] failed to get tags for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.getUserEssentialData] failed to get tags for user \"%s\", because %s", username, err.Error())
|
||||||
@@ -471,7 +618,7 @@ func (l *UserDataCli) getUserEssentialData(uid int64, username string) (accountM
|
|||||||
|
|
||||||
tagMap = l.tags.GetTagMapByList(tags)
|
tagMap = l.tags.GetTagMapByList(tags)
|
||||||
|
|
||||||
tagIndexs, err = l.tags.GetAllTagIdsOfAllTransactions(uid)
|
tagIndexs, err = l.tags.GetAllTagIdsOfAllTransactions(nil, uid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.BootErrorf("[user_data.getUserEssentialData] failed to get tag index for user \"%s\", because %s", username, err.Error())
|
log.BootErrorf("[user_data.getUserEssentialData] failed to get tag index for user \"%s\", because %s", username, err.Error())
|
||||||
|
|||||||
+27
-6
@@ -9,9 +9,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const requestIdFieldKey = "REQUEST_ID"
|
const requestIdFieldKey = "REQUEST_ID"
|
||||||
|
const textualTokenFieldKey = "TOKEN_STRING"
|
||||||
const tokenClaimsFieldKey = "TOKEN_CLAIMS"
|
const tokenClaimsFieldKey = "TOKEN_CLAIMS"
|
||||||
const responseErrorFieldKey = "RESPONSE_ERROR"
|
const responseErrorFieldKey = "RESPONSE_ERROR"
|
||||||
|
|
||||||
|
// AcceptLanguageHeaderName represents the header name of accept language
|
||||||
|
const AcceptLanguageHeaderName = "Accept-Language"
|
||||||
|
|
||||||
// ClientTimezoneOffsetHeaderName represents the header name of client timezone offset
|
// ClientTimezoneOffsetHeaderName represents the header name of client timezone offset
|
||||||
const ClientTimezoneOffsetHeaderName = "X-Timezone-Offset"
|
const ClientTimezoneOffsetHeaderName = "X-Timezone-Offset"
|
||||||
|
|
||||||
@@ -37,7 +41,23 @@ func (c *Context) GetRequestId() string {
|
|||||||
return requestId.(string)
|
return requestId.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTokenClaims sets the given user token id to context
|
// SetTextualToken sets the given user token to context
|
||||||
|
func (c *Context) SetTextualToken(token string) {
|
||||||
|
c.Set(textualTokenFieldKey, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTextualToken returns the current user textual token
|
||||||
|
func (c *Context) GetTextualToken() string {
|
||||||
|
token, exists := c.Get(textualTokenFieldKey)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return token.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTokenClaims sets the given user token to context
|
||||||
func (c *Context) SetTokenClaims(claims *UserTokenClaims) {
|
func (c *Context) SetTokenClaims(claims *UserTokenClaims) {
|
||||||
c.Set(tokenClaimsFieldKey, claims)
|
c.Set(tokenClaimsFieldKey, claims)
|
||||||
}
|
}
|
||||||
@@ -61,13 +81,14 @@ func (c *Context) GetCurrentUid() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
uid, err := strconv.ParseInt(claims.Id, 10, 64)
|
return claims.Uid
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
// GetClientLocale returns the client locale name
|
||||||
return 0
|
func (c *Context) GetClientLocale() string {
|
||||||
}
|
value := c.GetHeader(AcceptLanguageHeaderName)
|
||||||
|
|
||||||
return uid
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientTimezoneOffset returns the client timezone offset
|
// GetClientTimezoneOffset returns the client timezone offset
|
||||||
|
|||||||
+9
-2
@@ -1,12 +1,19 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import "github.com/mayswind/ezbookkeeping/pkg/errs"
|
import (
|
||||||
|
"net/http/httputil"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
)
|
||||||
|
|
||||||
// MiddlewareHandlerFunc represents the middleware handler function
|
// MiddlewareHandlerFunc represents the middleware handler function
|
||||||
type MiddlewareHandlerFunc func(*Context)
|
type MiddlewareHandlerFunc func(*Context)
|
||||||
|
|
||||||
// ApiHandlerFunc represents the api handler function
|
// ApiHandlerFunc represents the api handler function
|
||||||
type ApiHandlerFunc func(*Context) (interface{}, *errs.Error)
|
type ApiHandlerFunc func(*Context) (any, *errs.Error)
|
||||||
|
|
||||||
// DataHandlerFunc represents the handler function that returns byte array
|
// DataHandlerFunc represents the handler function that returns byte array
|
||||||
type DataHandlerFunc func(*Context) ([]byte, string, *errs.Error)
|
type DataHandlerFunc func(*Context) ([]byte, string, *errs.Error)
|
||||||
|
|
||||||
|
// ProxyHandlerFunc represents the reverse proxy handler function
|
||||||
|
type ProxyHandlerFunc func(*Context) (*httputil.ReverseProxy, *errs.Error)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dgrijalva/jwt-go"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TokenType represents token type
|
// TokenType represents token type
|
||||||
@@ -9,14 +11,52 @@ type TokenType byte
|
|||||||
|
|
||||||
// Token types
|
// Token types
|
||||||
const (
|
const (
|
||||||
USER_TOKEN_TYPE_NORMAL TokenType = 1
|
USER_TOKEN_TYPE_NORMAL TokenType = 1
|
||||||
USER_TOKEN_TYPE_REQUIRE_2FA TokenType = 2
|
USER_TOKEN_TYPE_REQUIRE_2FA TokenType = 2
|
||||||
|
USER_TOKEN_TYPE_EMAIL_VERIFY TokenType = 3
|
||||||
|
USER_TOKEN_TYPE_PASSWORD_RESET TokenType = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserTokenClaims represents user token
|
// UserTokenClaims represents user token
|
||||||
type UserTokenClaims struct {
|
type UserTokenClaims struct {
|
||||||
UserTokenId string `json:"userTokenId"`
|
UserTokenId string `json:"userTokenId"`
|
||||||
|
Uid int64 `json:"jti,string"`
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Type TokenType `json:"type"`
|
Type TokenType `json:"type"`
|
||||||
jwt.StandardClaims
|
IssuedAt int64 `json:"iat"`
|
||||||
|
ExpiresAt int64 `json:"exp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExpirationTime returns the expiration time of this token
|
||||||
|
func (c *UserTokenClaims) GetExpirationTime() (*jwt.NumericDate, error) {
|
||||||
|
return &jwt.NumericDate{
|
||||||
|
Time: time.Unix(c.ExpiresAt, 0),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssuedAt returns the issue time of this token
|
||||||
|
func (c *UserTokenClaims) GetIssuedAt() (*jwt.NumericDate, error) {
|
||||||
|
return &jwt.NumericDate{
|
||||||
|
Time: time.Unix(c.IssuedAt, 0),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNotBefore returns the earliest valid time of this token
|
||||||
|
func (c *UserTokenClaims) GetNotBefore() (*jwt.NumericDate, error) {
|
||||||
|
return &jwt.NumericDate{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssuer returns the issuer of this token
|
||||||
|
func (c *UserTokenClaims) GetIssuer() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubject returns the subject of this token
|
||||||
|
func (c *UserTokenClaims) GetSubject() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAudience returns the audience of this token
|
||||||
|
func (c *UserTokenClaims) GetAudience() (jwt.ClaimStrings, error) {
|
||||||
|
return jwt.ClaimStrings{}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
package datastore
|
package datastore
|
||||||
|
|
||||||
import "xorm.io/xorm"
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
)
|
||||||
|
|
||||||
// Database represents a database instance
|
// Database represents a database instance
|
||||||
type Database struct {
|
type Database struct {
|
||||||
*xorm.EngineGroup
|
engineGroup *xorm.EngineGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSession starts a new session with the specified context
|
||||||
|
func (db *Database) NewSession(c *core.Context) *xorm.Session {
|
||||||
|
return db.engineGroup.Context(NewXOrmContextAdapter(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoTransaction runs a new database transaction
|
// DoTransaction runs a new database transaction
|
||||||
func (db *Database) DoTransaction(fn func(sess *xorm.Session) error) (err error) {
|
func (db *Database) DoTransaction(c *core.Context, fn func(sess *xorm.Session) error) (err error) {
|
||||||
sess := db.NewSession()
|
sess := db.engineGroup.NewSession()
|
||||||
|
|
||||||
|
if c != nil {
|
||||||
|
sess.Context(NewXOrmContextAdapter(c))
|
||||||
|
}
|
||||||
|
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
|
|
||||||
if err = sess.Begin(); err != nil {
|
if err = sess.Begin(); err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package datastore
|
|||||||
import (
|
import (
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,21 +18,21 @@ func (s *DataStore) Choose(key int64) *Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query returns a new database session in a specific database by sharding key
|
// Query returns a new database session in a specific database by sharding key
|
||||||
func (s *DataStore) Query(key int64) *xorm.Session {
|
func (s *DataStore) Query(c *core.Context, key int64) *xorm.Session {
|
||||||
return s.Choose(key).NewSession()
|
return s.Choose(key).NewSession(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoTransaction runs a new database transaction in a specific database by sharding key
|
// DoTransaction runs a new database transaction in a specific database by sharding key
|
||||||
func (s *DataStore) DoTransaction(key int64, fn func(sess *xorm.Session) error) (err error) {
|
func (s *DataStore) DoTransaction(key int64, c *core.Context, fn func(sess *xorm.Session) error) (err error) {
|
||||||
return s.Choose(key).DoTransaction(fn)
|
return s.Choose(key).DoTransaction(c, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncStructs updates database structs by database models
|
// SyncStructs updates database structs by database models
|
||||||
func (s *DataStore) SyncStructs(beans ...interface{}) error {
|
func (s *DataStore) SyncStructs(beans ...any) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
for i := 0; i < len(s.databases); i++ {
|
for i := 0; i < len(s.databases); i++ {
|
||||||
err = s.databases[i].Sync2(beans...)
|
err = s.databases[i].engineGroup.Sync2(beans...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package datastore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -98,19 +99,19 @@ func initializeDatabase(dbConfig *settings.DatabaseConfig) (*Database, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
engineGroup.SetMaxIdleConns(dbConfig.MaxIdleConnection)
|
engineGroup.SetMaxIdleConns(int(dbConfig.MaxIdleConnection))
|
||||||
engineGroup.SetMaxOpenConns(dbConfig.MaxOpenConnection)
|
engineGroup.SetMaxOpenConns(int(dbConfig.MaxOpenConnection))
|
||||||
engineGroup.SetConnMaxLifetime(time.Duration(dbConfig.ConnectionMaxLifeTime) * time.Second)
|
engineGroup.SetConnMaxLifetime(time.Duration(dbConfig.ConnectionMaxLifeTime) * time.Second)
|
||||||
|
|
||||||
return &Database{
|
return &Database{
|
||||||
EngineGroup: engineGroup,
|
engineGroup: engineGroup,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDatabaseLogger(database *Database, config *settings.Config) {
|
func setDatabaseLogger(database *Database, config *settings.Config) {
|
||||||
if config.EnableQueryLog {
|
if config.EnableQueryLog {
|
||||||
database.SetLogger(NewXOrmLoggerAdapter(config.EnableQueryLog, config.LogLevel))
|
database.engineGroup.SetLogger(NewXOrmLoggerAdapter(config.EnableQueryLog, config.LogLevel))
|
||||||
database.ShowSQL(true)
|
database.engineGroup.ShowSQL(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,16 +127,12 @@ func getMysqlConnectionString(dbConfig *settings.DatabaseConfig) (string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getPostgresConnectionString(dbConfig *settings.DatabaseConfig) (string, error) {
|
func getPostgresConnectionString(dbConfig *settings.DatabaseConfig) (string, error) {
|
||||||
host, port := "", ""
|
host, port, err := net.SplitHostPort(dbConfig.DatabaseHost)
|
||||||
fields := strings.Split(dbConfig.DatabaseHost, ":")
|
|
||||||
|
|
||||||
if len(fields) != 2 {
|
if err != nil {
|
||||||
return "", errs.ErrDatabaseHostInvalid
|
return "", errs.ErrDatabaseHostInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
host = strings.TrimSpace(fields[0])
|
|
||||||
port = strings.TrimSpace(fields[1])
|
|
||||||
|
|
||||||
if strings.HasPrefix(dbConfig.DatabaseHost, "/") { // unix socket path
|
if strings.HasPrefix(dbConfig.DatabaseHost, "/") { // unix socket path
|
||||||
return fmt.Sprintf("postgres://%s:%s@:%s/%s?sslmode=%s&host=%s",
|
return fmt.Sprintf("postgres://%s:%s@:%s/%s?sslmode=%s&host=%s",
|
||||||
url.QueryEscape(dbConfig.DatabaseUser), url.QueryEscape(dbConfig.DatabasePassword), port, dbConfig.DatabaseName, dbConfig.DatabaseSSLMode, host), nil
|
url.QueryEscape(dbConfig.DatabaseUser), url.QueryEscape(dbConfig.DatabasePassword), port, dbConfig.DatabaseName, dbConfig.DatabaseSSLMode, host), nil
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package datastore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"xorm.io/xorm/log"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XOrmContextAdapter represents the context adapter for xorm
|
||||||
|
type XOrmContextAdapter struct {
|
||||||
|
requestId string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deadline does nothing
|
||||||
|
func (c *XOrmContextAdapter) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done always returns nil
|
||||||
|
func (c *XOrmContextAdapter) Done() <-chan struct{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err always returns nil
|
||||||
|
func (c *XOrmContextAdapter) Err() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value associated with this context for key, or nil
|
||||||
|
// if no value is associated with key.
|
||||||
|
func (c *XOrmContextAdapter) Value(key any) any {
|
||||||
|
if key == log.SessionIDKey && c.requestId != "" {
|
||||||
|
return fmt.Sprintf("r=%s", c.requestId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewXOrmContextAdapter(c *core.Context) *XOrmContextAdapter {
|
||||||
|
if c != nil {
|
||||||
|
return &XOrmContextAdapter{
|
||||||
|
requestId: c.GetRequestId(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &XOrmContextAdapter{}
|
||||||
|
}
|
||||||
@@ -14,42 +14,42 @@ type XOrmLoggerAdapter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debug logs debug log
|
// Debug logs debug log
|
||||||
func (logger XOrmLoggerAdapter) Debug(v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Debug(v ...any) {
|
||||||
log.SqlQuery(v...)
|
log.SqlQuery(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs debug log with custom format
|
// Debugf logs debug log with custom format
|
||||||
func (logger XOrmLoggerAdapter) Debugf(format string, v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Debugf(format string, v ...any) {
|
||||||
log.SqlQueryf(format, v...)
|
log.SqlQueryf(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info logs info log
|
// Info logs info log
|
||||||
func (logger XOrmLoggerAdapter) Info(v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Info(v ...any) {
|
||||||
log.SqlQuery(v...)
|
log.SqlQuery(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infof logs info log with custom format
|
// Infof logs info log with custom format
|
||||||
func (logger XOrmLoggerAdapter) Infof(format string, v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Infof(format string, v ...any) {
|
||||||
log.SqlQueryf(format, v...)
|
log.SqlQueryf(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn logs warn log
|
// Warn logs warn log
|
||||||
func (logger XOrmLoggerAdapter) Warn(v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Warn(v ...any) {
|
||||||
log.SqlQuery(v...)
|
log.SqlQuery(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnf logs warn log with custom format
|
// Warnf logs warn log with custom format
|
||||||
func (logger XOrmLoggerAdapter) Warnf(format string, v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Warnf(format string, v ...any) {
|
||||||
log.SqlQueryf(format, v...)
|
log.SqlQueryf(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error logs error log
|
// Error logs error log
|
||||||
func (logger XOrmLoggerAdapter) Error(v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Error(v ...any) {
|
||||||
log.SqlQuery(v...)
|
log.SqlQuery(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf logs error log with custom format
|
// Errorf logs error log with custom format
|
||||||
func (logger XOrmLoggerAdapter) Errorf(format string, v ...interface{}) {
|
func (logger XOrmLoggerAdapter) Errorf(format string, v ...any) {
|
||||||
log.SqlQueryf(format, v...)
|
log.SqlQueryf(format, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-8
@@ -1,7 +1,7 @@
|
|||||||
package errs
|
package errs
|
||||||
|
|
||||||
// ErrorCategory represents error category
|
// ErrorCategory represents error category
|
||||||
type ErrorCategory int
|
type ErrorCategory int32
|
||||||
|
|
||||||
// Error categories
|
// Error categories
|
||||||
const (
|
const (
|
||||||
@@ -14,6 +14,7 @@ const (
|
|||||||
SystemSubcategoryDefault = 0
|
SystemSubcategoryDefault = 0
|
||||||
SystemSubcategorySetting = 1
|
SystemSubcategorySetting = 1
|
||||||
SystemSubcategoryDatabase = 2
|
SystemSubcategoryDatabase = 2
|
||||||
|
SystemSubcategoryMail = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sub categories of normal error
|
// Sub categories of normal error
|
||||||
@@ -32,11 +33,12 @@ const (
|
|||||||
// Error represents the specific error returned to user
|
// Error represents the specific error returned to user
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Category ErrorCategory
|
Category ErrorCategory
|
||||||
SubCategory int
|
SubCategory int32
|
||||||
Index int
|
Index int32
|
||||||
HttpStatusCode int
|
HttpStatusCode int
|
||||||
Message string
|
Message string
|
||||||
BaseError []error
|
BaseError []error
|
||||||
|
Context any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the error message
|
// Error returns the error message
|
||||||
@@ -45,12 +47,12 @@ func (err *Error) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Code returns the error code
|
// Code returns the error code
|
||||||
func (err *Error) Code() int {
|
func (err *Error) Code() int32 {
|
||||||
return int(err.Category)*100000 + err.SubCategory*1000 + err.Index
|
return int32(err.Category)*100000 + err.SubCategory*1000 + err.Index
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new error instance
|
// New returns a new error instance
|
||||||
func New(category ErrorCategory, subCategory int, index int, httpStatusCode int, message string, baseError ...error) *Error {
|
func New(category ErrorCategory, subCategory int32, index int32, httpStatusCode int, message string, baseError ...error) *Error {
|
||||||
return &Error{
|
return &Error{
|
||||||
Category: category,
|
Category: category,
|
||||||
SubCategory: subCategory,
|
SubCategory: subCategory,
|
||||||
@@ -62,12 +64,12 @@ func New(category ErrorCategory, subCategory int, index int, httpStatusCode int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSystemError returns a new system error instance
|
// NewSystemError returns a new system error instance
|
||||||
func NewSystemError(subCategory int, index int, httpStatusCode int, message string) *Error {
|
func NewSystemError(subCategory int32, index int32, httpStatusCode int, message string) *Error {
|
||||||
return New(CATEGORY_SYSTEM, subCategory, index, httpStatusCode, message)
|
return New(CATEGORY_SYSTEM, subCategory, index, httpStatusCode, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNormalError returns a new normal error instance
|
// NewNormalError returns a new normal error instance
|
||||||
func NewNormalError(subCategory int, index int, httpStatusCode int, message string) *Error {
|
func NewNormalError(subCategory int32, index int32, httpStatusCode int, message string) *Error {
|
||||||
return New(CATEGORY_NORMAL, subCategory, index, httpStatusCode, message)
|
return New(CATEGORY_NORMAL, subCategory, index, httpStatusCode, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +82,19 @@ func NewIncompleteOrIncorrectSubmissionError(err error) *Error {
|
|||||||
ErrIncompleteOrIncorrectSubmission.Message, err)
|
ErrIncompleteOrIncorrectSubmission.Message, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewErrorWithContext returns a new error instance with specified context
|
||||||
|
func NewErrorWithContext(baseError *Error, context any) *Error {
|
||||||
|
return &Error{
|
||||||
|
Category: baseError.Category,
|
||||||
|
SubCategory: baseError.SubCategory,
|
||||||
|
Index: baseError.Index,
|
||||||
|
HttpStatusCode: baseError.HttpStatusCode,
|
||||||
|
Message: baseError.Message,
|
||||||
|
BaseError: baseError.BaseError,
|
||||||
|
Context: context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Or would return the error from err parameter if the this error is defined in this project,
|
// Or would return the error from err parameter if the this error is defined in this project,
|
||||||
// or return the default error
|
// or return the default error
|
||||||
func Or(err error, defaultErr *Error) *Error {
|
func Or(err error, defaultErr *Error) *Error {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package errs
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Error codes related to mail
|
||||||
|
var (
|
||||||
|
ErrSMTPServerNotEnabled = NewSystemError(SystemSubcategoryMail, 0, http.StatusInternalServerError, "SMTP server is not enabled")
|
||||||
|
ErrSMTPServerHostInvalid = NewSystemError(SystemSubcategoryMail, 1, http.StatusInternalServerError, "SMTP server host is invalid")
|
||||||
|
)
|
||||||
+7
-5
@@ -4,9 +4,11 @@ import "net/http"
|
|||||||
|
|
||||||
// Error codes related to settings
|
// Error codes related to settings
|
||||||
var (
|
var (
|
||||||
ErrInvalidProtocol = NewSystemError(SystemSubcategorySetting, 0, http.StatusInternalServerError, "invalid server protocol")
|
ErrInvalidProtocol = NewSystemError(SystemSubcategorySetting, 0, http.StatusInternalServerError, "invalid server protocol")
|
||||||
ErrInvalidLogMode = NewSystemError(SystemSubcategorySetting, 1, http.StatusInternalServerError, "invalid log mode")
|
ErrInvalidLogMode = NewSystemError(SystemSubcategorySetting, 1, http.StatusInternalServerError, "invalid log mode")
|
||||||
ErrGettingLocalAddress = NewSystemError(SystemSubcategorySetting, 2, http.StatusInternalServerError, "failed to get local address")
|
ErrGettingLocalAddress = NewSystemError(SystemSubcategorySetting, 2, http.StatusInternalServerError, "failed to get local address")
|
||||||
ErrInvalidUuidMode = NewSystemError(SystemSubcategorySetting, 3, http.StatusInternalServerError, "invalid uuid mode")
|
ErrInvalidUuidMode = NewSystemError(SystemSubcategorySetting, 3, http.StatusInternalServerError, "invalid uuid mode")
|
||||||
ErrInvalidExchangeRatesDataSource = NewSystemError(SystemSubcategorySetting, 4, http.StatusInternalServerError, "invalid exchange rates data source")
|
ErrInvalidExchangeRatesDataSource = NewSystemError(SystemSubcategorySetting, 4, http.StatusInternalServerError, "invalid exchange rates data source")
|
||||||
|
ErrInvalidMapProvider = NewSystemError(SystemSubcategorySetting, 5, http.StatusInternalServerError, "invalid map provider")
|
||||||
|
ErrInvalidAmapSecurityVerificationMethod = NewSystemError(SystemSubcategorySetting, 6, http.StatusInternalServerError, "invalid amap security verification method")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ var (
|
|||||||
ErrApiNotFound = NewSystemError(SystemSubcategoryDefault, 1, http.StatusNotFound, "api not found")
|
ErrApiNotFound = NewSystemError(SystemSubcategoryDefault, 1, http.StatusNotFound, "api not found")
|
||||||
ErrMethodNotAllowed = NewSystemError(SystemSubcategoryDefault, 2, http.StatusMethodNotAllowed, "method not allowed")
|
ErrMethodNotAllowed = NewSystemError(SystemSubcategoryDefault, 2, http.StatusMethodNotAllowed, "method not allowed")
|
||||||
ErrNotImplemented = NewSystemError(SystemSubcategoryDefault, 3, http.StatusNotImplemented, "not implemented")
|
ErrNotImplemented = NewSystemError(SystemSubcategoryDefault, 3, http.StatusNotImplemented, "not implemented")
|
||||||
|
ErrSystemIsBusy = NewSystemError(SystemSubcategoryDefault, 4, http.StatusNotImplemented, "system is busy")
|
||||||
)
|
)
|
||||||
|
|||||||
+15
-12
@@ -6,16 +6,19 @@ import (
|
|||||||
|
|
||||||
// Error codes related to tokens
|
// Error codes related to tokens
|
||||||
var (
|
var (
|
||||||
ErrTokenGenerating = NewNormalError(NormalSubcategoryToken, 0, http.StatusInternalServerError, "failed to generate token")
|
ErrTokenGenerating = NewNormalError(NormalSubcategoryToken, 0, http.StatusInternalServerError, "failed to generate token")
|
||||||
ErrUnauthorizedAccess = NewNormalError(NormalSubcategoryToken, 1, http.StatusUnauthorized, "unauthorized access")
|
ErrUnauthorizedAccess = NewNormalError(NormalSubcategoryToken, 1, http.StatusUnauthorized, "unauthorized access")
|
||||||
ErrCurrentInvalidToken = NewNormalError(NormalSubcategoryToken, 2, http.StatusUnauthorized, "current token is invalid")
|
ErrCurrentInvalidToken = NewNormalError(NormalSubcategoryToken, 2, http.StatusUnauthorized, "current token is invalid")
|
||||||
ErrCurrentTokenExpired = NewNormalError(NormalSubcategoryToken, 3, http.StatusUnauthorized, "current token is expired")
|
ErrCurrentTokenExpired = NewNormalError(NormalSubcategoryToken, 3, http.StatusUnauthorized, "current token is expired")
|
||||||
ErrCurrentInvalidTokenType = NewNormalError(NormalSubcategoryToken, 4, http.StatusUnauthorized, "current token type is invalid")
|
ErrCurrentInvalidTokenType = NewNormalError(NormalSubcategoryToken, 4, http.StatusUnauthorized, "current token type is invalid")
|
||||||
ErrCurrentTokenRequire2FA = NewNormalError(NormalSubcategoryToken, 5, http.StatusUnauthorized, "current token requires two factor authorization")
|
ErrCurrentTokenRequire2FA = NewNormalError(NormalSubcategoryToken, 5, http.StatusUnauthorized, "current token requires two factor authorization")
|
||||||
ErrCurrentTokenNotRequire2FA = NewNormalError(NormalSubcategoryToken, 6, http.StatusUnauthorized, "current token does not require two factor authorization")
|
ErrCurrentTokenNotRequire2FA = NewNormalError(NormalSubcategoryToken, 6, http.StatusUnauthorized, "current token does not require two factor authorization")
|
||||||
ErrInvalidToken = NewNormalError(NormalSubcategoryToken, 7, http.StatusBadRequest, "token is invalid")
|
ErrInvalidToken = NewNormalError(NormalSubcategoryToken, 7, http.StatusBadRequest, "token is invalid")
|
||||||
ErrInvalidTokenId = NewNormalError(NormalSubcategoryToken, 8, http.StatusBadRequest, "token id is invalid")
|
ErrInvalidTokenId = NewNormalError(NormalSubcategoryToken, 8, http.StatusBadRequest, "token id is invalid")
|
||||||
ErrInvalidUserTokenId = NewNormalError(NormalSubcategoryToken, 9, http.StatusBadRequest, "user token id is invalid")
|
ErrInvalidUserTokenId = NewNormalError(NormalSubcategoryToken, 9, http.StatusBadRequest, "user token id is invalid")
|
||||||
ErrTokenRecordNotFound = NewNormalError(NormalSubcategoryToken, 10, http.StatusBadRequest, "token is not found")
|
ErrTokenRecordNotFound = NewNormalError(NormalSubcategoryToken, 10, http.StatusBadRequest, "token is not found")
|
||||||
ErrTokenExpired = NewNormalError(NormalSubcategoryToken, 11, http.StatusBadRequest, "token is expired")
|
ErrTokenExpired = NewNormalError(NormalSubcategoryToken, 11, http.StatusBadRequest, "token is expired")
|
||||||
|
ErrTokenIsEmpty = NewNormalError(NormalSubcategoryToken, 12, http.StatusBadRequest, "token is empty")
|
||||||
|
ErrEmailVerifyTokenIsInvalidOrExpired = NewNormalError(NormalSubcategoryToken, 13, http.StatusBadRequest, "email verify token is invalid or expired")
|
||||||
|
ErrPasswordResetTokenIsInvalidOrExpired = NewNormalError(NormalSubcategoryToken, 14, http.StatusBadRequest, "password reset token is invalid or expired")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,4 +21,12 @@ var (
|
|||||||
ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists")
|
ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists")
|
||||||
ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists")
|
ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists")
|
||||||
ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed")
|
ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed")
|
||||||
|
ErrUserDefaultAccountIsInvalid = NewNormalError(NormalSubcategoryUser, 15, http.StatusBadRequest, "user default account is invalid")
|
||||||
|
ErrUserIsDisabled = NewNormalError(NormalSubcategoryUser, 16, http.StatusBadRequest, "user is disabled")
|
||||||
|
ErrEmptyIsInvalid = NewNormalError(NormalSubcategoryUser, 17, http.StatusBadRequest, "email is invalid")
|
||||||
|
ErrEmailIsEmptyOrInvalid = NewNormalError(NormalSubcategoryUser, 18, http.StatusBadRequest, "email is empty or invalid")
|
||||||
|
ErrNewPasswordEqualsOldInvalid = NewNormalError(NormalSubcategoryUser, 19, http.StatusBadRequest, "new password equals old password")
|
||||||
|
ErrEmailIsNotVerified = NewNormalError(NormalSubcategoryUser, 20, http.StatusBadRequest, "email is not verified")
|
||||||
|
ErrEmailIsVerified = NewNormalError(NormalSubcategoryUser, 21, http.StatusBadRequest, "email is verified")
|
||||||
|
ErrEmailValidationNotAllowed = NewNormalError(NormalSubcategoryUser, 22, http.StatusBadRequest, "email validation not allowed")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type BankOfCanadaExchangeRateData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BankOfCanadaObservationData represents the observation data from bank of Canada
|
// BankOfCanadaObservationData represents the observation data from bank of Canada
|
||||||
type BankOfCanadaObservationData map[string]interface{}
|
type BankOfCanadaObservationData map[string]any
|
||||||
|
|
||||||
// ToLatestExchangeRateResponse returns a view-object according to original data from bank of Canada
|
// ToLatestExchangeRateResponse returns a view-object according to original data from bank of Canada
|
||||||
func (e *BankOfCanadaExchangeRateData) ToLatestExchangeRateResponse(c *core.Context) *models.LatestExchangeRateResponse {
|
func (e *BankOfCanadaExchangeRateData) ToLatestExchangeRateResponse(c *core.Context) *models.LatestExchangeRateResponse {
|
||||||
@@ -62,7 +62,7 @@ func (e *BankOfCanadaExchangeRateData) ToLatestExchangeRateResponse(c *core.Cont
|
|||||||
|
|
||||||
currencyCode := utils.SubString(typeName, 2, 3)
|
currencyCode := utils.SubString(typeName, 2, 3)
|
||||||
|
|
||||||
if data, ok := exchangeRateData.(map[string]interface{}); ok {
|
if data, ok := exchangeRateData.(map[string]any); ok {
|
||||||
exchangeRate := data["v"]
|
exchangeRate := data["v"]
|
||||||
|
|
||||||
if exchangeRateValue, ok2 := exchangeRate.(string); ok2 {
|
if exchangeRateValue, ok2 := exchangeRate.(string); ok2 {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const euroCentralBankDataSource = "European Central Bank"
|
|||||||
const euroCentralBankBaseCurrency = "EUR"
|
const euroCentralBankBaseCurrency = "EUR"
|
||||||
|
|
||||||
const euroCentralBankDataUpdateDateFormat = "2006-01-02 15"
|
const euroCentralBankDataUpdateDateFormat = "2006-01-02 15"
|
||||||
const euroCentralBankDataUpdateDateTimezone = "Etc/GMT-1" // UTC+01:00
|
const euroCentralBankDataUpdateDateTimezone = "Europe/Berlin"
|
||||||
|
|
||||||
// EuroCentralBankDataSource defines the structure of exchange rates data source of euro central bank
|
// EuroCentralBankDataSource defines the structure of exchange rates data source of euro central bank
|
||||||
type EuroCentralBankDataSource struct {
|
type EuroCentralBankDataSource struct {
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ func InitializeExchangeRatesDataSource(config *settings.Config) error {
|
|||||||
} else if config.ExchangeRatesDataSource == settings.NationalBankOfPolandDataSource {
|
} else if config.ExchangeRatesDataSource == settings.NationalBankOfPolandDataSource {
|
||||||
Container.Current = &NationalBankOfPolandDataSource{}
|
Container.Current = &NationalBankOfPolandDataSource{}
|
||||||
return nil
|
return nil
|
||||||
|
} else if config.ExchangeRatesDataSource == settings.MonetaryAuthorityOfSingaporeDataSource {
|
||||||
|
Container.Current = &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errs.ErrInvalidExchangeRatesDataSource
|
return errs.ErrInvalidExchangeRatesDataSource
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package exchangerates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/validators"
|
||||||
|
)
|
||||||
|
|
||||||
|
const monetaryAuthorityOfSingaporeExchangeRateUrl = "https://eservices.mas.gov.sg/api/action/datastore/search.json?resource_id=95932927-c8bc-4e7a-b484-68a66a24edfe&sort=end_of_day+desc&limit=1"
|
||||||
|
const monetaryAuthorityOfSingaporeExchangeRateReferenceUrl = "https://eservices.mas.gov.sg/Statistics/msb/ExchangeRates.aspx"
|
||||||
|
const monetaryAuthorityOfSingaporeDataSource = "Monetary Authority of Singapore"
|
||||||
|
const monetaryAuthorityOfSingaporeBaseCurrency = "SGD"
|
||||||
|
|
||||||
|
const monetaryAuthorityOfSingaporeDataUpdateDateFormat = "2006-01-02 15"
|
||||||
|
const monetaryAuthorityOfSingaporeDataUpdateDateTimezone = "Asia/Singapore"
|
||||||
|
|
||||||
|
// MonetaryAuthorityOfSingaporeDataSource defines the structure of exchange rates data source of Monetary Authority of Singapore
|
||||||
|
type MonetaryAuthorityOfSingaporeDataSource struct {
|
||||||
|
ExchangeRatesDataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonetaryAuthorityOfSingaporeExchangeRateData represents the whole data from Monetary Authority of Singapore
|
||||||
|
type MonetaryAuthorityOfSingaporeExchangeRateData struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Result *MonetaryAuthorityOfSingaporeResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonetaryAuthorityOfSingaporeResult represents the actual result from Monetary Authority of Singapore
|
||||||
|
type MonetaryAuthorityOfSingaporeResult struct {
|
||||||
|
Records []MonetaryAuthorityOfSingaporeRecord `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonetaryAuthorityOfSingaporeRecord represents the record from Monetary Authority of Singapore
|
||||||
|
type MonetaryAuthorityOfSingaporeRecord map[string]string
|
||||||
|
|
||||||
|
// ToLatestExchangeRateResponse returns a view-object according to original data from Monetary Authority of Singapore
|
||||||
|
func (e *MonetaryAuthorityOfSingaporeExchangeRateData) ToLatestExchangeRateResponse(c *core.Context) *models.LatestExchangeRateResponse {
|
||||||
|
if !e.Success {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.ToLatestExchangeRateResponse] response is not success")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Result == nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.ToLatestExchangeRateResponse] result is null")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Result.Records) < 1 {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.ToLatestExchangeRateResponse] records is empty")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDayRecord := e.Result.Records[0]
|
||||||
|
exchangeRates := make(models.LatestExchangeRateSlice, 0, len(lastDayRecord))
|
||||||
|
latestUpdateDate := ""
|
||||||
|
|
||||||
|
for key, value := range lastDayRecord {
|
||||||
|
if key == "end_of_day" {
|
||||||
|
latestUpdateDate = value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeRate := e.parseExchangeRateResponse(c, key, value)
|
||||||
|
|
||||||
|
if exchangeRate == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeRates = append(exchangeRates, exchangeRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
timezone, err := time.LoadLocation(monetaryAuthorityOfSingaporeDataUpdateDateTimezone)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.ToLatestExchangeRateResponse] failed to get timezone, timezone name is %s", monetaryAuthorityOfSingaporeDataUpdateDateTimezone)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDateTime := latestUpdateDate + " 12" // These rates are the average of buying and selling interbank rates quoted around midday in Singapore
|
||||||
|
updateTime, err := time.ParseInLocation(monetaryAuthorityOfSingaporeDataUpdateDateFormat, updateDateTime, timezone)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.ToLatestExchangeRateResponse] failed to parse update date, datetime is %s", updateDateTime)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
latestExchangeRateResp := &models.LatestExchangeRateResponse{
|
||||||
|
DataSource: monetaryAuthorityOfSingaporeDataSource,
|
||||||
|
ReferenceUrl: monetaryAuthorityOfSingaporeExchangeRateReferenceUrl,
|
||||||
|
UpdateTime: updateTime.Unix(),
|
||||||
|
BaseCurrency: monetaryAuthorityOfSingaporeBaseCurrency,
|
||||||
|
ExchangeRates: exchangeRates,
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestExchangeRateResp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MonetaryAuthorityOfSingaporeExchangeRateData) parseExchangeRateResponse(c *core.Context, key string, value string) *models.LatestExchangeRate {
|
||||||
|
if !strings.Contains(key, "_") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
items := strings.Split(key, "_")
|
||||||
|
|
||||||
|
if len(items) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fromCurrencyCode := strings.ToUpper(items[0])
|
||||||
|
toCurrencyCode := strings.ToUpper(items[1])
|
||||||
|
|
||||||
|
if _, exists := validators.AllCurrencyNames[fromCurrencyCode]; !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if toCurrencyCode != monetaryAuthorityOfSingaporeBaseCurrency {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rate, err := utils.StringToFloat64(value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[monetary_authority_of_singapore_datasource.parseExchangeRateResponse] failed to parse rate, rate is %s", value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rate <= 0 {
|
||||||
|
log.WarnfWithRequestId(c, "[monetary_authority_of_singapore_datasource.parseExchangeRateResponse] rate is invalid, rate is %s", value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
finalRate := 1 / rate
|
||||||
|
|
||||||
|
if math.IsInf(finalRate, 0) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(items) == 3 && items[2] == "100" {
|
||||||
|
finalRate = finalRate * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.LatestExchangeRate{
|
||||||
|
Currency: fromCurrencyCode,
|
||||||
|
Rate: utils.Float64ToString(finalRate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequestUrls returns the Monetary Authority of Singapore data source urls
|
||||||
|
func (e *MonetaryAuthorityOfSingaporeDataSource) GetRequestUrls() []string {
|
||||||
|
return []string{monetaryAuthorityOfSingaporeExchangeRateUrl}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse returns the common response entity according to the Monetary Authority of Singapore data source raw response
|
||||||
|
func (e *MonetaryAuthorityOfSingaporeDataSource) Parse(c *core.Context, content []byte) (*models.LatestExchangeRateResponse, error) {
|
||||||
|
monetaryAuthorityOfSingaporeData := &MonetaryAuthorityOfSingaporeExchangeRateData{}
|
||||||
|
err := json.Unmarshal(content, monetaryAuthorityOfSingaporeData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.Parse] failed to parse json data, content is %s, because %s", string(content), err.Error())
|
||||||
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
|
}
|
||||||
|
|
||||||
|
latestExchangeRateResponse := monetaryAuthorityOfSingaporeData.ToLatestExchangeRateResponse(c)
|
||||||
|
|
||||||
|
if latestExchangeRateResponse == nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[monetary_authority_of_singapore_datasource.Parse] failed to parse latest exchange rate data, content is %s", string(content))
|
||||||
|
return nil, errs.ErrFailedToRequestRemoteApi
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestExchangeRateResponse, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package exchangerates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const monetaryAuthorityOfSingaporeMinimumRequiredContent = "{\n" +
|
||||||
|
" \"success\": true,\n" +
|
||||||
|
" \"result\": {\n" +
|
||||||
|
" \"records\": [\n" +
|
||||||
|
" {\n" +
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n" +
|
||||||
|
" \"usd_sgd\": \"1.3528\",\n" +
|
||||||
|
" \"cny_sgd_100\": \"19.16\"\n" +
|
||||||
|
" }\n" +
|
||||||
|
" ]\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}"
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_StandardDataExtractBaseCurrency(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte(monetaryAuthorityOfSingaporeMinimumRequiredContent))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "SGD", actualLatestExchangeRateResponse.BaseCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_StandardDataExtractExchangeRates(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte(monetaryAuthorityOfSingaporeMinimumRequiredContent))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Contains(t, actualLatestExchangeRateResponse.ExchangeRates, &models.LatestExchangeRate{
|
||||||
|
Currency: "USD",
|
||||||
|
Rate: "0.7392075694855116",
|
||||||
|
})
|
||||||
|
assert.Contains(t, actualLatestExchangeRateResponse.ExchangeRates, &models.LatestExchangeRate{
|
||||||
|
Currency: "CNY",
|
||||||
|
Rate: "5.219206680584551",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_BlankContent(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dataSource.Parse(context, []byte(""))
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_EmptyJsonObject(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dataSource.Parse(context, []byte("{}"))
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_ResponseSuccessIsFalseObject(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": false,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" {\n"+
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n"+
|
||||||
|
" \"usd_sgd\": \"1.3528\",\n"+
|
||||||
|
" \"cny_sgd_100\": \"19.16\"\n"+
|
||||||
|
" }\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_NoResultContent(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true\n"+
|
||||||
|
"}"))
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_EmptyRecordContent(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_TargetCurrencyIsNotBaseCurrency(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" {\n"+
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n"+
|
||||||
|
" \"usd_cny\": \"1\""+
|
||||||
|
" }\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Len(t, actualLatestExchangeRateResponse.ExchangeRates, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_InvalidCurrency(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" {\n"+
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n"+
|
||||||
|
" \"xxx_sgd\": \"1.3528\""+
|
||||||
|
" }\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Len(t, actualLatestExchangeRateResponse.ExchangeRates, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_EmptyRate(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" {\n"+
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n"+
|
||||||
|
" \"usd_sgd\": \"\""+
|
||||||
|
" }\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Len(t, actualLatestExchangeRateResponse.ExchangeRates, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonetaryAuthorityOfSingaporeDataSource_InvalidRate(t *testing.T) {
|
||||||
|
dataSource := &MonetaryAuthorityOfSingaporeDataSource{}
|
||||||
|
context := &core.Context{
|
||||||
|
Context: &gin.Context{},
|
||||||
|
}
|
||||||
|
|
||||||
|
actualLatestExchangeRateResponse, err := dataSource.Parse(context, []byte("{\n"+
|
||||||
|
" \"success\": true,\n"+
|
||||||
|
" \"result\": {\n"+
|
||||||
|
" \"records\": [\n"+
|
||||||
|
" {\n"+
|
||||||
|
" \"end_of_day\": \"2023-05-26\",\n"+
|
||||||
|
" \"usd_sgd\": null"+
|
||||||
|
" }\n"+
|
||||||
|
" ]\n"+
|
||||||
|
" }\n"+
|
||||||
|
"}"))
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Len(t, actualLatestExchangeRateResponse.ExchangeRates, 0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package locales
|
||||||
|
|
||||||
|
// DefaultLanguage represents the default language
|
||||||
|
var DefaultLanguage = en
|
||||||
|
|
||||||
|
// AllLanguages represents all the supported language
|
||||||
|
var AllLanguages = map[string]*LocaleInfo{
|
||||||
|
"en": {
|
||||||
|
Content: en,
|
||||||
|
},
|
||||||
|
"zh-Hans": {
|
||||||
|
Content: zhHans,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLocaleTextItems(locale string) *LocaleTextItems {
|
||||||
|
localeInfo, exists := AllLanguages[locale]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
return localeInfo.Content
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultLanguage
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package locales
|
||||||
|
|
||||||
|
// LocaleTextItems represents all text items need to be translated
|
||||||
|
type LocaleTextItems struct {
|
||||||
|
VerifyEmailTextItems *VerifyEmailTextItems
|
||||||
|
ForgetPasswordMailTextItems *ForgetPasswordMailTextItems
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyEmailTextItems represents text items need to be translated in verify mail
|
||||||
|
type VerifyEmailTextItems struct {
|
||||||
|
Title string
|
||||||
|
SalutationFormat string
|
||||||
|
DescriptionAboveBtn string
|
||||||
|
VerifyEmail string
|
||||||
|
DescriptionBelowBtnFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForgetPasswordMailTextItems represents text items need to be translated in forget password mail
|
||||||
|
type ForgetPasswordMailTextItems struct {
|
||||||
|
Title string
|
||||||
|
SalutationFormat string
|
||||||
|
DescriptionAboveBtn string
|
||||||
|
ResetPassword string
|
||||||
|
DescriptionBelowBtnFormat string
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package locales
|
||||||
|
|
||||||
|
var en = &LocaleTextItems{
|
||||||
|
VerifyEmailTextItems: &VerifyEmailTextItems{
|
||||||
|
Title: "Verify Email",
|
||||||
|
SalutationFormat: "Hi %s,",
|
||||||
|
DescriptionAboveBtn: "Please click the link below to confirm your email address.",
|
||||||
|
VerifyEmail: "Verify Email",
|
||||||
|
DescriptionBelowBtnFormat: "If you did not sign up for %s account, please simply disregard this email. If you cannot click the link above, please copy the above url and paste it into your browser. The verify email link will be expired after %v minutes.",
|
||||||
|
},
|
||||||
|
ForgetPasswordMailTextItems: &ForgetPasswordMailTextItems{
|
||||||
|
Title: "Reset Your Password",
|
||||||
|
SalutationFormat: "Hi %s,",
|
||||||
|
DescriptionAboveBtn: "We recently received a request to reset your password. You can click the link below to reset your password.",
|
||||||
|
ResetPassword: "Reset Password",
|
||||||
|
DescriptionBelowBtnFormat: "If you did not request to reset your password, please simply disregard this email. If you cannot click the link above, please copy the above url and paste it into your browser. The password reset link will be expired after %v minutes.",
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package locales
|
||||||
|
|
||||||
|
// LocaleInfo represents locale info
|
||||||
|
type LocaleInfo struct {
|
||||||
|
Aliases []string
|
||||||
|
Content *LocaleTextItems
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package locales
|
||||||
|
|
||||||
|
var zhHans = &LocaleTextItems{
|
||||||
|
VerifyEmailTextItems: &VerifyEmailTextItems{
|
||||||
|
Title: "验证邮箱",
|
||||||
|
SalutationFormat: "%s 您好,",
|
||||||
|
DescriptionAboveBtn: "请点击下方的链接确认您的邮箱地址。",
|
||||||
|
VerifyEmail: "验证邮箱",
|
||||||
|
DescriptionBelowBtnFormat: "如果您没有注册 %s 账户,请直接忽略本邮件。如果您无法点击上述链接,请复制下方的地址然后在您的浏览器中粘贴。邮箱验证链接将在 %v 分钟后过期。",
|
||||||
|
},
|
||||||
|
ForgetPasswordMailTextItems: &ForgetPasswordMailTextItems{
|
||||||
|
Title: "重置密码",
|
||||||
|
SalutationFormat: "%s 您好,",
|
||||||
|
DescriptionAboveBtn: "我们刚才收到重置您密码的请求。您可以点击下方链接重置您的密码。",
|
||||||
|
ResetPassword: "重置密码",
|
||||||
|
DescriptionBelowBtnFormat: "如果您没有请求重置密码,请直接忽略本邮件。如果您无法点击上述链接,请复制下方的地址然后在您的浏览器中粘贴。重置密码链接将在 %v 分钟后过期。",
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -41,12 +41,12 @@ func (f *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|||||||
b.WriteString("] ")
|
b.WriteString("] ")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString(entry.Message)
|
|
||||||
|
|
||||||
if requestId, exists := entry.Data[logFieldRequestId]; exists {
|
if requestId, exists := entry.Data[logFieldRequestId]; exists {
|
||||||
b.WriteString(fmt.Sprintf(", r=%s", requestId))
|
b.WriteString(fmt.Sprintf("[r=%s] ", requestId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.WriteString(entry.Message)
|
||||||
|
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
|
||||||
if extra, exists := entry.Data[logFieldExtra]; exists {
|
if extra, exists := entry.Data[logFieldExtra]; exists {
|
||||||
|
|||||||
+38
-33
@@ -39,11 +39,13 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetLoggerConfiguration sets the logger according to the config
|
// SetLoggerConfiguration sets the logger according to the config
|
||||||
func SetLoggerConfiguration(config *settings.Config) error {
|
func SetLoggerConfiguration(config *settings.Config, isDisableBootLog bool) error {
|
||||||
var bootWriters []io.Writer
|
var bootWriters []io.Writer
|
||||||
var writers []io.Writer
|
var writers []io.Writer
|
||||||
|
|
||||||
bootWriters = append(bootWriters, os.Stdout)
|
if !isDisableBootLog {
|
||||||
|
bootWriters = append(bootWriters, os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
if config.EnableConsoleLog {
|
if config.EnableConsoleLog {
|
||||||
writers = append(writers, os.Stdout)
|
writers = append(writers, os.Stdout)
|
||||||
@@ -56,7 +58,10 @@ func SetLoggerConfiguration(config *settings.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bootWriters = append(bootWriters, logFile)
|
if !isDisableBootLog {
|
||||||
|
bootWriters = append(bootWriters, logFile)
|
||||||
|
}
|
||||||
|
|
||||||
writers = append(writers, logFile)
|
writers = append(writers, logFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,93 +99,93 @@ func SetLoggerConfiguration(config *settings.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs debug log with custom format
|
// Debugf logs debug log with custom format
|
||||||
func Debugf(format string, args ...interface{}) {
|
func Debugf(format string, args ...any) {
|
||||||
defaultLogger.Debugf(getFinalLog(format, args...))
|
defaultLogger.Debug(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebugfWithRequestId logs debug log with custom format and request id
|
// DebugfWithRequestId logs debug log with custom format and request id
|
||||||
func DebugfWithRequestId(c *core.Context, format string, args ...interface{}) {
|
func DebugfWithRequestId(c *core.Context, format string, args ...any) {
|
||||||
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Debugf(getFinalLog(format, args...))
|
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Debug(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infof logs info log with custom format
|
// Infof logs info log with custom format
|
||||||
func Infof(format string, args ...interface{}) {
|
func Infof(format string, args ...any) {
|
||||||
defaultLogger.Infof(getFinalLog(format, args...))
|
defaultLogger.Info(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfofWithRequestId logs info log with custom format and request id
|
// InfofWithRequestId logs info log with custom format and request id
|
||||||
func InfofWithRequestId(c *core.Context, format string, args ...interface{}) {
|
func InfofWithRequestId(c *core.Context, format string, args ...any) {
|
||||||
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Infof(getFinalLog(format, args...))
|
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Info(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnf logs warn log with custom format
|
// Warnf logs warn log with custom format
|
||||||
func Warnf(format string, args ...interface{}) {
|
func Warnf(format string, args ...any) {
|
||||||
defaultLogger.Warnf(getFinalLog(format, args...))
|
defaultLogger.Warn(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// WarnfWithRequestId logs warn log with custom format and request id
|
// WarnfWithRequestId logs warn log with custom format and request id
|
||||||
func WarnfWithRequestId(c *core.Context, format string, args ...interface{}) {
|
func WarnfWithRequestId(c *core.Context, format string, args ...any) {
|
||||||
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Warnf(getFinalLog(format, args...))
|
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Warn(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf logs error log with custom format
|
// Errorf logs error log with custom format
|
||||||
func Errorf(format string, args ...interface{}) {
|
func Errorf(format string, args ...any) {
|
||||||
defaultLogger.Errorf(getFinalLog(format, args...))
|
defaultLogger.Error(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorfWithRequestId logs error log with custom format and request id
|
// ErrorfWithRequestId logs error log with custom format and request id
|
||||||
func ErrorfWithRequestId(c *core.Context, format string, args ...interface{}) {
|
func ErrorfWithRequestId(c *core.Context, format string, args ...any) {
|
||||||
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Errorf(getFinalLog(format, args...))
|
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).Error(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorfWithRequestIdAndExtra logs error log with custom format and request id and extra info
|
// ErrorfWithRequestIdAndExtra logs error log with custom format and request id and extra info
|
||||||
func ErrorfWithRequestIdAndExtra(c *core.Context, extraString string, format string, args ...interface{}) {
|
func ErrorfWithRequestIdAndExtra(c *core.Context, extraString string, format string, args ...any) {
|
||||||
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).WithField(logFieldExtra, extraString).Errorf(getFinalLog(format, args...))
|
defaultLogger.WithField(logFieldRequestId, c.GetRequestId()).WithField(logFieldExtra, extraString).Error(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// BootInfof logs boot info log
|
// BootInfof logs boot info log
|
||||||
func BootInfof(format string, args ...interface{}) {
|
func BootInfof(format string, args ...any) {
|
||||||
if bootLogger != nil {
|
if bootLogger != nil {
|
||||||
bootLogger.Infof(getFinalLog(format, args...))
|
bootLogger.Info(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BootWarnf logs boot warn log
|
// BootWarnf logs boot warn log
|
||||||
func BootWarnf(format string, args ...interface{}) {
|
func BootWarnf(format string, args ...any) {
|
||||||
if bootLogger != nil {
|
if bootLogger != nil {
|
||||||
bootLogger.Warnf(getFinalLog(format, args...))
|
bootLogger.Warn(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BootErrorf logs boot error log
|
// BootErrorf logs boot error log
|
||||||
func BootErrorf(format string, args ...interface{}) {
|
func BootErrorf(format string, args ...any) {
|
||||||
if bootLogger != nil {
|
if bootLogger != nil {
|
||||||
bootLogger.Errorf(getFinalLog(format, args...))
|
bootLogger.Error(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requestf logs http request log with custom format
|
// Requestf logs http request log with custom format
|
||||||
func Requestf(c *core.Context, format string, args ...interface{}) {
|
func Requestf(c *core.Context, format string, args ...any) {
|
||||||
if requestLogger != nil {
|
if requestLogger != nil {
|
||||||
requestLogger.WithField(logFieldRequestId, c.GetRequestId()).Infof(getFinalLog(format, args...))
|
requestLogger.WithField(logFieldRequestId, c.GetRequestId()).Info(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlQuery logs sql query log
|
// SqlQuery logs sql query log
|
||||||
func SqlQuery(args ...interface{}) {
|
func SqlQuery(args ...any) {
|
||||||
if sqlQueryLogger != nil {
|
if sqlQueryLogger != nil {
|
||||||
sqlQueryLogger.Info(args...)
|
sqlQueryLogger.Info(args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlQueryf logs sql query log with custom format
|
// SqlQueryf logs sql query log with custom format
|
||||||
func SqlQueryf(format string, args ...interface{}) {
|
func SqlQueryf(format string, args ...any) {
|
||||||
if sqlQueryLogger != nil {
|
if sqlQueryLogger != nil {
|
||||||
sqlQueryLogger.Infof(getFinalLog(format, args...))
|
sqlQueryLogger.Info(getFinalLog(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFinalLog(format string, args ...interface{}) string {
|
func getFinalLog(format string, args ...any) string {
|
||||||
result := fmt.Sprintf(format, args...)
|
result := fmt.Sprintf(format, args...)
|
||||||
result = strings.Replace(result, "\n", " ", -1)
|
result = strings.Replace(result, "\n", " ", -1)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"gopkg.in/mail.v2"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultMailer represents default mailer
|
||||||
|
type DefaultMailer struct {
|
||||||
|
dialer *mail.Dialer
|
||||||
|
fromAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultMailer returns a new default mailer
|
||||||
|
func NewDefaultMailer(smtpConfig *settings.SMTPConfig) (*DefaultMailer, error) {
|
||||||
|
host, portStr, err := net.SplitHostPort(smtpConfig.SMTPHost)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrSMTPServerHostInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := utils.StringToInt(portStr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrSMTPServerHostInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := mail.NewDialer(host, port, smtpConfig.SMTPUser, smtpConfig.SMTPPasswd)
|
||||||
|
dialer.TLSConfig = &tls.Config{
|
||||||
|
ServerName: host,
|
||||||
|
InsecureSkipVerify: smtpConfig.SMTPSkipTLSVerify,
|
||||||
|
}
|
||||||
|
|
||||||
|
mailer := &DefaultMailer{
|
||||||
|
dialer: dialer,
|
||||||
|
fromAddress: smtpConfig.FromAddress,
|
||||||
|
}
|
||||||
|
|
||||||
|
return mailer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMail sends an email according to argument
|
||||||
|
func (m *DefaultMailer) SendMail(message *MailMessage) error {
|
||||||
|
if m.dialer == nil {
|
||||||
|
return errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
mailMessage := mail.NewMessage()
|
||||||
|
mailMessage.SetHeader("From", m.fromAddress)
|
||||||
|
mailMessage.SetHeader("To", message.To)
|
||||||
|
mailMessage.SetHeader("Subject", message.Subject)
|
||||||
|
mailMessage.SetBody("text/html", message.Body)
|
||||||
|
|
||||||
|
err := m.dialer.DialAndSend(mailMessage)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package mail
|
||||||
|
|
||||||
|
// MailMessage represents an email entity
|
||||||
|
type MailMessage struct {
|
||||||
|
To string
|
||||||
|
Subject string
|
||||||
|
Body string
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package mail
|
||||||
|
|
||||||
|
// Mailer is email sender interface
|
||||||
|
type Mailer interface {
|
||||||
|
SendMail(message *MailMessage) error
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package mail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MailerContainer contains the current mailer
|
||||||
|
type MailerContainer struct {
|
||||||
|
Current Mailer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a mailer container singleton instance
|
||||||
|
var (
|
||||||
|
Container = &MailerContainer{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitializeMailer initializes the current mailer according to the config
|
||||||
|
func InitializeMailer(config *settings.Config) error {
|
||||||
|
if !config.EnableSMTP {
|
||||||
|
Container.Current = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mailer, err := NewDefaultMailer(config.SMTPConfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
Container.Current = mailer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMail sends an email according to argument
|
||||||
|
func (u *MailerContainer) SendMail(message *MailMessage) error {
|
||||||
|
return u.Current.SendMail(message)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const tokenCookieParam = "ebk_auth_token"
|
||||||
|
|
||||||
|
// AmapApiProxyAuthCookie adds amap api proxy auth cookie to cookies in response
|
||||||
|
func AmapApiProxyAuthCookie(c *core.Context, config *settings.Config) {
|
||||||
|
token := c.GetTextualToken()
|
||||||
|
|
||||||
|
if token != "" {
|
||||||
|
c.SetCookie(tokenCookieParam, token, int(config.TokenExpiredTime), "/_AMapService", "", false, true)
|
||||||
|
} else {
|
||||||
|
c.SetCookie(tokenCookieParam, "", -1, "/_AMapService", "", false, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
@@ -10,51 +10,36 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TokenSourceType represents token source
|
||||||
|
type TokenSourceType byte
|
||||||
|
|
||||||
|
// Token source types
|
||||||
|
const (
|
||||||
|
TOKEN_SOURCE_TYPE_HEADER TokenSourceType = 1
|
||||||
|
TOKEN_SOURCE_TYPE_ARGUMENT TokenSourceType = 2
|
||||||
|
TOKEN_SOURCE_TYPE_COOKIE TokenSourceType = 3
|
||||||
|
)
|
||||||
|
|
||||||
const tokenQueryStringParam = "token"
|
const tokenQueryStringParam = "token"
|
||||||
|
|
||||||
// JWTAuthorization verifies whether current request is valid by jwt token
|
// JWTAuthorization verifies whether current request is valid by jwt token in header
|
||||||
func JWTAuthorization(c *core.Context) {
|
func JWTAuthorization(c *core.Context) {
|
||||||
claims, err := getTokenClaims(c)
|
jwtAuthorization(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
utils.PrintJsonErrorResult(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
|
||||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorization] user \"uid:%s\" token requires 2fa", claims.Id)
|
|
||||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
|
||||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorization] user \"uid:%s\" token type is invalid", claims.Id)
|
|
||||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SetTokenClaims(claims)
|
|
||||||
c.Next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token
|
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token in query string
|
||||||
func JWTAuthorizationByQueryString(c *core.Context) {
|
func JWTAuthorizationByQueryString(c *core.Context) {
|
||||||
token, exists := c.GetQuery(tokenQueryStringParam)
|
jwtAuthorization(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||||
|
}
|
||||||
|
|
||||||
if !exists {
|
// JWTAuthorizationByCookie verifies whether current request is valid by jwt token in cookie
|
||||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] no token provided")
|
func JWTAuthorizationByCookie(c *core.Context) {
|
||||||
utils.PrintJsonErrorResult(c, errs.ErrUnauthorizedAccess)
|
jwtAuthorization(c, TOKEN_SOURCE_TYPE_COOKIE)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Request.Header.Set("Authorization", token)
|
|
||||||
|
|
||||||
JWTAuthorization(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTTwoFactorAuthorization verifies whether current request is valid by 2fa passcode
|
// JWTTwoFactorAuthorization verifies whether current request is valid by 2fa passcode
|
||||||
func JWTTwoFactorAuthorization(c *core.Context) {
|
func JWTTwoFactorAuthorization(c *core.Context) {
|
||||||
claims, err := getTokenClaims(c)
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.PrintJsonErrorResult(c, err)
|
utils.PrintJsonErrorResult(c, err)
|
||||||
@@ -62,7 +47,7 @@ func JWTTwoFactorAuthorization(c *core.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if claims.Type != core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
if claims.Type != core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||||
log.WarnfWithRequestId(c, "[authorization.JWTTwoFactorAuthorization] user \"uid:%s\" token is not need two factor authorization", claims.Id)
|
log.WarnfWithRequestId(c, "[authorization.JWTTwoFactorAuthorization] user \"uid:%d\" token is not need two factor authorization", claims.Uid)
|
||||||
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenNotRequire2FA)
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenNotRequire2FA)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -71,12 +56,74 @@ func JWTTwoFactorAuthorization(c *core.Context) {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTokenClaims(c *core.Context) (*core.UserTokenClaims, *errs.Error) {
|
// JWTEmailVerifyAuthorization verifies whether current request is email verification
|
||||||
token, claims, err := services.Tokens.ParseToken(c)
|
func JWTEmailVerifyAuthorization(c *core.Context) {
|
||||||
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrEmailVerifyTokenIsInvalidOrExpired)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Type != core.USER_TOKEN_TYPE_EMAIL_VERIFY {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.JWTEmailVerifyAuthorization] user \"uid:%d\" token is not for email verification", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidToken)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetTokenClaims(claims)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWTResetPasswordAuthorization verifies whether current request is password reset
|
||||||
|
func JWTResetPasswordAuthorization(c *core.Context) {
|
||||||
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrPasswordResetTokenIsInvalidOrExpired)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Type != core.USER_TOKEN_TYPE_PASSWORD_RESET {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.JWTResetPasswordAuthorization] user \"uid:%d\" token is not for password request", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidToken)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetTokenClaims(claims)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func jwtAuthorization(c *core.Context, source TokenSourceType) {
|
||||||
|
claims, err := getTokenClaims(c, source)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.PrintJsonErrorResult(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.jwtAuthorization] user \"uid:%d\" token requires 2fa", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.jwtAuthorization] user \"uid:%d\" token type is invalid", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetTokenClaims(claims)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenClaims(c *core.Context, source TokenSourceType) (*core.UserTokenClaims, *errs.Error) {
|
||||||
|
token, claims, err := parseToken(c, source)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] failed to parse token, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] failed to parse token, because %s", err.Error())
|
||||||
return nil, errs.ErrUnauthorizedAccess
|
return nil, errs.Or(err, errs.ErrUnauthorizedAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !token.Valid {
|
if !token.Valid {
|
||||||
@@ -84,15 +131,20 @@ func getTokenClaims(c *core.Context) (*core.UserTokenClaims, *errs.Error) {
|
|||||||
return nil, errs.ErrCurrentInvalidToken
|
return nil, errs.ErrCurrentInvalidToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
|
if claims.Uid <= 0 {
|
||||||
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] token is expired")
|
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] user id in token is invalid")
|
||||||
return nil, errs.ErrCurrentTokenExpired
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.Id == "" {
|
|
||||||
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] user id in token is empty")
|
|
||||||
return nil, errs.ErrCurrentInvalidToken
|
return nil, errs.ErrCurrentInvalidToken
|
||||||
}
|
}
|
||||||
|
|
||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseToken(c *core.Context, source TokenSourceType) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
|
if source == TOKEN_SOURCE_TYPE_ARGUMENT {
|
||||||
|
return services.Tokens.ParseTokenByArgument(c, tokenQueryStringParam)
|
||||||
|
} else if source == TOKEN_SOURCE_TYPE_COOKIE {
|
||||||
|
return services.Tokens.ParseTokenByCookie(c, tokenCookieParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return services.Tokens.ParseTokenByHeader(c)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
const utcOffsetQueryStringParam = "utc_offset"
|
|
||||||
|
|
||||||
// HeaderInQueryString puts some headers from query string
|
|
||||||
func HeaderInQueryString(c *core.Context) {
|
|
||||||
utcOffset, exists := c.GetQuery(utcOffsetQueryStringParam)
|
|
||||||
|
|
||||||
if exists {
|
|
||||||
c.Request.Header.Set(core.ClientTimezoneOffsetHeaderName, utcOffset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ package middlewares
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
@@ -49,7 +49,7 @@ func stack(skip int) []byte {
|
|||||||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
|
||||||
|
|
||||||
if file != lastFile {
|
if file != lastFile {
|
||||||
data, err := ioutil.ReadFile(file)
|
data, err := os.ReadFile(file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestLog logs the http request log
|
// RequestLog logs the http request log
|
||||||
@@ -18,7 +19,7 @@ func RequestLog(c *core.Context) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
statusCode := c.Writer.Status()
|
statusCode := c.Writer.Status()
|
||||||
errorCode := 0
|
errorCode := int32(0)
|
||||||
|
|
||||||
userId := "-"
|
userId := "-"
|
||||||
claims := c.GetTokenClaims()
|
claims := c.GetTokenClaims()
|
||||||
@@ -28,7 +29,7 @@ func RequestLog(c *core.Context) {
|
|||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
|
|
||||||
if claims != nil {
|
if claims != nil {
|
||||||
userId = claims.Id
|
userId = utils.Int64ToString(claims.Uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
@@ -15,16 +17,67 @@ func ServerSettingsCookie(config *settings.Config) core.MiddlewareHandlerFunc {
|
|||||||
return func(c *core.Context) {
|
return func(c *core.Context) {
|
||||||
settingsArr := []string{
|
settingsArr := []string{
|
||||||
buildBooleanSetting("r", config.EnableUserRegister),
|
buildBooleanSetting("r", config.EnableUserRegister),
|
||||||
|
buildBooleanSetting("f", config.EnableUserForgetPassword),
|
||||||
|
buildBooleanSetting("v", config.EnableUserVerifyEmail),
|
||||||
buildBooleanSetting("e", config.EnableDataExport),
|
buildBooleanSetting("e", config.EnableDataExport),
|
||||||
|
buildStringSetting("m", strings.Replace(config.MapProvider, "_", "-", -1)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.EnableMapDataFetchProxy &&
|
||||||
|
(config.MapProvider == settings.OpenStreetMapProvider ||
|
||||||
|
config.MapProvider == settings.OpenStreetMapHumanitarianStyleProvider ||
|
||||||
|
config.MapProvider == settings.OpenTopoMapProvider ||
|
||||||
|
config.MapProvider == settings.OPNVKarteMapProvider ||
|
||||||
|
config.MapProvider == settings.CyclOSMMapProvider ||
|
||||||
|
config.MapProvider == settings.TomTomMapProvider) {
|
||||||
|
settingsArr = append(settingsArr, buildBooleanSetting("mp", config.EnableMapDataFetchProxy))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.TomTomMapProvider && config.TomTomMapAPIKey != "" && !config.EnableMapDataFetchProxy {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("tmak", config.TomTomMapAPIKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.GoogleMapProvider && config.GoogleMapAPIKey != "" {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("gmak", config.GoogleMapAPIKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.BaiduMapProvider && config.BaiduMapAK != "" {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("bmak", config.BaiduMapAK))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.AmapProvider && config.AmapApplicationKey != "" {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("amak", config.AmapApplicationKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.MapProvider == settings.AmapProvider && config.AmapSecurityVerificationMethod != "" {
|
||||||
|
settingsArr = append(settingsArr, buildStringSetting("amsv", strings.Replace(config.AmapSecurityVerificationMethod, "_", "", -1)))
|
||||||
|
|
||||||
|
if config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationExternalProxyMethod {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("amep", config.AmapApiExternalProxyUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.AmapSecurityVerificationMethod == settings.AmapSecurityVerificationPlainTextMethod {
|
||||||
|
settingsArr = append(settingsArr, buildEncodedStringSetting("amas", config.AmapApplicationSecret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bundledSettings := strings.Join(settingsArr, "_")
|
bundledSettings := strings.Join(settingsArr, "_")
|
||||||
c.SetCookie(settingsCookieName, bundledSettings, config.TokenExpiredTime, "", "", false, false)
|
c.SetCookie(settingsCookieName, bundledSettings, int(config.TokenExpiredTime), "", "", false, false)
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildStringSetting(key string, value string) string {
|
||||||
|
return fmt.Sprintf("%s.%s", key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildEncodedStringSetting(key string, value string) string {
|
||||||
|
urlEncodedValue := url.QueryEscape(value)
|
||||||
|
base64Value := base64.StdEncoding.EncodeToString([]byte(urlEncodedValue))
|
||||||
|
return fmt.Sprintf("%s.%s", key, base64Value)
|
||||||
|
}
|
||||||
|
|
||||||
func buildBooleanSetting(key string, value bool) string {
|
func buildBooleanSetting(key string, value bool) string {
|
||||||
if value {
|
if value {
|
||||||
return fmt.Sprintf("%s.1", key)
|
return fmt.Sprintf("%s.1", key)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ type Account struct {
|
|||||||
Type AccountType `xorm:"NOT NULL"`
|
Type AccountType `xorm:"NOT NULL"`
|
||||||
ParentAccountId int64 `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
|
ParentAccountId int64 `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
|
||||||
Name string `xorm:"VARCHAR(32) NOT NULL"`
|
Name string `xorm:"VARCHAR(32) NOT NULL"`
|
||||||
DisplayOrder int `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
|
DisplayOrder int32 `xorm:"INDEX(IDX_account_uid_deleted_parent_account_id_order) NOT NULL"`
|
||||||
Icon int64 `xorm:"NOT NULL"`
|
Icon int64 `xorm:"NOT NULL"`
|
||||||
Color string `xorm:"VARCHAR(6) NOT NULL"`
|
Color string `xorm:"VARCHAR(6) NOT NULL"`
|
||||||
Currency string `xorm:"VARCHAR(3) NOT NULL"`
|
Currency string `xorm:"VARCHAR(3) NOT NULL"`
|
||||||
@@ -116,7 +116,7 @@ type AccountMoveRequest struct {
|
|||||||
// AccountNewDisplayOrderRequest represents a data pair of id and display order
|
// AccountNewDisplayOrderRequest represents a data pair of id and display order
|
||||||
type AccountNewDisplayOrderRequest struct {
|
type AccountNewDisplayOrderRequest struct {
|
||||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountDeleteRequest represents all parameters of account deleting request
|
// AccountDeleteRequest represents all parameters of account deleting request
|
||||||
@@ -136,7 +136,7 @@ type AccountInfoResponse struct {
|
|||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
Balance int64 `json:"balance"`
|
Balance int64 `json:"balance"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
IsAsset bool `json:"isAsset,omitempty"`
|
IsAsset bool `json:"isAsset,omitempty"`
|
||||||
IsLiability bool `json:"isLiability,omitempty"`
|
IsLiability bool `json:"isLiability,omitempty"`
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool `json:"hidden"`
|
||||||
|
|||||||
@@ -6,3 +6,10 @@ type AuthResponse struct {
|
|||||||
Need2FA bool `json:"need2FA"`
|
Need2FA bool `json:"need2FA"`
|
||||||
User *UserBasicInfo `json:"user"`
|
User *UserBasicInfo `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterResponse returns a view-object of user register response
|
||||||
|
type RegisterResponse struct {
|
||||||
|
AuthResponse
|
||||||
|
NeedVerifyEmail bool `json:"needVerifyEmail"`
|
||||||
|
PresetCategoriesSaved bool `json:"presetCategoriesSaved"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,3 +4,11 @@ package models
|
|||||||
type ClearDataRequest struct {
|
type ClearDataRequest struct {
|
||||||
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DataStatisticsResponse represents a view-object of user data statistic
|
||||||
|
type DataStatisticsResponse struct {
|
||||||
|
TotalAccountCount int64 `json:"totalAccountCount,string"`
|
||||||
|
TotalTransactionCategoryCount int64 `json:"totalTransactionCategoryCount,string"`
|
||||||
|
TotalTransactionTagCount int64 `json:"totalTransactionTagCount,string"`
|
||||||
|
TotalTransactionCount int64 `json:"totalTransactionCount,string"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// WeekDay represents week day
|
||||||
|
type WeekDay byte
|
||||||
|
|
||||||
|
// Week days
|
||||||
|
const (
|
||||||
|
WEEKDAY_SUNDAY WeekDay = 0
|
||||||
|
WEEKDAY_MONDAY WeekDay = 1
|
||||||
|
WEEKDAY_TUESDAY WeekDay = 2
|
||||||
|
WEEKDAY_WEDNESDAY WeekDay = 3
|
||||||
|
WEEKDAY_THURSDAY WeekDay = 4
|
||||||
|
WEEKDAY_FRIDAY WeekDay = 5
|
||||||
|
WEEKDAY_SATURDAY WeekDay = 6
|
||||||
|
WEEKDAY_INVALID WeekDay = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a textual representation of the week day enum
|
||||||
|
func (d WeekDay) String() string {
|
||||||
|
switch d {
|
||||||
|
case WEEKDAY_SUNDAY:
|
||||||
|
return "Sunday"
|
||||||
|
case WEEKDAY_MONDAY:
|
||||||
|
return "Monday"
|
||||||
|
case WEEKDAY_TUESDAY:
|
||||||
|
return "Tuesday"
|
||||||
|
case WEEKDAY_WEDNESDAY:
|
||||||
|
return "Wednesday"
|
||||||
|
case WEEKDAY_THURSDAY:
|
||||||
|
return "Thursday"
|
||||||
|
case WEEKDAY_FRIDAY:
|
||||||
|
return "Friday"
|
||||||
|
case WEEKDAY_SATURDAY:
|
||||||
|
return "Saturday"
|
||||||
|
case WEEKDAY_INVALID:
|
||||||
|
return "Invalid"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Invalid(%d)", int(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LongDateFormat represents long date format
|
||||||
|
type LongDateFormat byte
|
||||||
|
|
||||||
|
// Long Date Format
|
||||||
|
const (
|
||||||
|
LONG_DATE_FORMAT_DEFAULT LongDateFormat = 0
|
||||||
|
LONG_DATE_FORMAT_YYYY_M_D LongDateFormat = 1
|
||||||
|
LONG_DATE_FORMAT_M_D_YYYY LongDateFormat = 2
|
||||||
|
LONG_DATE_FORMAT_D_M_YYYY LongDateFormat = 3
|
||||||
|
LONG_DATE_FORMAT_INVALID LongDateFormat = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a textual representation of the long date format enum
|
||||||
|
func (f LongDateFormat) String() string {
|
||||||
|
switch f {
|
||||||
|
case LONG_DATE_FORMAT_DEFAULT:
|
||||||
|
return "Default"
|
||||||
|
case LONG_DATE_FORMAT_YYYY_M_D:
|
||||||
|
return "YYYY_MM_D"
|
||||||
|
case LONG_DATE_FORMAT_M_D_YYYY:
|
||||||
|
return "M_D_YYYY"
|
||||||
|
case LONG_DATE_FORMAT_D_M_YYYY:
|
||||||
|
return "D_M_YYYY"
|
||||||
|
case LONG_DATE_FORMAT_INVALID:
|
||||||
|
return "Invalid"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Invalid(%d)", int(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortDateFormat represents short date format
|
||||||
|
type ShortDateFormat byte
|
||||||
|
|
||||||
|
// Short Date Format
|
||||||
|
const (
|
||||||
|
SHORT_DATE_FORMAT_DEFAULT ShortDateFormat = 0
|
||||||
|
SHORT_DATE_FORMAT_YYYY_M_D ShortDateFormat = 1
|
||||||
|
SHORT_DATE_FORMAT_M_D_YYYY ShortDateFormat = 2
|
||||||
|
SHORT_DATE_FORMAT_D_M_YYYY ShortDateFormat = 3
|
||||||
|
SHORT_DATE_FORMAT_INVALID ShortDateFormat = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a textual representation of the short date format enum
|
||||||
|
func (f ShortDateFormat) String() string {
|
||||||
|
switch f {
|
||||||
|
case SHORT_DATE_FORMAT_DEFAULT:
|
||||||
|
return "Default"
|
||||||
|
case SHORT_DATE_FORMAT_YYYY_M_D:
|
||||||
|
return "YYYY_MM_D"
|
||||||
|
case SHORT_DATE_FORMAT_M_D_YYYY:
|
||||||
|
return "M_D_YYYY"
|
||||||
|
case SHORT_DATE_FORMAT_D_M_YYYY:
|
||||||
|
return "D_M_YYYY"
|
||||||
|
case SHORT_DATE_FORMAT_INVALID:
|
||||||
|
return "Invalid"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Invalid(%d)", int(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LongTimeFormat represents long time format
|
||||||
|
type LongTimeFormat byte
|
||||||
|
|
||||||
|
// Long Time Format
|
||||||
|
const (
|
||||||
|
LONG_TIME_FORMAT_DEFAULT LongTimeFormat = 0
|
||||||
|
LONG_TIME_FORMAT_HH_MM_SS LongTimeFormat = 1
|
||||||
|
LONG_TIME_FORMAT_A_HH_MM_SS LongTimeFormat = 2
|
||||||
|
LONG_TIME_FORMAT_HH_MM_SS_A LongTimeFormat = 3
|
||||||
|
LONG_TIME_FORMAT_INVALID LongTimeFormat = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a textual representation of the long time format enum
|
||||||
|
func (f LongTimeFormat) String() string {
|
||||||
|
switch f {
|
||||||
|
case LONG_TIME_FORMAT_DEFAULT:
|
||||||
|
return "Default"
|
||||||
|
case LONG_TIME_FORMAT_HH_MM_SS:
|
||||||
|
return "HH_MM_SS"
|
||||||
|
case LONG_TIME_FORMAT_A_HH_MM_SS:
|
||||||
|
return "A_HH_MM_SS"
|
||||||
|
case LONG_TIME_FORMAT_HH_MM_SS_A:
|
||||||
|
return "HH_MM_SS_A"
|
||||||
|
case LONG_TIME_FORMAT_INVALID:
|
||||||
|
return "Invalid"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Invalid(%d)", int(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortTimeFormat represents short time format
|
||||||
|
type ShortTimeFormat byte
|
||||||
|
|
||||||
|
// Short Time Format
|
||||||
|
const (
|
||||||
|
SHORT_TIME_FORMAT_DEFAULT ShortTimeFormat = 0
|
||||||
|
SHORT_TIME_FORMAT_HH_MM ShortTimeFormat = 1
|
||||||
|
SHORT_TIME_FORMAT_A_HH_MM ShortTimeFormat = 2
|
||||||
|
SHORT_TIME_FORMAT_HH_MM_A ShortTimeFormat = 3
|
||||||
|
SHORT_TIME_FORMAT_INVALID ShortTimeFormat = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a textual representation of the short time format enum
|
||||||
|
func (f ShortTimeFormat) String() string {
|
||||||
|
switch f {
|
||||||
|
case SHORT_TIME_FORMAT_DEFAULT:
|
||||||
|
return "Default"
|
||||||
|
case SHORT_TIME_FORMAT_HH_MM:
|
||||||
|
return "HH_MM"
|
||||||
|
case SHORT_TIME_FORMAT_A_HH_MM:
|
||||||
|
return "A_HH_MM"
|
||||||
|
case SHORT_TIME_FORMAT_HH_MM_A:
|
||||||
|
return "HH_MM_A"
|
||||||
|
case SHORT_TIME_FORMAT_INVALID:
|
||||||
|
return "Invalid"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Invalid(%d)", int(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// ForgetPasswordRequest represents all parameters of forget password request
|
||||||
|
type ForgetPasswordRequest struct {
|
||||||
|
Email string `json:"email" binding:"required,notBlank,max=100,validEmail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordResetRequest represents all parameters of reset password request
|
||||||
|
type PasswordResetRequest struct {
|
||||||
|
Email string `json:"email" binding:"required,notBlank,max=100,validEmail"`
|
||||||
|
Password string `json:"password" binding:"required,min=6,max=128"`
|
||||||
|
}
|
||||||
+78
-31
@@ -35,8 +35,8 @@ const (
|
|||||||
// Transaction represents transaction data stored in database
|
// Transaction represents transaction data stored in database
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
TransactionId int64 `xorm:"PK"`
|
TransactionId int64 `xorm:"PK"`
|
||||||
Uid int64 `xorm:"UNIQUE(UQE_transaction_uid_time) INDEX(IDX_transaction_uid_deleted_time) INDEX(IDX_transaction_uid_deleted_type_time) INDEX(IDX_transaction_uid_deleted_category_id_time) INDEX(IDX_transaction_uid_deleted_account_id_time) NOT NULL"`
|
Uid int64 `xorm:"UNIQUE(UQE_transaction_uid_time) INDEX(IDX_transaction_uid_deleted_time) INDEX(IDX_transaction_uid_deleted_type_time) INDEX(IDX_transaction_uid_deleted_category_id_time) INDEX(IDX_transaction_uid_deleted_account_id_time) INDEX(IDX_transaction_uid_deleted_time_longitude_latitude) NOT NULL"`
|
||||||
Deleted bool `xorm:"INDEX(IDX_transaction_uid_deleted_time) INDEX(IDX_transaction_uid_deleted_type_time) INDEX(IDX_transaction_uid_deleted_category_id_time) INDEX(IDX_transaction_uid_deleted_account_id_time) NOT NULL"`
|
Deleted bool `xorm:"INDEX(IDX_transaction_uid_deleted_time) INDEX(IDX_transaction_uid_deleted_type_time) INDEX(IDX_transaction_uid_deleted_category_id_time) INDEX(IDX_transaction_uid_deleted_account_id_time) INDEX(IDX_transaction_uid_deleted_time_longitude_latitude) NOT NULL"`
|
||||||
Type TransactionDbType `xorm:"INDEX(IDX_transaction_uid_deleted_type_time) NOT NULL"`
|
Type TransactionDbType `xorm:"INDEX(IDX_transaction_uid_deleted_type_time) NOT NULL"`
|
||||||
CategoryId int64 `xorm:"INDEX(IDX_transaction_uid_deleted_category_id_time) NOT NULL"`
|
CategoryId int64 `xorm:"INDEX(IDX_transaction_uid_deleted_category_id_time) NOT NULL"`
|
||||||
AccountId int64 `xorm:"INDEX(IDX_transaction_uid_deleted_account_id_time) NOT NULL"`
|
AccountId int64 `xorm:"INDEX(IDX_transaction_uid_deleted_account_id_time) NOT NULL"`
|
||||||
@@ -48,39 +48,50 @@ type Transaction struct {
|
|||||||
RelatedAccountAmount int64 `xorm:"NOT NULL"`
|
RelatedAccountAmount int64 `xorm:"NOT NULL"`
|
||||||
HideAmount bool `xorm:"NOT NULL"`
|
HideAmount bool `xorm:"NOT NULL"`
|
||||||
Comment string `xorm:"VARCHAR(255) NOT NULL"`
|
Comment string `xorm:"VARCHAR(255) NOT NULL"`
|
||||||
|
GeoLongitude float64 `xorm:"INDEX(IDX_transaction_uid_deleted_time_longitude_latitude)"`
|
||||||
|
GeoLatitude float64 `xorm:"INDEX(IDX_transaction_uid_deleted_time_longitude_latitude)"`
|
||||||
|
CreatedIp string `xorm:"VARCHAR(39)"`
|
||||||
CreatedUnixTime int64
|
CreatedUnixTime int64
|
||||||
UpdatedUnixTime int64
|
UpdatedUnixTime int64
|
||||||
DeletedUnixTime int64
|
DeletedUnixTime int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionGeoLocationRequest represents all parameters of transaction geographic location info update request
|
||||||
|
type TransactionGeoLocationRequest struct {
|
||||||
|
Latitude float64 `json:"latitude" binding:"required"`
|
||||||
|
Longitude float64 `json:"longitude" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionCreateRequest represents all parameters of transaction creation request
|
// TransactionCreateRequest represents all parameters of transaction creation request
|
||||||
type TransactionCreateRequest struct {
|
type TransactionCreateRequest struct {
|
||||||
Type TransactionType `json:"type" binding:"required"`
|
Type TransactionType `json:"type" binding:"required"`
|
||||||
CategoryId int64 `json:"categoryId,string"`
|
CategoryId int64 `json:"categoryId,string"`
|
||||||
Time int64 `json:"time" binding:"required,min=1"`
|
Time int64 `json:"time" binding:"required,min=1"`
|
||||||
UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"`
|
UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"`
|
||||||
SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"`
|
SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"`
|
||||||
DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"`
|
DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"`
|
||||||
SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"`
|
SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"`
|
||||||
DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"`
|
DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"`
|
||||||
HideAmount bool `json:"hideAmount"`
|
HideAmount bool `json:"hideAmount"`
|
||||||
TagIds []string `json:"tagIds"`
|
TagIds []string `json:"tagIds"`
|
||||||
Comment string `json:"comment" binding:"max=255"`
|
Comment string `json:"comment" binding:"max=255"`
|
||||||
|
GeoLocation *TransactionGeoLocationRequest `json:"geoLocation" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionModifyRequest represents all parameters of transaction modification request
|
// TransactionModifyRequest represents all parameters of transaction modification request
|
||||||
type TransactionModifyRequest struct {
|
type TransactionModifyRequest struct {
|
||||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||||
CategoryId int64 `json:"categoryId,string"`
|
CategoryId int64 `json:"categoryId,string"`
|
||||||
Time int64 `json:"time" binding:"required,min=1"`
|
Time int64 `json:"time" binding:"required,min=1"`
|
||||||
UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"`
|
UtcOffset int16 `json:"utcOffset" binding:"min=-720,max=840"`
|
||||||
SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"`
|
SourceAccountId int64 `json:"sourceAccountId,string" binding:"required,min=1"`
|
||||||
DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"`
|
DestinationAccountId int64 `json:"destinationAccountId,string" binding:"min=0"`
|
||||||
SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"`
|
SourceAmount int64 `json:"sourceAmount" binding:"min=-99999999999,max=99999999999"`
|
||||||
DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"`
|
DestinationAmount int64 `json:"destinationAmount" binding:"min=-99999999999,max=99999999999"`
|
||||||
HideAmount bool `json:"hideAmount"`
|
HideAmount bool `json:"hideAmount"`
|
||||||
TagIds []string `json:"tagIds"`
|
TagIds []string `json:"tagIds"`
|
||||||
Comment string `json:"comment" binding:"max=255"`
|
Comment string `json:"comment" binding:"max=255"`
|
||||||
|
GeoLocation *TransactionGeoLocationRequest `json:"geoLocation" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionCountRequest represents transaction count request
|
// TransactionCountRequest represents transaction count request
|
||||||
@@ -101,7 +112,9 @@ type TransactionListByMaxTimeRequest struct {
|
|||||||
Keyword string `form:"keyword"`
|
Keyword string `form:"keyword"`
|
||||||
MaxTime int64 `form:"max_time" binding:"min=0"`
|
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||||
MinTime int64 `form:"min_time" binding:"min=0"`
|
MinTime int64 `form:"min_time" binding:"min=0"`
|
||||||
Count int `form:"count" binding:"required,min=1,max=50"`
|
Page int32 `form:"page" binding:"min=0"`
|
||||||
|
Count int32 `form:"count" binding:"required,min=1,max=50"`
|
||||||
|
WithCount bool `form:"with_count"`
|
||||||
TrimAccount bool `form:"trim_account"`
|
TrimAccount bool `form:"trim_account"`
|
||||||
TrimCategory bool `form:"trim_category"`
|
TrimCategory bool `form:"trim_category"`
|
||||||
TrimTag bool `form:"trim_tag"`
|
TrimTag bool `form:"trim_tag"`
|
||||||
@@ -109,14 +122,12 @@ type TransactionListByMaxTimeRequest struct {
|
|||||||
|
|
||||||
// TransactionListInMonthByPageRequest represents all parameters of transaction listing by month request
|
// TransactionListInMonthByPageRequest represents all parameters of transaction listing by month request
|
||||||
type TransactionListInMonthByPageRequest struct {
|
type TransactionListInMonthByPageRequest struct {
|
||||||
Year int `form:"year" binding:"required,min=1"`
|
Year int32 `form:"year" binding:"required,min=1"`
|
||||||
Month int `form:"month" binding:"required,min=1"`
|
Month int32 `form:"month" binding:"required,min=1"`
|
||||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||||
CategoryId int64 `form:"category_id" binding:"min=0"`
|
CategoryId int64 `form:"category_id" binding:"min=0"`
|
||||||
AccountId int64 `form:"account_id" binding:"min=0"`
|
AccountId int64 `form:"account_id" binding:"min=0"`
|
||||||
Keyword string `form:"keyword"`
|
Keyword string `form:"keyword"`
|
||||||
Page int `form:"page" binding:"required,min=1"`
|
|
||||||
Count int `form:"count" binding:"required,min=1,max=50"`
|
|
||||||
TrimAccount bool `form:"trim_account"`
|
TrimAccount bool `form:"trim_account"`
|
||||||
TrimCategory bool `form:"trim_category"`
|
TrimCategory bool `form:"trim_category"`
|
||||||
TrimTag bool `form:"trim_tag"`
|
TrimTag bool `form:"trim_tag"`
|
||||||
@@ -169,6 +180,12 @@ type TransactionAccountAmount struct {
|
|||||||
TotalExpenseAmount int64
|
TotalExpenseAmount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionGeoLocationResponse represents a view-object of transaction geographic location info
|
||||||
|
type TransactionGeoLocationResponse struct {
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionInfoResponse represents a view-object of transaction
|
// TransactionInfoResponse represents a view-object of transaction
|
||||||
type TransactionInfoResponse struct {
|
type TransactionInfoResponse struct {
|
||||||
Id int64 `json:"id,string"`
|
Id int64 `json:"id,string"`
|
||||||
@@ -188,6 +205,7 @@ type TransactionInfoResponse struct {
|
|||||||
TagIds []string `json:"tagIds"`
|
TagIds []string `json:"tagIds"`
|
||||||
Tags []*TransactionTagInfoResponse `json:"tags,omitempty"`
|
Tags []*TransactionTagInfoResponse `json:"tags,omitempty"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
|
GeoLocation *TransactionGeoLocationResponse `json:"geoLocation,omitempty"`
|
||||||
Editable bool `json:"editable"`
|
Editable bool `json:"editable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +218,7 @@ type TransactionCountResponse struct {
|
|||||||
type TransactionInfoPageWrapperResponse struct {
|
type TransactionInfoPageWrapperResponse struct {
|
||||||
Items TransactionInfoResponseSlice `json:"items"`
|
Items TransactionInfoResponseSlice `json:"items"`
|
||||||
NextTimeSequenceId *int64 `json:"nextTimeSequenceId,string"`
|
NextTimeSequenceId *int64 `json:"nextTimeSequenceId,string"`
|
||||||
|
TotalCount *int64 `json:"totalCount,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionInfoPageWrapperResponse2 represents a response of transaction which contains items and count
|
// TransactionInfoPageWrapperResponse2 represents a response of transaction which contains items and count
|
||||||
@@ -231,8 +250,8 @@ type TransactionAmountsResponseItem struct {
|
|||||||
|
|
||||||
// TransactionMonthAmountsResponseItem represents an item of transaction month amounts
|
// TransactionMonthAmountsResponseItem represents an item of transaction month amounts
|
||||||
type TransactionMonthAmountsResponseItem struct {
|
type TransactionMonthAmountsResponseItem struct {
|
||||||
Year int `json:"year"`
|
Year int32 `json:"year"`
|
||||||
Month int `json:"month"`
|
Month int32 `json:"month"`
|
||||||
Amounts []*TransactionAmountsResponseItemAmountInfo `json:"amounts"`
|
Amounts []*TransactionAmountsResponseItemAmountInfo `json:"amounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +316,15 @@ func (t *Transaction) ToTransactionInfoResponse(tagIds []int64, editable bool) *
|
|||||||
destinationAmount = t.Amount
|
destinationAmount = t.Amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
geoLocation := &TransactionGeoLocationResponse{}
|
||||||
|
|
||||||
|
if t.GeoLongitude != 0 || t.GeoLatitude != 0 {
|
||||||
|
geoLocation.Longitude = t.GeoLongitude
|
||||||
|
geoLocation.Latitude = t.GeoLatitude
|
||||||
|
} else {
|
||||||
|
geoLocation = nil
|
||||||
|
}
|
||||||
|
|
||||||
return &TransactionInfoResponse{
|
return &TransactionInfoResponse{
|
||||||
Id: t.TransactionId,
|
Id: t.TransactionId,
|
||||||
TimeSequenceId: t.TransactionTime,
|
TimeSequenceId: t.TransactionTime,
|
||||||
@@ -311,6 +339,7 @@ func (t *Transaction) ToTransactionInfoResponse(tagIds []int64, editable bool) *
|
|||||||
HideAmount: t.HideAmount,
|
HideAmount: t.HideAmount,
|
||||||
TagIds: utils.Int64ArrayToStringArray(tagIds),
|
TagIds: utils.Int64ArrayToStringArray(tagIds),
|
||||||
Comment: t.Comment,
|
Comment: t.Comment,
|
||||||
|
GeoLocation: geoLocation,
|
||||||
Editable: editable,
|
Editable: editable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,3 +452,21 @@ func (s TransactionMonthAmountsResponseItemSlice) Less(i, j int) bool {
|
|||||||
|
|
||||||
return s[i].Month > s[j].Month
|
return s[i].Month > s[j].Month
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionAmountsResponseItemAmountInfoSlice represents the slice data structure of TransactionAmountsResponseItemAmountInfo
|
||||||
|
type TransactionAmountsResponseItemAmountInfoSlice []*TransactionAmountsResponseItemAmountInfo
|
||||||
|
|
||||||
|
// Len returns the count of items
|
||||||
|
func (s TransactionAmountsResponseItemAmountInfoSlice) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps two items
|
||||||
|
func (s TransactionAmountsResponseItemAmountInfoSlice) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less reports whether the first item is less than the second one
|
||||||
|
func (s TransactionAmountsResponseItemAmountInfoSlice) Less(i, j int) bool {
|
||||||
|
return strings.Compare(s[i].Currency, s[j].Currency) < 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type TransactionCategory struct {
|
|||||||
Type TransactionCategoryType `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
Type TransactionCategoryType `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||||
ParentCategoryId int64 `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
ParentCategoryId int64 `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||||
Name string `xorm:"VARCHAR(32) NOT NULL"`
|
Name string `xorm:"VARCHAR(32) NOT NULL"`
|
||||||
DisplayOrder int `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
DisplayOrder int32 `xorm:"INDEX(IDX_category_uid_deleted_type_parent_category_id_order) NOT NULL"`
|
||||||
Icon int64 `xorm:"NOT NULL"`
|
Icon int64 `xorm:"NOT NULL"`
|
||||||
Color string `xorm:"VARCHAR(6) NOT NULL"`
|
Color string `xorm:"VARCHAR(6) NOT NULL"`
|
||||||
Hidden bool `xorm:"NOT NULL"`
|
Hidden bool `xorm:"NOT NULL"`
|
||||||
@@ -91,7 +91,7 @@ type TransactionCategoryMoveRequest struct {
|
|||||||
// TransactionCategoryNewDisplayOrderRequest represents a data pair of id and display order
|
// TransactionCategoryNewDisplayOrderRequest represents a data pair of id and display order
|
||||||
type TransactionCategoryNewDisplayOrderRequest struct {
|
type TransactionCategoryNewDisplayOrderRequest struct {
|
||||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionCategoryDeleteRequest represents all parameters of transaction category deleting request
|
// TransactionCategoryDeleteRequest represents all parameters of transaction category deleting request
|
||||||
@@ -108,7 +108,7 @@ type TransactionCategoryInfoResponse struct {
|
|||||||
Icon int64 `json:"icon,string"`
|
Icon int64 `json:"icon,string"`
|
||||||
Color string `json:"color"`
|
Color string `json:"color"`
|
||||||
Comment string `json:"comment"`
|
Comment string `json:"comment"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool `json:"hidden"`
|
||||||
SubCategories TransactionCategoryInfoResponseSlice `json:"subCategories,omitempty"`
|
SubCategories TransactionCategoryInfoResponseSlice `json:"subCategories,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ type TransactionTag struct {
|
|||||||
Uid int64 `xorm:"INDEX(IDX_tag_uid_deleted_name) NOT NULL"`
|
Uid int64 `xorm:"INDEX(IDX_tag_uid_deleted_name) NOT NULL"`
|
||||||
Deleted bool `xorm:"INDEX(IDX_tag_uid_deleted_name) NOT NULL"`
|
Deleted bool `xorm:"INDEX(IDX_tag_uid_deleted_name) NOT NULL"`
|
||||||
Name string `xorm:"INDEX(IDX_tag_uid_deleted_name) VARCHAR(32) NOT NULL"`
|
Name string `xorm:"INDEX(IDX_tag_uid_deleted_name) VARCHAR(32) NOT NULL"`
|
||||||
DisplayOrder int `xorm:"NOT NULL"`
|
DisplayOrder int32 `xorm:"NOT NULL"`
|
||||||
Hidden bool `xorm:"NOT NULL"`
|
Hidden bool `xorm:"NOT NULL"`
|
||||||
CreatedUnixTime int64
|
CreatedUnixTime int64
|
||||||
UpdatedUnixTime int64
|
UpdatedUnixTime int64
|
||||||
@@ -43,7 +43,7 @@ type TransactionTagMoveRequest struct {
|
|||||||
// TransactionTagNewDisplayOrderRequest represents a data pair of id and display order
|
// TransactionTagNewDisplayOrderRequest represents a data pair of id and display order
|
||||||
type TransactionTagNewDisplayOrderRequest struct {
|
type TransactionTagNewDisplayOrderRequest struct {
|
||||||
Id int64 `json:"id,string" binding:"required,min=1"`
|
Id int64 `json:"id,string" binding:"required,min=1"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionTagDeleteRequest represents all parameters of transaction tag deleting request
|
// TransactionTagDeleteRequest represents all parameters of transaction tag deleting request
|
||||||
@@ -55,7 +55,7 @@ type TransactionTagDeleteRequest struct {
|
|||||||
type TransactionTagInfoResponse struct {
|
type TransactionTagInfoResponse struct {
|
||||||
Id int64 `json:"id,string"`
|
Id int64 `json:"id,string"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DisplayOrder int `json:"displayOrder"`
|
DisplayOrder int32 `json:"displayOrder"`
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool `json:"hidden"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+95
-51
@@ -4,48 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WeekDay represents week day
|
|
||||||
type WeekDay byte
|
|
||||||
|
|
||||||
// Week days
|
|
||||||
const (
|
|
||||||
WEEKDAY_SUNDAY WeekDay = 0
|
|
||||||
WEEKDAY_MONDAY WeekDay = 1
|
|
||||||
WEEKDAY_TUESDAY WeekDay = 2
|
|
||||||
WEEKDAY_WEDNESDAY WeekDay = 3
|
|
||||||
WEEKDAY_THURSDAY WeekDay = 4
|
|
||||||
WEEKDAY_FRIDAY WeekDay = 5
|
|
||||||
WEEKDAY_SATURDAY WeekDay = 6
|
|
||||||
WEEKDAY_INVALID WeekDay = 255
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns a textual representation of the week day enum
|
|
||||||
func (d WeekDay) String() string {
|
|
||||||
switch d {
|
|
||||||
case WEEKDAY_SUNDAY:
|
|
||||||
return "Sunday"
|
|
||||||
case WEEKDAY_MONDAY:
|
|
||||||
return "Monday"
|
|
||||||
case WEEKDAY_TUESDAY:
|
|
||||||
return "Tuesday"
|
|
||||||
case WEEKDAY_WEDNESDAY:
|
|
||||||
return "Wednesday"
|
|
||||||
case WEEKDAY_THURSDAY:
|
|
||||||
return "Thursday"
|
|
||||||
case WEEKDAY_FRIDAY:
|
|
||||||
return "Friday"
|
|
||||||
case WEEKDAY_SATURDAY:
|
|
||||||
return "Saturday"
|
|
||||||
case WEEKDAY_INVALID:
|
|
||||||
return "Invalid"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("Invalid(%d)", int(d))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionEditScope represents the scope which transaction can be edited
|
// TransactionEditScope represents the scope which transaction can be edited
|
||||||
type TransactionEditScope byte
|
type TransactionEditScope byte
|
||||||
|
|
||||||
@@ -87,15 +49,22 @@ func (s TransactionEditScope) String() string {
|
|||||||
|
|
||||||
// User represents user data stored in database
|
// User represents user data stored in database
|
||||||
type User struct {
|
type User struct {
|
||||||
Uid int64 `xorm:"PK"`
|
Uid int64 `xorm:"PK"`
|
||||||
Username string `xorm:"VARCHAR(32) UNIQUE NOT NULL"`
|
Username string `xorm:"VARCHAR(32) UNIQUE NOT NULL"`
|
||||||
Email string `xorm:"VARCHAR(100) UNIQUE NOT NULL"`
|
Email string `xorm:"VARCHAR(100) UNIQUE NOT NULL"`
|
||||||
Nickname string `xorm:"VARCHAR(64) NOT NULL"`
|
Nickname string `xorm:"VARCHAR(64) NOT NULL"`
|
||||||
Password string `xorm:"VARCHAR(64) NOT NULL"`
|
Password string `xorm:"VARCHAR(64) NOT NULL"`
|
||||||
Salt string `xorm:"VARCHAR(10) NOT NULL"`
|
Salt string `xorm:"VARCHAR(10) NOT NULL"`
|
||||||
|
DefaultAccountId int64
|
||||||
|
TransactionEditScope TransactionEditScope `xorm:"TINYINT NOT NULL"`
|
||||||
|
Language string `xorm:"VARCHAR(10)"`
|
||||||
DefaultCurrency string `xorm:"VARCHAR(3) NOT NULL"`
|
DefaultCurrency string `xorm:"VARCHAR(3) NOT NULL"`
|
||||||
FirstDayOfWeek WeekDay `xorm:"TINYINT NOT NULL"`
|
FirstDayOfWeek WeekDay `xorm:"TINYINT NOT NULL"`
|
||||||
TransactionEditScope TransactionEditScope `xorm:"TINYINT NOT NULL"`
|
LongDateFormat LongDateFormat `xorm:"TINYINT"`
|
||||||
|
ShortDateFormat ShortDateFormat `xorm:"TINYINT"`
|
||||||
|
LongTimeFormat LongTimeFormat `xorm:"TINYINT"`
|
||||||
|
ShortTimeFormat ShortTimeFormat `xorm:"TINYINT"`
|
||||||
|
Disabled bool `xorm:"NOT NULL"`
|
||||||
Deleted bool `xorm:"NOT NULL"`
|
Deleted bool `xorm:"NOT NULL"`
|
||||||
EmailVerified bool `xorm:"NOT NULL"`
|
EmailVerified bool `xorm:"NOT NULL"`
|
||||||
CreatedUnixTime int64
|
CreatedUnixTime int64
|
||||||
@@ -109,9 +78,18 @@ type UserBasicInfo struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
|
AvatarUrl string `json:"avatar"`
|
||||||
|
AvatarProvider string `json:"avatarProvider,omitempty"`
|
||||||
|
DefaultAccountId int64 `json:"defaultAccountId,string"`
|
||||||
|
TransactionEditScope TransactionEditScope `json:"transactionEditScope"`
|
||||||
|
Language string `json:"language"`
|
||||||
DefaultCurrency string `json:"defaultCurrency"`
|
DefaultCurrency string `json:"defaultCurrency"`
|
||||||
FirstDayOfWeek WeekDay `json:"firstDayOfWeek"`
|
FirstDayOfWeek WeekDay `json:"firstDayOfWeek"`
|
||||||
TransactionEditScope TransactionEditScope `json:"transactionEditScope"`
|
LongDateFormat LongDateFormat `json:"longDateFormat"`
|
||||||
|
ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
|
||||||
|
LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
|
||||||
|
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
|
||||||
|
EmailVerified bool `json:"emailVerified"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserLoginRequest represents all parameters of user login request
|
// UserLoginRequest represents all parameters of user login request
|
||||||
@@ -126,8 +104,27 @@ type UserRegisterRequest struct {
|
|||||||
Email string `json:"email" binding:"required,notBlank,max=100,validEmail"`
|
Email string `json:"email" binding:"required,notBlank,max=100,validEmail"`
|
||||||
Nickname string `json:"nickname" binding:"required,notBlank,max=64"`
|
Nickname string `json:"nickname" binding:"required,notBlank,max=64"`
|
||||||
Password string `json:"password" binding:"required,min=6,max=128"`
|
Password string `json:"password" binding:"required,min=6,max=128"`
|
||||||
|
Language string `json:"language" binding:"required,min=2,max=16"`
|
||||||
DefaultCurrency string `json:"defaultCurrency" binding:"required,len=3,validCurrency"`
|
DefaultCurrency string `json:"defaultCurrency" binding:"required,len=3,validCurrency"`
|
||||||
FirstDayOfWeek WeekDay `json:"firstDayOfWeek" binding:"min=0,max=6"`
|
FirstDayOfWeek WeekDay `json:"firstDayOfWeek" binding:"min=0,max=6"`
|
||||||
|
TransactionCategoryCreateBatchRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserVerifyEmailRequest represents all parameters of user verify email request
|
||||||
|
type UserVerifyEmailRequest struct {
|
||||||
|
RequestNewToken bool `json:"requestNewToken" binding:"omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserVerifyEmailResponse represents all response parameters after user have verified email
|
||||||
|
type UserVerifyEmailResponse struct {
|
||||||
|
NewToken string `json:"newToken,omitempty"`
|
||||||
|
User *UserBasicInfo `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserResendVerifyEmailRequest represents all parameters of user resend verify email request
|
||||||
|
type UserResendVerifyEmailRequest struct {
|
||||||
|
Email string `json:"email" binding:"omitempty,max=100,validEmail"`
|
||||||
|
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserProfileUpdateRequest represents all parameters of user updating profile request
|
// UserProfileUpdateRequest represents all parameters of user updating profile request
|
||||||
@@ -136,9 +133,15 @@ type UserProfileUpdateRequest struct {
|
|||||||
Nickname string `json:"nickname" binding:"omitempty,notBlank,max=64"`
|
Nickname string `json:"nickname" binding:"omitempty,notBlank,max=64"`
|
||||||
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
Password string `json:"password" binding:"omitempty,min=6,max=128"`
|
||||||
OldPassword string `json:"oldPassword" binding:"omitempty,min=6,max=128"`
|
OldPassword string `json:"oldPassword" binding:"omitempty,min=6,max=128"`
|
||||||
|
DefaultAccountId int64 `json:"defaultAccountId,string" binding:"omitempty,min=1"`
|
||||||
|
TransactionEditScope *TransactionEditScope `json:"transactionEditScope" binding:"omitempty,min=0,max=7"`
|
||||||
|
Language string `json:"language" binding:"omitempty,min=2,max=16"`
|
||||||
DefaultCurrency string `json:"defaultCurrency" binding:"omitempty,len=3,validCurrency"`
|
DefaultCurrency string `json:"defaultCurrency" binding:"omitempty,len=3,validCurrency"`
|
||||||
FirstDayOfWeek *WeekDay `json:"firstDayOfWeek" binding:"omitempty,min=0,max=6"`
|
FirstDayOfWeek *WeekDay `json:"firstDayOfWeek" binding:"omitempty,min=0,max=6"`
|
||||||
TransactionEditScope *TransactionEditScope `json:"transactionEditScope" binding:"omitempty,min=0,max=7"`
|
LongDateFormat *LongDateFormat `json:"longDateFormat" binding:"omitempty,min=0,max=3"`
|
||||||
|
ShortDateFormat *ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"`
|
||||||
|
LongTimeFormat *LongTimeFormat `json:"longTimeFormat" binding:"omitempty,min=0,max=3"`
|
||||||
|
ShortTimeFormat *ShortTimeFormat `json:"shortTimeFormat" binding:"omitempty,min=0,max=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserProfileUpdateResponse represents the data returns to frontend after updating profile
|
// UserProfileUpdateResponse represents the data returns to frontend after updating profile
|
||||||
@@ -152,9 +155,18 @@ type UserProfileResponse struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
|
AvatarUrl string `json:"avatar"`
|
||||||
|
AvatarProvider string `json:"avatarProvider,omitempty"`
|
||||||
|
DefaultAccountId int64 `json:"defaultAccountId,string"`
|
||||||
|
TransactionEditScope TransactionEditScope `json:"transactionEditScope"`
|
||||||
|
Language string `json:"language"`
|
||||||
DefaultCurrency string `json:"defaultCurrency"`
|
DefaultCurrency string `json:"defaultCurrency"`
|
||||||
FirstDayOfWeek WeekDay `json:"firstDayOfWeek"`
|
FirstDayOfWeek WeekDay `json:"firstDayOfWeek"`
|
||||||
TransactionEditScope TransactionEditScope `json:"transactionEditScope"`
|
LongDateFormat LongDateFormat `json:"longDateFormat"`
|
||||||
|
ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
|
||||||
|
LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
|
||||||
|
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
|
||||||
|
EmailVerified bool `json:"emailVerified"`
|
||||||
LastLoginAt int64 `json:"lastLoginAt"`
|
LastLoginAt int64 `json:"lastLoginAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,9 +218,18 @@ func (u *User) ToUserBasicInfo() *UserBasicInfo {
|
|||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
Nickname: u.Nickname,
|
Nickname: u.Nickname,
|
||||||
|
AvatarUrl: u.getAvatarUrl(),
|
||||||
|
AvatarProvider: u.getAvatarProvider(),
|
||||||
|
DefaultAccountId: u.DefaultAccountId,
|
||||||
|
TransactionEditScope: u.TransactionEditScope,
|
||||||
|
Language: u.Language,
|
||||||
DefaultCurrency: u.DefaultCurrency,
|
DefaultCurrency: u.DefaultCurrency,
|
||||||
FirstDayOfWeek: u.FirstDayOfWeek,
|
FirstDayOfWeek: u.FirstDayOfWeek,
|
||||||
TransactionEditScope: u.TransactionEditScope,
|
LongDateFormat: u.LongDateFormat,
|
||||||
|
ShortDateFormat: u.ShortDateFormat,
|
||||||
|
LongTimeFormat: u.LongTimeFormat,
|
||||||
|
ShortTimeFormat: u.ShortTimeFormat,
|
||||||
|
EmailVerified: u.EmailVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +239,32 @@ func (u *User) ToUserProfileResponse() *UserProfileResponse {
|
|||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
Email: u.Email,
|
Email: u.Email,
|
||||||
Nickname: u.Nickname,
|
Nickname: u.Nickname,
|
||||||
|
AvatarUrl: u.getAvatarUrl(),
|
||||||
|
AvatarProvider: u.getAvatarProvider(),
|
||||||
|
DefaultAccountId: u.DefaultAccountId,
|
||||||
|
TransactionEditScope: u.TransactionEditScope,
|
||||||
|
Language: u.Language,
|
||||||
DefaultCurrency: u.DefaultCurrency,
|
DefaultCurrency: u.DefaultCurrency,
|
||||||
FirstDayOfWeek: u.FirstDayOfWeek,
|
FirstDayOfWeek: u.FirstDayOfWeek,
|
||||||
TransactionEditScope: u.TransactionEditScope,
|
LongDateFormat: u.LongDateFormat,
|
||||||
|
ShortDateFormat: u.ShortDateFormat,
|
||||||
|
LongTimeFormat: u.LongTimeFormat,
|
||||||
|
ShortTimeFormat: u.ShortTimeFormat,
|
||||||
|
EmailVerified: u.EmailVerified,
|
||||||
LastLoginAt: u.LastLoginUnixTime,
|
LastLoginAt: u.LastLoginUnixTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) getAvatarProvider() string {
|
||||||
|
return settings.Container.Current.AvatarProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) getAvatarUrl() string {
|
||||||
|
avatarProvider := settings.Container.Current.AvatarProvider
|
||||||
|
|
||||||
|
if avatarProvider == settings.GravatarProvider {
|
||||||
|
return utils.GetGravatarUrl(u.Email)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ type RequestIdInfo struct {
|
|||||||
type DefaultRequestIdGenerator struct {
|
type DefaultRequestIdGenerator struct {
|
||||||
serverUniqId uint16
|
serverUniqId uint16
|
||||||
instanceUniqId uint16
|
instanceUniqId uint16
|
||||||
requestSeqId uint32
|
requestSeqId atomic.Uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultRequestIdGenerator returns a new default request id generator
|
// NewDefaultRequestIdGenerator returns a new default request id generator
|
||||||
@@ -154,7 +154,7 @@ func (r *DefaultRequestIdGenerator) getRequestId(serverUniqId uint16, instanceUn
|
|||||||
|
|
||||||
secondsAndRandomNumber := (secondsLow17bits << randomNumberBits) | randomNumberLow15bits
|
secondsAndRandomNumber := (secondsLow17bits << randomNumberBits) | randomNumberLow15bits
|
||||||
|
|
||||||
seqId := atomic.AddUint32(&r.requestSeqId, 1)
|
seqId := r.requestSeqId.Add(1)
|
||||||
seqIdLow31bits := seqId & reqSeqNumberBitsMask
|
seqIdLow31bits := seqId & reqSeqNumberBitsMask
|
||||||
|
|
||||||
seqIdAndClientIpv6Flag := (seqIdLow31bits << clientIpv6Bit) | (clientIpv6Flag & clientIpv6BitMask)
|
seqIdAndClientIpv6Flag := (seqIdLow31bits << clientIpv6Bit) | (clientIpv6Flag & clientIpv6BitMask)
|
||||||
|
|||||||
+66
-21
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -30,20 +31,31 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTotalAccountCountByUid returns total account count of user
|
||||||
|
func (s *AccountService) GetTotalAccountCountByUid(c *core.Context, uid int64) (int64, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return 0, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Count(&models.Account{})
|
||||||
|
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllAccountsByUid returns all account models of user
|
// GetAllAccountsByUid returns all account models of user
|
||||||
func (s *AccountService) GetAllAccountsByUid(uid int64) ([]*models.Account, error) {
|
func (s *AccountService) GetAllAccountsByUid(c *core.Context, uid int64) ([]*models.Account, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var accounts []*models.Account
|
var accounts []*models.Account
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).OrderBy("parent_account_id asc, display_order asc").Find(&accounts)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).OrderBy("parent_account_id asc, display_order asc").Find(&accounts)
|
||||||
|
|
||||||
return accounts, err
|
return accounts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountAndSubAccountsByAccountId returns account model and sub account models according to account id
|
// GetAccountAndSubAccountsByAccountId returns account model and sub account models according to account id
|
||||||
func (s *AccountService) GetAccountAndSubAccountsByAccountId(uid int64, accountId int64) ([]*models.Account, error) {
|
func (s *AccountService) GetAccountAndSubAccountsByAccountId(c *core.Context, uid int64, accountId int64) ([]*models.Account, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -53,13 +65,29 @@ func (s *AccountService) GetAccountAndSubAccountsByAccountId(uid int64, accountI
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accounts []*models.Account
|
var accounts []*models.Account
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=? AND (account_id=? OR parent_account_id=?)", uid, false, accountId, accountId).OrderBy("parent_account_id asc, display_order asc").Find(&accounts)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=? AND (account_id=? OR parent_account_id=?)", uid, false, accountId, accountId).OrderBy("parent_account_id asc, display_order asc").Find(&accounts)
|
||||||
|
|
||||||
|
return accounts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubAccountsByAccountId returns sub account models according to account id
|
||||||
|
func (s *AccountService) GetSubAccountsByAccountId(c *core.Context, uid int64, accountId int64) ([]*models.Account, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return nil, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if accountId <= 0 {
|
||||||
|
return nil, errs.ErrAccountIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
var accounts []*models.Account
|
||||||
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=? AND parent_account_id=?", uid, false, accountId).OrderBy("display_order asc").Find(&accounts)
|
||||||
|
|
||||||
return accounts, err
|
return accounts, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsByAccountIds returns account models according to account ids
|
// GetAccountsByAccountIds returns account models according to account ids
|
||||||
func (s *AccountService) GetAccountsByAccountIds(uid int64, accountIds []int64) (map[int64]*models.Account, error) {
|
func (s *AccountService) GetAccountsByAccountIds(c *core.Context, uid int64, accountIds []int64) (map[int64]*models.Account, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -69,7 +97,7 @@ func (s *AccountService) GetAccountsByAccountIds(uid int64, accountIds []int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accounts []*models.Account
|
var accounts []*models.Account
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).In("account_id", accountIds).Find(&accounts)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).In("account_id", accountIds).Find(&accounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -80,13 +108,13 @@ func (s *AccountService) GetAccountsByAccountIds(uid int64, accountIds []int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxDisplayOrder returns the max display order according to account category
|
// GetMaxDisplayOrder returns the max display order according to account category
|
||||||
func (s *AccountService) GetMaxDisplayOrder(uid int64, category models.AccountCategory) (int, error) {
|
func (s *AccountService) GetMaxDisplayOrder(c *core.Context, uid int64, category models.AccountCategory) (int32, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
account := &models.Account{}
|
account := &models.Account{}
|
||||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=? AND category=?", uid, false, models.LevelOneAccountParentId, category).OrderBy("display_order desc").Limit(1).Get(account)
|
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=? AND category=?", uid, false, models.LevelOneAccountParentId, category).OrderBy("display_order desc").Limit(1).Get(account)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -100,7 +128,7 @@ func (s *AccountService) GetMaxDisplayOrder(uid int64, category models.AccountCa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxSubAccountDisplayOrder returns the max display order of sub account according to account category and parent account id
|
// GetMaxSubAccountDisplayOrder returns the max display order of sub account according to account category and parent account id
|
||||||
func (s *AccountService) GetMaxSubAccountDisplayOrder(uid int64, category models.AccountCategory, parentAccountId int64) (int, error) {
|
func (s *AccountService) GetMaxSubAccountDisplayOrder(c *core.Context, uid int64, category models.AccountCategory, parentAccountId int64) (int32, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -110,7 +138,7 @@ func (s *AccountService) GetMaxSubAccountDisplayOrder(uid int64, category models
|
|||||||
}
|
}
|
||||||
|
|
||||||
account := &models.Account{}
|
account := &models.Account{}
|
||||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=? AND category=?", uid, false, parentAccountId, category).OrderBy("display_order desc").Limit(1).Get(account)
|
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "parent_account_id", "display_order").Where("uid=? AND deleted=? AND parent_account_id=? AND category=?", uid, false, parentAccountId, category).OrderBy("display_order desc").Limit(1).Get(account)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -124,7 +152,7 @@ func (s *AccountService) GetMaxSubAccountDisplayOrder(uid int64, category models
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccounts saves a new account model to database
|
// CreateAccounts saves a new account model to database
|
||||||
func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAccounts []*models.Account) error {
|
func (s *AccountService) CreateAccounts(c *core.Context, mainAccount *models.Account, childrenAccounts []*models.Account, utcOffset int16) error {
|
||||||
if mainAccount.Uid <= 0 {
|
if mainAccount.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -135,12 +163,22 @@ func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAcc
|
|||||||
var allInitTransactions []*models.Transaction
|
var allInitTransactions []*models.Transaction
|
||||||
|
|
||||||
mainAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
|
mainAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
|
||||||
|
|
||||||
|
if mainAccount.AccountId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
allAccounts[0] = mainAccount
|
allAccounts[0] = mainAccount
|
||||||
|
|
||||||
if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
|
if mainAccount.Type == models.ACCOUNT_TYPE_MULTI_SUB_ACCOUNTS {
|
||||||
for i := 0; i < len(childrenAccounts); i++ {
|
for i := 0; i < len(childrenAccounts); i++ {
|
||||||
childAccount := childrenAccounts[i]
|
childAccount := childrenAccounts[i]
|
||||||
childAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
|
childAccount.AccountId = s.GenerateUuid(uuid.UUID_TYPE_ACCOUNT)
|
||||||
|
|
||||||
|
if childAccount.AccountId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
childAccount.ParentAccountId = mainAccount.AccountId
|
childAccount.ParentAccountId = mainAccount.AccountId
|
||||||
childAccount.Uid = mainAccount.Uid
|
childAccount.Uid = mainAccount.Uid
|
||||||
childAccount.Type = models.ACCOUNT_TYPE_SINGLE_ACCOUNT
|
childAccount.Type = models.ACCOUNT_TYPE_SINGLE_ACCOUNT
|
||||||
@@ -157,12 +195,19 @@ func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAcc
|
|||||||
allAccounts[i].UpdatedUnixTime = now
|
allAccounts[i].UpdatedUnixTime = now
|
||||||
|
|
||||||
if allAccounts[i].Balance != 0 {
|
if allAccounts[i].Balance != 0 {
|
||||||
|
transactionId := s.GenerateUuid(uuid.UUID_TYPE_TRANSACTION)
|
||||||
|
|
||||||
|
if transactionId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
newTransaction := &models.Transaction{
|
newTransaction := &models.Transaction{
|
||||||
TransactionId: s.GenerateUuid(uuid.UUID_TYPE_TRANSACTION),
|
TransactionId: transactionId,
|
||||||
Uid: allAccounts[i].Uid,
|
Uid: allAccounts[i].Uid,
|
||||||
Deleted: false,
|
Deleted: false,
|
||||||
Type: models.TRANSACTION_DB_TYPE_MODIFY_BALANCE,
|
Type: models.TRANSACTION_DB_TYPE_MODIFY_BALANCE,
|
||||||
TransactionTime: transactionTime,
|
TransactionTime: transactionTime,
|
||||||
|
TimezoneUtcOffset: utcOffset,
|
||||||
AccountId: allAccounts[i].AccountId,
|
AccountId: allAccounts[i].AccountId,
|
||||||
Amount: allAccounts[i].Balance,
|
Amount: allAccounts[i].Balance,
|
||||||
RelatedAccountId: allAccounts[i].AccountId,
|
RelatedAccountId: allAccounts[i].AccountId,
|
||||||
@@ -176,7 +221,7 @@ func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAcc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(mainAccount.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(mainAccount.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(allAccounts); i++ {
|
for i := 0; i < len(allAccounts); i++ {
|
||||||
account := allAccounts[i]
|
account := allAccounts[i]
|
||||||
_, err := sess.Insert(account)
|
_, err := sess.Insert(account)
|
||||||
@@ -200,7 +245,7 @@ func (s *AccountService) CreateAccounts(mainAccount *models.Account, childrenAcc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyAccounts saves an existed account model to database
|
// ModifyAccounts saves an existed account model to database
|
||||||
func (s *AccountService) ModifyAccounts(uid int64, accounts []*models.Account) error {
|
func (s *AccountService) ModifyAccounts(c *core.Context, uid int64, accounts []*models.Account) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -211,7 +256,7 @@ func (s *AccountService) ModifyAccounts(uid int64, accounts []*models.Account) e
|
|||||||
accounts[i].UpdatedUnixTime = now
|
accounts[i].UpdatedUnixTime = now
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(accounts); i++ {
|
for i := 0; i < len(accounts); i++ {
|
||||||
account := accounts[i]
|
account := accounts[i]
|
||||||
updatedRows, err := sess.ID(account.AccountId).Cols("name", "category", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account)
|
updatedRows, err := sess.ID(account.AccountId).Cols("name", "category", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account)
|
||||||
@@ -228,7 +273,7 @@ func (s *AccountService) ModifyAccounts(uid int64, accounts []*models.Account) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HideAccount updates hidden field of given accounts
|
// HideAccount updates hidden field of given accounts
|
||||||
func (s *AccountService) HideAccount(uid int64, ids []int64, hidden bool) error {
|
func (s *AccountService) HideAccount(c *core.Context, uid int64, ids []int64, hidden bool) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -240,7 +285,7 @@ func (s *AccountService) HideAccount(uid int64, ids []int64, hidden bool) error
|
|||||||
UpdatedUnixTime: now,
|
UpdatedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("account_id", ids).Update(updateModel)
|
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("account_id", ids).Update(updateModel)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -254,7 +299,7 @@ func (s *AccountService) HideAccount(uid int64, ids []int64, hidden bool) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyAccountDisplayOrders updates display order of given accounts
|
// ModifyAccountDisplayOrders updates display order of given accounts
|
||||||
func (s *AccountService) ModifyAccountDisplayOrders(uid int64, accounts []*models.Account) error {
|
func (s *AccountService) ModifyAccountDisplayOrders(c *core.Context, uid int64, accounts []*models.Account) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -263,7 +308,7 @@ func (s *AccountService) ModifyAccountDisplayOrders(uid int64, accounts []*model
|
|||||||
accounts[i].UpdatedUnixTime = time.Now().Unix()
|
accounts[i].UpdatedUnixTime = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(accounts); i++ {
|
for i := 0; i < len(accounts); i++ {
|
||||||
account := accounts[i]
|
account := accounts[i]
|
||||||
updatedRows, err := sess.ID(account.AccountId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account)
|
updatedRows, err := sess.ID(account.AccountId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(account)
|
||||||
@@ -280,7 +325,7 @@ func (s *AccountService) ModifyAccountDisplayOrders(uid int64, accounts []*model
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAccount deletes an existed account from database
|
// DeleteAccount deletes an existed account from database
|
||||||
func (s *AccountService) DeleteAccount(uid int64, accountId int64) error {
|
func (s *AccountService) DeleteAccount(c *core.Context, uid int64, accountId int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -293,7 +338,7 @@ func (s *AccountService) DeleteAccount(uid int64, accountId int64) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
var accountAndSubAccounts []*models.Account
|
var accountAndSubAccounts []*models.Account
|
||||||
err := sess.Where("uid=? AND deleted=? AND (account_id=? OR parent_account_id=?)", uid, false, accountId, accountId).Find(&accountAndSubAccounts)
|
err := sess.Where("uid=? AND deleted=? AND (account_id=? OR parent_account_id=?)", uid, false, accountId, accountId).Find(&accountAndSubAccounts)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/mail"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
||||||
)
|
)
|
||||||
@@ -36,6 +38,20 @@ func (s *ServiceUsingConfig) CurrentConfig() *settings.Config {
|
|||||||
return s.container.Current
|
return s.container.Current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceUsingMailer represents a service that need to use mailer
|
||||||
|
type ServiceUsingMailer struct {
|
||||||
|
container *mail.MailerContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMail sends an email according to argument
|
||||||
|
func (s *ServiceUsingMailer) SendMail(message *mail.MailMessage) error {
|
||||||
|
if s.container.Current == nil {
|
||||||
|
return errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.container.Current.SendMail(message)
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceUsingUuid represents a service that need to use uuid
|
// ServiceUsingUuid represents a service that need to use uuid
|
||||||
type ServiceUsingUuid struct {
|
type ServiceUsingUuid struct {
|
||||||
container *uuid.UuidContainer
|
container *uuid.UuidContainer
|
||||||
@@ -45,3 +61,8 @@ type ServiceUsingUuid struct {
|
|||||||
func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
|
func (s *ServiceUsingUuid) GenerateUuid(uuidType uuid.UuidType) int64 {
|
||||||
return s.container.GenerateUuid(uuidType)
|
return s.container.GenerateUuid(uuidType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateUuids generates new uuids according to given uuid type and count
|
||||||
|
func (s *ServiceUsingUuid) GenerateUuids(uuidType uuid.UuidType, count uint8) []int64 {
|
||||||
|
return s.container.GenerateUuids(uuidType, count)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/locales"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/mail"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
const passwordResetUrlFormat = "%sdesktop/#/resetpassword?token=%s"
|
||||||
|
|
||||||
|
// ForgetPasswordService represents forget password service
|
||||||
|
type ForgetPasswordService struct {
|
||||||
|
ServiceUsingConfig
|
||||||
|
ServiceUsingMailer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a forget password service singleton instance
|
||||||
|
var (
|
||||||
|
ForgetPasswords = &ForgetPasswordService{
|
||||||
|
ServiceUsingConfig: ServiceUsingConfig{
|
||||||
|
container: settings.Container,
|
||||||
|
},
|
||||||
|
ServiceUsingMailer: ServiceUsingMailer{
|
||||||
|
container: mail.Container,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendPasswordResetEmail sends password reset email according to specified parameters
|
||||||
|
func (s *ForgetPasswordService) SendPasswordResetEmail(c *core.Context, user *models.User, passwordResetToken string, backupLocale string) error {
|
||||||
|
if !s.CurrentConfig().EnableSMTP {
|
||||||
|
return errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
locale := user.Language
|
||||||
|
|
||||||
|
if locale == "" {
|
||||||
|
locale = backupLocale
|
||||||
|
}
|
||||||
|
|
||||||
|
localeTextItems := locales.GetLocaleTextItems(locale)
|
||||||
|
forgetPasswordTextItems := localeTextItems.ForgetPasswordMailTextItems
|
||||||
|
|
||||||
|
expireTimeInMinutes := s.CurrentConfig().PasswordResetTokenExpiredTimeDuration.Minutes()
|
||||||
|
passwordResetUrl := fmt.Sprintf(passwordResetUrlFormat, s.CurrentConfig().RootUrl, url.QueryEscape(passwordResetToken))
|
||||||
|
|
||||||
|
tmpl, err := templates.GetTemplate(templates.TEMPLATE_PASSWORD_RESET)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
templateParams := map[string]any{
|
||||||
|
"AppName": s.CurrentConfig().AppName,
|
||||||
|
"ForgetPasswordMail": map[string]any{
|
||||||
|
"Title": forgetPasswordTextItems.Title,
|
||||||
|
"Salutation": fmt.Sprintf(forgetPasswordTextItems.SalutationFormat, user.Nickname),
|
||||||
|
"DescriptionAboveBtn": forgetPasswordTextItems.DescriptionAboveBtn,
|
||||||
|
"ResetPasswordUrl": passwordResetUrl,
|
||||||
|
"ResetPassword": forgetPasswordTextItems.ResetPassword,
|
||||||
|
"DescriptionBelowBtn": fmt.Sprintf(forgetPasswordTextItems.DescriptionBelowBtnFormat, expireTimeInMinutes),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var bodyBuffer bytes.Buffer
|
||||||
|
err = tmpl.Execute(&bodyBuffer, templateParams)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
message := &mail.MailMessage{
|
||||||
|
To: user.Email,
|
||||||
|
Subject: forgetPasswordTextItems.Title,
|
||||||
|
Body: bodyBuffer.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SendMail(message)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
+132
-75
@@ -6,8 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/dgrijalva/jwt-go/request"
|
"github.com/golang-jwt/jwt/v5/request"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
@@ -38,19 +38,19 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetAllTokensByUid returns all token models of given user
|
// GetAllTokensByUid returns all token models of given user
|
||||||
func (s *TokenService) GetAllTokensByUid(uid int64) ([]*models.TokenRecord, error) {
|
func (s *TokenService) GetAllTokensByUid(c *core.Context, uid int64) ([]*models.TokenRecord, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenRecords []*models.TokenRecord
|
var tokenRecords []*models.TokenRecord
|
||||||
err := s.TokenDB(uid).Cols("uid", "user_token_id", "token_type", "user_agent", "created_unix_time", "expired_unix_time").Where("uid=?", uid).Find(&tokenRecords)
|
err := s.TokenDB(uid).NewSession(c).Cols("uid", "user_token_id", "token_type", "user_agent", "created_unix_time", "expired_unix_time").Where("uid=?", uid).Find(&tokenRecords)
|
||||||
|
|
||||||
return tokenRecords, err
|
return tokenRecords, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllUnexpiredNormalTokensByUid returns all available token models of given user
|
// GetAllUnexpiredNormalTokensByUid returns all available token models of given user
|
||||||
func (s *TokenService) GetAllUnexpiredNormalTokensByUid(uid int64) ([]*models.TokenRecord, error) {
|
func (s *TokenService) GetAllUnexpiredNormalTokensByUid(c *core.Context, uid int64) ([]*models.TokenRecord, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -58,66 +58,48 @@ func (s *TokenService) GetAllUnexpiredNormalTokensByUid(uid int64) ([]*models.To
|
|||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
|
|
||||||
var tokenRecords []*models.TokenRecord
|
var tokenRecords []*models.TokenRecord
|
||||||
err := s.TokenDB(uid).Cols("uid", "user_token_id", "token_type", "user_agent", "created_unix_time", "expired_unix_time").Where("uid=? AND token_type=? AND expired_unix_time>?", uid, core.USER_TOKEN_TYPE_NORMAL, now).Find(&tokenRecords)
|
err := s.TokenDB(uid).NewSession(c).Cols("uid", "user_token_id", "token_type", "user_agent", "created_unix_time", "expired_unix_time").Where("uid=? AND token_type=? AND expired_unix_time>?", uid, core.USER_TOKEN_TYPE_NORMAL, now).Find(&tokenRecords)
|
||||||
|
|
||||||
return tokenRecords, err
|
return tokenRecords, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseToken returns the token model according to request data
|
// ParseTokenByHeader returns the token model according to request data
|
||||||
func (s *TokenService) ParseToken(c *core.Context) (*jwt.Token, *core.UserTokenClaims, error) {
|
func (s *TokenService) ParseTokenByHeader(c *core.Context) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
claims := &core.UserTokenClaims{}
|
return s.parseToken(c, request.BearerExtractor{})
|
||||||
|
}
|
||||||
|
|
||||||
token, err := request.ParseFromRequest(c.Request, request.AuthorizationHeaderExtractor,
|
// ParseTokenByArgument returns the token model according to request data
|
||||||
func(token *jwt.Token) (interface{}, error) {
|
func (s *TokenService) ParseTokenByArgument(c *core.Context, tokenParameterName string) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
uid, err := utils.StringToInt64(claims.Id)
|
return s.parseToken(c, request.ArgumentExtractor{tokenParameterName})
|
||||||
now := time.Now().Unix()
|
}
|
||||||
|
|
||||||
if err != nil {
|
// ParseTokenByCookie returns the token model according to request data
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] user \"uid:%s\" in token is invalid, because %s", claims.Id, err.Error())
|
func (s *TokenService) ParseTokenByCookie(c *core.Context, tokenCookieName string) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
return nil, errs.ErrInvalidToken
|
return s.parseToken(c, utils.CookieExtractor{tokenCookieName})
|
||||||
}
|
|
||||||
|
|
||||||
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" in token of user \"uid:%s\" is invalid, because %s", claims.UserTokenId, claims.Id, err.Error())
|
|
||||||
return nil, errs.ErrInvalidUserTokenId
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenRecord, err := s.getTokenRecord(uid, userTokenId, claims.IssuedAt)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%s\" record not found, because %s", claims.UserTokenId, claims.Id, err.Error())
|
|
||||||
return nil, errs.ErrTokenRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokenRecord.ExpiredUnixTime < now {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%s\" record is expired", claims.UserTokenId, claims.Id)
|
|
||||||
return nil, errs.ErrTokenExpired
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(tokenRecord.Secret), nil
|
|
||||||
}, request.WithClaims(claims))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, claims, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateToken generates a new normal token and saves to database
|
// CreateToken generates a new normal token and saves to database
|
||||||
func (s *TokenService) CreateToken(user *models.User, ctx *core.Context) (string, *core.UserTokenClaims, error) {
|
func (s *TokenService) CreateToken(c *core.Context, user *models.User) (string, *core.UserTokenClaims, error) {
|
||||||
return s.createToken(user, core.USER_TOKEN_TYPE_NORMAL, s.getUserAgent(ctx), s.CurrentConfig().TokenExpiredTimeDuration)
|
return s.createToken(c, user, core.USER_TOKEN_TYPE_NORMAL, s.getUserAgent(c), s.CurrentConfig().TokenExpiredTimeDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRequire2FAToken generates a new token requiring user to verify 2fa passcode and saves to database
|
// CreateRequire2FAToken generates a new token requiring user to verify 2fa passcode and saves to database
|
||||||
func (s *TokenService) CreateRequire2FAToken(user *models.User, ctx *core.Context) (string, *core.UserTokenClaims, error) {
|
func (s *TokenService) CreateRequire2FAToken(c *core.Context, user *models.User) (string, *core.UserTokenClaims, error) {
|
||||||
return s.createToken(user, core.USER_TOKEN_TYPE_REQUIRE_2FA, s.getUserAgent(ctx), s.CurrentConfig().TemporaryTokenExpiredTimeDuration)
|
return s.createToken(c, user, core.USER_TOKEN_TYPE_REQUIRE_2FA, s.getUserAgent(c), s.CurrentConfig().TemporaryTokenExpiredTimeDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEmailVerifyToken generates a new email verify token and saves to database
|
||||||
|
func (s *TokenService) CreateEmailVerifyToken(c *core.Context, user *models.User) (string, *core.UserTokenClaims, error) {
|
||||||
|
return s.createToken(c, user, core.USER_TOKEN_TYPE_EMAIL_VERIFY, s.getUserAgent(c), s.CurrentConfig().EmailVerifyTokenExpiredTimeDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePasswordResetToken generates a new password reset token and saves to database
|
||||||
|
func (s *TokenService) CreatePasswordResetToken(c *core.Context, user *models.User) (string, *core.UserTokenClaims, error) {
|
||||||
|
return s.createToken(c, user, core.USER_TOKEN_TYPE_PASSWORD_RESET, s.getUserAgent(c), s.CurrentConfig().PasswordResetTokenExpiredTimeDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteToken deletes given token from database
|
// DeleteToken deletes given token from database
|
||||||
func (s *TokenService) DeleteToken(tokenRecord *models.TokenRecord) error {
|
func (s *TokenService) DeleteToken(c *core.Context, tokenRecord *models.TokenRecord) error {
|
||||||
if tokenRecord.Uid <= 0 {
|
if tokenRecord.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -126,7 +108,7 @@ func (s *TokenService) DeleteToken(tokenRecord *models.TokenRecord) error {
|
|||||||
return errs.ErrInvalidUserTokenId
|
return errs.ErrInvalidUserTokenId
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.TokenDB(tokenRecord.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.TokenDB(tokenRecord.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
deletedRows, err := sess.Where("uid=? AND user_token_id=? AND created_unix_time=?", tokenRecord.Uid, tokenRecord.UserTokenId, tokenRecord.CreatedUnixTime).Delete(&models.TokenRecord{})
|
deletedRows, err := sess.Where("uid=? AND user_token_id=? AND created_unix_time=?", tokenRecord.Uid, tokenRecord.UserTokenId, tokenRecord.CreatedUnixTime).Delete(&models.TokenRecord{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -140,12 +122,12 @@ func (s *TokenService) DeleteToken(tokenRecord *models.TokenRecord) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTokens deletes given tokens from database
|
// DeleteTokens deletes given tokens from database
|
||||||
func (s *TokenService) DeleteTokens(uid int64, tokenRecords []*models.TokenRecord) error {
|
func (s *TokenService) DeleteTokens(c *core.Context, uid int64, tokenRecords []*models.TokenRecord) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.TokenDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.TokenDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(tokenRecords); i++ {
|
for i := 0; i < len(tokenRecords); i++ {
|
||||||
tokenRecord := tokenRecords[i]
|
tokenRecord := tokenRecords[i]
|
||||||
deletedRows, err := sess.Where("uid=? AND user_token_id=? AND created_unix_time=?", uid, tokenRecord.UserTokenId, tokenRecord.CreatedUnixTime).Delete(&models.TokenRecord{})
|
deletedRows, err := sess.Where("uid=? AND user_token_id=? AND created_unix_time=?", uid, tokenRecord.UserTokenId, tokenRecord.CreatedUnixTime).Delete(&models.TokenRecord{})
|
||||||
@@ -162,34 +144,55 @@ func (s *TokenService) DeleteTokens(uid int64, tokenRecords []*models.TokenRecor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTokenByClaims deletes given token from database
|
// DeleteTokenByClaims deletes given token from database
|
||||||
func (s *TokenService) DeleteTokenByClaims(claims *core.UserTokenClaims) error {
|
func (s *TokenService) DeleteTokenByClaims(c *core.Context, claims *core.UserTokenClaims) error {
|
||||||
uid, err := utils.StringToInt64(claims.Id)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errs.ErrUserIdInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.ErrInvalidUserTokenId
|
return errs.ErrInvalidUserTokenId
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.DeleteToken(&models.TokenRecord{Uid: uid, UserTokenId: userTokenId, CreatedUnixTime: claims.IssuedAt})
|
return s.DeleteToken(c, &models.TokenRecord{
|
||||||
|
Uid: claims.Uid,
|
||||||
|
UserTokenId: userTokenId,
|
||||||
|
CreatedUnixTime: claims.IssuedAt,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTokensBeforeTime deletes tokens that is created before specific tim
|
// DeleteTokensBeforeTime deletes tokens that is created before specific time
|
||||||
func (s *TokenService) DeleteTokensBeforeTime(uid int64, expireTime int64) error {
|
func (s *TokenService) DeleteTokensBeforeTime(c *core.Context, uid int64, expireTime int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.TokenDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.TokenDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Where("uid=? AND created_unix_time<?", uid, expireTime).Delete(&models.TokenRecord{})
|
_, err := sess.Where("uid=? AND created_unix_time<?", uid, expireTime).Delete(&models.TokenRecord{})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteTokensByType deletes specified type tokens
|
||||||
|
func (s *TokenService) DeleteTokensByType(c *core.Context, uid int64, tokenType core.TokenType) error {
|
||||||
|
if uid <= 0 {
|
||||||
|
return errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.TokenDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
|
_, err := sess.Where("uid=? AND token_type=?", uid, tokenType).Delete(&models.TokenRecord{})
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistsValidTokenByType returns whether the given token type exists
|
||||||
|
func (s *TokenService) ExistsValidTokenByType(c *core.Context, uid int64, tokenType core.TokenType) (bool, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return false, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
return s.TokenDB(uid).NewSession(c).Cols("uid", "user_token_id", "expired_unix_time").Where("uid=? AND token_type=? AND expired_unix_time>?", uid, tokenType, now).Exist(&models.TokenRecord{})
|
||||||
|
}
|
||||||
|
|
||||||
// ParseFromTokenId returns token model according to token id
|
// ParseFromTokenId returns token model according to token id
|
||||||
func (s *TokenService) ParseFromTokenId(tokenId string) (*models.TokenRecord, error) {
|
func (s *TokenService) ParseFromTokenId(tokenId string) (*models.TokenRecord, error) {
|
||||||
pairs := strings.Split(tokenId, ":")
|
pairs := strings.Split(tokenId, ":")
|
||||||
@@ -230,7 +233,63 @@ func (s *TokenService) GenerateTokenId(tokenRecord *models.TokenRecord) string {
|
|||||||
return fmt.Sprintf("%d:%d:%d", tokenRecord.Uid, tokenRecord.CreatedUnixTime, tokenRecord.UserTokenId)
|
return fmt.Sprintf("%d:%d:%d", tokenRecord.Uid, tokenRecord.CreatedUnixTime, tokenRecord.UserTokenId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TokenService) createToken(user *models.User, tokenType core.TokenType, userAgent string, expiryDate time.Duration) (string, *core.UserTokenClaims, error) {
|
func (s *TokenService) parseToken(c *core.Context, extractor request.Extractor) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
|
claims := &core.UserTokenClaims{}
|
||||||
|
|
||||||
|
token, err := request.ParseFromRequest(c.Request, extractor,
|
||||||
|
func(token *jwt.Token) (any, error) {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" in token of user \"uid:%d\" is invalid, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
||||||
|
return nil, errs.ErrInvalidUserTokenId
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenRecord, err := s.getTokenRecord(c, claims.Uid, userTokenId, claims.IssuedAt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record not found, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
||||||
|
return nil, errs.ErrTokenRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokenRecord.ExpiredUnixTime < now {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record is expired", claims.UserTokenId, claims.Uid)
|
||||||
|
return nil, errs.ErrTokenExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(tokenRecord.Secret), nil
|
||||||
|
},
|
||||||
|
request.WithClaims(claims),
|
||||||
|
request.WithParser(jwt.NewParser(jwt.WithIssuedAt())),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == request.ErrNoTokenInRequest {
|
||||||
|
return nil, nil, errs.ErrTokenIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenMalformed || err == jwt.ErrTokenUnverifiable || err == jwt.ErrTokenSignatureInvalid {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because %s", err.Error())
|
||||||
|
return nil, nil, errs.ErrCurrentInvalidToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenExpired {
|
||||||
|
return nil, nil, errs.ErrCurrentTokenExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenUsedBeforeIssued {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because issue time is later than now")
|
||||||
|
return nil, nil, errs.ErrCurrentInvalidToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, claims, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TokenService) createToken(c *core.Context, user *models.User, tokenType core.TokenType, userAgent string, expiryDate time.Duration) (string, *core.UserTokenClaims, error) {
|
||||||
var err error
|
var err error
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@@ -249,13 +308,11 @@ func (s *TokenService) createToken(user *models.User, tokenType core.TokenType,
|
|||||||
|
|
||||||
claims := &core.UserTokenClaims{
|
claims := &core.UserTokenClaims{
|
||||||
UserTokenId: utils.Int64ToString(tokenRecord.UserTokenId),
|
UserTokenId: utils.Int64ToString(tokenRecord.UserTokenId),
|
||||||
|
Uid: tokenRecord.Uid,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Type: tokenRecord.TokenType,
|
Type: tokenRecord.TokenType,
|
||||||
StandardClaims: jwt.StandardClaims{
|
IssuedAt: tokenRecord.CreatedUnixTime,
|
||||||
Id: utils.Int64ToString(tokenRecord.Uid),
|
ExpiresAt: tokenRecord.ExpiredUnixTime,
|
||||||
IssuedAt: tokenRecord.CreatedUnixTime,
|
|
||||||
ExpiresAt: tokenRecord.ExpiredUnixTime,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
@@ -265,7 +322,7 @@ func (s *TokenService) createToken(user *models.User, tokenType core.TokenType,
|
|||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.createTokenRecord(tokenRecord)
|
err = s.createTokenRecord(c, tokenRecord)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
@@ -274,7 +331,7 @@ func (s *TokenService) createToken(user *models.User, tokenType core.TokenType,
|
|||||||
return tokenString, claims, err
|
return tokenString, claims, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TokenService) getTokenRecord(uid int64, userTokenId int64, createUnixTime int64) (*models.TokenRecord, error) {
|
func (s *TokenService) getTokenRecord(c *core.Context, uid int64, userTokenId int64, createUnixTime int64) (*models.TokenRecord, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -284,7 +341,7 @@ func (s *TokenService) getTokenRecord(uid int64, userTokenId int64, createUnixTi
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenRecord := &models.TokenRecord{}
|
tokenRecord := &models.TokenRecord{}
|
||||||
has, err := s.TokenDB(uid).Where("uid=? AND user_token_id=? AND created_unix_time=?", uid, userTokenId, createUnixTime).Limit(1).Get(tokenRecord)
|
has, err := s.TokenDB(uid).NewSession(c).Where("uid=? AND user_token_id=? AND created_unix_time=?", uid, userTokenId, createUnixTime).Limit(1).Get(tokenRecord)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -297,7 +354,7 @@ func (s *TokenService) getTokenRecord(uid int64, userTokenId int64, createUnixTi
|
|||||||
return tokenRecord, nil
|
return tokenRecord, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TokenService) createTokenRecord(tokenRecord *models.TokenRecord) error {
|
func (s *TokenService) createTokenRecord(c *core.Context, tokenRecord *models.TokenRecord) error {
|
||||||
if tokenRecord.Uid <= 0 {
|
if tokenRecord.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -306,7 +363,7 @@ func (s *TokenService) createTokenRecord(tokenRecord *models.TokenRecord) error
|
|||||||
return errs.ErrInvalidUserTokenId
|
return errs.ErrInvalidUserTokenId
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.TokenDB(tokenRecord.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.TokenDB(tokenRecord.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Insert(tokenRecord)
|
_, err := sess.Insert(tokenRecord)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -29,14 +30,25 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTotalCategoryCountByUid returns total category count of user
|
||||||
|
func (s *TransactionCategoryService) GetTotalCategoryCountByUid(c *core.Context, uid int64) (int64, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return 0, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Count(&models.TransactionCategory{})
|
||||||
|
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllCategoriesByUid returns all transaction category models of user
|
// GetAllCategoriesByUid returns all transaction category models of user
|
||||||
func (s *TransactionCategoryService) GetAllCategoriesByUid(uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) ([]*models.TransactionCategory, error) {
|
func (s *TransactionCategoryService) GetAllCategoriesByUid(c *core.Context, uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) ([]*models.TransactionCategory, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
condition := "uid=? AND deleted=?"
|
condition := "uid=? AND deleted=?"
|
||||||
conditionParams := make([]interface{}, 0, 8)
|
conditionParams := make([]any, 0, 8)
|
||||||
conditionParams = append(conditionParams, uid)
|
conditionParams = append(conditionParams, uid)
|
||||||
conditionParams = append(conditionParams, false)
|
conditionParams = append(conditionParams, false)
|
||||||
|
|
||||||
@@ -51,13 +63,13 @@ func (s *TransactionCategoryService) GetAllCategoriesByUid(uid int64, categoryTy
|
|||||||
}
|
}
|
||||||
|
|
||||||
var categories []*models.TransactionCategory
|
var categories []*models.TransactionCategory
|
||||||
err := s.UserDataDB(uid).Where(condition, conditionParams...).OrderBy("type asc, parent_category_id asc, display_order asc").Find(&categories)
|
err := s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...).OrderBy("type asc, parent_category_id asc, display_order asc").Find(&categories)
|
||||||
|
|
||||||
return categories, err
|
return categories, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCategoryByCategoryId returns a transaction category model according to transaction category id
|
// GetCategoryByCategoryId returns a transaction category model according to transaction category id
|
||||||
func (s *TransactionCategoryService) GetCategoryByCategoryId(uid int64, categoryId int64) (*models.TransactionCategory, error) {
|
func (s *TransactionCategoryService) GetCategoryByCategoryId(c *core.Context, uid int64, categoryId int64) (*models.TransactionCategory, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -67,7 +79,7 @@ func (s *TransactionCategoryService) GetCategoryByCategoryId(uid int64, category
|
|||||||
}
|
}
|
||||||
|
|
||||||
category := &models.TransactionCategory{}
|
category := &models.TransactionCategory{}
|
||||||
has, err := s.UserDataDB(uid).ID(categoryId).Where("uid=? AND deleted=?", uid, false).Get(category)
|
has, err := s.UserDataDB(uid).NewSession(c).ID(categoryId).Where("uid=? AND deleted=?", uid, false).Get(category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -79,7 +91,7 @@ func (s *TransactionCategoryService) GetCategoryByCategoryId(uid int64, category
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCategoriesByCategoryIds returns transaction category models according to transaction category ids
|
// GetCategoriesByCategoryIds returns transaction category models according to transaction category ids
|
||||||
func (s *TransactionCategoryService) GetCategoriesByCategoryIds(uid int64, categoryIds []int64) (map[int64]*models.TransactionCategory, error) {
|
func (s *TransactionCategoryService) GetCategoriesByCategoryIds(c *core.Context, uid int64, categoryIds []int64) (map[int64]*models.TransactionCategory, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -89,7 +101,7 @@ func (s *TransactionCategoryService) GetCategoriesByCategoryIds(uid int64, categ
|
|||||||
}
|
}
|
||||||
|
|
||||||
var categories []*models.TransactionCategory
|
var categories []*models.TransactionCategory
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).In("category_id", categoryIds).Find(&categories)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).In("category_id", categoryIds).Find(&categories)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -100,13 +112,13 @@ func (s *TransactionCategoryService) GetCategoriesByCategoryIds(uid int64, categ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxDisplayOrder returns the max display order according to transaction category type
|
// GetMaxDisplayOrder returns the max display order according to transaction category type
|
||||||
func (s *TransactionCategoryService) GetMaxDisplayOrder(uid int64, categoryType models.TransactionCategoryType) (int, error) {
|
func (s *TransactionCategoryService) GetMaxDisplayOrder(c *core.Context, uid int64, categoryType models.TransactionCategoryType) (int32, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
category := &models.TransactionCategory{}
|
category := &models.TransactionCategory{}
|
||||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, models.LevelOneTransactionParentId).OrderBy("display_order desc").Limit(1).Get(category)
|
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, models.LevelOneTransactionParentId).OrderBy("display_order desc").Limit(1).Get(category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -120,7 +132,7 @@ func (s *TransactionCategoryService) GetMaxDisplayOrder(uid int64, categoryType
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxSubCategoryDisplayOrder returns the max display order of sub transaction category according to transaction category type and parent transaction category id
|
// GetMaxSubCategoryDisplayOrder returns the max display order of sub transaction category according to transaction category type and parent transaction category id
|
||||||
func (s *TransactionCategoryService) GetMaxSubCategoryDisplayOrder(uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) (int, error) {
|
func (s *TransactionCategoryService) GetMaxSubCategoryDisplayOrder(c *core.Context, uid int64, categoryType models.TransactionCategoryType, parentCategoryId int64) (int32, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -130,7 +142,7 @@ func (s *TransactionCategoryService) GetMaxSubCategoryDisplayOrder(uid int64, ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
category := &models.TransactionCategory{}
|
category := &models.TransactionCategory{}
|
||||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, parentCategoryId).OrderBy("display_order desc").Limit(1).Get(category)
|
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "parent_category_id", "display_order").Where("uid=? AND deleted=? AND type=? AND parent_category_id=?", uid, false, categoryType, parentCategoryId).OrderBy("display_order desc").Limit(1).Get(category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -144,25 +156,29 @@ func (s *TransactionCategoryService) GetMaxSubCategoryDisplayOrder(uid int64, ca
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateCategory saves a new transaction category model to database
|
// CreateCategory saves a new transaction category model to database
|
||||||
func (s *TransactionCategoryService) CreateCategory(category *models.TransactionCategory) error {
|
func (s *TransactionCategoryService) CreateCategory(c *core.Context, category *models.TransactionCategory) error {
|
||||||
if category.Uid <= 0 {
|
if category.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
category.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
category.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
||||||
|
|
||||||
|
if category.CategoryId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
category.Deleted = false
|
category.Deleted = false
|
||||||
category.CreatedUnixTime = time.Now().Unix()
|
category.CreatedUnixTime = time.Now().Unix()
|
||||||
category.UpdatedUnixTime = time.Now().Unix()
|
category.UpdatedUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDataDB(category.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(category.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Insert(category)
|
_, err := sess.Insert(category)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCategories saves a few transaction category models to database
|
// CreateCategories saves a few transaction category models to database
|
||||||
func (s *TransactionCategoryService) CreateCategories(uid int64, categories map[*models.TransactionCategory][]*models.TransactionCategory) ([]*models.TransactionCategory, error) {
|
func (s *TransactionCategoryService) CreateCategories(c *core.Context, uid int64, categories map[*models.TransactionCategory][]*models.TransactionCategory) ([]*models.TransactionCategory, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -174,6 +190,10 @@ func (s *TransactionCategoryService) CreateCategories(uid int64, categories map[
|
|||||||
primaryCategory := primaryCategories[i]
|
primaryCategory := primaryCategories[i]
|
||||||
primaryCategory.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
primaryCategory.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
||||||
|
|
||||||
|
if primaryCategory.CategoryId < 1 {
|
||||||
|
return nil, errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
primaryCategory.Deleted = false
|
primaryCategory.Deleted = false
|
||||||
primaryCategory.CreatedUnixTime = time.Now().Unix()
|
primaryCategory.CreatedUnixTime = time.Now().Unix()
|
||||||
primaryCategory.UpdatedUnixTime = time.Now().Unix()
|
primaryCategory.UpdatedUnixTime = time.Now().Unix()
|
||||||
@@ -185,6 +205,11 @@ func (s *TransactionCategoryService) CreateCategories(uid int64, categories map[
|
|||||||
for j := 0; j < len(secondaryCategories); j++ {
|
for j := 0; j < len(secondaryCategories); j++ {
|
||||||
secondaryCategory := secondaryCategories[j]
|
secondaryCategory := secondaryCategories[j]
|
||||||
secondaryCategory.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
secondaryCategory.CategoryId = s.GenerateUuid(uuid.UUID_TYPE_CATEGORY)
|
||||||
|
|
||||||
|
if secondaryCategory.CategoryId < 1 {
|
||||||
|
return nil, errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
secondaryCategory.ParentCategoryId = primaryCategory.CategoryId
|
secondaryCategory.ParentCategoryId = primaryCategory.CategoryId
|
||||||
|
|
||||||
secondaryCategory.Deleted = false
|
secondaryCategory.Deleted = false
|
||||||
@@ -195,7 +220,7 @@ func (s *TransactionCategoryService) CreateCategories(uid int64, categories map[
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
err := s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(allCategories); i++ {
|
for i := 0; i < len(allCategories); i++ {
|
||||||
category := allCategories[i]
|
category := allCategories[i]
|
||||||
_, err := sess.Insert(category)
|
_, err := sess.Insert(category)
|
||||||
@@ -216,14 +241,14 @@ func (s *TransactionCategoryService) CreateCategories(uid int64, categories map[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyCategory saves an existed transaction category model to database
|
// ModifyCategory saves an existed transaction category model to database
|
||||||
func (s *TransactionCategoryService) ModifyCategory(category *models.TransactionCategory) error {
|
func (s *TransactionCategoryService) ModifyCategory(c *core.Context, category *models.TransactionCategory) error {
|
||||||
if category.Uid <= 0 {
|
if category.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
category.UpdatedUnixTime = time.Now().Unix()
|
category.UpdatedUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDataDB(category.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(category.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.ID(category.CategoryId).Cols("name", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", category.Uid, false).Update(category)
|
updatedRows, err := sess.ID(category.CategoryId).Cols("name", "icon", "color", "comment", "hidden", "updated_unix_time").Where("uid=? AND deleted=?", category.Uid, false).Update(category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -237,7 +262,7 @@ func (s *TransactionCategoryService) ModifyCategory(category *models.Transaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HideCategory updates hidden field of given transaction categories
|
// HideCategory updates hidden field of given transaction categories
|
||||||
func (s *TransactionCategoryService) HideCategory(uid int64, ids []int64, hidden bool) error {
|
func (s *TransactionCategoryService) HideCategory(c *core.Context, uid int64, ids []int64, hidden bool) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -249,7 +274,7 @@ func (s *TransactionCategoryService) HideCategory(uid int64, ids []int64, hidden
|
|||||||
UpdatedUnixTime: now,
|
UpdatedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("category_id", ids).Update(updateModel)
|
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("category_id", ids).Update(updateModel)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -263,7 +288,7 @@ func (s *TransactionCategoryService) HideCategory(uid int64, ids []int64, hidden
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyCategoryDisplayOrders updates display order of given transaction categories
|
// ModifyCategoryDisplayOrders updates display order of given transaction categories
|
||||||
func (s *TransactionCategoryService) ModifyCategoryDisplayOrders(uid int64, categories []*models.TransactionCategory) error {
|
func (s *TransactionCategoryService) ModifyCategoryDisplayOrders(c *core.Context, uid int64, categories []*models.TransactionCategory) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -272,7 +297,7 @@ func (s *TransactionCategoryService) ModifyCategoryDisplayOrders(uid int64, cate
|
|||||||
categories[i].UpdatedUnixTime = time.Now().Unix()
|
categories[i].UpdatedUnixTime = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(categories); i++ {
|
for i := 0; i < len(categories); i++ {
|
||||||
category := categories[i]
|
category := categories[i]
|
||||||
updatedRows, err := sess.ID(category.CategoryId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(category)
|
updatedRows, err := sess.ID(category.CategoryId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(category)
|
||||||
@@ -289,7 +314,7 @@ func (s *TransactionCategoryService) ModifyCategoryDisplayOrders(uid int64, cate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCategory deletes an existed transaction category from database
|
// DeleteCategory deletes an existed transaction category from database
|
||||||
func (s *TransactionCategoryService) DeleteCategory(uid int64, categoryId int64) error {
|
func (s *TransactionCategoryService) DeleteCategory(c *core.Context, uid int64, categoryId int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -301,7 +326,7 @@ func (s *TransactionCategoryService) DeleteCategory(uid int64, categoryId int64)
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
var categoryAndSubCategories []*models.TransactionCategory
|
var categoryAndSubCategories []*models.TransactionCategory
|
||||||
err := sess.Where("uid=? AND deleted=? AND (category_id=? OR parent_category_id=?)", uid, false, categoryId, categoryId).Find(&categoryAndSubCategories)
|
err := sess.Where("uid=? AND deleted=? AND (category_id=? OR parent_category_id=?)", uid, false, categoryId, categoryId).Find(&categoryAndSubCategories)
|
||||||
|
|
||||||
@@ -338,7 +363,7 @@ func (s *TransactionCategoryService) DeleteCategory(uid int64, categoryId int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllCategories deletes all existed transaction categories from database
|
// DeleteAllCategories deletes all existed transaction categories from database
|
||||||
func (s *TransactionCategoryService) DeleteAllCategories(uid int64) error {
|
func (s *TransactionCategoryService) DeleteAllCategories(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -350,7 +375,7 @@ func (s *TransactionCategoryService) DeleteAllCategories(uid int64) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
exists, err := sess.Cols("uid", "deleted", "category_id").Where("uid=? AND deleted=? AND category_id<>?", uid, false, 0).Limit(1).Exist(&models.Transaction{})
|
exists, err := sess.Cols("uid", "deleted", "category_id").Where("uid=? AND deleted=? AND category_id<>?", uid, false, 0).Limit(1).Exist(&models.Transaction{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -29,20 +30,31 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTotalTagCountByUid returns total tag count of user
|
||||||
|
func (s *TransactionTagService) GetTotalTagCountByUid(c *core.Context, uid int64) (int64, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return 0, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Count(&models.TransactionTag{})
|
||||||
|
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllTagsByUid returns all transaction tag models of user
|
// GetAllTagsByUid returns all transaction tag models of user
|
||||||
func (s *TransactionTagService) GetAllTagsByUid(uid int64) ([]*models.TransactionTag, error) {
|
func (s *TransactionTagService) GetAllTagsByUid(c *core.Context, uid int64) ([]*models.TransactionTag, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags []*models.TransactionTag
|
var tags []*models.TransactionTag
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).Find(&tags)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Find(&tags)
|
||||||
|
|
||||||
return tags, err
|
return tags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTagByTagId returns a transaction tag model according to transaction tag id
|
// GetTagByTagId returns a transaction tag model according to transaction tag id
|
||||||
func (s *TransactionTagService) GetTagByTagId(uid int64, tagId int64) (*models.TransactionTag, error) {
|
func (s *TransactionTagService) GetTagByTagId(c *core.Context, uid int64, tagId int64) (*models.TransactionTag, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -52,7 +64,7 @@ func (s *TransactionTagService) GetTagByTagId(uid int64, tagId int64) (*models.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
tag := &models.TransactionTag{}
|
tag := &models.TransactionTag{}
|
||||||
has, err := s.UserDataDB(uid).ID(tagId).Where("uid=? AND deleted=?", uid, false).Get(tag)
|
has, err := s.UserDataDB(uid).NewSession(c).ID(tagId).Where("uid=? AND deleted=?", uid, false).Get(tag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -64,7 +76,7 @@ func (s *TransactionTagService) GetTagByTagId(uid int64, tagId int64) (*models.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTagsByTagIds returns transaction tag models according to transaction tag ids
|
// GetTagsByTagIds returns transaction tag models according to transaction tag ids
|
||||||
func (s *TransactionTagService) GetTagsByTagIds(uid int64, tagIds []int64) (map[int64]*models.TransactionTag, error) {
|
func (s *TransactionTagService) GetTagsByTagIds(c *core.Context, uid int64, tagIds []int64) (map[int64]*models.TransactionTag, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -74,7 +86,7 @@ func (s *TransactionTagService) GetTagsByTagIds(uid int64, tagIds []int64) (map[
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tags []*models.TransactionTag
|
var tags []*models.TransactionTag
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).In("tag_id", tagIds).Find(&tags)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).In("tag_id", tagIds).Find(&tags)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -85,13 +97,13 @@ func (s *TransactionTagService) GetTagsByTagIds(uid int64, tagIds []int64) (map[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxDisplayOrder returns the max display order
|
// GetMaxDisplayOrder returns the max display order
|
||||||
func (s *TransactionTagService) GetMaxDisplayOrder(uid int64) (int, error) {
|
func (s *TransactionTagService) GetMaxDisplayOrder(c *core.Context, uid int64) (int32, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
tag := &models.TransactionTag{}
|
tag := &models.TransactionTag{}
|
||||||
has, err := s.UserDataDB(uid).Cols("uid", "deleted", "display_order").Where("uid=? AND deleted=?", uid, false).OrderBy("display_order desc").Limit(1).Get(tag)
|
has, err := s.UserDataDB(uid).NewSession(c).Cols("uid", "deleted", "display_order").Where("uid=? AND deleted=?", uid, false).OrderBy("display_order desc").Limit(1).Get(tag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -105,13 +117,13 @@ func (s *TransactionTagService) GetMaxDisplayOrder(uid int64) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllTagIdsOfAllTransactions returns all transaction tag ids
|
// GetAllTagIdsOfAllTransactions returns all transaction tag ids
|
||||||
func (s *TransactionTagService) GetAllTagIdsOfAllTransactions(uid int64) (map[int64][]int64, error) {
|
func (s *TransactionTagService) GetAllTagIdsOfAllTransactions(c *core.Context, uid int64) (map[int64][]int64, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagIndexs []*models.TransactionTagIndex
|
var tagIndexs []*models.TransactionTagIndex
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).Find(&tagIndexs)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Find(&tagIndexs)
|
||||||
|
|
||||||
allTransactionTagIds := s.getGroupedTransactionTagIds(tagIndexs)
|
allTransactionTagIds := s.getGroupedTransactionTagIds(tagIndexs)
|
||||||
|
|
||||||
@@ -119,13 +131,13 @@ func (s *TransactionTagService) GetAllTagIdsOfAllTransactions(uid int64) (map[in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllTagIdsOfTransactions returns transaction tag ids for given transactions
|
// GetAllTagIdsOfTransactions returns transaction tag ids for given transactions
|
||||||
func (s *TransactionTagService) GetAllTagIdsOfTransactions(uid int64, transactionIds []int64) (map[int64][]int64, error) {
|
func (s *TransactionTagService) GetAllTagIdsOfTransactions(c *core.Context, uid int64, transactionIds []int64) (map[int64][]int64, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagIndexs []*models.TransactionTagIndex
|
var tagIndexs []*models.TransactionTagIndex
|
||||||
err := s.UserDataDB(uid).Where("uid=? AND deleted=?", uid, false).In("transaction_id", transactionIds).Find(&tagIndexs)
|
err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).In("transaction_id", transactionIds).Find(&tagIndexs)
|
||||||
|
|
||||||
allTransactionTagIds := s.getGroupedTransactionTagIds(tagIndexs)
|
allTransactionTagIds := s.getGroupedTransactionTagIds(tagIndexs)
|
||||||
|
|
||||||
@@ -133,12 +145,12 @@ func (s *TransactionTagService) GetAllTagIdsOfTransactions(uid int64, transactio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTag saves a new transaction tag model to database
|
// CreateTag saves a new transaction tag model to database
|
||||||
func (s *TransactionTagService) CreateTag(tag *models.TransactionTag) error {
|
func (s *TransactionTagService) CreateTag(c *core.Context, tag *models.TransactionTag) error {
|
||||||
if tag.Uid <= 0 {
|
if tag.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err := s.ExistsTagName(tag.Uid, tag.Name)
|
exists, err := s.ExistsTagName(c, tag.Uid, tag.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -148,23 +160,27 @@ func (s *TransactionTagService) CreateTag(tag *models.TransactionTag) error {
|
|||||||
|
|
||||||
tag.TagId = s.GenerateUuid(uuid.UUID_TYPE_TAG)
|
tag.TagId = s.GenerateUuid(uuid.UUID_TYPE_TAG)
|
||||||
|
|
||||||
|
if tag.TagId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
tag.Deleted = false
|
tag.Deleted = false
|
||||||
tag.CreatedUnixTime = time.Now().Unix()
|
tag.CreatedUnixTime = time.Now().Unix()
|
||||||
tag.UpdatedUnixTime = time.Now().Unix()
|
tag.UpdatedUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDataDB(tag.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(tag.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Insert(tag)
|
_, err := sess.Insert(tag)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyTag saves an existed transaction tag model to database
|
// ModifyTag saves an existed transaction tag model to database
|
||||||
func (s *TransactionTagService) ModifyTag(tag *models.TransactionTag) error {
|
func (s *TransactionTagService) ModifyTag(c *core.Context, tag *models.TransactionTag) error {
|
||||||
if tag.Uid <= 0 {
|
if tag.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err := s.ExistsTagName(tag.Uid, tag.Name)
|
exists, err := s.ExistsTagName(c, tag.Uid, tag.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -174,7 +190,7 @@ func (s *TransactionTagService) ModifyTag(tag *models.TransactionTag) error {
|
|||||||
|
|
||||||
tag.UpdatedUnixTime = time.Now().Unix()
|
tag.UpdatedUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDataDB(tag.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(tag.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.ID(tag.TagId).Cols("name", "updated_unix_time").Where("uid=? AND deleted=?", tag.Uid, false).Update(tag)
|
updatedRows, err := sess.ID(tag.TagId).Cols("name", "updated_unix_time").Where("uid=? AND deleted=?", tag.Uid, false).Update(tag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -188,7 +204,7 @@ func (s *TransactionTagService) ModifyTag(tag *models.TransactionTag) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HideTag updates hidden field of given transaction tags
|
// HideTag updates hidden field of given transaction tags
|
||||||
func (s *TransactionTagService) HideTag(uid int64, ids []int64, hidden bool) error {
|
func (s *TransactionTagService) HideTag(c *core.Context, uid int64, ids []int64, hidden bool) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -200,7 +216,7 @@ func (s *TransactionTagService) HideTag(uid int64, ids []int64, hidden bool) err
|
|||||||
UpdatedUnixTime: now,
|
UpdatedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("tag_id", ids).Update(updateModel)
|
updatedRows, err := sess.Cols("hidden", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).In("tag_id", ids).Update(updateModel)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -214,7 +230,7 @@ func (s *TransactionTagService) HideTag(uid int64, ids []int64, hidden bool) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyTagDisplayOrders updates display order of given transaction tags
|
// ModifyTagDisplayOrders updates display order of given transaction tags
|
||||||
func (s *TransactionTagService) ModifyTagDisplayOrders(uid int64, tags []*models.TransactionTag) error {
|
func (s *TransactionTagService) ModifyTagDisplayOrders(c *core.Context, uid int64, tags []*models.TransactionTag) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -223,7 +239,7 @@ func (s *TransactionTagService) ModifyTagDisplayOrders(uid int64, tags []*models
|
|||||||
tags[i].UpdatedUnixTime = time.Now().Unix()
|
tags[i].UpdatedUnixTime = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
for i := 0; i < len(tags); i++ {
|
for i := 0; i < len(tags); i++ {
|
||||||
tag := tags[i]
|
tag := tags[i]
|
||||||
updatedRows, err := sess.ID(tag.TagId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(tag)
|
updatedRows, err := sess.ID(tag.TagId).Cols("display_order", "updated_unix_time").Where("uid=? AND deleted=?", uid, false).Update(tag)
|
||||||
@@ -240,7 +256,7 @@ func (s *TransactionTagService) ModifyTagDisplayOrders(uid int64, tags []*models
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTag deletes an existed transaction tag from database
|
// DeleteTag deletes an existed transaction tag from database
|
||||||
func (s *TransactionTagService) DeleteTag(uid int64, tagId int64) error {
|
func (s *TransactionTagService) DeleteTag(c *core.Context, uid int64, tagId int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -252,7 +268,7 @@ func (s *TransactionTagService) DeleteTag(uid int64, tagId int64) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
exists, err := sess.Cols("uid", "tag_id").Where("uid=? AND deleted=? AND tag_id=?", uid, false, tagId).Limit(1).Exist(&models.TransactionTagIndex{})
|
exists, err := sess.Cols("uid", "tag_id").Where("uid=? AND deleted=? AND tag_id=?", uid, false, tagId).Limit(1).Exist(&models.TransactionTagIndex{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -274,7 +290,7 @@ func (s *TransactionTagService) DeleteTag(uid int64, tagId int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllTags deletes all existed transaction tags from database
|
// DeleteAllTags deletes all existed transaction tags from database
|
||||||
func (s *TransactionTagService) DeleteAllTags(uid int64) error {
|
func (s *TransactionTagService) DeleteAllTags(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -286,7 +302,7 @@ func (s *TransactionTagService) DeleteAllTags(uid int64) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
exists, err := sess.Cols("uid", "deleted").Where("uid=? AND deleted=?", uid, false).Limit(1).Exist(&models.TransactionTagIndex{})
|
exists, err := sess.Cols("uid", "deleted").Where("uid=? AND deleted=?", uid, false).Limit(1).Exist(&models.TransactionTagIndex{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -306,12 +322,12 @@ func (s *TransactionTagService) DeleteAllTags(uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExistsTagName returns whether the given tag name exists
|
// ExistsTagName returns whether the given tag name exists
|
||||||
func (s *TransactionTagService) ExistsTagName(uid int64, name string) (bool, error) {
|
func (s *TransactionTagService) ExistsTagName(c *core.Context, uid int64, name string) (bool, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return false, errs.ErrTransactionTagNameIsEmpty
|
return false, errs.ErrTransactionTagNameIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).Cols("name").Where("uid=? AND deleted=? AND name=?", uid, false, name).Exist(&models.TransactionTag{})
|
return s.UserDataDB(uid).NewSession(c).Cols("name").Where("uid=? AND deleted=? AND name=?", uid, false, name).Exist(&models.TransactionTag{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTagMapByList returns a transaction tag map by a list
|
// GetTagMapByList returns a transaction tag map by a list
|
||||||
|
|||||||
+142
-64
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -32,13 +33,24 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTotalTransactionCountByUid returns total transaction count of user
|
||||||
|
func (s *TransactionService) GetTotalTransactionCountByUid(c *core.Context, uid int64) (int64, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return 0, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := s.UserDataDB(uid).NewSession(c).Where("uid=? AND deleted=?", uid, false).Count(&models.Transaction{})
|
||||||
|
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllTransactions returns all transactions
|
// GetAllTransactions returns all transactions
|
||||||
func (s *TransactionService) GetAllTransactions(uid int64, pageCount int, noDuplicated bool) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetAllTransactions(c *core.Context, uid int64, pageCount int32, noDuplicated bool) ([]*models.Transaction, error) {
|
||||||
maxTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
maxTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
||||||
var allTransactions []*models.Transaction
|
var allTransactions []*models.Transaction
|
||||||
|
|
||||||
for maxTransactionTime > 0 {
|
for maxTransactionTime > 0 {
|
||||||
transactions, err := s.GetAllTransactionsByMaxTime(uid, maxTransactionTime, pageCount, noDuplicated)
|
transactions, err := s.GetAllTransactionsByMaxTime(c, uid, maxTransactionTime, pageCount, noDuplicated)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -46,7 +58,7 @@ func (s *TransactionService) GetAllTransactions(uid int64, pageCount int, noDupl
|
|||||||
|
|
||||||
allTransactions = append(allTransactions, transactions...)
|
allTransactions = append(allTransactions, transactions...)
|
||||||
|
|
||||||
if len(transactions) < pageCount {
|
if len(transactions) < int(pageCount) {
|
||||||
maxTransactionTime = 0
|
maxTransactionTime = 0
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -58,16 +70,22 @@ func (s *TransactionService) GetAllTransactions(uid int64, pageCount int, noDupl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllTransactionsByMaxTime returns all transactions before given time
|
// GetAllTransactionsByMaxTime returns all transactions before given time
|
||||||
func (s *TransactionService) GetAllTransactionsByMaxTime(uid int64, maxTransactionTime int64, count int, noDuplicated bool) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetAllTransactionsByMaxTime(c *core.Context, uid int64, maxTransactionTime int64, count int32, noDuplicated bool) ([]*models.Transaction, error) {
|
||||||
return s.GetTransactionsByMaxTime(uid, maxTransactionTime, 0, 0, nil, 0, "", count, noDuplicated)
|
return s.GetTransactionsByMaxTime(c, uid, maxTransactionTime, 0, 0, nil, nil, "", 1, count, false, noDuplicated)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionsByMaxTime returns transactions before given time
|
// GetTransactionsByMaxTime returns transactions before given time
|
||||||
func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, count int, noDuplicated bool) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetTransactionsByMaxTime(c *core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, keyword string, page int32, count int32, needOneMoreItem bool, noDuplicated bool) ([]*models.Transaction, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if page < 0 {
|
||||||
|
return nil, errs.ErrPageIndexInvalid
|
||||||
|
} else if page == 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
if count < 1 {
|
if count < 1 {
|
||||||
return nil, errs.ErrPageCountInvalid
|
return nil, errs.ErrPageCountInvalid
|
||||||
}
|
}
|
||||||
@@ -75,47 +93,58 @@ func (s *TransactionService) GetTransactionsByMaxTime(uid int64, maxTransactionT
|
|||||||
var transactions []*models.Transaction
|
var transactions []*models.Transaction
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, noDuplicated)
|
actualCount := count
|
||||||
err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, 0).OrderBy("transaction_time desc").Find(&transactions)
|
|
||||||
|
if needOneMoreItem {
|
||||||
|
actualCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, keyword, noDuplicated)
|
||||||
|
err = s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...).Limit(int(actualCount), int(count*(page-1))).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
|
||||||
return transactions, err
|
return transactions, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionsInMonthByPage returns transactions in given year and month
|
// GetTransactionsInMonthByPage returns all transactions in given year and month
|
||||||
func (s *TransactionService) GetTransactionsInMonthByPage(uid int64, year int, month int, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, page int, count int, utcOffset int16) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetTransactionsInMonthByPage(c *core.Context, uid int64, year int32, month int32, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, keyword string) ([]*models.Transaction, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
if page < 1 {
|
startMinUnixTime, err := utils.ParseFromLongDateTimeToMinUnixTime(fmt.Sprintf("%d-%02d-01 00:00:00", year, month))
|
||||||
return nil, errs.ErrPageIndexInvalid
|
startMaxUnixTime, err := utils.ParseFromLongDateTimeToMaxUnixTime(fmt.Sprintf("%d-%02d-01 00:00:00", year, month))
|
||||||
}
|
|
||||||
|
|
||||||
if count < 1 {
|
|
||||||
return nil, errs.ErrPageCountInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
startTime, err := utils.ParseFromLongDateTime(fmt.Sprintf("%d-%02d-01 00:00:00", year, month), utcOffset)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.ErrSystemError
|
return nil, errs.ErrSystemError
|
||||||
}
|
}
|
||||||
|
|
||||||
endTime := startTime.AddDate(0, 1, 0)
|
endMaxUnixTime := startMaxUnixTime.AddDate(0, 1, 0)
|
||||||
|
|
||||||
minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix())
|
minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startMinUnixTime.Unix())
|
||||||
maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - 1
|
maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endMaxUnixTime.Unix()) - 1
|
||||||
|
|
||||||
var transactions []*models.Transaction
|
var transactions []*models.Transaction
|
||||||
|
|
||||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, true)
|
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, keyword, true)
|
||||||
err = s.UserDataDB(uid).Where(condition, conditionParams...).Limit(count, count*(page-1)).OrderBy("transaction_time desc").Find(&transactions)
|
err = s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
|
||||||
return transactions, err
|
transactionsInMonth := make([]*models.Transaction, 0, len(transactions))
|
||||||
|
|
||||||
|
for i := 0; i < len(transactions); i++ {
|
||||||
|
transaction := transactions[i]
|
||||||
|
transactionUnixTime := utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime)
|
||||||
|
transactionTimeZone := time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||||
|
|
||||||
|
if utils.IsUnixTimeEqualsYearAndMonth(transactionUnixTime, transactionTimeZone, year, month) {
|
||||||
|
transactionsInMonth = append(transactionsInMonth, transaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionsInMonth, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionByTransactionId returns a transaction model according to transaction id
|
// GetTransactionByTransactionId returns a transaction model according to transaction id
|
||||||
func (s *TransactionService) GetTransactionByTransactionId(uid int64, transactionId int64) (*models.Transaction, error) {
|
func (s *TransactionService) GetTransactionByTransactionId(c *core.Context, uid int64, transactionId int64) (*models.Transaction, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -125,7 +154,7 @@ func (s *TransactionService) GetTransactionByTransactionId(uid int64, transactio
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction := &models.Transaction{}
|
transaction := &models.Transaction{}
|
||||||
has, err := s.UserDataDB(uid).ID(transactionId).Where("uid=? AND deleted=?", uid, false).Get(transaction)
|
has, err := s.UserDataDB(uid).NewSession(c).ID(transactionId).Where("uid=? AND deleted=?", uid, false).Get(transaction)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -137,12 +166,12 @@ func (s *TransactionService) GetTransactionByTransactionId(uid int64, transactio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllTransactionCount returns total count of transactions
|
// GetAllTransactionCount returns total count of transactions
|
||||||
func (s *TransactionService) GetAllTransactionCount(uid int64) (int64, error) {
|
func (s *TransactionService) GetAllTransactionCount(c *core.Context, uid int64) (int64, error) {
|
||||||
return s.GetTransactionCount(uid, 0, 0, 0, nil, 0, "")
|
return s.GetTransactionCount(c, uid, 0, 0, 0, nil, nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMonthTransactionCount returns total count of transactions in given year and month
|
// GetMonthTransactionCount returns total count of transactions in given year and month
|
||||||
func (s *TransactionService) GetMonthTransactionCount(uid int64, year int, month int, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, utcOffset int16) (int64, error) {
|
func (s *TransactionService) GetMonthTransactionCount(c *core.Context, uid int64, year int32, month int32, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, keyword string, utcOffset int16) (int64, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -158,21 +187,21 @@ func (s *TransactionService) GetMonthTransactionCount(uid int64, year int, month
|
|||||||
minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix())
|
minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix())
|
||||||
maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - 1
|
maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - 1
|
||||||
|
|
||||||
return s.GetTransactionCount(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword)
|
return s.GetTransactionCount(c, uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, keyword)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionCount returns count of transactions
|
// GetTransactionCount returns count of transactions
|
||||||
func (s *TransactionService) GetTransactionCount(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string) (int64, error) {
|
func (s *TransactionService) GetTransactionCount(c *core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, keyword string) (int64, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return 0, errs.ErrUserIdInvalid
|
return 0, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword, true)
|
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, keyword, true)
|
||||||
return s.UserDataDB(uid).Where(condition, conditionParams...).Count(&models.Transaction{})
|
return s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...).Count(&models.Transaction{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTransaction saves a new transaction to database
|
// CreateTransaction saves a new transaction to database
|
||||||
func (s *TransactionService) CreateTransaction(transaction *models.Transaction, tagIds []int64) error {
|
func (s *TransactionService) CreateTransaction(c *core.Context, transaction *models.Transaction, tagIds []int64) error {
|
||||||
if transaction.Uid <= 0 {
|
if transaction.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -186,7 +215,24 @@ func (s *TransactionService) CreateTransaction(transaction *models.Transaction,
|
|||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
|
|
||||||
transaction.TransactionId = s.GenerateUuid(uuid.UUID_TYPE_TRANSACTION)
|
needUuidCount := 1
|
||||||
|
|
||||||
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
|
needUuidCount = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
uuids := s.GenerateUuids(uuid.UUID_TYPE_TRANSACTION, uint8(needUuidCount))
|
||||||
|
|
||||||
|
if len(uuids) < needUuidCount {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction.TransactionId = uuids[0]
|
||||||
|
|
||||||
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
|
transaction.RelatedId = uuids[1]
|
||||||
|
}
|
||||||
|
|
||||||
transaction.TransactionTime = utils.GetMinTransactionTimeFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))
|
transaction.TransactionTime = utils.GetMinTransactionTimeFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))
|
||||||
|
|
||||||
transaction.CreatedUnixTime = now
|
transaction.CreatedUnixTime = now
|
||||||
@@ -196,8 +242,14 @@ func (s *TransactionService) CreateTransaction(transaction *models.Transaction,
|
|||||||
transactionTagIndexs := make([]*models.TransactionTagIndex, len(tagIds))
|
transactionTagIndexs := make([]*models.TransactionTagIndex, len(tagIds))
|
||||||
|
|
||||||
for i := 0; i < len(tagIds); i++ {
|
for i := 0; i < len(tagIds); i++ {
|
||||||
|
tagIndexId := s.GenerateUuid(uuid.UUID_TYPE_TAG_INDEX)
|
||||||
|
|
||||||
|
if tagIndexId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
transactionTagIndexs[i] = &models.TransactionTagIndex{
|
transactionTagIndexs[i] = &models.TransactionTagIndex{
|
||||||
TagIndexId: s.GenerateUuid(uuid.UUID_TYPE_TAG_INDEX),
|
TagIndexId: tagIndexId,
|
||||||
Uid: transaction.Uid,
|
Uid: transaction.Uid,
|
||||||
Deleted: false,
|
Deleted: false,
|
||||||
TagId: tagIds[i],
|
TagId: tagIds[i],
|
||||||
@@ -207,7 +259,7 @@ func (s *TransactionService) CreateTransaction(transaction *models.Transaction,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(transaction.Uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(transaction.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
// Get and verify source and destination account
|
// Get and verify source and destination account
|
||||||
sourceAccount, destinationAccount, err := s.getAccountModels(sess, transaction)
|
sourceAccount, destinationAccount, err := s.getAccountModels(sess, transaction)
|
||||||
|
|
||||||
@@ -256,8 +308,7 @@ func (s *TransactionService) CreateTransaction(transaction *models.Transaction,
|
|||||||
var relatedTransaction *models.Transaction
|
var relatedTransaction *models.Transaction
|
||||||
|
|
||||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
relatedTransaction = s.GetRelatedTransferTransaction(transaction, s.GenerateUuid(uuid.UUID_TYPE_TRANSACTION))
|
relatedTransaction = s.GetRelatedTransferTransaction(transaction)
|
||||||
transaction.RelatedId = relatedTransaction.TransactionId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createdRows, err := sess.Insert(transaction)
|
createdRows, err := sess.Insert(transaction)
|
||||||
@@ -372,7 +423,7 @@ func (s *TransactionService) CreateTransaction(transaction *models.Transaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModifyTransaction saves an existed transaction to database
|
// ModifyTransaction saves an existed transaction to database
|
||||||
func (s *TransactionService) ModifyTransaction(transaction *models.Transaction, addTagIds []int64, removeTagIds []int64) error {
|
func (s *TransactionService) ModifyTransaction(c *core.Context, transaction *models.Transaction, addTagIds []int64, removeTagIds []int64) error {
|
||||||
if transaction.Uid <= 0 {
|
if transaction.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -391,8 +442,14 @@ func (s *TransactionService) ModifyTransaction(transaction *models.Transaction,
|
|||||||
transactionTagIndexs := make([]*models.TransactionTagIndex, len(addTagIds))
|
transactionTagIndexs := make([]*models.TransactionTagIndex, len(addTagIds))
|
||||||
|
|
||||||
for i := 0; i < len(addTagIds); i++ {
|
for i := 0; i < len(addTagIds); i++ {
|
||||||
|
tagIndexId := s.GenerateUuid(uuid.UUID_TYPE_TAG_INDEX)
|
||||||
|
|
||||||
|
if tagIndexId < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
transactionTagIndexs[i] = &models.TransactionTagIndex{
|
transactionTagIndexs[i] = &models.TransactionTagIndex{
|
||||||
TagIndexId: s.GenerateUuid(uuid.UUID_TYPE_TAG_INDEX),
|
TagIndexId: tagIndexId,
|
||||||
Uid: transaction.Uid,
|
Uid: transaction.Uid,
|
||||||
Deleted: false,
|
Deleted: false,
|
||||||
TagId: addTagIds[i],
|
TagId: addTagIds[i],
|
||||||
@@ -402,7 +459,7 @@ func (s *TransactionService) ModifyTransaction(transaction *models.Transaction,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.UserDataDB(transaction.Uid).DoTransaction(func(sess *xorm.Session) error {
|
err := s.UserDataDB(transaction.Uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
// Get and verify current transaction
|
// Get and verify current transaction
|
||||||
oldTransaction := &models.Transaction{}
|
oldTransaction := &models.Transaction{}
|
||||||
has, err := sess.ID(transaction.TransactionId).Where("uid=? AND deleted=?", transaction.Uid, false).Get(oldTransaction)
|
has, err := sess.ID(transaction.TransactionId).Where("uid=? AND deleted=?", transaction.Uid, false).Get(oldTransaction)
|
||||||
@@ -520,6 +577,14 @@ func (s *TransactionService) ModifyTransaction(transaction *models.Transaction,
|
|||||||
updateCols = append(updateCols, "comment")
|
updateCols = append(updateCols, "comment")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if transaction.GeoLongitude != oldTransaction.GeoLongitude {
|
||||||
|
updateCols = append(updateCols, "geo_longitude")
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.GeoLatitude != oldTransaction.GeoLatitude {
|
||||||
|
updateCols = append(updateCols, "geo_latitude")
|
||||||
|
}
|
||||||
|
|
||||||
// Get and verify tags
|
// Get and verify tags
|
||||||
err = s.isTagsValid(sess, transaction, transactionTagIndexs, addTagIds)
|
err = s.isTagsValid(sess, transaction, transactionTagIndexs, addTagIds)
|
||||||
|
|
||||||
@@ -537,7 +602,7 @@ func (s *TransactionService) ModifyTransaction(transaction *models.Transaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
relatedTransaction := s.GetRelatedTransferTransaction(transaction, transaction.RelatedId)
|
relatedTransaction := s.GetRelatedTransferTransaction(transaction)
|
||||||
|
|
||||||
if utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime) != utils.GetUnixTimeFromTransactionTime(relatedTransaction.TransactionTime) {
|
if utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime) != utils.GetUnixTimeFromTransactionTime(relatedTransaction.TransactionTime) {
|
||||||
return errs.ErrTooMuchTransactionInOneSecond
|
return errs.ErrTooMuchTransactionInOneSecond
|
||||||
@@ -735,7 +800,7 @@ func (s *TransactionService) ModifyTransaction(transaction *models.Transaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTransaction deletes an existed transaction from database
|
// DeleteTransaction deletes an existed transaction from database
|
||||||
func (s *TransactionService) DeleteTransaction(uid int64, transactionId int64) error {
|
func (s *TransactionService) DeleteTransaction(c *core.Context, uid int64, transactionId int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -752,7 +817,7 @@ func (s *TransactionService) DeleteTransaction(uid int64, transactionId int64) e
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
// Get and verify current transaction
|
// Get and verify current transaction
|
||||||
oldTransaction := &models.Transaction{}
|
oldTransaction := &models.Transaction{}
|
||||||
has, err := sess.ID(transactionId).Where("uid=? AND deleted=?", uid, false).Get(oldTransaction)
|
has, err := sess.ID(transactionId).Where("uid=? AND deleted=?", uid, false).Get(oldTransaction)
|
||||||
@@ -855,7 +920,7 @@ func (s *TransactionService) DeleteTransaction(uid int64, transactionId int64) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllTransactions deletes all existed transactions from database
|
// DeleteAllTransactions deletes all existed transactions from database
|
||||||
func (s *TransactionService) DeleteAllTransactions(uid int64) error {
|
func (s *TransactionService) DeleteAllTransactions(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -878,7 +943,7 @@ func (s *TransactionService) DeleteAllTransactions(uid int64) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDataDB(uid).DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDataDB(uid).DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
// Update all transaction to deleted
|
// Update all transaction to deleted
|
||||||
_, err := sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(updateModel)
|
_, err := sess.Cols("deleted", "deleted_unix_time").Where("uid=? AND deleted=?", uid, false).Update(updateModel)
|
||||||
|
|
||||||
@@ -905,7 +970,7 @@ func (s *TransactionService) DeleteAllTransactions(uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetRelatedTransferTransaction returns the related transaction for transfer transaction
|
// GetRelatedTransferTransaction returns the related transaction for transfer transaction
|
||||||
func (s *TransactionService) GetRelatedTransferTransaction(originalTransaction *models.Transaction, relatedTransactionId int64) *models.Transaction {
|
func (s *TransactionService) GetRelatedTransferTransaction(originalTransaction *models.Transaction) *models.Transaction {
|
||||||
var relatedType models.TransactionDbType
|
var relatedType models.TransactionDbType
|
||||||
var relatedTransactionTime int64
|
var relatedTransactionTime int64
|
||||||
|
|
||||||
@@ -920,7 +985,7 @@ func (s *TransactionService) GetRelatedTransferTransaction(originalTransaction *
|
|||||||
}
|
}
|
||||||
|
|
||||||
relatedTransaction := &models.Transaction{
|
relatedTransaction := &models.Transaction{
|
||||||
TransactionId: relatedTransactionId,
|
TransactionId: originalTransaction.RelatedId,
|
||||||
Uid: originalTransaction.Uid,
|
Uid: originalTransaction.Uid,
|
||||||
Deleted: originalTransaction.Deleted,
|
Deleted: originalTransaction.Deleted,
|
||||||
Type: relatedType,
|
Type: relatedType,
|
||||||
@@ -933,6 +998,9 @@ func (s *TransactionService) GetRelatedTransferTransaction(originalTransaction *
|
|||||||
RelatedAccountId: originalTransaction.AccountId,
|
RelatedAccountId: originalTransaction.AccountId,
|
||||||
RelatedAccountAmount: originalTransaction.Amount,
|
RelatedAccountAmount: originalTransaction.Amount,
|
||||||
Comment: originalTransaction.Comment,
|
Comment: originalTransaction.Comment,
|
||||||
|
GeoLongitude: originalTransaction.GeoLongitude,
|
||||||
|
GeoLatitude: originalTransaction.GeoLatitude,
|
||||||
|
CreatedIp: originalTransaction.CreatedIp,
|
||||||
CreatedUnixTime: originalTransaction.CreatedUnixTime,
|
CreatedUnixTime: originalTransaction.CreatedUnixTime,
|
||||||
UpdatedUnixTime: originalTransaction.UpdatedUnixTime,
|
UpdatedUnixTime: originalTransaction.UpdatedUnixTime,
|
||||||
DeletedUnixTime: originalTransaction.DeletedUnixTime,
|
DeletedUnixTime: originalTransaction.DeletedUnixTime,
|
||||||
@@ -942,7 +1010,7 @@ func (s *TransactionService) GetRelatedTransferTransaction(originalTransaction *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsTotalIncomeAndExpense returns the every accounts total income and expense amount by specific date range
|
// GetAccountsTotalIncomeAndExpense returns the every accounts total income and expense amount by specific date range
|
||||||
func (s *TransactionService) GetAccountsTotalIncomeAndExpense(uid int64, startUnixTime int64, endUnixTime int64) (map[int64]int64, map[int64]int64, error) {
|
func (s *TransactionService) GetAccountsTotalIncomeAndExpense(c *core.Context, uid int64, startUnixTime int64, endUnixTime int64) (map[int64]int64, map[int64]int64, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, nil, errs.ErrUserIdInvalid
|
return nil, nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -951,7 +1019,7 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(uid int64, startUn
|
|||||||
endTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(endUnixTime)
|
endTransactionTime := utils.GetMaxTransactionTimeFromUnixTime(endUnixTime)
|
||||||
|
|
||||||
var transactionTotalAmounts []*models.Transaction
|
var transactionTotalAmounts []*models.Transaction
|
||||||
err := s.UserDataDB(uid).Select("uid, type, account_id, SUM(amount) as amount").Where("uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?", uid, false, models.TRANSACTION_DB_TYPE_INCOME, models.TRANSACTION_DB_TYPE_EXPENSE, startTransactionTime, endTransactionTime).GroupBy("type, account_id").Find(&transactionTotalAmounts)
|
err := s.UserDataDB(uid).NewSession(c).Select("type, account_id, SUM(amount) as amount").Where("uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?", uid, false, models.TRANSACTION_DB_TYPE_INCOME, models.TRANSACTION_DB_TYPE_EXPENSE, startTransactionTime, endTransactionTime).GroupBy("type, account_id").Find(&transactionTotalAmounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -974,7 +1042,7 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(uid int64, startUn
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsMonthTotalIncomeAndExpense returns the every accounts total income and expense amount in month by specific date range
|
// GetAccountsMonthTotalIncomeAndExpense returns the every accounts total income and expense amount in month by specific date range
|
||||||
func (s *TransactionService) GetAccountsMonthTotalIncomeAndExpense(uid int64, startUnixTime int64, endUnixTime int64, pageCount int) (map[string]models.TransactionAccountsAmount, error) {
|
func (s *TransactionService) GetAccountsMonthTotalIncomeAndExpense(c *core.Context, uid int64, startUnixTime int64, endUnixTime int64, pageCount int) (map[string]models.TransactionAccountsAmount, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -989,7 +1057,7 @@ func (s *TransactionService) GetAccountsMonthTotalIncomeAndExpense(uid int64, st
|
|||||||
for maxTransactionTime > 0 {
|
for maxTransactionTime > 0 {
|
||||||
var transactions []*models.Transaction
|
var transactions []*models.Transaction
|
||||||
|
|
||||||
err := s.UserDataDB(uid).Select("uid, type, account_id, transaction_time, timezone_utc_offset, amount").Where("uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?", uid, false, models.TRANSACTION_DB_TYPE_INCOME, models.TRANSACTION_DB_TYPE_EXPENSE, minTransactionTime, maxTransactionTime).Limit(pageCount, 0).OrderBy("transaction_time desc").Find(&transactions)
|
err := s.UserDataDB(uid).NewSession(c).Select("uid, type, account_id, transaction_time, timezone_utc_offset, amount").Where("uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?", uid, false, models.TRANSACTION_DB_TYPE_INCOME, models.TRANSACTION_DB_TYPE_EXPENSE, minTransactionTime, maxTransactionTime).Limit(pageCount, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1041,13 +1109,13 @@ func (s *TransactionService) GetAccountsMonthTotalIncomeAndExpense(uid int64, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range
|
// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range
|
||||||
func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(uid int64, startUnixTime int64, endUnixTime int64) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c *core.Context, uid int64, startUnixTime int64, endUnixTime int64) ([]*models.Transaction, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
condition := "uid=? AND deleted=? AND (type=? OR type=?)"
|
condition := "uid=? AND deleted=? AND (type=? OR type=?)"
|
||||||
conditionParams := make([]interface{}, 0, 8)
|
conditionParams := make([]any, 0, 8)
|
||||||
conditionParams = append(conditionParams, uid)
|
conditionParams = append(conditionParams, uid)
|
||||||
conditionParams = append(conditionParams, false)
|
conditionParams = append(conditionParams, false)
|
||||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
||||||
@@ -1064,7 +1132,7 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(uid i
|
|||||||
}
|
}
|
||||||
|
|
||||||
var transactionTotalAmounts []*models.Transaction
|
var transactionTotalAmounts []*models.Transaction
|
||||||
err := s.UserDataDB(uid).Select("uid, category_id, account_id, SUM(amount) as amount").Where(condition, conditionParams...).GroupBy("category_id, account_id").Find(&transactionTotalAmounts)
|
err := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, SUM(amount) as amount").Where(condition, conditionParams...).GroupBy("category_id, account_id").Find(&transactionTotalAmounts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1085,9 +1153,9 @@ func (s *TransactionService) GetTransactionMapByList(transactions []*models.Tran
|
|||||||
return transactionMap
|
return transactionMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, noDuplicated bool) (string, []interface{}) {
|
func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, keyword string, noDuplicated bool) (string, []any) {
|
||||||
condition := "uid=? AND deleted=?"
|
condition := "uid=? AND deleted=?"
|
||||||
conditionParams := make([]interface{}, 0, 16)
|
conditionParams := make([]any, 0, 16)
|
||||||
conditionParams = append(conditionParams, uid)
|
conditionParams = append(conditionParams, uid)
|
||||||
conditionParams = append(conditionParams, false)
|
conditionParams = append(conditionParams, false)
|
||||||
|
|
||||||
@@ -1105,7 +1173,7 @@ func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransact
|
|||||||
condition = condition + " AND type=?"
|
condition = condition + " AND type=?"
|
||||||
conditionParams = append(conditionParams, transactionType)
|
conditionParams = append(conditionParams, transactionType)
|
||||||
} else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
} else if transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionType == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
if accountId == 0 {
|
if len(accountIds) == 0 {
|
||||||
condition = condition + " AND type=?"
|
condition = condition + " AND type=?"
|
||||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT)
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT)
|
||||||
} else {
|
} else {
|
||||||
@@ -1114,7 +1182,7 @@ func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransact
|
|||||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN)
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if noDuplicated && accountId == 0 {
|
if noDuplicated && len(accountIds) == 0 {
|
||||||
condition = condition + " AND (type=? OR type=? OR type=? OR type=?)"
|
condition = condition + " AND (type=? OR type=? OR type=? OR type=?)"
|
||||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE)
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_MODIFY_BALANCE)
|
||||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
||||||
@@ -1138,9 +1206,19 @@ func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransact
|
|||||||
condition = condition + " AND category_id IN (" + conditions.String() + ")"
|
condition = condition + " AND category_id IN (" + conditions.String() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if accountId > 0 {
|
if len(accountIds) > 0 {
|
||||||
condition = condition + " AND account_id=?"
|
var conditions strings.Builder
|
||||||
conditionParams = append(conditionParams, accountId)
|
|
||||||
|
for i := 0; i < len(accountIds); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
conditions.WriteString(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions.WriteString("?")
|
||||||
|
conditionParams = append(conditionParams, accountIds[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
condition = condition + " AND account_id IN (" + conditions.String() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if keyword != "" {
|
if keyword != "" {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
@@ -45,13 +46,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetUserTwoFactorSettingByUid returns the 2fa setting model according to user uid
|
// GetUserTwoFactorSettingByUid returns the 2fa setting model according to user uid
|
||||||
func (s *TwoFactorAuthorizationService) GetUserTwoFactorSettingByUid(uid int64) (*models.TwoFactor, error) {
|
func (s *TwoFactorAuthorizationService) GetUserTwoFactorSettingByUid(c *core.Context, uid int64) (*models.TwoFactor, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
twoFactor := &models.TwoFactor{}
|
twoFactor := &models.TwoFactor{}
|
||||||
has, err := s.UserDB().Where("uid=?", uid).Get(twoFactor)
|
has, err := s.UserDB().NewSession(c).Where("uid=?", uid).Get(twoFactor)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -69,7 +70,7 @@ func (s *TwoFactorAuthorizationService) GetUserTwoFactorSettingByUid(uid int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTwoFactorSecret generates a new 2fa secret
|
// GenerateTwoFactorSecret generates a new 2fa secret
|
||||||
func (s *TwoFactorAuthorizationService) GenerateTwoFactorSecret(user *models.User) (*otp.Key, error) {
|
func (s *TwoFactorAuthorizationService) GenerateTwoFactorSecret(c *core.Context, user *models.User) (*otp.Key, error) {
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, errs.ErrUserNotFound
|
return nil, errs.ErrUserNotFound
|
||||||
}
|
}
|
||||||
@@ -85,7 +86,7 @@ func (s *TwoFactorAuthorizationService) GenerateTwoFactorSecret(user *models.Use
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTwoFactorSetting saves a new 2fa setting to database
|
// CreateTwoFactorSetting saves a new 2fa setting to database
|
||||||
func (s *TwoFactorAuthorizationService) CreateTwoFactorSetting(twoFactor *models.TwoFactor) error {
|
func (s *TwoFactorAuthorizationService) CreateTwoFactorSetting(c *core.Context, twoFactor *models.TwoFactor) error {
|
||||||
if twoFactor.Uid <= 0 {
|
if twoFactor.Uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -99,19 +100,19 @@ func (s *TwoFactorAuthorizationService) CreateTwoFactorSetting(twoFactor *models
|
|||||||
|
|
||||||
twoFactor.CreatedUnixTime = time.Now().Unix()
|
twoFactor.CreatedUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Insert(twoFactor)
|
_, err := sess.Insert(twoFactor)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTwoFactorSetting deletes an existed 2fa setting from database
|
// DeleteTwoFactorSetting deletes an existed 2fa setting from database
|
||||||
func (s *TwoFactorAuthorizationService) DeleteTwoFactorSetting(uid int64) error {
|
func (s *TwoFactorAuthorizationService) DeleteTwoFactorSetting(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
deletedRows, err := sess.Where("uid=?", uid).Delete(&models.TwoFactor{})
|
deletedRows, err := sess.Where("uid=?", uid).Delete(&models.TwoFactor{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -125,22 +126,22 @@ func (s *TwoFactorAuthorizationService) DeleteTwoFactorSetting(uid int64) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExistsTwoFactorSetting returns whether the given user has existed 2fa setting
|
// ExistsTwoFactorSetting returns whether the given user has existed 2fa setting
|
||||||
func (s *TwoFactorAuthorizationService) ExistsTwoFactorSetting(uid int64) (bool, error) {
|
func (s *TwoFactorAuthorizationService) ExistsTwoFactorSetting(c *core.Context, uid int64) (bool, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return false, errs.ErrUserIdInvalid
|
return false, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().Cols("uid").Where("uid=?", uid).Exist(&models.TwoFactor{})
|
return s.UserDB().NewSession(c).Cols("uid").Where("uid=?", uid).Exist(&models.TwoFactor{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAndUseUserTwoFactorRecoveryCode checks whether the given 2fa recovery code exists and marks it used
|
// GetAndUseUserTwoFactorRecoveryCode checks whether the given 2fa recovery code exists and marks it used
|
||||||
func (s *TwoFactorAuthorizationService) GetAndUseUserTwoFactorRecoveryCode(uid int64, recoveryCode string, salt string) error {
|
func (s *TwoFactorAuthorizationService) GetAndUseUserTwoFactorRecoveryCode(c *core.Context, uid int64, recoveryCode string, salt string) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
recoveryCode = utils.EncodePassword(recoveryCode, salt)
|
recoveryCode = utils.EncodePassword(recoveryCode, salt)
|
||||||
exists, err := s.UserDB().Cols("uid", "recovery_code").Where("uid=? AND recovery_code=? AND used=?", uid, recoveryCode, false).Exist(&models.TwoFactorRecoveryCode{})
|
exists, err := s.UserDB().NewSession(c).Cols("uid", "recovery_code").Where("uid=? AND recovery_code=? AND used=?", uid, recoveryCode, false).Exist(&models.TwoFactorRecoveryCode{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -148,7 +149,7 @@ func (s *TwoFactorAuthorizationService) GetAndUseUserTwoFactorRecoveryCode(uid i
|
|||||||
return errs.ErrTwoFactorRecoveryCodeNotExist
|
return errs.ErrTwoFactorRecoveryCodeNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Cols("used", "used_unix_time").Where("uid=? AND recovery_code=?", uid, recoveryCode).Update(&models.TwoFactorRecoveryCode{Used: true, UsedUnixTime: time.Now().Unix()})
|
_, err := sess.Cols("used", "used_unix_time").Where("uid=? AND recovery_code=?", uid, recoveryCode).Update(&models.TwoFactorRecoveryCode{Used: true, UsedUnixTime: time.Now().Unix()})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -159,7 +160,7 @@ func (s *TwoFactorAuthorizationService) GenerateTwoFactorRecoveryCodes() ([]stri
|
|||||||
recoveryCodes := make([]string, twoFactorRecoveryCodeCount)
|
recoveryCodes := make([]string, twoFactorRecoveryCodeCount)
|
||||||
|
|
||||||
for i := 0; i < twoFactorRecoveryCodeCount; i++ {
|
for i := 0; i < twoFactorRecoveryCodeCount; i++ {
|
||||||
recoveryCode, err := utils.GetRandomNumberOrLetter(twoFactorRecoveryCodeLength)
|
recoveryCode, err := utils.GetRandomNumberOrLowercaseLetter(twoFactorRecoveryCodeLength)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -172,7 +173,7 @@ func (s *TwoFactorAuthorizationService) GenerateTwoFactorRecoveryCodes() ([]stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateTwoFactorRecoveryCodes saves new 2fa recovery codes to database
|
// CreateTwoFactorRecoveryCodes saves new 2fa recovery codes to database
|
||||||
func (s *TwoFactorAuthorizationService) CreateTwoFactorRecoveryCodes(uid int64, recoveryCodes []string, salt string) error {
|
func (s *TwoFactorAuthorizationService) CreateTwoFactorRecoveryCodes(c *core.Context, uid int64, recoveryCodes []string, salt string) error {
|
||||||
twoFactorRecoveryCodes := make([]*models.TwoFactorRecoveryCode, len(recoveryCodes))
|
twoFactorRecoveryCodes := make([]*models.TwoFactorRecoveryCode, len(recoveryCodes))
|
||||||
|
|
||||||
for i := 0; i < len(recoveryCodes); i++ {
|
for i := 0; i < len(recoveryCodes); i++ {
|
||||||
@@ -184,7 +185,7 @@ func (s *TwoFactorAuthorizationService) CreateTwoFactorRecoveryCodes(uid int64,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Where("uid=?", uid).Delete(&models.TwoFactorRecoveryCode{})
|
_, err := sess.Where("uid=?", uid).Delete(&models.TwoFactorRecoveryCode{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -205,12 +206,12 @@ func (s *TwoFactorAuthorizationService) CreateTwoFactorRecoveryCodes(uid int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTwoFactorRecoveryCodes deletes existed 2fa recovery codes from database
|
// DeleteTwoFactorRecoveryCodes deletes existed 2fa recovery codes from database
|
||||||
func (s *TwoFactorAuthorizationService) DeleteTwoFactorRecoveryCodes(uid int64) error {
|
func (s *TwoFactorAuthorizationService) DeleteTwoFactorRecoveryCodes(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Where("uid=?", uid).Delete(&models.TwoFactorRecoveryCode{})
|
_, err := sess.Where("uid=?", uid).Delete(&models.TwoFactorRecoveryCode{})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|||||||
+227
-31
@@ -1,20 +1,32 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
"github.com/mayswind/ezbookkeeping/pkg/datastore"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/locales"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/mail"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/models"
|
"github.com/mayswind/ezbookkeeping/pkg/models"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/settings"
|
||||||
|
"github.com/mayswind/ezbookkeeping/pkg/templates"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
"github.com/mayswind/ezbookkeeping/pkg/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const verifyEmailUrlFormat = "%sdesktop/#/verify_email?token=%s"
|
||||||
|
|
||||||
// UserService represents user service
|
// UserService represents user service
|
||||||
type UserService struct {
|
type UserService struct {
|
||||||
ServiceUsingDB
|
ServiceUsingDB
|
||||||
|
ServiceUsingConfig
|
||||||
|
ServiceUsingMailer
|
||||||
ServiceUsingUuid
|
ServiceUsingUuid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +36,12 @@ var (
|
|||||||
ServiceUsingDB: ServiceUsingDB{
|
ServiceUsingDB: ServiceUsingDB{
|
||||||
container: datastore.Container,
|
container: datastore.Container,
|
||||||
},
|
},
|
||||||
|
ServiceUsingConfig: ServiceUsingConfig{
|
||||||
|
container: settings.Container,
|
||||||
|
},
|
||||||
|
ServiceUsingMailer: ServiceUsingMailer{
|
||||||
|
container: mail.Container,
|
||||||
|
},
|
||||||
ServiceUsingUuid: ServiceUsingUuid{
|
ServiceUsingUuid: ServiceUsingUuid{
|
||||||
container: uuid.Container,
|
container: uuid.Container,
|
||||||
},
|
},
|
||||||
@@ -31,14 +49,14 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetUserByUsernameOrEmailAndPassword returns the user model according to login name and password
|
// GetUserByUsernameOrEmailAndPassword returns the user model according to login name and password
|
||||||
func (s *UserService) GetUserByUsernameOrEmailAndPassword(loginname string, password string) (*models.User, error) {
|
func (s *UserService) GetUserByUsernameOrEmailAndPassword(c *core.Context, loginname string, password string) (*models.User, error) {
|
||||||
var user *models.User
|
var user *models.User
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if utils.IsValidUsername(loginname) {
|
if utils.IsValidUsername(loginname) {
|
||||||
user, err = s.GetUserByUsername(loginname)
|
user, err = s.GetUserByUsername(c, loginname)
|
||||||
} else if utils.IsValidEmail(loginname) {
|
} else if utils.IsValidEmail(loginname) {
|
||||||
user, err = s.GetUserByEmail(loginname)
|
user, err = s.GetUserByEmail(c, loginname)
|
||||||
} else {
|
} else {
|
||||||
err = errs.ErrLoginNameInvalid
|
err = errs.ErrLoginNameInvalid
|
||||||
}
|
}
|
||||||
@@ -55,13 +73,13 @@ func (s *UserService) GetUserByUsernameOrEmailAndPassword(loginname string, pass
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserById returns the user model according to user uid
|
// GetUserById returns the user model according to user uid
|
||||||
func (s *UserService) GetUserById(uid int64) (*models.User, error) {
|
func (s *UserService) GetUserById(c *core.Context, uid int64) (*models.User, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &models.User{}
|
user := &models.User{}
|
||||||
has, err := s.UserDB().ID(uid).Where("deleted=?", false).Get(user)
|
has, err := s.UserDB().NewSession(c).ID(uid).Where("deleted=?", false).Get(user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -73,13 +91,13 @@ func (s *UserService) GetUserById(uid int64) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByUsername returns the user model according to user name
|
// GetUserByUsername returns the user model according to user name
|
||||||
func (s *UserService) GetUserByUsername(username string) (*models.User, error) {
|
func (s *UserService) GetUserByUsername(c *core.Context, username string) (*models.User, error) {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return nil, errs.ErrUsernameIsEmpty
|
return nil, errs.ErrUsernameIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &models.User{}
|
user := &models.User{}
|
||||||
has, err := s.UserDB().Where("username=? AND deleted=?", username, false).Get(user)
|
has, err := s.UserDB().NewSession(c).Where("username=? AND deleted=?", username, false).Get(user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -91,13 +109,13 @@ func (s *UserService) GetUserByUsername(username string) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByEmail returns the user model according to user email
|
// GetUserByEmail returns the user model according to user email
|
||||||
func (s *UserService) GetUserByEmail(email string) (*models.User, error) {
|
func (s *UserService) GetUserByEmail(c *core.Context, email string) (*models.User, error) {
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return nil, errs.ErrEmailIsEmpty
|
return nil, errs.ErrEmailIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &models.User{}
|
user := &models.User{}
|
||||||
has, err := s.UserDB().Where("email=? AND deleted=?", email, false).Get(user)
|
has, err := s.UserDB().NewSession(c).Where("email=? AND deleted=?", email, false).Get(user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -109,8 +127,8 @@ func (s *UserService) GetUserByEmail(email string) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser saves a new user model to database
|
// CreateUser saves a new user model to database
|
||||||
func (s *UserService) CreateUser(user *models.User) error {
|
func (s *UserService) CreateUser(c *core.Context, user *models.User) error {
|
||||||
exists, err := s.ExistsUsername(user.Username)
|
exists, err := s.ExistsUsername(c, user.Username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -118,7 +136,7 @@ func (s *UserService) CreateUser(user *models.User) error {
|
|||||||
return errs.ErrUsernameAlreadyExists
|
return errs.ErrUsernameAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err = s.ExistsEmail(user.Email)
|
exists, err = s.ExistsEmail(c, user.Email)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -135,6 +153,11 @@ func (s *UserService) CreateUser(user *models.User) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user.Uid = s.GenerateUuid(uuid.UUID_TYPE_USER)
|
user.Uid = s.GenerateUuid(uuid.UUID_TYPE_USER)
|
||||||
|
|
||||||
|
if user.Uid < 1 {
|
||||||
|
return errs.ErrSystemIsBusy
|
||||||
|
}
|
||||||
|
|
||||||
user.Password = utils.EncodePassword(user.Password, user.Salt)
|
user.Password = utils.EncodePassword(user.Password, user.Salt)
|
||||||
|
|
||||||
user.Deleted = false
|
user.Deleted = false
|
||||||
@@ -143,36 +166,39 @@ func (s *UserService) CreateUser(user *models.User) error {
|
|||||||
user.UpdatedUnixTime = time.Now().Unix()
|
user.UpdatedUnixTime = time.Now().Unix()
|
||||||
user.LastLoginUnixTime = time.Now().Unix()
|
user.LastLoginUnixTime = time.Now().Unix()
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.Insert(user)
|
_, err := sess.Insert(user)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser saves an existed user model to database
|
// UpdateUser saves an existed user model to database
|
||||||
func (s *UserService) UpdateUser(user *models.User) (keyProfileUpdated bool, err error) {
|
func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserLanguage bool) (keyProfileUpdated bool, emailSetToUnverified bool, err error) {
|
||||||
if user.Uid <= 0 {
|
if user.Uid <= 0 {
|
||||||
return false, errs.ErrUserIdInvalid
|
return false, false, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCols := make([]string, 0, 8)
|
updateCols := make([]string, 0, 8)
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
keyProfileUpdated = false
|
keyProfileUpdated = false
|
||||||
|
emailSetToUnverified = false
|
||||||
|
|
||||||
if user.Email != "" {
|
if user.Email != "" {
|
||||||
exists, err := s.ExistsEmail(user.Email)
|
exists, err := s.ExistsEmail(c, user.Email)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, false, err
|
||||||
} else if exists {
|
} else if exists {
|
||||||
return false, errs.ErrUserEmailAlreadyExists
|
return false, false, errs.ErrUserEmailAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
user.EmailVerified = false
|
user.EmailVerified = false
|
||||||
|
|
||||||
updateCols = append(updateCols, "email")
|
updateCols = append(updateCols, "email")
|
||||||
updateCols = append(updateCols, "email_verified")
|
updateCols = append(updateCols, "email_verified")
|
||||||
|
|
||||||
|
emailSetToUnverified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Password != "" {
|
if user.Password != "" {
|
||||||
@@ -186,6 +212,18 @@ func (s *UserService) UpdateUser(user *models.User) (keyProfileUpdated bool, err
|
|||||||
updateCols = append(updateCols, "nickname")
|
updateCols = append(updateCols, "nickname")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.DefaultAccountId > 0 {
|
||||||
|
updateCols = append(updateCols, "default_account_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.TRANSACTION_EDIT_SCOPE_NONE <= user.TransactionEditScope && user.TransactionEditScope <= models.TRANSACTION_EDIT_SCOPE_THIS_YEAR_OR_LATER {
|
||||||
|
updateCols = append(updateCols, "transaction_edit_scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyUserLanguage || user.Language != "" {
|
||||||
|
updateCols = append(updateCols, "language")
|
||||||
|
}
|
||||||
|
|
||||||
if user.DefaultCurrency != "" {
|
if user.DefaultCurrency != "" {
|
||||||
updateCols = append(updateCols, "default_currency")
|
updateCols = append(updateCols, "default_currency")
|
||||||
}
|
}
|
||||||
@@ -194,14 +232,26 @@ func (s *UserService) UpdateUser(user *models.User) (keyProfileUpdated bool, err
|
|||||||
updateCols = append(updateCols, "first_day_of_week")
|
updateCols = append(updateCols, "first_day_of_week")
|
||||||
}
|
}
|
||||||
|
|
||||||
if models.TRANSACTION_EDIT_SCOPE_NONE <= user.TransactionEditScope && user.TransactionEditScope <= models.TRANSACTION_EDIT_SCOPE_THIS_YEAR_OR_LATER {
|
if models.LONG_DATE_FORMAT_DEFAULT <= user.LongDateFormat && user.LongDateFormat <= models.LONG_DATE_FORMAT_D_M_YYYY {
|
||||||
updateCols = append(updateCols, "transaction_edit_scope")
|
updateCols = append(updateCols, "long_date_format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.SHORT_DATE_FORMAT_DEFAULT <= user.ShortDateFormat && user.ShortDateFormat <= models.SHORT_DATE_FORMAT_D_M_YYYY {
|
||||||
|
updateCols = append(updateCols, "short_date_format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.LONG_TIME_FORMAT_DEFAULT <= user.LongTimeFormat && user.LongTimeFormat <= models.LONG_TIME_FORMAT_HH_MM_SS_A {
|
||||||
|
updateCols = append(updateCols, "long_time_format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.SHORT_TIME_FORMAT_DEFAULT <= user.ShortTimeFormat && user.ShortTimeFormat <= models.SHORT_TIME_FORMAT_HH_MM_A {
|
||||||
|
updateCols = append(updateCols, "short_time_format")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.UpdatedUnixTime = now
|
user.UpdatedUnixTime = now
|
||||||
updateCols = append(updateCols, "updated_unix_time")
|
updateCols = append(updateCols, "updated_unix_time")
|
||||||
|
|
||||||
err = s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
err = s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
updatedRows, err := sess.ID(user.Uid).Cols(updateCols...).Where("deleted=?", false).Update(user)
|
updatedRows, err := sess.ID(user.Uid).Cols(updateCols...).Where("deleted=?", false).Update(user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -214,26 +264,118 @@ func (s *UserService) UpdateUser(user *models.User) (keyProfileUpdated bool, err
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyProfileUpdated, nil
|
return keyProfileUpdated, emailSetToUnverified, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserLastLoginTime updates the last login time field
|
// UpdateUserLastLoginTime updates the last login time field
|
||||||
func (s *UserService) UpdateUserLastLoginTime(uid int64) error {
|
func (s *UserService) UpdateUserLastLoginTime(c *core.Context, uid int64) error {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return errs.ErrUserIdInvalid
|
return errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().DoTransaction(func(sess *xorm.Session) error {
|
return s.UserDB().DoTransaction(c, func(sess *xorm.Session) error {
|
||||||
_, err := sess.ID(uid).Cols("last_login_unix_time").Where("deleted=?", false).Update(&models.User{LastLoginUnixTime: time.Now().Unix()})
|
_, err := sess.ID(uid).Cols("last_login_unix_time").Where("deleted=?", false).Update(&models.User{LastLoginUnixTime: time.Now().Unix()})
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableUser sets user enabled
|
||||||
|
func (s *UserService) EnableUser(c *core.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
updateModel := &models.User{
|
||||||
|
Disabled: false,
|
||||||
|
UpdatedUnixTime: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedRows, err := s.UserDB().NewSession(c).Cols("disabled", "updated_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if updatedRows < 1 {
|
||||||
|
return errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableUser sets user disabled
|
||||||
|
func (s *UserService) DisableUser(c *core.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
updateModel := &models.User{
|
||||||
|
Disabled: true,
|
||||||
|
UpdatedUnixTime: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedRows, err := s.UserDB().NewSession(c).Cols("disabled", "updated_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if updatedRows < 1 {
|
||||||
|
return errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserEmailVerified sets user email address verified
|
||||||
|
func (s *UserService) SetUserEmailVerified(c *core.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
updateModel := &models.User{
|
||||||
|
EmailVerified: true,
|
||||||
|
UpdatedUnixTime: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedRows, err := s.UserDB().NewSession(c).Cols("email_verified", "updated_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if updatedRows < 1 {
|
||||||
|
return errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserEmailUnverified sets user email address unverified
|
||||||
|
func (s *UserService) SetUserEmailUnverified(c *core.Context, username string) error {
|
||||||
|
if username == "" {
|
||||||
|
return errs.ErrUsernameIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
updateModel := &models.User{
|
||||||
|
EmailVerified: false,
|
||||||
|
UpdatedUnixTime: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedRows, err := s.UserDB().NewSession(c).Cols("email_verified", "updated_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if updatedRows < 1 {
|
||||||
|
return errs.ErrUserNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteUser deletes an existed user from database
|
// DeleteUser deletes an existed user from database
|
||||||
func (s *UserService) DeleteUser(username string) error {
|
func (s *UserService) DeleteUser(c *core.Context, username string) error {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errs.ErrUsernameIsEmpty
|
return errs.ErrUsernameIsEmpty
|
||||||
}
|
}
|
||||||
@@ -245,7 +387,7 @@ func (s *UserService) DeleteUser(username string) error {
|
|||||||
DeletedUnixTime: now,
|
DeletedUnixTime: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
deletedRows, err := s.UserDB().Cols("deleted", "deleted_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
deletedRows, err := s.UserDB().NewSession(c).Cols("deleted", "deleted_unix_time").Where("username=? AND deleted=?", username, false).Update(updateModel)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -256,21 +398,75 @@ func (s *UserService) DeleteUser(username string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExistsUsername returns whether the given user name exists
|
// ExistsUsername returns whether the given user name exists
|
||||||
func (s *UserService) ExistsUsername(username string) (bool, error) {
|
func (s *UserService) ExistsUsername(c *core.Context, username string) (bool, error) {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return false, errs.ErrUsernameIsEmpty
|
return false, errs.ErrUsernameIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().Cols("username").Where("username=? AND deleted=?", username, false).Exist(&models.User{})
|
return s.UserDB().NewSession(c).Cols("username").Where("username=? AND deleted=?", username, false).Exist(&models.User{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExistsEmail returns whether the given user email exists
|
// ExistsEmail returns whether the given user email exists
|
||||||
func (s *UserService) ExistsEmail(email string) (bool, error) {
|
func (s *UserService) ExistsEmail(c *core.Context, email string) (bool, error) {
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return false, errs.ErrEmailIsEmpty
|
return false, errs.ErrEmailIsEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.UserDB().Cols("email").Where("email=? AND deleted=?", email, false).Exist(&models.User{})
|
return s.UserDB().NewSession(c).Cols("email").Where("email=? AND deleted=?", email, false).Exist(&models.User{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendVerifyEmail sends verify email according to specified parameters
|
||||||
|
func (s *UserService) SendVerifyEmail(user *models.User, verifyEmailToken string, backupLocale string) error {
|
||||||
|
if !s.CurrentConfig().EnableSMTP {
|
||||||
|
return errs.ErrSMTPServerNotEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
locale := user.Language
|
||||||
|
|
||||||
|
if locale == "" {
|
||||||
|
locale = backupLocale
|
||||||
|
}
|
||||||
|
|
||||||
|
localeTextItems := locales.GetLocaleTextItems(locale)
|
||||||
|
verifyEmailTextItems := localeTextItems.VerifyEmailTextItems
|
||||||
|
|
||||||
|
expireTimeInMinutes := s.CurrentConfig().EmailVerifyTokenExpiredTimeDuration.Minutes()
|
||||||
|
verifyEmailUrl := fmt.Sprintf(verifyEmailUrlFormat, s.CurrentConfig().RootUrl, url.QueryEscape(verifyEmailToken))
|
||||||
|
|
||||||
|
tmpl, err := templates.GetTemplate(templates.TEMPLATE_VERIFY_EMAIL)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
templateParams := map[string]any{
|
||||||
|
"AppName": s.CurrentConfig().AppName,
|
||||||
|
"VerifyEmail": map[string]any{
|
||||||
|
"Title": verifyEmailTextItems.Title,
|
||||||
|
"Salutation": fmt.Sprintf(verifyEmailTextItems.SalutationFormat, user.Nickname),
|
||||||
|
"DescriptionAboveBtn": verifyEmailTextItems.DescriptionAboveBtn,
|
||||||
|
"VerifyEmailUrl": verifyEmailUrl,
|
||||||
|
"VerifyEmail": verifyEmailTextItems.VerifyEmail,
|
||||||
|
"DescriptionBelowBtn": fmt.Sprintf(verifyEmailTextItems.DescriptionBelowBtnFormat, s.CurrentConfig().AppName, expireTimeInMinutes),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var bodyBuffer bytes.Buffer
|
||||||
|
err = tmpl.Execute(&bodyBuffer, templateParams)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
message := &mail.MailMessage{
|
||||||
|
To: user.Email,
|
||||||
|
Subject: verifyEmailTextItems.Title,
|
||||||
|
Body: bodyBuffer.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.SendMail(message)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPasswordEqualsUserPassword returns whether the given password is correct
|
// IsPasswordEqualsUserPassword returns whether the given password is correct
|
||||||
|
|||||||
+257
-46
@@ -62,36 +62,64 @@ const (
|
|||||||
InternalUuidGeneratorType string = "internal"
|
InternalUuidGeneratorType string = "internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// User avatar provider types
|
||||||
|
const (
|
||||||
|
GravatarProvider string = "gravatar"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map provider types
|
||||||
|
const (
|
||||||
|
OpenStreetMapProvider string = "openstreetmap"
|
||||||
|
OpenStreetMapHumanitarianStyleProvider string = "openstreetmap_humanitarian"
|
||||||
|
OpenTopoMapProvider string = "opentopomap"
|
||||||
|
OPNVKarteMapProvider string = "opnvkarte"
|
||||||
|
CyclOSMMapProvider string = "cyclosm"
|
||||||
|
GoogleMapProvider string = "googlemap"
|
||||||
|
TomTomMapProvider string = "tomtom"
|
||||||
|
BaiduMapProvider string = "baidumap"
|
||||||
|
AmapProvider string = "amap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Amap security verification method
|
||||||
|
const (
|
||||||
|
AmapSecurityVerificationInternalProxyMethod string = "internal_proxy"
|
||||||
|
AmapSecurityVerificationExternalProxyMethod string = "external_proxy"
|
||||||
|
AmapSecurityVerificationPlainTextMethod string = "plain_text"
|
||||||
|
)
|
||||||
|
|
||||||
// Exchange rates data source types
|
// Exchange rates data source types
|
||||||
const (
|
const (
|
||||||
EuroCentralBankDataSource string = "euro_central_bank"
|
EuroCentralBankDataSource string = "euro_central_bank"
|
||||||
BankOfCanadaDataSource string = "bank_of_canada"
|
BankOfCanadaDataSource string = "bank_of_canada"
|
||||||
ReserveBankOfAustraliaDataSource string = "reserve_bank_of_australia"
|
ReserveBankOfAustraliaDataSource string = "reserve_bank_of_australia"
|
||||||
CzechNationalBankDataSource string = "czech_national_bank"
|
CzechNationalBankDataSource string = "czech_national_bank"
|
||||||
NationalBankOfPolandDataSource string = "national_bank_of_poland"
|
NationalBankOfPolandDataSource string = "national_bank_of_poland"
|
||||||
|
MonetaryAuthorityOfSingaporeDataSource string = "monetary_authority_of_singapore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultAppName string = "ezBookkeeping"
|
defaultAppName string = "ezBookkeeping"
|
||||||
|
|
||||||
defaultHttpAddr string = "0.0.0.0"
|
defaultHttpAddr string = "0.0.0.0"
|
||||||
defaultHttpPort int = 8080
|
defaultHttpPort uint16 = 8080
|
||||||
defaultDomain string = "localhost"
|
defaultDomain string = "localhost"
|
||||||
|
|
||||||
defaultDatabaseHost string = "127.0.0.1:3306"
|
defaultDatabaseHost string = "127.0.0.1:3306"
|
||||||
defaultDatabaseName string = "ezbookkeeping"
|
defaultDatabaseName string = "ezbookkeeping"
|
||||||
defaultDatabaseMaxIdleConn int = 2
|
defaultDatabaseMaxIdleConn uint16 = 2
|
||||||
defaultDatabaseMaxOpenConn int = 0
|
defaultDatabaseMaxOpenConn uint16 = 0
|
||||||
defaultDatabaseConnMaxLifetime int = 14400
|
defaultDatabaseConnMaxLifetime uint32 = 14400
|
||||||
|
|
||||||
defaultLogMode string = "console"
|
defaultLogMode string = "console"
|
||||||
defaultLoglevel Level = LOGLEVEL_INFO
|
defaultLoglevel Level = LOGLEVEL_INFO
|
||||||
|
|
||||||
defaultSecretKey string = "ezbookkeeping"
|
defaultSecretKey string = "ezbookkeeping"
|
||||||
defaultTokenExpiredTime int = 604800 // 7 days
|
defaultTokenExpiredTime uint32 = 604800 // 7 days
|
||||||
defaultTemporaryTokenExpiredTime int = 300 // 5 minutes
|
defaultTemporaryTokenExpiredTime uint32 = 300 // 5 minutes
|
||||||
|
defaultEmailVerifyTokenExpiredTime uint32 = 3600 // 60 minutes
|
||||||
|
defaultPasswordResetTokenExpiredTime uint32 = 3600 // 60 minutes
|
||||||
|
|
||||||
defaultExchangeRatesDataRequestTimeout int = 10000 // 10 seconds
|
defaultExchangeRatesDataRequestTimeout uint32 = 10000 // 10 seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
// DatabaseConfig represents the database setting config
|
// DatabaseConfig represents the database setting config
|
||||||
@@ -106,9 +134,18 @@ type DatabaseConfig struct {
|
|||||||
|
|
||||||
DatabasePath string
|
DatabasePath string
|
||||||
|
|
||||||
MaxIdleConnection int
|
MaxIdleConnection uint16
|
||||||
MaxOpenConnection int
|
MaxOpenConnection uint16
|
||||||
ConnectionMaxLifeTime int
|
ConnectionMaxLifeTime uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMTPConfig represents the SMTP setting config
|
||||||
|
type SMTPConfig struct {
|
||||||
|
SMTPHost string
|
||||||
|
SMTPUser string
|
||||||
|
SMTPPasswd string
|
||||||
|
SMTPSkipTLSVerify bool
|
||||||
|
FromAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config represents the global setting config
|
// Config represents the global setting config
|
||||||
@@ -121,7 +158,7 @@ type Config struct {
|
|||||||
// Server
|
// Server
|
||||||
Protocol Scheme
|
Protocol Scheme
|
||||||
HttpAddr string
|
HttpAddr string
|
||||||
HttpPort int
|
HttpPort uint16
|
||||||
|
|
||||||
Domain string
|
Domain string
|
||||||
RootUrl string
|
RootUrl string
|
||||||
@@ -141,6 +178,10 @@ type Config struct {
|
|||||||
EnableQueryLog bool
|
EnableQueryLog bool
|
||||||
AutoUpdateDatabase bool
|
AutoUpdateDatabase bool
|
||||||
|
|
||||||
|
// Mail
|
||||||
|
EnableSMTP bool
|
||||||
|
SMTPConfig *SMTPConfig
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
LogModes []string
|
LogModes []string
|
||||||
EnableConsoleLog bool
|
EnableConsoleLog bool
|
||||||
@@ -154,23 +195,44 @@ type Config struct {
|
|||||||
UuidServerId uint8
|
UuidServerId uint8
|
||||||
|
|
||||||
// Secret
|
// Secret
|
||||||
SecretKey string
|
SecretKey string
|
||||||
EnableTwoFactor bool
|
EnableTwoFactor bool
|
||||||
TokenExpiredTime int
|
TokenExpiredTime uint32
|
||||||
TokenExpiredTimeDuration time.Duration
|
TokenExpiredTimeDuration time.Duration
|
||||||
TemporaryTokenExpiredTime int
|
TemporaryTokenExpiredTime uint32
|
||||||
TemporaryTokenExpiredTimeDuration time.Duration
|
TemporaryTokenExpiredTimeDuration time.Duration
|
||||||
EnableRequestIdHeader bool
|
EmailVerifyTokenExpiredTime uint32
|
||||||
|
EmailVerifyTokenExpiredTimeDuration time.Duration
|
||||||
|
PasswordResetTokenExpiredTime uint32
|
||||||
|
PasswordResetTokenExpiredTimeDuration time.Duration
|
||||||
|
EnableRequestIdHeader bool
|
||||||
|
|
||||||
// User
|
// User
|
||||||
EnableUserRegister bool
|
EnableUserRegister bool
|
||||||
|
EnableUserVerifyEmail bool
|
||||||
|
EnableUserForceVerifyEmail bool
|
||||||
|
EnableUserForgetPassword bool
|
||||||
|
ForgetPasswordRequireVerifyEmail bool
|
||||||
|
AvatarProvider string
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
EnableDataExport bool
|
EnableDataExport bool
|
||||||
|
|
||||||
|
// Map
|
||||||
|
MapProvider string
|
||||||
|
TomTomMapAPIKey string
|
||||||
|
GoogleMapAPIKey string
|
||||||
|
BaiduMapAK string
|
||||||
|
AmapApplicationKey string
|
||||||
|
AmapSecurityVerificationMethod string
|
||||||
|
AmapApplicationSecret string
|
||||||
|
AmapApiExternalProxyUrl string
|
||||||
|
EnableMapDataFetchProxy bool
|
||||||
|
|
||||||
// Exchange Rates
|
// Exchange Rates
|
||||||
ExchangeRatesDataSource string
|
ExchangeRatesDataSource string
|
||||||
ExchangeRatesRequestTimeout int
|
ExchangeRatesRequestTimeout uint32
|
||||||
|
ExchangeRatesSkipTLSVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfiguration loads setting config from given config file path
|
// LoadConfiguration loads setting config from given config file path
|
||||||
@@ -208,6 +270,12 @@ func LoadConfiguration(configFilePath string) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = loadMailConfiguration(config, cfgFile, "mail")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = loadLogConfiguration(config, cfgFile, "log")
|
err = loadLogConfiguration(config, cfgFile, "log")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -238,6 +306,12 @@ func LoadConfiguration(configFilePath string) (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = loadMapConfiguration(config, cfgFile, "map")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = loadExchangeRatesConfiguration(config, cfgFile, "exchange_rates")
|
err = loadExchangeRatesConfiguration(config, cfgFile, "exchange_rates")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -281,12 +355,12 @@ func loadServerConfiguration(config *Config, configFile *ini.File, sectionName s
|
|||||||
config.Protocol = SCHEME_HTTP
|
config.Protocol = SCHEME_HTTP
|
||||||
|
|
||||||
config.HttpAddr = getConfigItemStringValue(configFile, sectionName, "http_addr", defaultHttpAddr)
|
config.HttpAddr = getConfigItemStringValue(configFile, sectionName, "http_addr", defaultHttpAddr)
|
||||||
config.HttpPort = getConfigItemIntValue(configFile, sectionName, "http_port", defaultHttpPort)
|
config.HttpPort = getConfigItemUint16Value(configFile, sectionName, "http_port", defaultHttpPort)
|
||||||
} else if getConfigItemStringValue(configFile, sectionName, "protocol") == "https" {
|
} else if getConfigItemStringValue(configFile, sectionName, "protocol") == "https" {
|
||||||
config.Protocol = SCHEME_HTTPS
|
config.Protocol = SCHEME_HTTPS
|
||||||
|
|
||||||
config.HttpAddr = getConfigItemStringValue(configFile, sectionName, "http_addr", defaultHttpAddr)
|
config.HttpAddr = getConfigItemStringValue(configFile, sectionName, "http_addr", defaultHttpAddr)
|
||||||
config.HttpPort = getConfigItemIntValue(configFile, sectionName, "http_port", defaultHttpPort)
|
config.HttpPort = getConfigItemUint16Value(configFile, sectionName, "http_port", defaultHttpPort)
|
||||||
|
|
||||||
config.CertFile = getConfigItemStringValue(configFile, sectionName, "cert_file")
|
config.CertFile = getConfigItemStringValue(configFile, sectionName, "cert_file")
|
||||||
config.CertKeyFile = getConfigItemStringValue(configFile, sectionName, "cert_key_file")
|
config.CertKeyFile = getConfigItemStringValue(configFile, sectionName, "cert_key_file")
|
||||||
@@ -339,9 +413,9 @@ func loadDatabaseConfiguration(config *Config, configFile *ini.File, sectionName
|
|||||||
dbConfig.DatabasePath = finalStaticDBPath
|
dbConfig.DatabasePath = finalStaticDBPath
|
||||||
}
|
}
|
||||||
|
|
||||||
dbConfig.MaxIdleConnection = getConfigItemIntValue(configFile, sectionName, "max_idle_conn", defaultDatabaseMaxIdleConn)
|
dbConfig.MaxIdleConnection = getConfigItemUint16Value(configFile, sectionName, "max_idle_conn", defaultDatabaseMaxIdleConn)
|
||||||
dbConfig.MaxOpenConnection = getConfigItemIntValue(configFile, sectionName, "max_open_conn", defaultDatabaseMaxOpenConn)
|
dbConfig.MaxOpenConnection = getConfigItemUint16Value(configFile, sectionName, "max_open_conn", defaultDatabaseMaxOpenConn)
|
||||||
dbConfig.ConnectionMaxLifeTime = getConfigItemIntValue(configFile, sectionName, "conn_max_lifetime", defaultDatabaseConnMaxLifetime)
|
dbConfig.ConnectionMaxLifeTime = getConfigItemUint32Value(configFile, sectionName, "conn_max_lifetime", defaultDatabaseConnMaxLifetime)
|
||||||
|
|
||||||
config.DatabaseConfig = dbConfig
|
config.DatabaseConfig = dbConfig
|
||||||
config.EnableQueryLog = getConfigItemBoolValue(configFile, sectionName, "log_query", false)
|
config.EnableQueryLog = getConfigItemBoolValue(configFile, sectionName, "log_query", false)
|
||||||
@@ -350,6 +424,22 @@ func loadDatabaseConfiguration(config *Config, configFile *ini.File, sectionName
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadMailConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
||||||
|
config.EnableSMTP = getConfigItemBoolValue(configFile, sectionName, "enable_smtp", false)
|
||||||
|
|
||||||
|
smtpConfig := &SMTPConfig{}
|
||||||
|
smtpConfig.SMTPHost = getConfigItemStringValue(configFile, sectionName, "smtp_host")
|
||||||
|
smtpConfig.SMTPUser = getConfigItemStringValue(configFile, sectionName, "smtp_user")
|
||||||
|
smtpConfig.SMTPPasswd = getConfigItemStringValue(configFile, sectionName, "smtp_passwd")
|
||||||
|
smtpConfig.SMTPSkipTLSVerify = getConfigItemBoolValue(configFile, sectionName, "smtp_skip_tls_verify", false)
|
||||||
|
|
||||||
|
smtpConfig.FromAddress = getConfigItemStringValue(configFile, sectionName, "from_address")
|
||||||
|
|
||||||
|
config.SMTPConfig = smtpConfig
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func loadLogConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
func loadLogConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
||||||
config.LogModes = strings.Split(getConfigItemStringValue(configFile, sectionName, "mode", defaultLogMode), " ")
|
config.LogModes = strings.Split(getConfigItemStringValue(configFile, sectionName, "mode", defaultLogMode), " ")
|
||||||
|
|
||||||
@@ -383,7 +473,7 @@ func loadUuidConfiguration(config *Config, configFile *ini.File, sectionName str
|
|||||||
return errs.ErrInvalidUuidMode
|
return errs.ErrInvalidUuidMode
|
||||||
}
|
}
|
||||||
|
|
||||||
config.UuidServerId = uint8(getConfigItemIntValue(configFile, sectionName, "server_id", 0))
|
config.UuidServerId = getConfigItemUint8Value(configFile, sectionName, "server_id", 0)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -392,12 +482,18 @@ func loadSecurityConfiguration(config *Config, configFile *ini.File, sectionName
|
|||||||
config.SecretKey = getConfigItemStringValue(configFile, sectionName, "secret_key", defaultSecretKey)
|
config.SecretKey = getConfigItemStringValue(configFile, sectionName, "secret_key", defaultSecretKey)
|
||||||
config.EnableTwoFactor = getConfigItemBoolValue(configFile, sectionName, "enable_two_factor", true)
|
config.EnableTwoFactor = getConfigItemBoolValue(configFile, sectionName, "enable_two_factor", true)
|
||||||
|
|
||||||
config.TokenExpiredTime = getConfigItemIntValue(configFile, sectionName, "token_expired_time", defaultTokenExpiredTime)
|
config.TokenExpiredTime = getConfigItemUint32Value(configFile, sectionName, "token_expired_time", defaultTokenExpiredTime)
|
||||||
config.TokenExpiredTimeDuration = time.Duration(config.TokenExpiredTime) * time.Second
|
config.TokenExpiredTimeDuration = time.Duration(config.TokenExpiredTime) * time.Second
|
||||||
|
|
||||||
config.TemporaryTokenExpiredTime = getConfigItemIntValue(configFile, sectionName, "temporary_token_expired_time", defaultTemporaryTokenExpiredTime)
|
config.TemporaryTokenExpiredTime = getConfigItemUint32Value(configFile, sectionName, "temporary_token_expired_time", defaultTemporaryTokenExpiredTime)
|
||||||
config.TemporaryTokenExpiredTimeDuration = time.Duration(config.TemporaryTokenExpiredTime) * time.Second
|
config.TemporaryTokenExpiredTimeDuration = time.Duration(config.TemporaryTokenExpiredTime) * time.Second
|
||||||
|
|
||||||
|
config.EmailVerifyTokenExpiredTime = getConfigItemUint32Value(configFile, sectionName, "email_verify_token_expired_time", defaultEmailVerifyTokenExpiredTime)
|
||||||
|
config.EmailVerifyTokenExpiredTimeDuration = time.Duration(config.EmailVerifyTokenExpiredTime) * time.Second
|
||||||
|
|
||||||
|
config.PasswordResetTokenExpiredTime = getConfigItemUint32Value(configFile, sectionName, "password_reset_token_expired_time", defaultPasswordResetTokenExpiredTime)
|
||||||
|
config.PasswordResetTokenExpiredTimeDuration = time.Duration(config.PasswordResetTokenExpiredTime) * time.Second
|
||||||
|
|
||||||
config.EnableRequestIdHeader = getConfigItemBoolValue(configFile, sectionName, "request_id_header", true)
|
config.EnableRequestIdHeader = getConfigItemBoolValue(configFile, sectionName, "request_id_header", true)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -405,6 +501,16 @@ func loadSecurityConfiguration(config *Config, configFile *ini.File, sectionName
|
|||||||
|
|
||||||
func loadUserConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
func loadUserConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
||||||
config.EnableUserRegister = getConfigItemBoolValue(configFile, sectionName, "enable_register", false)
|
config.EnableUserRegister = getConfigItemBoolValue(configFile, sectionName, "enable_register", false)
|
||||||
|
config.EnableUserVerifyEmail = getConfigItemBoolValue(configFile, sectionName, "enable_email_verify", false)
|
||||||
|
config.EnableUserForceVerifyEmail = getConfigItemBoolValue(configFile, sectionName, "enable_force_email_verify", false)
|
||||||
|
config.EnableUserForgetPassword = getConfigItemBoolValue(configFile, sectionName, "enable_forget_password", false)
|
||||||
|
config.ForgetPasswordRequireVerifyEmail = getConfigItemBoolValue(configFile, sectionName, "forget_password_require_email_verify", false)
|
||||||
|
|
||||||
|
if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == "" {
|
||||||
|
config.AvatarProvider = ""
|
||||||
|
} else if getConfigItemStringValue(configFile, sectionName, "avatar_provider") == GravatarProvider {
|
||||||
|
config.AvatarProvider = GravatarProvider
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -415,22 +521,77 @@ func loadDataConfiguration(config *Config, configFile *ini.File, sectionName str
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadMapConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
||||||
|
mapProvider := getConfigItemStringValue(configFile, sectionName, "map_provider")
|
||||||
|
|
||||||
|
if mapProvider == "" {
|
||||||
|
config.MapProvider = ""
|
||||||
|
} else if mapProvider == OpenStreetMapProvider {
|
||||||
|
config.MapProvider = OpenStreetMapProvider
|
||||||
|
} else if mapProvider == OpenStreetMapHumanitarianStyleProvider {
|
||||||
|
config.MapProvider = OpenStreetMapHumanitarianStyleProvider
|
||||||
|
} else if mapProvider == OpenTopoMapProvider {
|
||||||
|
config.MapProvider = OpenTopoMapProvider
|
||||||
|
} else if mapProvider == OPNVKarteMapProvider {
|
||||||
|
config.MapProvider = OPNVKarteMapProvider
|
||||||
|
} else if mapProvider == CyclOSMMapProvider {
|
||||||
|
config.MapProvider = CyclOSMMapProvider
|
||||||
|
} else if mapProvider == GoogleMapProvider {
|
||||||
|
config.MapProvider = GoogleMapProvider
|
||||||
|
} else if mapProvider == TomTomMapProvider {
|
||||||
|
config.MapProvider = TomTomMapProvider
|
||||||
|
} else if mapProvider == BaiduMapProvider {
|
||||||
|
config.MapProvider = BaiduMapProvider
|
||||||
|
} else if mapProvider == AmapProvider {
|
||||||
|
config.MapProvider = AmapProvider
|
||||||
|
} else {
|
||||||
|
return errs.ErrInvalidMapProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
config.EnableMapDataFetchProxy = getConfigItemBoolValue(configFile, sectionName, "map_data_fetch_proxy", false)
|
||||||
|
config.TomTomMapAPIKey = getConfigItemStringValue(configFile, sectionName, "tomtom_map_api_key")
|
||||||
|
config.GoogleMapAPIKey = getConfigItemStringValue(configFile, sectionName, "google_map_api_key")
|
||||||
|
config.BaiduMapAK = getConfigItemStringValue(configFile, sectionName, "baidu_map_ak")
|
||||||
|
config.AmapApplicationKey = getConfigItemStringValue(configFile, sectionName, "amap_application_key")
|
||||||
|
|
||||||
|
amapSecurityVerificationMethod := getConfigItemStringValue(configFile, sectionName, "amap_security_verification_method")
|
||||||
|
|
||||||
|
if amapSecurityVerificationMethod == AmapSecurityVerificationInternalProxyMethod {
|
||||||
|
config.AmapSecurityVerificationMethod = AmapSecurityVerificationInternalProxyMethod
|
||||||
|
} else if amapSecurityVerificationMethod == AmapSecurityVerificationExternalProxyMethod {
|
||||||
|
config.AmapSecurityVerificationMethod = AmapSecurityVerificationExternalProxyMethod
|
||||||
|
} else if amapSecurityVerificationMethod == AmapSecurityVerificationPlainTextMethod {
|
||||||
|
config.AmapSecurityVerificationMethod = AmapSecurityVerificationPlainTextMethod
|
||||||
|
} else {
|
||||||
|
return errs.ErrInvalidAmapSecurityVerificationMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
config.AmapApplicationSecret = getConfigItemStringValue(configFile, sectionName, "amap_application_secret")
|
||||||
|
config.AmapApiExternalProxyUrl = getConfigItemStringValue(configFile, sectionName, "amap_api_external_proxy_url")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func loadExchangeRatesConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
func loadExchangeRatesConfiguration(config *Config, configFile *ini.File, sectionName string) error {
|
||||||
if getConfigItemStringValue(configFile, sectionName, "data_source") == EuroCentralBankDataSource {
|
dataSource := getConfigItemStringValue(configFile, sectionName, "data_source")
|
||||||
|
|
||||||
|
if dataSource == EuroCentralBankDataSource {
|
||||||
config.ExchangeRatesDataSource = EuroCentralBankDataSource
|
config.ExchangeRatesDataSource = EuroCentralBankDataSource
|
||||||
} else if getConfigItemStringValue(configFile, sectionName, "data_source") == BankOfCanadaDataSource {
|
} else if dataSource == BankOfCanadaDataSource {
|
||||||
config.ExchangeRatesDataSource = BankOfCanadaDataSource
|
config.ExchangeRatesDataSource = BankOfCanadaDataSource
|
||||||
} else if getConfigItemStringValue(configFile, sectionName, "data_source") == ReserveBankOfAustraliaDataSource {
|
} else if dataSource == ReserveBankOfAustraliaDataSource {
|
||||||
config.ExchangeRatesDataSource = ReserveBankOfAustraliaDataSource
|
config.ExchangeRatesDataSource = ReserveBankOfAustraliaDataSource
|
||||||
} else if getConfigItemStringValue(configFile, sectionName, "data_source") == CzechNationalBankDataSource {
|
} else if dataSource == CzechNationalBankDataSource {
|
||||||
config.ExchangeRatesDataSource = CzechNationalBankDataSource
|
config.ExchangeRatesDataSource = CzechNationalBankDataSource
|
||||||
} else if getConfigItemStringValue(configFile, sectionName, "data_source") == NationalBankOfPolandDataSource {
|
} else if dataSource == NationalBankOfPolandDataSource {
|
||||||
config.ExchangeRatesDataSource = NationalBankOfPolandDataSource
|
config.ExchangeRatesDataSource = NationalBankOfPolandDataSource
|
||||||
|
} else if dataSource == MonetaryAuthorityOfSingaporeDataSource {
|
||||||
|
config.ExchangeRatesDataSource = MonetaryAuthorityOfSingaporeDataSource
|
||||||
} else {
|
} else {
|
||||||
return errs.ErrInvalidExchangeRatesDataSource
|
return errs.ErrInvalidExchangeRatesDataSource
|
||||||
}
|
}
|
||||||
|
|
||||||
config.ExchangeRatesRequestTimeout = getConfigItemIntValue(configFile, sectionName, "request_timeout", defaultExchangeRatesDataRequestTimeout)
|
config.ExchangeRatesRequestTimeout = getConfigItemUint32Value(configFile, sectionName, "request_timeout", defaultExchangeRatesDataRequestTimeout)
|
||||||
|
config.ExchangeRatesSkipTLSVerify = getConfigItemBoolValue(configFile, sectionName, "skip_tls_verify", false)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -482,23 +643,73 @@ func getConfigItemStringValue(configFile *ini.File, sectionName string, itemName
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigItemIntValue(configFile *ini.File, sectionName string, itemName string, defaultValue ...int) int {
|
func getConfigItemUint8Value(configFile *ini.File, sectionName string, itemName string, defaultValue uint8) uint8 {
|
||||||
environmentKey := getEnvironmentKey(sectionName, itemName)
|
environmentKey := getEnvironmentKey(sectionName, itemName)
|
||||||
environmentValue := os.Getenv(environmentKey)
|
environmentValue := os.Getenv(environmentKey)
|
||||||
|
|
||||||
if len(environmentValue) > 0 {
|
if len(environmentValue) > 0 {
|
||||||
value, err := strconv.ParseInt(environmentValue, 0, 64)
|
value, err := strconv.ParseUint(environmentValue, 10, 8)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return int(value)
|
return uint8(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section := configFile.Section(sectionName)
|
section := configFile.Section(sectionName)
|
||||||
return section.Key(itemName).MustInt(defaultValue...)
|
value, err := strconv.ParseUint(section.Key(itemName).String(), 10, 8)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return uint8(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigItemBoolValue(configFile *ini.File, sectionName string, itemName string, defaultValue ...bool) bool {
|
func getConfigItemUint16Value(configFile *ini.File, sectionName string, itemName string, defaultValue uint16) uint16 {
|
||||||
|
environmentKey := getEnvironmentKey(sectionName, itemName)
|
||||||
|
environmentValue := os.Getenv(environmentKey)
|
||||||
|
|
||||||
|
if len(environmentValue) > 0 {
|
||||||
|
value, err := strconv.ParseUint(environmentValue, 10, 16)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return uint16(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section := configFile.Section(sectionName)
|
||||||
|
value, err := strconv.ParseUint(section.Key(itemName).String(), 10, 16)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return uint16(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigItemUint32Value(configFile *ini.File, sectionName string, itemName string, defaultValue uint32) uint32 {
|
||||||
|
environmentKey := getEnvironmentKey(sectionName, itemName)
|
||||||
|
environmentValue := os.Getenv(environmentKey)
|
||||||
|
|
||||||
|
if len(environmentValue) > 0 {
|
||||||
|
value, err := strconv.ParseUint(environmentValue, 10, 32)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return uint32(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section := configFile.Section(sectionName)
|
||||||
|
value, err := strconv.ParseUint(section.Key(itemName).String(), 10, 32)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return uint32(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigItemBoolValue(configFile *ini.File, sectionName string, itemName string, defaultValue bool) bool {
|
||||||
environmentKey := getEnvironmentKey(sectionName, itemName)
|
environmentKey := getEnvironmentKey(sectionName, itemName)
|
||||||
environmentValue := os.Getenv(environmentKey)
|
environmentValue := os.Getenv(environmentKey)
|
||||||
|
|
||||||
@@ -511,7 +722,7 @@ func getConfigItemBoolValue(configFile *ini.File, sectionName string, itemName s
|
|||||||
}
|
}
|
||||||
|
|
||||||
section := configFile.Section(sectionName)
|
section := configFile.Section(sectionName)
|
||||||
return section.Key(itemName).MustBool(defaultValue...)
|
return section.Key(itemName).MustBool(defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvironmentKey(sectionName string, itemName string) string {
|
func getEnvironmentKey(sectionName string, itemName string) string {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ type ConfigContainer struct {
|
|||||||
|
|
||||||
// Initialize a config container singleton instance
|
// Initialize a config container singleton instance
|
||||||
var (
|
var (
|
||||||
Container = &ConfigContainer{}
|
Version string
|
||||||
|
CommitHash string
|
||||||
|
Container = &ConfigContainer{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetCurrentConfig sets the current config by a given config
|
// SetCurrentConfig sets the current config by a given config
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
type KnownTemplate string
|
||||||
|
|
||||||
|
// Known templates
|
||||||
|
const (
|
||||||
|
TEMPLATE_VERIFY_EMAIL KnownTemplate = "email/verify_email"
|
||||||
|
TEMPLATE_PASSWORD_RESET KnownTemplate = "email/password_reset"
|
||||||
|
)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const templateBasePath = "templates"
|
||||||
|
const templateFileExtension = "tmpl"
|
||||||
|
|
||||||
|
var templateCache = make(map[KnownTemplate]*CachedTemplate)
|
||||||
|
|
||||||
|
// CachedTemplate represents a cached template
|
||||||
|
type CachedTemplate struct {
|
||||||
|
templateName KnownTemplate
|
||||||
|
templateContent *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplate returns a cached template instance according to the template name
|
||||||
|
func GetTemplate(templateName KnownTemplate) (*template.Template, error) {
|
||||||
|
fullPath := filepath.Join(templateBasePath, fmt.Sprintf("%s.%s", templateName, templateFileExtension))
|
||||||
|
|
||||||
|
cachedTemplate, exists := templateCache[templateName]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
return cachedTemplate.templateContent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.ParseFiles(fullPath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
templateCache[templateName] = &CachedTemplate{
|
||||||
|
templateName: templateName,
|
||||||
|
templateContent: tmpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpl, err
|
||||||
|
}
|
||||||
+9
-3
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PrintJsonSuccessResult writes success response in json format to current http context
|
// PrintJsonSuccessResult writes success response in json format to current http context
|
||||||
func PrintJsonSuccessResult(c *core.Context, result interface{}) {
|
func PrintJsonSuccessResult(c *core.Context, result any) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"result": result,
|
"result": result,
|
||||||
@@ -45,12 +45,18 @@ func PrintJsonErrorResult(c *core.Context, err *errs.Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AbortWithStatusJSON(err.HttpStatusCode, gin.H{
|
result := gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"errorCode": err.Code(),
|
"errorCode": err.Code(),
|
||||||
"errorMessage": errorMessage,
|
"errorMessage": errorMessage,
|
||||||
"path": c.Request.URL.Path,
|
"path": c.Request.URL.Path,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if err.Context != nil {
|
||||||
|
result["context"] = err.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatusJSON(err.HttpStatusCode, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintDataErrorResult writes error response in custom content type to current http context
|
// PrintDataErrorResult writes error response in custom content type to current http context
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const gravatarUrlFormat = "https://www.gravatar.com/avatar/%s"
|
||||||
|
|
||||||
|
// GetGravatarUrl returns the Gravatar url according to the specified user email address
|
||||||
|
func GetGravatarUrl(email string) string {
|
||||||
|
email = strings.TrimSpace(email)
|
||||||
|
email = strings.ToLower(email)
|
||||||
|
emailMd5 := MD5EncodeToString([]byte(email))
|
||||||
|
return fmt.Sprintf(gravatarUrlFormat, emailMd5)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user