mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-21 17:21:09 +01:00
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -1,15 +0,0 @@
|
||||
[submodule "backend/core"]
|
||||
path = backend/core
|
||||
url = https://github.com/crawlab-team/crawlab-core
|
||||
[submodule "backend/db"]
|
||||
path = backend/db
|
||||
url = https://github.com/crawlab-team/crawlab-db
|
||||
[submodule "backend/fs"]
|
||||
path = backend/fs
|
||||
url = https://github.com/crawlab-team/crawlab-fs
|
||||
[submodule "backend/vcs"]
|
||||
path = backend/vcs
|
||||
url = https://github.com/crawlab-team/crawlab-vcs
|
||||
[submodule "backend/log"]
|
||||
path = backend/log
|
||||
url = https://github.com/crawlab-team/crawlab-log
|
||||
@@ -20,13 +20,13 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/crawlab-team/crawlab-db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484 // indirect
|
||||
github.com/crawlab-team/crawlab-fs v0.6.3 // indirect
|
||||
github.com/crawlab-team/crawlab/db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484 // indirect
|
||||
github.com/crawlab-team/crawlab/fs v0.6.3 // indirect
|
||||
github.com/crawlab-team/crawlab-grpc v0.6.0-beta.20211219.1930.0.20221020032435-afa1c691f73c // indirect
|
||||
github.com/crawlab-team/crawlab-vcs v0.6.2-0.20230629045457-afe0be0e2185 // indirect
|
||||
github.com/crawlab-team/go-trace v0.1.1 // indirect
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.1 // indirect
|
||||
github.com/crawlab-team/goseaweedfs v0.6.0-beta.20211101.1936.0.20220912021203-dfee5f74dd69 // indirect
|
||||
github.com/crawlab-team/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e // indirect
|
||||
github.com/crawlab-team/crawlab/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/elastic/elastic-transport-go/v8 v8.2.0 // indirect
|
||||
|
||||
@@ -5,14 +5,14 @@ go 1.16
|
||||
replace (
|
||||
github.com/crawlab-team/crawlab-core => ../../crawlab-core
|
||||
github.com/crawlab-team/crawlab-vcs => ../../crawlab-vcs
|
||||
github.com/crawlab-team/crawlab-fs => ../../crawlab-fs
|
||||
github.com/crawlab-team/crawlab-db => ../../crawlab-db
|
||||
github.com/crawlab-team/crawlab/fs => ../../crawlab-fs
|
||||
github.com/crawlab-team/crawlab/db => ../../crawlab-db
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/apex/log v1.9.0
|
||||
github.com/crawlab-team/crawlab-core v0.6.0-beta.20211230.1200
|
||||
github.com/crawlab-team/go-trace v0.1.1
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.1
|
||||
github.com/gin-gonic/gin v1.7.1
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/viper v1.7.1
|
||||
|
||||
@@ -5,14 +5,14 @@ go 1.16
|
||||
replace (
|
||||
github.com/crawlab-team/crawlab-core => /libs/crawlab-team/crawlab-core
|
||||
github.com/crawlab-team/crawlab-vcs => /libs/crawlab-team/crawlab-vcs
|
||||
github.com/crawlab-team/crawlab-fs => /libs/crawlab-team/crawlab-fs
|
||||
github.com/crawlab-team/crawlab-db => /libs/crawlab-team/crawlab-db
|
||||
github.com/crawlab-team/crawlab/fs => /libs/crawlab-team/crawlab-fs
|
||||
github.com/crawlab-team/crawlab/db => /libs/crawlab-team/crawlab-db
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/apex/log v1.9.0
|
||||
github.com/crawlab-team/crawlab-core v0.6.0-beta.20211230.1200
|
||||
github.com/crawlab-team/go-trace v0.1.1
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.1
|
||||
github.com/gin-gonic/gin v1.7.1
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/viper v1.7.1
|
||||
|
||||
@@ -155,24 +155,24 @@ github.com/crawlab-team/crawlab-core v0.6.3-0.20231021045242-07956209f653 h1:uOC
|
||||
github.com/crawlab-team/crawlab-core v0.6.3-0.20231021045242-07956209f653/go.mod h1:HNuAjSVVZpHhpyUP4k1F2YKxEiarZESFolSyx7YjgZ8=
|
||||
github.com/crawlab-team/crawlab-core v0.6.3-0.20231031044528-37e6d73eb203 h1:nyANfzoPgTSYJxuTye1uj44An8Cjou9QmcKRES7Gdwg=
|
||||
github.com/crawlab-team/crawlab-core v0.6.3-0.20231031044528-37e6d73eb203/go.mod h1:HNuAjSVVZpHhpyUP4k1F2YKxEiarZESFolSyx7YjgZ8=
|
||||
github.com/crawlab-team/crawlab-db v0.6.0-1/go.mod h1:gfeF0nAnFuup6iYvgHkY0in/HpO/+JktXqVNMdhoxhU=
|
||||
github.com/crawlab-team/crawlab-db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484 h1:1CXWC3lYcVWcgPRc3PNKzZ3fcfX5WZ/V8xwzHEMUFHQ=
|
||||
github.com/crawlab-team/crawlab-db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484/go.mod h1:gfeF0nAnFuup6iYvgHkY0in/HpO/+JktXqVNMdhoxhU=
|
||||
github.com/crawlab-team/crawlab-fs v0.6.0-beta.20211101.1940.0.20221218100256-a28d12756f73 h1:xIgfVPa3ZJWC72Y57oHS41n4jRtGZPn1YDEYBgMj2EU=
|
||||
github.com/crawlab-team/crawlab-fs v0.6.0-beta.20211101.1940.0.20221218100256-a28d12756f73/go.mod h1:y9YhLLR3GuPrDuPKe7ZuiHCITK9K2IcI8nlznF8YIEc=
|
||||
github.com/crawlab-team/crawlab-fs v0.6.3 h1:mS91sYu+tOPavjYvt4CZ8YwY5okEiwCAuyx/5RIbXJY=
|
||||
github.com/crawlab-team/crawlab-fs v0.6.3/go.mod h1:y9YhLLR3GuPrDuPKe7ZuiHCITK9K2IcI8nlznF8YIEc=
|
||||
github.com/crawlab-team/crawlab/db v0.6.0-1/go.mod h1:gfeF0nAnFuup6iYvgHkY0in/HpO/+JktXqVNMdhoxhU=
|
||||
github.com/crawlab-team/crawlab/db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484 h1:1CXWC3lYcVWcgPRc3PNKzZ3fcfX5WZ/V8xwzHEMUFHQ=
|
||||
github.com/crawlab-team/crawlab/db v0.6.0-beta.20220417.1300.0.20221226064900-5a357ee73484/go.mod h1:gfeF0nAnFuup6iYvgHkY0in/HpO/+JktXqVNMdhoxhU=
|
||||
github.com/crawlab-team/crawlab/fs v0.6.0-beta.20211101.1940.0.20221218100256-a28d12756f73 h1:xIgfVPa3ZJWC72Y57oHS41n4jRtGZPn1YDEYBgMj2EU=
|
||||
github.com/crawlab-team/crawlab/fs v0.6.0-beta.20211101.1940.0.20221218100256-a28d12756f73/go.mod h1:y9YhLLR3GuPrDuPKe7ZuiHCITK9K2IcI8nlznF8YIEc=
|
||||
github.com/crawlab-team/crawlab/fs v0.6.3 h1:mS91sYu+tOPavjYvt4CZ8YwY5okEiwCAuyx/5RIbXJY=
|
||||
github.com/crawlab-team/crawlab/fs v0.6.3/go.mod h1:y9YhLLR3GuPrDuPKe7ZuiHCITK9K2IcI8nlznF8YIEc=
|
||||
github.com/crawlab-team/crawlab-grpc v0.6.0-beta.20211219.1930.0.20221020032435-afa1c691f73c h1:jX0iax3WHwomWGQVWrCTy8a4zYDsKKyuspP3+04XCcU=
|
||||
github.com/crawlab-team/crawlab-grpc v0.6.0-beta.20211219.1930.0.20221020032435-afa1c691f73c/go.mod h1:Bq2Pm967EYWbjhP5Ghc4DV2LZgbOLMzLftJXDJYz/gs=
|
||||
github.com/crawlab-team/crawlab-vcs v0.6.2-0.20230629045457-afe0be0e2185 h1:A/XSUuGgGMn+z+lFd2ye2ClgIKhDZYUerhOL5jePQhU=
|
||||
github.com/crawlab-team/crawlab-vcs v0.6.2-0.20230629045457-afe0be0e2185/go.mod h1:YHMYUEoSqfXUZHsWW/M/DaLh/zOpRtiElaRWcrGyv/I=
|
||||
github.com/crawlab-team/go-trace v0.1.0/go.mod h1:LcWyn68HoT+d29CHM8L41pFHxsAcBMF1xjqJmWdyFh8=
|
||||
github.com/crawlab-team/go-trace v0.1.1 h1:AecgAOld+ZrSVvujyhK3zoaOmViGKHSCT8/weJ7adB8=
|
||||
github.com/crawlab-team/go-trace v0.1.1/go.mod h1:4U+pWgLhRuD3pbXHonwcaHcW+y8AUqyOfKoZnvKwCug=
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.0/go.mod h1:LcWyn68HoT+d29CHM8L41pFHxsAcBMF1xjqJmWdyFh8=
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.1 h1:AecgAOld+ZrSVvujyhK3zoaOmViGKHSCT8/weJ7adB8=
|
||||
github.com/crawlab-team/crawlab/vcs v0.1.1/go.mod h1:4U+pWgLhRuD3pbXHonwcaHcW+y8AUqyOfKoZnvKwCug=
|
||||
github.com/crawlab-team/goseaweedfs v0.6.0-beta.20211101.1936.0.20220912021203-dfee5f74dd69 h1:qPLsh2aWqI5HioWBymzQirt+HQxfRgd7BSoOqfN33Q0=
|
||||
github.com/crawlab-team/goseaweedfs v0.6.0-beta.20211101.1936.0.20220912021203-dfee5f74dd69/go.mod h1:u+rwfqb0rnYllTLjCctE/z1Yp+TC8L+CbbWH8E2NstA=
|
||||
github.com/crawlab-team/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e h1:Gwg9kKNZUAI4bSssomlzXCN01Q3MapgwQOCeOxGX/NU=
|
||||
github.com/crawlab-team/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e/go.mod h1:FImmp7V0VcIdTRM68F3PQUqewzuShvUjYBhAHRjD1Aw=
|
||||
github.com/crawlab-team/crawlab/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e h1:Gwg9kKNZUAI4bSssomlzXCN01Q3MapgwQOCeOxGX/NU=
|
||||
github.com/crawlab-team/crawlab/template-parser v0.0.4-0.20221006034646-9bb77a7ae86e/go.mod h1:FImmp7V0VcIdTRM68F3PQUqewzuShvUjYBhAHRjD1Aw=
|
||||
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=
|
||||
|
||||
13
core/.editorconfig
Normal file
13
core/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.yaml,*.yml,package.json}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
36
core/.github/workflows/test.yml
vendored
Normal file
36
core/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: "Test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main, develop ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-20.04
|
||||
services:
|
||||
mongo:
|
||||
image: mongo:5
|
||||
ports:
|
||||
- 27017:27017
|
||||
env:
|
||||
CRAWLAB_SERVER_PORT: 9999
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '^1.22'
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
mods=(\
|
||||
"github.com/crawlab-team/crawlab/core/controllers" \
|
||||
"github.com/crawlab-team/crawlab/core/models/client" \
|
||||
"github.com/crawlab-team/crawlab/core/models/service" \
|
||||
)
|
||||
for pkg in ${mods[@]}; do
|
||||
go test ${pkg}
|
||||
done
|
||||
10
core/.gitignore
vendored
Normal file
10
core/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
vendor/
|
||||
tmp/
|
||||
build/
|
||||
dist/
|
||||
*.log
|
||||
gen/
|
||||
*.exe
|
||||
*.txt
|
||||
201
core/LICENSE
Normal file
201
core/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
2
core/README.md
Normal file
2
core/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# crawlab-core
|
||||
Backend core modules for Crawlab
|
||||
126
core/apps/api.go
Normal file
126
core/apps/api.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/controllers"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/middlewares"
|
||||
"github.com/crawlab-team/crawlab/core/routes"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// set gin mode
|
||||
if viper.GetString("gin.mode") == "" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
} else {
|
||||
gin.SetMode(viper.GetString("gin.mode"))
|
||||
}
|
||||
}
|
||||
|
||||
type Api struct {
|
||||
// dependencies
|
||||
interfaces.WithConfigPath
|
||||
|
||||
// internals
|
||||
app *gin.Engine
|
||||
ln net.Listener
|
||||
srv *http.Server
|
||||
ready bool
|
||||
}
|
||||
|
||||
func (app *Api) Init() {
|
||||
// initialize controllers
|
||||
_ = initModule("controllers", controllers.InitControllers)
|
||||
|
||||
// initialize middlewares
|
||||
_ = app.initModuleWithApp("middlewares", middlewares.InitMiddlewares)
|
||||
|
||||
// initialize routes
|
||||
_ = app.initModuleWithApp("routes", routes.InitRoutes)
|
||||
}
|
||||
|
||||
func (app *Api) Start() {
|
||||
// address
|
||||
host := viper.GetString("server.host")
|
||||
port := viper.GetString("server.port")
|
||||
address := net.JoinHostPort(host, port)
|
||||
|
||||
// http server
|
||||
app.srv = &http.Server{
|
||||
Handler: app.app,
|
||||
Addr: address,
|
||||
}
|
||||
|
||||
// listen
|
||||
var err error
|
||||
app.ln, err = net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.ready = true
|
||||
|
||||
// serve
|
||||
if err := http.Serve(app.ln, app.app); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error("run server error:" + err.Error())
|
||||
} else {
|
||||
log.Info("server graceful down")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Api) Wait() {
|
||||
DefaultWait()
|
||||
}
|
||||
|
||||
func (app *Api) Stop() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := app.srv.Shutdown(ctx); err != nil {
|
||||
log.Error("run server error:" + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Api) GetGinEngine() *gin.Engine {
|
||||
return app.app
|
||||
}
|
||||
|
||||
func (app *Api) GetHttpServer() *http.Server {
|
||||
return app.srv
|
||||
}
|
||||
|
||||
func (app *Api) Ready() (ok bool) {
|
||||
return app.ready
|
||||
}
|
||||
|
||||
func (app *Api) initModuleWithApp(name string, fn func(app *gin.Engine) error) (err error) {
|
||||
return initModule(name, func() error {
|
||||
return fn(app.app)
|
||||
})
|
||||
}
|
||||
|
||||
func NewApi() *Api {
|
||||
api := &Api{
|
||||
app: gin.New(),
|
||||
}
|
||||
api.Init()
|
||||
return api
|
||||
}
|
||||
|
||||
var api *Api
|
||||
|
||||
func GetApi() *Api {
|
||||
if api != nil {
|
||||
return api
|
||||
}
|
||||
api = NewApi()
|
||||
return api
|
||||
}
|
||||
122
core/apps/api_v2.go
Normal file
122
core/apps/api_v2.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/controllers"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/middlewares"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// set gin mode
|
||||
if viper.GetString("gin.mode") == "" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
} else {
|
||||
gin.SetMode(viper.GetString("gin.mode"))
|
||||
}
|
||||
}
|
||||
|
||||
type ApiV2 struct {
|
||||
// dependencies
|
||||
interfaces.WithConfigPath
|
||||
|
||||
// internals
|
||||
app *gin.Engine
|
||||
ln net.Listener
|
||||
srv *http.Server
|
||||
ready bool
|
||||
}
|
||||
|
||||
func (app *ApiV2) Init() {
|
||||
// initialize middlewares
|
||||
_ = app.initModuleWithApp("middlewares", middlewares.InitMiddlewares)
|
||||
|
||||
// initialize routes
|
||||
_ = app.initModuleWithApp("routes", controllers.InitRoutes)
|
||||
}
|
||||
|
||||
func (app *ApiV2) Start() {
|
||||
// address
|
||||
host := viper.GetString("server.host")
|
||||
port := viper.GetString("server.port")
|
||||
address := net.JoinHostPort(host, port)
|
||||
|
||||
// http server
|
||||
app.srv = &http.Server{
|
||||
Handler: app.app,
|
||||
Addr: address,
|
||||
}
|
||||
|
||||
// listen
|
||||
var err error
|
||||
app.ln, err = net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
app.ready = true
|
||||
|
||||
// serve
|
||||
if err := http.Serve(app.ln, app.app); err != nil {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error("run server error:" + err.Error())
|
||||
} else {
|
||||
log.Info("server graceful down")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *ApiV2) Wait() {
|
||||
DefaultWait()
|
||||
}
|
||||
|
||||
func (app *ApiV2) Stop() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := app.srv.Shutdown(ctx); err != nil {
|
||||
log.Error("run server error:" + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (app *ApiV2) GetGinEngine() *gin.Engine {
|
||||
return app.app
|
||||
}
|
||||
|
||||
func (app *ApiV2) GetHttpServer() *http.Server {
|
||||
return app.srv
|
||||
}
|
||||
|
||||
func (app *ApiV2) Ready() (ok bool) {
|
||||
return app.ready
|
||||
}
|
||||
|
||||
func (app *ApiV2) initModuleWithApp(name string, fn func(app *gin.Engine) error) (err error) {
|
||||
return initModule(name, func() error {
|
||||
return fn(app.app)
|
||||
})
|
||||
}
|
||||
|
||||
func NewApiV2() *ApiV2 {
|
||||
api := &ApiV2{
|
||||
app: gin.New(),
|
||||
}
|
||||
api.Init()
|
||||
return api
|
||||
}
|
||||
|
||||
var apiV2 *ApiV2
|
||||
|
||||
func GetApiV2() *ApiV2 {
|
||||
if apiV2 != nil {
|
||||
return apiV2
|
||||
}
|
||||
apiV2 = NewApiV2()
|
||||
return apiV2
|
||||
}
|
||||
199
core/apps/docker.go
Normal file
199
core/apps/docker.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/sys_exec"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/imroc/req"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Docker struct {
|
||||
// parent
|
||||
parent ServerApp
|
||||
|
||||
// dependencies
|
||||
interfaces.WithConfigPath
|
||||
|
||||
// seaweedfs log
|
||||
fsLogFilePath string
|
||||
fsLogFile *os.File
|
||||
fsReady bool
|
||||
}
|
||||
|
||||
func (app *Docker) Init() {
|
||||
var err error
|
||||
app.fsLogFile, err = os.OpenFile(app.fsLogFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0777))
|
||||
if err != nil {
|
||||
trace.PrintError(err)
|
||||
}
|
||||
|
||||
// replace paths
|
||||
if err := app.replacePaths(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// start nginx
|
||||
go app.startNginx()
|
||||
|
||||
// start seaweedfs
|
||||
go app.startSeaweedFs()
|
||||
}
|
||||
|
||||
func (app *Docker) Start() {
|
||||
// import demo
|
||||
//if utils.IsDemo() && utils.InitializedDemo() {
|
||||
// go app.importDemo()
|
||||
//}
|
||||
}
|
||||
|
||||
func (app *Docker) Wait() {
|
||||
DefaultWait()
|
||||
}
|
||||
|
||||
func (app *Docker) Stop() {
|
||||
}
|
||||
|
||||
func (app *Docker) GetParent() (parent ServerApp) {
|
||||
return app.parent
|
||||
}
|
||||
|
||||
func (app *Docker) SetParent(parent ServerApp) {
|
||||
app.parent = parent
|
||||
}
|
||||
|
||||
func (app *Docker) Ready() (ok bool) {
|
||||
return app.fsReady &&
|
||||
app.parent.GetApi().Ready()
|
||||
}
|
||||
|
||||
func (app *Docker) replacePaths() (err error) {
|
||||
// read
|
||||
indexHtmlPath := "/app/dist/index.html"
|
||||
indexHtmlBytes, err := os.ReadFile(indexHtmlPath)
|
||||
if err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
indexHtml := string(indexHtmlBytes)
|
||||
|
||||
// replace paths
|
||||
baseUrl := viper.GetString("base.url")
|
||||
if baseUrl != "" {
|
||||
indexHtml = app._replacePath(indexHtml, "js", baseUrl)
|
||||
indexHtml = app._replacePath(indexHtml, "css", baseUrl)
|
||||
indexHtml = app._replacePath(indexHtml, "<link rel=\"stylesheet\" href=\"", baseUrl)
|
||||
indexHtml = app._replacePath(indexHtml, "<link rel=\"stylesheet\" href=\"", baseUrl)
|
||||
indexHtml = app._replacePath(indexHtml, "window.VUE_APP_API_BASE_URL = '", baseUrl)
|
||||
}
|
||||
|
||||
// replace path of baidu tongji
|
||||
initBaiduTongji := viper.GetString("string")
|
||||
if initBaiduTongji != "" {
|
||||
indexHtml = strings.ReplaceAll(indexHtml, "window.VUE_APP_INIT_BAIDU_TONGJI = ''", fmt.Sprintf("window.VUE_APP_INIT_BAIDU_TONGJI = '%s'", initBaiduTongji))
|
||||
}
|
||||
|
||||
// replace path of umeng
|
||||
initUmeng := viper.GetString("string")
|
||||
if initUmeng != "" {
|
||||
indexHtml = strings.ReplaceAll(indexHtml, "window.VUE_APP_INIT_UMENG = ''", fmt.Sprintf("window.VUE_APP_INIT_UMENG = '%s'", initUmeng))
|
||||
}
|
||||
|
||||
// write
|
||||
if err := os.WriteFile(indexHtmlPath, []byte(indexHtml), os.FileMode(0766)); err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *Docker) _replacePath(text, path, baseUrl string) (res string) {
|
||||
text = strings.ReplaceAll(text, path, fmt.Sprintf("%s/%s", baseUrl, path))
|
||||
return text
|
||||
}
|
||||
|
||||
func (app *Docker) startNginx() {
|
||||
cmd := exec.Command("service", "nginx", "start")
|
||||
sys_exec.ConfigureCmdLogging(cmd, func(scanner *bufio.Scanner) {
|
||||
for scanner.Scan() {
|
||||
line := fmt.Sprintf("[nginx] %s\n", scanner.Text())
|
||||
_, _ = os.Stdout.WriteString(line)
|
||||
}
|
||||
})
|
||||
if err := cmd.Run(); err != nil {
|
||||
trace.PrintError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Docker) startSeaweedFs() {
|
||||
seaweedFsDataPath := "/data/seaweedfs"
|
||||
if !utils.Exists(seaweedFsDataPath) {
|
||||
_ = os.MkdirAll(seaweedFsDataPath, os.FileMode(0777))
|
||||
}
|
||||
cmd := exec.Command("weed", "server",
|
||||
"-dir", "/data",
|
||||
"-master.dir", seaweedFsDataPath,
|
||||
"-volume.dir.idx", seaweedFsDataPath,
|
||||
"-ip", "localhost",
|
||||
"-volume.port", "9999",
|
||||
"-volume.minFreeSpace", "1GiB",
|
||||
"-filer",
|
||||
)
|
||||
sys_exec.ConfigureCmdLogging(cmd, func(scanner *bufio.Scanner) {
|
||||
for scanner.Scan() {
|
||||
line := fmt.Sprintf("[seaweedfs] %s\n", scanner.Text())
|
||||
_, _ = app.fsLogFile.WriteString(line)
|
||||
}
|
||||
})
|
||||
go func() {
|
||||
if err := cmd.Run(); err != nil {
|
||||
trace.PrintError(err)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
_, err := req.Get("http://localhost:8888")
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
app.fsReady = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Docker) importDemo() {
|
||||
for {
|
||||
if app.Ready() {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
_ = utils.ImportDemo()
|
||||
}
|
||||
|
||||
func NewDocker(svr ServerApp) *Docker {
|
||||
dck := &Docker{
|
||||
parent: svr,
|
||||
fsLogFilePath: "/var/log/weed.log",
|
||||
}
|
||||
|
||||
dck.Init()
|
||||
|
||||
return dck
|
||||
}
|
||||
|
||||
var dck *Docker
|
||||
|
||||
func GetDocker(svr ServerApp) *Docker {
|
||||
if dck != nil {
|
||||
return dck
|
||||
}
|
||||
dck = NewDocker(svr)
|
||||
return dck
|
||||
}
|
||||
39
core/apps/interfaces.go
Normal file
39
core/apps/interfaces.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type App interface {
|
||||
Init()
|
||||
Start()
|
||||
Wait()
|
||||
Stop()
|
||||
}
|
||||
|
||||
type ApiApp interface {
|
||||
App
|
||||
GetGinEngine() (engine *gin.Engine)
|
||||
GetHttpServer() (svr *http.Server)
|
||||
Ready() (ok bool)
|
||||
}
|
||||
|
||||
type NodeApp interface {
|
||||
App
|
||||
interfaces.WithConfigPath
|
||||
}
|
||||
|
||||
type ServerApp interface {
|
||||
NodeApp
|
||||
GetApi() (api ApiApp)
|
||||
GetNodeService() (masterSvc interfaces.NodeService)
|
||||
}
|
||||
|
||||
type DockerApp interface {
|
||||
App
|
||||
GetParent() (parent NodeApp)
|
||||
SetParent(parent NodeApp)
|
||||
Ready() (ok bool)
|
||||
}
|
||||
149
core/apps/server.go
Normal file
149
core/apps/server.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/config"
|
||||
"github.com/crawlab-team/crawlab/core/controllers"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/node/service"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func init() {
|
||||
injectModules()
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
// settings
|
||||
grpcAddress interfaces.Address
|
||||
|
||||
// dependencies
|
||||
interfaces.WithConfigPath
|
||||
nodeSvc interfaces.NodeService
|
||||
|
||||
// modules
|
||||
api *Api
|
||||
dck *Docker
|
||||
|
||||
// internals
|
||||
quit chan int
|
||||
}
|
||||
|
||||
func (app *Server) SetGrpcAddress(address interfaces.Address) {
|
||||
app.grpcAddress = address
|
||||
}
|
||||
|
||||
func (app *Server) GetApi() (api ApiApp) {
|
||||
return app.api
|
||||
}
|
||||
|
||||
func (app *Server) GetNodeService() (svc interfaces.NodeService) {
|
||||
return app.nodeSvc
|
||||
}
|
||||
|
||||
func (app *Server) Init() {
|
||||
// log node info
|
||||
app.logNodeInfo()
|
||||
|
||||
if utils.IsMaster() {
|
||||
|
||||
// initialize controllers
|
||||
if err := controllers.InitControllers(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// pprof
|
||||
app.initPprof()
|
||||
}
|
||||
|
||||
func (app *Server) Start() {
|
||||
if utils.IsMaster() {
|
||||
// start docker app
|
||||
if utils.IsDocker() {
|
||||
go app.dck.Start()
|
||||
}
|
||||
|
||||
// start api
|
||||
go app.api.Start()
|
||||
}
|
||||
|
||||
// start node service
|
||||
go app.nodeSvc.Start()
|
||||
}
|
||||
|
||||
func (app *Server) Wait() {
|
||||
<-app.quit
|
||||
}
|
||||
|
||||
func (app *Server) Stop() {
|
||||
app.api.Stop()
|
||||
app.quit <- 1
|
||||
}
|
||||
|
||||
func (app *Server) logNodeInfo() {
|
||||
log.Infof("current node type: %s", utils.GetNodeType())
|
||||
if utils.IsDocker() {
|
||||
log.Infof("running in docker container")
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Server) initPprof() {
|
||||
if viper.GetBool("pprof") {
|
||||
go func() {
|
||||
fmt.Println(http.ListenAndServe("0.0.0.0:6060", nil))
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func NewServer() (app NodeApp) {
|
||||
// server
|
||||
svr := &Server{
|
||||
WithConfigPath: config.NewConfigPathService(),
|
||||
quit: make(chan int, 1),
|
||||
}
|
||||
|
||||
// service options
|
||||
var svcOpts []service.Option
|
||||
if svr.grpcAddress != nil {
|
||||
svcOpts = append(svcOpts, service.WithAddress(svr.grpcAddress))
|
||||
}
|
||||
|
||||
// master modules
|
||||
if utils.IsMaster() {
|
||||
// api
|
||||
svr.api = GetApi()
|
||||
|
||||
// docker
|
||||
if utils.IsDocker() {
|
||||
svr.dck = GetDocker(svr)
|
||||
}
|
||||
}
|
||||
|
||||
// node service
|
||||
var err error
|
||||
if utils.IsMaster() {
|
||||
svr.nodeSvc, err = service.NewMasterService(svcOpts...)
|
||||
} else {
|
||||
svr.nodeSvc, err = service.NewWorkerService(svcOpts...)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return svr
|
||||
}
|
||||
|
||||
var server NodeApp
|
||||
|
||||
func GetServer() NodeApp {
|
||||
if server != nil {
|
||||
return server
|
||||
}
|
||||
server = NewServer()
|
||||
return server
|
||||
}
|
||||
29
core/apps/server_test.go
Normal file
29
core/apps/server_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/imroc/req"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
_ = os.Setenv("CRAWLAB_DEMO", "false")
|
||||
}
|
||||
|
||||
func TestServer_Start(t *testing.T) {
|
||||
svr := GetServer()
|
||||
|
||||
// start
|
||||
go Start(svr)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
res, err := req.Get(fmt.Sprintf("http://localhost:%s/system-info", viper.GetString("server.port")))
|
||||
require.Nil(t, err)
|
||||
resStr, err := res.ToString()
|
||||
require.Nil(t, err)
|
||||
require.Contains(t, resStr, "success")
|
||||
}
|
||||
126
core/apps/server_v2.go
Normal file
126
core/apps/server_v2.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/config"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/node/service"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
type ServerV2 struct {
|
||||
// settings
|
||||
grpcAddress interfaces.Address
|
||||
|
||||
// dependencies
|
||||
interfaces.WithConfigPath
|
||||
|
||||
// modules
|
||||
nodeSvc interfaces.NodeService
|
||||
api *ApiV2
|
||||
dck *Docker
|
||||
|
||||
// internals
|
||||
quit chan int
|
||||
}
|
||||
|
||||
func (app *ServerV2) Init() {
|
||||
// log node info
|
||||
app.logNodeInfo()
|
||||
|
||||
// pprof
|
||||
app.initPprof()
|
||||
}
|
||||
|
||||
func (app *ServerV2) Start() {
|
||||
if utils.IsMaster() {
|
||||
// start docker app
|
||||
if utils.IsDocker() {
|
||||
go app.dck.Start()
|
||||
}
|
||||
|
||||
// start api
|
||||
go app.api.Start()
|
||||
}
|
||||
|
||||
// start node service
|
||||
go app.nodeSvc.Start()
|
||||
}
|
||||
|
||||
func (app *ServerV2) Wait() {
|
||||
<-app.quit
|
||||
}
|
||||
|
||||
func (app *ServerV2) Stop() {
|
||||
app.api.Stop()
|
||||
app.quit <- 1
|
||||
}
|
||||
|
||||
func (app *ServerV2) GetApi() ApiApp {
|
||||
return app.api
|
||||
}
|
||||
|
||||
func (app *ServerV2) GetNodeService() interfaces.NodeService {
|
||||
return app.nodeSvc
|
||||
}
|
||||
|
||||
func (app *ServerV2) logNodeInfo() {
|
||||
log.Infof("current node type: %s", utils.GetNodeType())
|
||||
if utils.IsDocker() {
|
||||
log.Infof("running in docker container")
|
||||
}
|
||||
}
|
||||
|
||||
func (app *ServerV2) initPprof() {
|
||||
if viper.GetBool("pprof") {
|
||||
go func() {
|
||||
fmt.Println(http.ListenAndServe("0.0.0.0:6060", nil))
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func NewServerV2() (app NodeApp) {
|
||||
// server
|
||||
svr := &ServerV2{
|
||||
WithConfigPath: config.NewConfigPathService(),
|
||||
quit: make(chan int, 1),
|
||||
}
|
||||
|
||||
// master modules
|
||||
if utils.IsMaster() {
|
||||
// api
|
||||
svr.api = GetApiV2()
|
||||
|
||||
// docker
|
||||
if utils.IsDocker() {
|
||||
svr.dck = GetDocker(svr)
|
||||
}
|
||||
}
|
||||
|
||||
// node service
|
||||
var err error
|
||||
if utils.IsMaster() {
|
||||
svr.nodeSvc, err = service.NewMasterServiceV2()
|
||||
} else {
|
||||
svr.nodeSvc, err = service.NewWorkerServiceV2()
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return svr
|
||||
}
|
||||
|
||||
var serverV2 NodeApp
|
||||
|
||||
func GetServerV2() NodeApp {
|
||||
if serverV2 != nil {
|
||||
return serverV2
|
||||
}
|
||||
serverV2 = NewServerV2()
|
||||
return serverV2
|
||||
}
|
||||
92
core/apps/utils.go
Normal file
92
core/apps/utils.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/color"
|
||||
"github.com/crawlab-team/crawlab/core/config"
|
||||
"github.com/crawlab-team/crawlab/core/container"
|
||||
grpcclient "github.com/crawlab-team/crawlab/core/grpc/client"
|
||||
grpcserver "github.com/crawlab-team/crawlab/core/grpc/server"
|
||||
modelsclient "github.com/crawlab-team/crawlab/core/models/client"
|
||||
modelsservice "github.com/crawlab-team/crawlab/core/models/service"
|
||||
nodeconfig "github.com/crawlab-team/crawlab/core/node/config"
|
||||
"github.com/crawlab-team/crawlab/core/schedule"
|
||||
"github.com/crawlab-team/crawlab/core/spider/admin"
|
||||
"github.com/crawlab-team/crawlab/core/stats"
|
||||
"github.com/crawlab-team/crawlab/core/task/handler"
|
||||
"github.com/crawlab-team/crawlab/core/task/scheduler"
|
||||
taskstats "github.com/crawlab-team/crawlab/core/task/stats"
|
||||
"github.com/crawlab-team/crawlab/core/user"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
)
|
||||
|
||||
func Start(app App) {
|
||||
start(app)
|
||||
}
|
||||
|
||||
func start(app App) {
|
||||
app.Init()
|
||||
go app.Start()
|
||||
app.Wait()
|
||||
app.Stop()
|
||||
}
|
||||
|
||||
func DefaultWait() {
|
||||
utils.DefaultWait()
|
||||
}
|
||||
|
||||
func initModule(name string, fn func() error) (err error) {
|
||||
if err := fn(); err != nil {
|
||||
log.Error(fmt.Sprintf("init %s error: %s", name, err.Error()))
|
||||
_ = trace.TraceError(err)
|
||||
panic(err)
|
||||
}
|
||||
log.Info(fmt.Sprintf("initialized %s successfully", name))
|
||||
return nil
|
||||
}
|
||||
|
||||
func initApp(name string, app App) {
|
||||
_ = initModule(name, func() error {
|
||||
app.Init()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var injectors = []interface{}{
|
||||
modelsservice.GetService,
|
||||
modelsclient.NewServiceDelegate,
|
||||
modelsclient.NewNodeServiceDelegate,
|
||||
modelsclient.NewSpiderServiceDelegate,
|
||||
modelsclient.NewTaskServiceDelegate,
|
||||
modelsclient.NewTaskStatServiceDelegate,
|
||||
modelsclient.NewEnvironmentServiceDelegate,
|
||||
grpcclient.NewClient,
|
||||
grpcclient.NewPool,
|
||||
grpcserver.GetServer,
|
||||
grpcserver.NewModelDelegateServer,
|
||||
grpcserver.NewModelBaseServiceServer,
|
||||
grpcserver.NewNodeServer,
|
||||
grpcserver.NewTaskServer,
|
||||
grpcserver.NewMessageServer,
|
||||
config.NewConfigPathService,
|
||||
user.GetUserService,
|
||||
schedule.GetScheduleService,
|
||||
admin.GetSpiderAdminService,
|
||||
stats.GetStatsService,
|
||||
nodeconfig.NewNodeConfigService,
|
||||
taskstats.GetTaskStatsService,
|
||||
color.NewService,
|
||||
scheduler.GetTaskSchedulerService,
|
||||
handler.GetTaskHandlerService,
|
||||
}
|
||||
|
||||
func injectModules() {
|
||||
c := container.GetContainer()
|
||||
for _, injector := range injectors {
|
||||
if err := c.Provide(injector); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
core/cmd/root.go
Normal file
33
core/cmd/root.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// Used for flags.
|
||||
cfgFile string
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "crawlab",
|
||||
Short: "CLI tool for Crawlab",
|
||||
Long: `The CLI tool is for controlling against Crawlab.
|
||||
Crawlab is a distributed web crawler and task admin platform
|
||||
aimed at making web crawling and task management easier.
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
// Execute executes the root command.
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
// GetRootCmd get rootCmd instance
|
||||
func GetRootCmd() *cobra.Command {
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "c", "", "Use Custom Config File")
|
||||
}
|
||||
25
core/cmd/server.go
Normal file
25
core/cmd/server.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/apps"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
}
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Aliases: []string{"s"},
|
||||
Short: "Start Crawlab server",
|
||||
Long: `Start Crawlab node server that can serve as API, task scheduler, task runner, etc.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// app
|
||||
//svr := apps.GetServer(opts...)
|
||||
svr := apps.GetServerV2()
|
||||
|
||||
// start
|
||||
apps.Start(svr)
|
||||
},
|
||||
}
|
||||
17
core/cmd/server_test.go
Normal file
17
core/cmd/server_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/apps"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCmdServer(t *testing.T) {
|
||||
_ = os.Setenv("CRAWLAB_PPROF", "true")
|
||||
|
||||
// app
|
||||
svr := apps.GetServerV2()
|
||||
|
||||
// start
|
||||
apps.Start(svr)
|
||||
}
|
||||
82
core/color/service.go
Normal file
82
core/color/service.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/crawlab-team/crawlab/core/data"
|
||||
"github.com/crawlab-team/crawlab/core/entity"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewService() (svc interfaces.ColorService, err error) {
|
||||
var cl []*entity.Color
|
||||
cm := map[string]*entity.Color{}
|
||||
|
||||
if err := json.Unmarshal([]byte(data.ColorsDataText), &cl); err != nil {
|
||||
return nil, trace.TraceError(err)
|
||||
}
|
||||
|
||||
for _, c := range cl {
|
||||
cm[c.Name] = c
|
||||
}
|
||||
|
||||
return &Service{
|
||||
cl: cl,
|
||||
cm: cm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
cl []*entity.Color
|
||||
cm map[string]*entity.Color
|
||||
}
|
||||
|
||||
func (svc *Service) Inject() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *Service) GetByName(name string) (res interfaces.Color, err error) {
|
||||
res, ok := svc.cm[name]
|
||||
if !ok {
|
||||
return res, errors.ErrorModelNotFound
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (svc *Service) GetRandom() (res interfaces.Color, err error) {
|
||||
if len(svc.cl) == 0 {
|
||||
hexStr, err := svc.getRandomColorHex()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return &entity.Color{Hex: hexStr}, nil
|
||||
}
|
||||
|
||||
idx := rand.Intn(len(svc.cl))
|
||||
return svc.cl[idx], nil
|
||||
}
|
||||
|
||||
func (svc *Service) getRandomColorHex() (res string, err error) {
|
||||
n := 6
|
||||
arr := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
arr[i], err = svc.getRandomHexChar()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
return strings.Join(arr, ""), nil
|
||||
}
|
||||
|
||||
func (svc *Service) getRandomHexChar() (res string, err error) {
|
||||
n := rand.Intn(16)
|
||||
b := []byte(strconv.Itoa(n))
|
||||
h := make([]byte, 1)
|
||||
hex.Encode(h, b)
|
||||
return string(h), nil
|
||||
}
|
||||
21
core/config/base.go
Normal file
21
core/config/base.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var HomeDirPath, _ = homedir.Dir()
|
||||
|
||||
const configDirName = ".crawlab"
|
||||
|
||||
const configName = "config.json"
|
||||
|
||||
func GetConfigPath() string {
|
||||
if viper.GetString("metadata") != "" {
|
||||
MetadataPath := viper.GetString("metadata")
|
||||
return filepath.Join(MetadataPath, configName)
|
||||
}
|
||||
return filepath.Join(HomeDirPath, configDirName, configName)
|
||||
}
|
||||
88
core/config/config.go
Normal file
88
core/config/config.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// config instance
|
||||
c := Config{Name: ""}
|
||||
|
||||
// init config file
|
||||
if err := c.Init(); err != nil {
|
||||
log.Warn("unable to init config")
|
||||
return
|
||||
}
|
||||
|
||||
// watch config change and load responsively
|
||||
c.WatchConfig()
|
||||
|
||||
// init log level
|
||||
c.initLogLevel()
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type InitConfigOptions struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c *Config) WatchConfig() {
|
||||
viper.WatchConfig()
|
||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||
log.Infof("Config file changed: %s", e.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Config) Init() (err error) {
|
||||
// config
|
||||
if c.Name != "" {
|
||||
viper.SetConfigFile(c.Name) // if config file is set, load it accordingly
|
||||
} else {
|
||||
viper.AddConfigPath("./conf") // if no config file is set, load by default
|
||||
viper.SetConfigName("config")
|
||||
}
|
||||
|
||||
// config type as yaml
|
||||
viper.SetConfigType("yaml") // default yaml
|
||||
|
||||
// auto env
|
||||
viper.AutomaticEnv() // load matched environment variables
|
||||
|
||||
// env prefix
|
||||
viper.SetEnvPrefix("CRAWLAB") // environment variable prefix as CRAWLAB
|
||||
|
||||
// replacer
|
||||
replacer := strings.NewReplacer(".", "_")
|
||||
viper.SetEnvKeyReplacer(replacer)
|
||||
|
||||
// read default config
|
||||
defaultConfBuf := bytes.NewBufferString(DefaultConfigYaml)
|
||||
if err := viper.ReadConfig(defaultConfBuf); err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
|
||||
// merge config
|
||||
if err := viper.MergeInConfig(); err != nil { // viper parsing config file
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) initLogLevel() {
|
||||
// set log level
|
||||
logLevel := viper.GetString("log.level")
|
||||
l, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
l = log.InfoLevel
|
||||
}
|
||||
log.SetLevel(l)
|
||||
}
|
||||
11
core/config/config_test.go
Normal file
11
core/config/config_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInitConfig(t *testing.T) {
|
||||
err := InitConfig()
|
||||
require.Nil(t, err)
|
||||
}
|
||||
40
core/config/default_config.go
Normal file
40
core/config/default_config.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package config
|
||||
|
||||
var DefaultConfigYaml = `
|
||||
info:
|
||||
version: v0.6.3
|
||||
edition: global.edition.community
|
||||
mongo:
|
||||
host: localhost
|
||||
port: 27017
|
||||
db: crawlab_test
|
||||
username: ""
|
||||
password: ""
|
||||
authSource: "admin"
|
||||
server:
|
||||
host: 0.0.0.0
|
||||
port: 8000
|
||||
spider:
|
||||
fs: "/spiders"
|
||||
workspace: "/workspace"
|
||||
repo: "/repo"
|
||||
task:
|
||||
workers: 16
|
||||
cancelWaitSeconds: 30
|
||||
grpc:
|
||||
address: localhost:9666
|
||||
server:
|
||||
address: 0.0.0.0:9666
|
||||
authKey: Crawlab2021!
|
||||
fs:
|
||||
filer:
|
||||
proxy: http://localhost:8888
|
||||
url: http://localhost:8000/filer
|
||||
authKey: Crawlab2021!
|
||||
node:
|
||||
master: Y
|
||||
api:
|
||||
endpoint: http://localhost:8000
|
||||
log:
|
||||
path: /var/log/crawlab
|
||||
`
|
||||
23
core/config/path.go
Normal file
23
core/config/path.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
)
|
||||
|
||||
type PathService struct {
|
||||
cfgPath string
|
||||
}
|
||||
|
||||
func (svc *PathService) GetConfigPath() (path string) {
|
||||
return svc.cfgPath
|
||||
}
|
||||
|
||||
func (svc *PathService) SetConfigPath(path string) {
|
||||
svc.cfgPath = path
|
||||
}
|
||||
|
||||
func NewConfigPathService() (svc interfaces.WithConfigPath) {
|
||||
svc = &PathService{}
|
||||
svc.SetConfigPath(GetConfigPath())
|
||||
return svc
|
||||
}
|
||||
12
core/config/version.go
Normal file
12
core/config/version.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package config
|
||||
|
||||
import "strings"
|
||||
|
||||
const Version = "v0.6.3"
|
||||
|
||||
func GetVersion() (v string) {
|
||||
if strings.HasPrefix(Version, "v") {
|
||||
return Version
|
||||
}
|
||||
return "v" + Version
|
||||
}
|
||||
8
core/constants/action.go
Normal file
8
core/constants/action.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ActionTypeVisit = "visit"
|
||||
ActionTypeInstallDep = "install_dep"
|
||||
ActionTypeInstallLang = "install_lang"
|
||||
ActionTypeViewDisclaimer = "view_disclaimer"
|
||||
)
|
||||
8
core/constants/anchor.go
Normal file
8
core/constants/anchor.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
AnchorStartStage = "START_STAGE"
|
||||
AnchorStartUrl = "START_URL"
|
||||
AnchorItems = "ITEMS"
|
||||
AnchorParsers = "PARSERS"
|
||||
)
|
||||
7
core/constants/auth.go
Normal file
7
core/constants/auth.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
OwnerTypeAll = "all"
|
||||
OwnerTypeMe = "me"
|
||||
OwnerTypePublic = "public"
|
||||
)
|
||||
8
core/constants/cache.go
Normal file
8
core/constants/cache.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
CacheColName = "cache"
|
||||
CacheColKey = "k"
|
||||
CacheColValue = "v"
|
||||
CacheColTime = "t"
|
||||
)
|
||||
9
core/constants/channels.go
Normal file
9
core/constants/channels.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ChannelAllNode = "nodes:public"
|
||||
|
||||
ChannelWorkerNode = "nodes:"
|
||||
|
||||
ChannelMasterNode = "nodes:master"
|
||||
)
|
||||
6
core/constants/common.go
Normal file
6
core/constants/common.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ASCENDING = "asc"
|
||||
DESCENDING = "dsc"
|
||||
)
|
||||
6
core/constants/config_spider.go
Normal file
6
core/constants/config_spider.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
EngineScrapy = "scrapy"
|
||||
EngineColly = "colly"
|
||||
)
|
||||
5
core/constants/data_collection.go
Normal file
5
core/constants/data_collection.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DataCollectionKey = "_col"
|
||||
)
|
||||
12
core/constants/data_field.go
Normal file
12
core/constants/data_field.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DataFieldTypeGeneral = "general"
|
||||
DataFieldTypeNumeric = "numeric"
|
||||
DataFieldTypeDate = "date"
|
||||
DataFieldTypeCurrency = "currency"
|
||||
DataFieldTypeUrl = "url"
|
||||
DataFieldTypeImage = "image"
|
||||
DataFieldTypeAudio = "audio"
|
||||
DataFieldTypeVideo = "video"
|
||||
)
|
||||
5
core/constants/database.go
Normal file
5
core/constants/database.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ColJob = "jobs"
|
||||
)
|
||||
1
core/constants/delegate.go
Normal file
1
core/constants/delegate.go
Normal file
@@ -0,0 +1 @@
|
||||
package constants
|
||||
31
core/constants/ds.go
Normal file
31
core/constants/ds.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DataSourceTypeMongo = "mongo"
|
||||
DataSourceTypeMysql = "mysql"
|
||||
DataSourceTypePostgresql = "postgresql"
|
||||
DataSourceTypeMssql = "mssql"
|
||||
DataSourceTypeSqlite = "sqlite"
|
||||
DataSourceTypeCockroachdb = "cockroachdb"
|
||||
DataSourceTypeElasticSearch = "elasticsearch"
|
||||
DataSourceTypeKafka = "kafka"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHost = "localhost"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultMongoPort = "27017"
|
||||
DefaultMysqlPort = "3306"
|
||||
DefaultPostgresqlPort = "5432"
|
||||
DefaultMssqlPort = "1433"
|
||||
DefaultCockroachdbPort = "26257"
|
||||
DefaultElasticsearchPort = "9200"
|
||||
DefaultKafkaPort = "9092"
|
||||
)
|
||||
|
||||
const (
|
||||
DataSourceStatusOnline = "on"
|
||||
DataSourceStatusOffline = "off"
|
||||
)
|
||||
5
core/constants/encrypt.go
Normal file
5
core/constants/encrypt.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DefaultEncryptServerKey = "0123456789abcdef"
|
||||
)
|
||||
30
core/constants/errors.go
Normal file
30
core/constants/errors.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
//ErrorMongoError = e.NewSystemOPError(1001, "system error:[mongo]%s", http.StatusInternalServerError)
|
||||
//ErrorUserNotFound = e.NewBusinessError(10001, "user not found.", http.StatusUnauthorized)
|
||||
//ErrorUsernameOrPasswordInvalid = e.NewBusinessError(11001, "username or password invalid", http.StatusUnauthorized)
|
||||
ErrAlreadyExists = errors.New("already exists")
|
||||
ErrNotExists = errors.New("not exists")
|
||||
ErrForbidden = errors.New("forbidden")
|
||||
ErrInvalidOperation = errors.New("invalid operation")
|
||||
ErrInvalidOptions = errors.New("invalid options")
|
||||
ErrNoTasksAvailable = errors.New("no tasks available")
|
||||
ErrInvalidType = errors.New("invalid type")
|
||||
ErrInvalidSignal = errors.New("invalid signal")
|
||||
ErrEmptyValue = errors.New("empty value")
|
||||
ErrTaskError = errors.New("task error")
|
||||
ErrTaskLost = errors.New("task lost")
|
||||
ErrTaskCancelled = errors.New("task cancelled")
|
||||
ErrUnableToCancel = errors.New("unable to cancel")
|
||||
ErrUnableToDispose = errors.New("unable to dispose")
|
||||
ErrAlreadyDisposed = errors.New("already disposed")
|
||||
ErrStopped = errors.New("stopped")
|
||||
ErrMissingCol = errors.New("missing col")
|
||||
ErrInvalidValue = errors.New("invalid value")
|
||||
ErrInvalidCronSpec = errors.New("invalid cron spec")
|
||||
)
|
||||
6
core/constants/event.go
Normal file
6
core/constants/event.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
GrpcEventServiceTypeRegister = "register"
|
||||
GrpcEventServiceTypeSend = "send"
|
||||
)
|
||||
6
core/constants/export.go
Normal file
6
core/constants/export.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ExportTypeCsv = "csv"
|
||||
ExportTypeJson = "json"
|
||||
)
|
||||
5
core/constants/file.go
Normal file
5
core/constants/file.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const EmptyFileData = " "
|
||||
|
||||
const FsKeepFileName = ".gitkeep"
|
||||
5
core/constants/filer.go
Normal file
5
core/constants/filer.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DefaultFilerAuthKey = "Crawlab2021!"
|
||||
)
|
||||
28
core/constants/filter.go
Normal file
28
core/constants/filter.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
FilterQueryFieldConditions = "conditions"
|
||||
FilterQueryFieldAll = "all"
|
||||
)
|
||||
|
||||
const (
|
||||
FilterObjectTypeString = "string"
|
||||
FilterObjectTypeNumber = "number"
|
||||
FilterObjectTypeBoolean = "boolean"
|
||||
)
|
||||
|
||||
const (
|
||||
FilterOpNotSet = "ns"
|
||||
FilterOpContains = "c"
|
||||
FilterOpNotContains = "nc"
|
||||
FilterOpRegex = "r"
|
||||
FilterOpEqual = "eq"
|
||||
FilterOpNotEqual = "ne"
|
||||
FilterOpIn = "in"
|
||||
FilterOpNotIn = "nin"
|
||||
FilterOpGreaterThan = "gt"
|
||||
FilterOpLessThan = "lt"
|
||||
FilterOpGreaterThanEqual = "gte"
|
||||
FilterOpLessThanEqual = "lte"
|
||||
FilterOpSearch = "s"
|
||||
)
|
||||
16
core/constants/git.go
Normal file
16
core/constants/git.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
GitAuthTypeHttp = "http"
|
||||
GitAuthTypeSsh = "ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
GitRemoteNameUpstream = "upstream"
|
||||
GitRemoteNameOrigin = "origin"
|
||||
)
|
||||
|
||||
const (
|
||||
GitBranchMaster = "master"
|
||||
GitBranchMain = "main"
|
||||
)
|
||||
17
core/constants/grpc.go
Normal file
17
core/constants/grpc.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
DefaultGrpcServerHost = ""
|
||||
DefaultGrpcServerPort = "9666"
|
||||
DefaultGrpcClientRemoteHost = "localhost"
|
||||
DefaultGrpcClientRemotePort = DefaultGrpcServerPort
|
||||
DefaultGrpcAuthKey = "Crawlab2021!"
|
||||
)
|
||||
|
||||
const (
|
||||
GrpcHeaderAuthorization = "authorization"
|
||||
)
|
||||
|
||||
const (
|
||||
GrpcSubscribeTypeNode = "node"
|
||||
)
|
||||
11
core/constants/http.go
Normal file
11
core/constants/http.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
HttpResponseStatusOk = "ok"
|
||||
HttpResponseMessageSuccess = "success"
|
||||
HttpResponseMessageError = "error"
|
||||
)
|
||||
|
||||
const (
|
||||
HttpContentTypeApplicationJson = "application/json"
|
||||
)
|
||||
5
core/constants/log.go
Normal file
5
core/constants/log.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ErrorRegexPattern = "(?:[ :,.]|^)((?:error|exception|traceback)s?)(?:[ :,.]|$)"
|
||||
)
|
||||
9
core/constants/message.go
Normal file
9
core/constants/message.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
MsgTypeGetLog = "get-log"
|
||||
MsgTypeGetSystemInfo = "get-sys-info"
|
||||
MsgTypeCancelTask = "cancel-task"
|
||||
MsgTypeRemoveLog = "remove-log"
|
||||
MsgTypeRemoveSpider = "remove-spider"
|
||||
)
|
||||
8
core/constants/node.go
Normal file
8
core/constants/node.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
NodeStatusUnregistered = "u"
|
||||
NodeStatusRegistered = "r"
|
||||
NodeStatusOnline = "on"
|
||||
NodeStatusOffline = "off"
|
||||
)
|
||||
8
core/constants/notification.go
Normal file
8
core/constants/notification.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
NotificationTriggerTaskFinish = "task_finish"
|
||||
NotificationTriggerTaskError = "task_error"
|
||||
NotificationTriggerTaskEmptyResults = "task_empty_results"
|
||||
NotificationTriggerTaskNever = "task_never"
|
||||
)
|
||||
4
core/constants/pagination.go
Normal file
4
core/constants/pagination.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package constants
|
||||
|
||||
var PaginationDefaultPage = 1
|
||||
var PaginationDefaultSize = 10
|
||||
8
core/constants/register.go
Normal file
8
core/constants/register.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
RegisterTypeMac = "mac"
|
||||
RegisterTypeIp = "ip"
|
||||
RegisterTypeHostname = "hostname"
|
||||
RegisterTypeCustomName = "customName"
|
||||
)
|
||||
10
core/constants/results.go
Normal file
10
core/constants/results.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
HashKey = "_h"
|
||||
)
|
||||
|
||||
const (
|
||||
DedupTypeIgnore = "ignore"
|
||||
DedupTypeOverwrite = "overwrite"
|
||||
)
|
||||
12
core/constants/rpc.go
Normal file
12
core/constants/rpc.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
RpcInstallLang = "install_lang"
|
||||
RpcInstallDep = "install_dep"
|
||||
RpcUninstallDep = "uninstall_dep"
|
||||
RpcGetInstalledDepList = "get_installed_dep_list"
|
||||
RpcGetLang = "get_lang"
|
||||
RpcCancelTask = "cancel_task"
|
||||
RpcGetSystemInfoService = "get_system_info"
|
||||
RpcRemoveSpider = "remove_spider"
|
||||
)
|
||||
10
core/constants/schedule.go
Normal file
10
core/constants/schedule.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ScheduleStatusStop = "stopped"
|
||||
ScheduleStatusRunning = "running"
|
||||
ScheduleStatusError = "error"
|
||||
|
||||
ScheduleStatusErrorNotFoundNode = "Not Found Node"
|
||||
ScheduleStatusErrorNotFoundSpider = "Not Found Spider"
|
||||
)
|
||||
5
core/constants/scrapy.go
Normal file
5
core/constants/scrapy.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const ScrapyProtectedStageNames = ""
|
||||
|
||||
const ScrapyProtectedFieldNames = "_id,task_id,ts"
|
||||
5
core/constants/signal.go
Normal file
5
core/constants/signal.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
SignalQuit = iota
|
||||
)
|
||||
5
core/constants/sort.go
Normal file
5
core/constants/sort.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
SortQueryField = "sort"
|
||||
)
|
||||
25
core/constants/system.go
Normal file
25
core/constants/system.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
Windows = "windows"
|
||||
Linux = "linux"
|
||||
Darwin = "darwin"
|
||||
)
|
||||
|
||||
const (
|
||||
Python = "python"
|
||||
Nodejs = "node"
|
||||
Java = "java"
|
||||
)
|
||||
|
||||
const (
|
||||
InstallStatusNotInstalled = "not-installed"
|
||||
InstallStatusInstalling = "installing"
|
||||
InstallStatusInstallingOther = "installing-other"
|
||||
InstallStatusInstalled = "installed"
|
||||
)
|
||||
|
||||
const (
|
||||
LangTypeLang = "lang"
|
||||
LangTypeWebDriver = "webdriver"
|
||||
)
|
||||
39
core/constants/task.go
Normal file
39
core/constants/task.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
TaskStatusPending = "pending"
|
||||
TaskStatusRunning = "running"
|
||||
TaskStatusFinished = "finished"
|
||||
TaskStatusError = "error"
|
||||
TaskStatusCancelled = "cancelled"
|
||||
TaskStatusAbnormal = "abnormal"
|
||||
)
|
||||
|
||||
const (
|
||||
RunTypeAllNodes = "all-nodes"
|
||||
RunTypeRandom = "random"
|
||||
RunTypeSelectedNodes = "selected-nodes"
|
||||
)
|
||||
|
||||
const (
|
||||
TaskTypeSpider = "spider"
|
||||
TaskTypeSystem = "system"
|
||||
)
|
||||
|
||||
type TaskSignal int
|
||||
|
||||
const (
|
||||
TaskSignalFinish TaskSignal = iota
|
||||
TaskSignalCancel
|
||||
TaskSignalError
|
||||
TaskSignalLost
|
||||
)
|
||||
|
||||
const (
|
||||
TaskListQueuePrefixPublic = "tasks:public"
|
||||
TaskListQueuePrefixNodes = "tasks:nodes"
|
||||
)
|
||||
|
||||
const (
|
||||
TaskKey = "_tid"
|
||||
)
|
||||
15
core/constants/user.go
Normal file
15
core/constants/user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
RoleAdmin = "admin"
|
||||
RoleNormal = "normal"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultAdminUsername = "admin"
|
||||
DefaultAdminPassword = "admin"
|
||||
)
|
||||
|
||||
const (
|
||||
UserContextKey = "user"
|
||||
)
|
||||
9
core/constants/variable.go
Normal file
9
core/constants/variable.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
String = "string"
|
||||
Number = "number"
|
||||
Boolean = "boolean"
|
||||
Array = "array"
|
||||
Object = "object"
|
||||
)
|
||||
11
core/container/container.go
Normal file
11
core/container/container.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"go.uber.org/dig"
|
||||
)
|
||||
|
||||
var c = dig.New()
|
||||
|
||||
func GetContainer() *dig.Container {
|
||||
return c
|
||||
}
|
||||
69
core/controllers/base.go
Normal file
69
core/controllers/base.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package controllers
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
const (
|
||||
ControllerIdNode = iota << 1
|
||||
ControllerIdProject
|
||||
ControllerIdSpider
|
||||
ControllerIdTask
|
||||
ControllerIdJob
|
||||
ControllerIdSchedule
|
||||
ControllerIdUser
|
||||
ControllerIdSetting
|
||||
ControllerIdToken
|
||||
ControllerIdVariable
|
||||
ControllerIdTag
|
||||
ControllerIdLogin
|
||||
ControllerIdColor
|
||||
ControllerIdDataSource
|
||||
ControllerIdDataCollection
|
||||
ControllerIdResult
|
||||
ControllerIdStats
|
||||
ControllerIdFiler
|
||||
ControllerIdGit
|
||||
ControllerIdRole
|
||||
ControllerIdPermission
|
||||
ControllerIdExport
|
||||
ControllerIdNotification
|
||||
ControllerIdFilter
|
||||
ControllerIdEnvironment
|
||||
ControllerIdSync
|
||||
|
||||
ControllerIdVersion
|
||||
ControllerIdI18n
|
||||
ControllerIdSystemInfo
|
||||
ControllerIdDemo
|
||||
)
|
||||
|
||||
type ControllerId int
|
||||
|
||||
type BasicController interface {
|
||||
Get(c *gin.Context)
|
||||
Post(c *gin.Context)
|
||||
Put(c *gin.Context)
|
||||
Delete(c *gin.Context)
|
||||
}
|
||||
|
||||
type ListController interface {
|
||||
BasicController
|
||||
GetList(c *gin.Context)
|
||||
PutList(c *gin.Context)
|
||||
PostList(c *gin.Context)
|
||||
DeleteList(c *gin.Context)
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
Method string
|
||||
Path string
|
||||
HandlerFunc gin.HandlerFunc
|
||||
}
|
||||
|
||||
type ActionController interface {
|
||||
Actions() (actions []Action)
|
||||
}
|
||||
|
||||
type ListActionController interface {
|
||||
ListController
|
||||
ActionController
|
||||
}
|
||||
226
core/controllers/base_v2.go
Normal file
226
core/controllers/base_v2.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
type BaseControllerV2[T any] struct {
|
||||
modelSvc *service.ModelServiceV2[T]
|
||||
actions []Action
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) GetById(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
model, err := ctr.modelSvc.GetById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, model)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) GetList(c *gin.Context) {
|
||||
// get all if query field "all" is set true
|
||||
all := MustGetFilterAll(c)
|
||||
if all {
|
||||
ctr.getAll(c)
|
||||
return
|
||||
}
|
||||
|
||||
// get list
|
||||
ctr.getList(c)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) Post(c *gin.Context) {
|
||||
var model T
|
||||
if err := c.ShouldBindJSON(&model); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
u := GetUserFromContextV2(c)
|
||||
m := any(&model).(interfaces.ModelV2)
|
||||
m.SetId(primitive.NewObjectID())
|
||||
m.SetCreated(u.Id)
|
||||
m.SetUpdated(u.Id)
|
||||
col := ctr.modelSvc.GetCol()
|
||||
res, err := col.GetCollection().InsertOne(col.GetContext(), m)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := ctr.modelSvc.GetById(res.InsertedID.(primitive.ObjectID))
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, result)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) PutById(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
var model T
|
||||
if err := c.ShouldBindJSON(&model); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
u := GetUserFromContextV2(c)
|
||||
m := any(&model).(interfaces.ModelV2)
|
||||
m.SetId(primitive.NewObjectID())
|
||||
m.SetUpdated(u.Id)
|
||||
|
||||
if err := ctr.modelSvc.ReplaceById(id, model); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := ctr.modelSvc.GetById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, result)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) PatchList(c *gin.Context) {
|
||||
type Payload struct {
|
||||
Ids []primitive.ObjectID `json:"ids"`
|
||||
Update bson.M `json:"update"`
|
||||
}
|
||||
|
||||
var payload Payload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// query
|
||||
query := bson.M{
|
||||
"_id": bson.M{
|
||||
"$in": payload.Ids,
|
||||
},
|
||||
}
|
||||
|
||||
// update
|
||||
if err := ctr.modelSvc.UpdateMany(query, bson.M{"$set": payload.Update}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) DeleteById(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctr.modelSvc.DeleteById(id); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) DeleteList(c *gin.Context) {
|
||||
type Payload struct {
|
||||
Ids []primitive.ObjectID `json:"ids"`
|
||||
}
|
||||
|
||||
var payload Payload
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctr.modelSvc.DeleteMany(bson.M{
|
||||
"_id": bson.M{
|
||||
"$in": payload.Ids,
|
||||
},
|
||||
}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) getAll(c *gin.Context) {
|
||||
models, err := ctr.modelSvc.GetMany(nil, &mongo.FindOptions{
|
||||
Sort: bson.D{{"_id", -1}},
|
||||
})
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
total, err := ctr.modelSvc.Count(nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithListData(c, models, total)
|
||||
}
|
||||
|
||||
func (ctr *BaseControllerV2[T]) getList(c *gin.Context) {
|
||||
// params
|
||||
pagination := MustGetPagination(c)
|
||||
query := MustGetFilterQuery(c)
|
||||
sort := MustGetSortOption(c)
|
||||
|
||||
// get list
|
||||
models, err := ctr.modelSvc.GetMany(query, &mongo.FindOptions{
|
||||
Sort: sort,
|
||||
Skip: pagination.Size * (pagination.Page - 1),
|
||||
Limit: pagination.Size,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo2.ErrNoDocuments) {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
} else {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// total count
|
||||
total, err := ctr.modelSvc.Count(query)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
HandleSuccessWithListData(c, models, total)
|
||||
}
|
||||
|
||||
func NewControllerV2[T any](actions ...Action) *BaseControllerV2[T] {
|
||||
ctr := &BaseControllerV2[T]{
|
||||
modelSvc: service.NewModelServiceV2[T](),
|
||||
actions: actions,
|
||||
}
|
||||
return ctr
|
||||
}
|
||||
165
core/controllers/base_v2_test.go
Normal file
165
core/controllers/base_v2_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package controllers_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/crawlab-team/crawlab/core/controllers"
|
||||
"github.com/crawlab-team/crawlab/core/middlewares"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/core/user"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
}
|
||||
|
||||
// TestModel is a simple struct to be used as a model in tests
|
||||
type TestModel models.TestModel
|
||||
|
||||
var TestToken string
|
||||
|
||||
// SetupTestDB sets up the test database
|
||||
func SetupTestDB() {
|
||||
viper.Set("mongo.db", "testdb")
|
||||
modelSvc := service.NewModelServiceV2[models.UserV2]()
|
||||
u := models.UserV2{
|
||||
Username: "admin",
|
||||
}
|
||||
id, err := modelSvc.InsertOne(u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
u.SetId(id)
|
||||
|
||||
userSvc, err := user.GetUserServiceV2()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
token, err := userSvc.MakeToken(&u)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
TestToken = token
|
||||
}
|
||||
|
||||
// SetupRouter sets up the gin router for testing
|
||||
func SetupRouter() *gin.Engine {
|
||||
router := gin.Default()
|
||||
return router
|
||||
}
|
||||
|
||||
// CleanupTestDB cleans up the test database
|
||||
func CleanupTestDB() {
|
||||
mongo.GetMongoDb("testdb").Drop(context.Background())
|
||||
}
|
||||
|
||||
func TestBaseControllerV2_GetById(t *testing.T) {
|
||||
SetupTestDB()
|
||||
defer CleanupTestDB()
|
||||
|
||||
// Insert a test document
|
||||
id, err := service.NewModelServiceV2[TestModel]().InsertOne(TestModel{Name: "test"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Initialize the controller
|
||||
ctr := controllers.NewControllerV2[TestModel]()
|
||||
|
||||
// Set up the router
|
||||
router := SetupRouter()
|
||||
router.Use(middlewares.AuthorizationMiddlewareV2())
|
||||
router.GET("/testmodels/:id", ctr.GetById)
|
||||
|
||||
// Create a test request
|
||||
req, _ := http.NewRequest("GET", "/testmodels/"+id.Hex(), nil)
|
||||
req.Header.Set("Authorization", TestToken)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Serve the request
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response controllers.Response[TestModel]
|
||||
err = json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", response.Data.Name)
|
||||
}
|
||||
|
||||
func TestBaseControllerV2_Post(t *testing.T) {
|
||||
SetupTestDB()
|
||||
defer CleanupTestDB()
|
||||
|
||||
// Initialize the controller
|
||||
ctr := controllers.NewControllerV2[TestModel]()
|
||||
|
||||
// Set up the router
|
||||
router := SetupRouter()
|
||||
router.Use(middlewares.AuthorizationMiddlewareV2())
|
||||
router.POST("/testmodels", ctr.Post)
|
||||
|
||||
// Create a test request
|
||||
testModel := TestModel{Name: "test"}
|
||||
jsonValue, _ := json.Marshal(testModel)
|
||||
req, _ := http.NewRequest("POST", "/testmodels", bytes.NewBuffer(jsonValue))
|
||||
req.Header.Set("Authorization", TestToken)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Serve the request
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response controllers.Response[TestModel]
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", response.Data.Name)
|
||||
|
||||
// Check if the document was inserted into the database
|
||||
result, err := service.NewModelServiceV2[TestModel]().GetById(response.Data.Id)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", result.Name)
|
||||
}
|
||||
|
||||
func TestBaseControllerV2_DeleteById(t *testing.T) {
|
||||
SetupTestDB()
|
||||
defer CleanupTestDB()
|
||||
|
||||
// Insert a test document
|
||||
id, err := service.NewModelServiceV2[TestModel]().InsertOne(TestModel{Name: "test"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Initialize the controller
|
||||
ctr := controllers.NewControllerV2[TestModel]()
|
||||
|
||||
// Set up the router
|
||||
router := SetupRouter()
|
||||
router.Use(middlewares.AuthorizationMiddlewareV2())
|
||||
router.DELETE("/testmodels/:id", ctr.DeleteById)
|
||||
|
||||
// Create a test request
|
||||
req, _ := http.NewRequest("DELETE", "/testmodels/"+id.Hex(), nil)
|
||||
req.Header.Set("Authorization", TestToken)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Serve the request
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Check if the document was deleted from the database
|
||||
_, err = service.NewModelServiceV2[TestModel]().GetById(id)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
14
core/controllers/binder.go
Normal file
14
core/controllers/binder.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/entity"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type BinderInterface interface {
|
||||
Bind(c *gin.Context) (res interfaces.Model, err error)
|
||||
BindList(c *gin.Context) (res []interfaces.Model, err error)
|
||||
BindBatchRequestPayload(c *gin.Context) (payload entity.BatchRequestPayload, err error)
|
||||
BindBatchRequestPayloadWithStringData(c *gin.Context) (payload entity.BatchRequestPayloadWithStringData, res interfaces.Model, err error)
|
||||
}
|
||||
208
core/controllers/binder_json.go
Normal file
208
core/controllers/binder_json.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/crawlab-team/crawlab/core/entity"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewJsonBinder(id ControllerId) (b *JsonBinder) {
|
||||
return &JsonBinder{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
type JsonBinder struct {
|
||||
id ControllerId
|
||||
}
|
||||
|
||||
func (b *JsonBinder) Bind(c *gin.Context) (res interfaces.Model, err error) {
|
||||
// declare
|
||||
m := models.NewModelMap()
|
||||
|
||||
switch b.id {
|
||||
case ControllerIdNode:
|
||||
err = c.ShouldBindJSON(&m.Node)
|
||||
return &m.Node, err
|
||||
case ControllerIdProject:
|
||||
err = c.ShouldBindJSON(&m.Project)
|
||||
return &m.Project, err
|
||||
case ControllerIdSpider:
|
||||
err = c.ShouldBindJSON(&m.Spider)
|
||||
return &m.Spider, err
|
||||
case ControllerIdTask:
|
||||
err = c.ShouldBindJSON(&m.Task)
|
||||
return &m.Task, err
|
||||
case ControllerIdJob:
|
||||
err = c.ShouldBindJSON(&m.Job)
|
||||
return &m.Job, err
|
||||
case ControllerIdSchedule:
|
||||
err = c.ShouldBindJSON(&m.Schedule)
|
||||
return &m.Schedule, err
|
||||
case ControllerIdUser:
|
||||
err = c.ShouldBindJSON(&m.User)
|
||||
return &m.User, nil
|
||||
case ControllerIdSetting:
|
||||
err = c.ShouldBindJSON(&m.Setting)
|
||||
return &m.Setting, nil
|
||||
case ControllerIdToken:
|
||||
err = c.ShouldBindJSON(&m.Token)
|
||||
return &m.Token, nil
|
||||
case ControllerIdVariable:
|
||||
err = c.ShouldBindJSON(&m.Variable)
|
||||
return &m.Variable, nil
|
||||
case ControllerIdTag:
|
||||
err = c.ShouldBindJSON(&m.Tag)
|
||||
return &m.Tag, nil
|
||||
case ControllerIdDataSource:
|
||||
err = c.ShouldBindJSON(&m.DataSource)
|
||||
return &m.DataSource, nil
|
||||
case ControllerIdDataCollection:
|
||||
err = c.ShouldBindJSON(&m.DataCollection)
|
||||
return &m.DataCollection, nil
|
||||
case ControllerIdGit:
|
||||
err = c.ShouldBindJSON(&m.Git)
|
||||
return &m.Git, nil
|
||||
case ControllerIdRole:
|
||||
err = c.ShouldBindJSON(&m.Role)
|
||||
return &m.Role, nil
|
||||
case ControllerIdPermission:
|
||||
err = c.ShouldBindJSON(&m.Permission)
|
||||
return &m.Permission, nil
|
||||
case ControllerIdEnvironment:
|
||||
err = c.ShouldBindJSON(&m.Environment)
|
||||
return &m.Environment, nil
|
||||
default:
|
||||
return nil, errors.ErrorControllerInvalidControllerId
|
||||
}
|
||||
}
|
||||
|
||||
func (b *JsonBinder) BindList(c *gin.Context) (res interface{}, err error) {
|
||||
// declare
|
||||
m := models.NewModelListMap()
|
||||
|
||||
// bind
|
||||
switch b.id {
|
||||
case ControllerIdNode:
|
||||
err = c.ShouldBindJSON(&m.Nodes)
|
||||
return m.Nodes, err
|
||||
case ControllerIdProject:
|
||||
err = c.ShouldBindJSON(&m.Projects)
|
||||
return m.Projects, err
|
||||
case ControllerIdSpider:
|
||||
err = c.ShouldBindJSON(&m.Spiders)
|
||||
return m.Spiders, err
|
||||
case ControllerIdTask:
|
||||
err = c.ShouldBindJSON(&m.Tasks)
|
||||
return m.Tasks, err
|
||||
case ControllerIdJob:
|
||||
err = c.ShouldBindJSON(&m.Jobs)
|
||||
return m.Jobs, err
|
||||
case ControllerIdSchedule:
|
||||
err = c.ShouldBindJSON(&m.Schedules)
|
||||
return m.Schedules, err
|
||||
case ControllerIdUser:
|
||||
err = c.ShouldBindJSON(&m.Users)
|
||||
return m.Users, nil
|
||||
case ControllerIdSetting:
|
||||
err = c.ShouldBindJSON(&m.Settings)
|
||||
return m.Settings, nil
|
||||
case ControllerIdToken:
|
||||
err = c.ShouldBindJSON(&m.Tokens)
|
||||
return m.Tokens, nil
|
||||
case ControllerIdVariable:
|
||||
err = c.ShouldBindJSON(&m.Variables)
|
||||
return m.Variables, nil
|
||||
case ControllerIdTag:
|
||||
err = c.ShouldBindJSON(&m.Tags)
|
||||
return m.Tags, nil
|
||||
case ControllerIdDataSource:
|
||||
err = c.ShouldBindJSON(&m.DataSources)
|
||||
return m.DataSources, nil
|
||||
case ControllerIdDataCollection:
|
||||
err = c.ShouldBindJSON(&m.DataCollections)
|
||||
return m.DataCollections, nil
|
||||
case ControllerIdGit:
|
||||
err = c.ShouldBindJSON(&m.Gits)
|
||||
return m.Gits, nil
|
||||
case ControllerIdRole:
|
||||
err = c.ShouldBindJSON(&m.Roles)
|
||||
return m.Roles, nil
|
||||
case ControllerIdEnvironment:
|
||||
err = c.ShouldBindJSON(&m.Environments)
|
||||
return m.Environments, nil
|
||||
default:
|
||||
return nil, errors.ErrorControllerInvalidControllerId
|
||||
}
|
||||
}
|
||||
|
||||
func (b *JsonBinder) BindBatchRequestPayload(c *gin.Context) (payload entity.BatchRequestPayload, err error) {
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
return payload, err
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (b *JsonBinder) BindBatchRequestPayloadWithStringData(c *gin.Context) (payload entity.BatchRequestPayloadWithStringData, res interfaces.Model, err error) {
|
||||
// declare
|
||||
m := models.NewModelMap()
|
||||
|
||||
// bind
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
return payload, nil, err
|
||||
}
|
||||
|
||||
// validate
|
||||
if len(payload.Ids) == 0 ||
|
||||
len(payload.Fields) == 0 {
|
||||
return payload, nil, errors.ErrorControllerRequestPayloadInvalid
|
||||
}
|
||||
|
||||
// unmarshall
|
||||
switch b.id {
|
||||
case ControllerIdNode:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Node)
|
||||
return payload, &m.Node, err
|
||||
case ControllerIdProject:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Project)
|
||||
return payload, &m.Project, err
|
||||
case ControllerIdSpider:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Spider)
|
||||
return payload, &m.Spider, err
|
||||
case ControllerIdTask:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Task)
|
||||
return payload, &m.Task, err
|
||||
case ControllerIdJob:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Job)
|
||||
return payload, &m.Job, err
|
||||
case ControllerIdSchedule:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Schedule)
|
||||
return payload, &m.Schedule, err
|
||||
case ControllerIdUser:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.User)
|
||||
return payload, &m.User, err
|
||||
case ControllerIdSetting:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Setting)
|
||||
return payload, &m.Setting, err
|
||||
case ControllerIdToken:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Token)
|
||||
return payload, &m.Token, err
|
||||
case ControllerIdVariable:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Variable)
|
||||
return payload, &m.Variable, err
|
||||
case ControllerIdDataSource:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.DataSource)
|
||||
return payload, &m.DataSource, err
|
||||
case ControllerIdDataCollection:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.DataCollection)
|
||||
return payload, &m.DataCollection, err
|
||||
case ControllerIdEnvironment:
|
||||
err = json.Unmarshal([]byte(payload.Data), &m.Environment)
|
||||
return payload, &m.Environment, err
|
||||
default:
|
||||
return payload, nil, errors.ErrorControllerInvalidControllerId
|
||||
}
|
||||
}
|
||||
103
core/controllers/data_collection.go
Normal file
103
core/controllers/data_collection.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/container"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var DataCollectionController *dataCollectionController
|
||||
|
||||
func getDataCollectionActions() []Action {
|
||||
ctx := newDataCollectionContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/indexes",
|
||||
HandlerFunc: ctx.postIndexes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type dataCollectionController struct {
|
||||
ListActionControllerDelegate
|
||||
d ListActionControllerDelegate
|
||||
ctx *dataCollectionContext
|
||||
}
|
||||
|
||||
type dataCollectionContext struct {
|
||||
modelSvc service.ModelService
|
||||
resultSvc interfaces.ResultService
|
||||
}
|
||||
|
||||
func (ctx *dataCollectionContext) postIndexes(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
dc, err := ctx.modelSvc.GetDataCollectionById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range dc.Fields {
|
||||
if err := mongo.GetMongoCol(dc.Name).CreateIndex(mongo2.IndexModel{
|
||||
Keys: f.Key,
|
||||
}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
var _dataCollectionCtx *dataCollectionContext
|
||||
|
||||
func newDataCollectionContext() *dataCollectionContext {
|
||||
if _dataCollectionCtx != nil {
|
||||
return _dataCollectionCtx
|
||||
}
|
||||
|
||||
// context
|
||||
ctx := &dataCollectionContext{}
|
||||
|
||||
// dependency injection
|
||||
if err := container.GetContainer().Invoke(func(
|
||||
modelSvc service.ModelService,
|
||||
) {
|
||||
ctx.modelSvc = modelSvc
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_dataCollectionCtx = ctx
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func newDataCollectionController() *dataCollectionController {
|
||||
actions := getDataCollectionActions()
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListPostActionControllerDelegate(ControllerIdDataCollection, modelSvc.GetBaseService(interfaces.ModelIdDataCollection), actions)
|
||||
d := NewListPostActionControllerDelegate(ControllerIdDataCollection, modelSvc.GetBaseService(interfaces.ModelIdDataCollection), actions)
|
||||
ctx := newDataCollectionContext()
|
||||
|
||||
return &dataCollectionController{
|
||||
ListActionControllerDelegate: *ctr,
|
||||
d: *d,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
148
core/controllers/data_source.go
Normal file
148
core/controllers/data_source.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/ds"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
interfaces2 "github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var DataSourceController *dataSourceController
|
||||
|
||||
func getDataSourceActions() []Action {
|
||||
ctx := newDataSourceContext()
|
||||
return []Action{
|
||||
{
|
||||
Path: "/:id/change-password",
|
||||
Method: http.MethodPost,
|
||||
HandlerFunc: ctx.changePassword,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type dataSourceController struct {
|
||||
ListActionControllerDelegate
|
||||
d ListActionControllerDelegate
|
||||
ctx *dataSourceContext
|
||||
}
|
||||
|
||||
func (ctr *dataSourceController) Post(c *gin.Context) {
|
||||
// data source
|
||||
var _ds models.DataSource
|
||||
if err := c.ShouldBindJSON(&_ds); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// add data source to db
|
||||
if err := mongo.RunTransaction(func(ctx mongo2.SessionContext) error {
|
||||
if err := delegate.NewModelDelegate(&_ds).Add(); err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
pwd, err := utils.EncryptAES(_ds.Password)
|
||||
if err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
p := models.Password{Id: _ds.Id, Password: pwd}
|
||||
if err := delegate.NewModelDelegate(&p).Add(); err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check data source status
|
||||
go func() { _ = ctr.ctx.dsSvc.CheckStatus(_ds.Id) }()
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctr *dataSourceController) Put(c *gin.Context) {
|
||||
// data source
|
||||
var _ds models.DataSource
|
||||
if err := c.ShouldBindJSON(&_ds); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := delegate.NewModelDelegate(&_ds).Save(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check data source status
|
||||
go func() { _ = ctr.ctx.dsSvc.CheckStatus(_ds.Id) }()
|
||||
}
|
||||
|
||||
type dataSourceContext struct {
|
||||
dsSvc interfaces.DataSourceService
|
||||
}
|
||||
|
||||
var _dataSourceCtx *dataSourceContext
|
||||
|
||||
func newDataSourceContext() *dataSourceContext {
|
||||
if _dataSourceCtx != nil {
|
||||
return _dataSourceCtx
|
||||
}
|
||||
dsSvc, err := ds.GetDataSourceService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_dataSourceCtx = &dataSourceContext{
|
||||
dsSvc: dsSvc,
|
||||
}
|
||||
return _dataSourceCtx
|
||||
}
|
||||
|
||||
func (ctx *dataSourceContext) changePassword(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
var payload map[string]string
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
password, ok := payload["password"]
|
||||
if !ok {
|
||||
HandleErrorBadRequest(c, errors.ErrorDataSourceMissingRequiredFields)
|
||||
return
|
||||
}
|
||||
if err := ctx.dsSvc.ChangePassword(id, password); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func newDataSourceController() *dataSourceController {
|
||||
actions := getDataSourceActions()
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListPostActionControllerDelegate(ControllerIdDataSource, modelSvc.GetBaseService(interfaces2.ModelIdDataSource), actions)
|
||||
d := NewListPostActionControllerDelegate(ControllerIdDataSource, modelSvc.GetBaseService(interfaces2.ModelIdDataSource), actions)
|
||||
ctx := newDataSourceContext()
|
||||
|
||||
return &dataSourceController{
|
||||
ListActionControllerDelegate: *ctr,
|
||||
d: *d,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
17
core/controllers/delegate_action.go
Normal file
17
core/controllers/delegate_action.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package controllers
|
||||
|
||||
func NewActionControllerDelegate(id ControllerId, actions []Action) (d *ActionControllerDelegate) {
|
||||
return &ActionControllerDelegate{
|
||||
id: id,
|
||||
actions: actions,
|
||||
}
|
||||
}
|
||||
|
||||
type ActionControllerDelegate struct {
|
||||
id ControllerId
|
||||
actions []Action
|
||||
}
|
||||
|
||||
func (ctr *ActionControllerDelegate) Actions() (actions []Action) {
|
||||
return ctr.actions
|
||||
}
|
||||
99
core/controllers/delegate_basic.go
Normal file
99
core/controllers/delegate_basic.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
delegate2 "github.com/crawlab-team/crawlab/core/models/delegate"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func NewBasicControllerDelegate(id ControllerId, svc interfaces.ModelBaseService) (d *BasicControllerDelegate) {
|
||||
return &BasicControllerDelegate{
|
||||
id: id,
|
||||
svc: svc,
|
||||
}
|
||||
}
|
||||
|
||||
type BasicControllerDelegate struct {
|
||||
id ControllerId
|
||||
svc interfaces.ModelBaseService
|
||||
}
|
||||
|
||||
func (d *BasicControllerDelegate) Get(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
doc, err := d.svc.GetById(id)
|
||||
if err == mongo2.ErrNoDocuments {
|
||||
HandleErrorNotFound(c, err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, doc)
|
||||
}
|
||||
|
||||
func (d *BasicControllerDelegate) Post(c *gin.Context) {
|
||||
doc, err := NewJsonBinder(d.id).Bind(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := delegate2.NewModelDelegate(doc, GetUserFromContext(c)).Add(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, doc)
|
||||
}
|
||||
|
||||
func (d *BasicControllerDelegate) Put(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
doc, err := NewJsonBinder(d.id).Bind(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if doc.GetId() != id {
|
||||
HandleErrorBadRequest(c, errors.ErrorHttpBadRequest)
|
||||
return
|
||||
}
|
||||
_, err = d.svc.GetById(id)
|
||||
if err != nil {
|
||||
HandleErrorNotFound(c, err)
|
||||
return
|
||||
}
|
||||
if err := delegate2.NewModelDelegate(doc, GetUserFromContext(c)).Save(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, doc)
|
||||
}
|
||||
|
||||
func (d *BasicControllerDelegate) Delete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
doc, err := d.svc.GetById(oid)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if err := delegate2.NewModelDelegate(doc, GetUserFromContext(c)).Delete(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
222
core/controllers/delegate_list.go
Normal file
222
core/controllers/delegate_list.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/apex/log"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewListControllerDelegate(id ControllerId, svc interfaces.ModelBaseService) (d *ListControllerDelegate) {
|
||||
if svc == nil {
|
||||
panic(errors.ErrorControllerNoModelService)
|
||||
}
|
||||
|
||||
return &ListControllerDelegate{
|
||||
id: id,
|
||||
svc: svc,
|
||||
bc: NewBasicControllerDelegate(id, svc),
|
||||
}
|
||||
}
|
||||
|
||||
type ListControllerDelegate struct {
|
||||
id ControllerId
|
||||
svc interfaces.ModelBaseService
|
||||
bc BasicController
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) Get(c *gin.Context) {
|
||||
d.bc.Get(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) Post(c *gin.Context) {
|
||||
d.bc.Post(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) Put(c *gin.Context) {
|
||||
d.bc.Put(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) Delete(c *gin.Context) {
|
||||
d.bc.Delete(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) GetList(c *gin.Context) {
|
||||
// get all if query field "all" is set true
|
||||
all := MustGetFilterAll(c)
|
||||
if all {
|
||||
d.getAll(c)
|
||||
return
|
||||
}
|
||||
|
||||
// get list and total
|
||||
l, total, err := d.getList(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
HandleSuccessWithListData(c, l, total)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) PostList(c *gin.Context) {
|
||||
// bind
|
||||
docs, err := NewJsonBinder(d.id).BindList(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// success ids
|
||||
var ids []primitive.ObjectID
|
||||
|
||||
// reflect
|
||||
switch reflect.TypeOf(docs).Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
s := reflect.ValueOf(docs)
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
item := s.Index(i)
|
||||
if !item.CanAddr() {
|
||||
HandleErrorInternalServerError(c, errors.ErrorModelInvalidType)
|
||||
return
|
||||
}
|
||||
ptr := item.Addr()
|
||||
doc, ok := ptr.Interface().(interfaces.Model)
|
||||
if !ok {
|
||||
HandleErrorInternalServerError(c, errors.ErrorModelInvalidType)
|
||||
return
|
||||
}
|
||||
if err := delegate.NewModelDelegate(doc, GetUserFromContext(c)).Add(); err != nil {
|
||||
_ = trace.TraceError(err)
|
||||
continue
|
||||
}
|
||||
ids = append(ids, doc.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
items, err := utils.GetArrayItems(docs)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if len(ids) < len(items) {
|
||||
HandleErrorInternalServerError(c, errors.ErrorControllerAddError)
|
||||
return
|
||||
}
|
||||
|
||||
// success
|
||||
HandleSuccessWithData(c, docs)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) PutList(c *gin.Context) {
|
||||
payload, doc, err := NewJsonBinder(d.id).BindBatchRequestPayloadWithStringData(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// query
|
||||
query := bson.M{
|
||||
"_id": bson.M{
|
||||
"$in": payload.Ids,
|
||||
},
|
||||
}
|
||||
|
||||
// update
|
||||
if err := d.svc.UpdateDoc(query, doc, payload.Fields); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) DeleteList(c *gin.Context) {
|
||||
payload, err := NewJsonBinder(d.id).BindBatchRequestPayload(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := d.svc.DeleteList(bson.M{
|
||||
"_id": bson.M{
|
||||
"$in": payload.Ids,
|
||||
},
|
||||
}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) getAll(c *gin.Context) {
|
||||
// get list
|
||||
tic := time.Now()
|
||||
log.Debugf("getAll -> d.svc.GetMany:start")
|
||||
list, err := d.svc.GetList(nil, &mongo.FindOptions{
|
||||
Sort: bson.D{{"_id", -1}},
|
||||
})
|
||||
if err != nil {
|
||||
if err == mongo2.ErrNoDocuments {
|
||||
HandleErrorNotFound(c, err)
|
||||
} else {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Debugf("getAll -> d.svc.GetMany:end. elapsed: %d ms", time.Now().Sub(tic).Milliseconds())
|
||||
tic = time.Now()
|
||||
|
||||
// total count
|
||||
tic = time.Now()
|
||||
log.Debugf("getAll -> d.svc.Count:start")
|
||||
total, err := d.svc.Count(nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
log.Debugf("getAll -> d.svc.Count:end. elapsed: %d ms", time.Now().Sub(tic).Milliseconds())
|
||||
|
||||
// response
|
||||
HandleSuccessWithListData(c, list, total)
|
||||
}
|
||||
|
||||
func (d *ListControllerDelegate) getList(c *gin.Context) (l interfaces.List, total int, err error) {
|
||||
// params
|
||||
pagination := MustGetPagination(c)
|
||||
query := MustGetFilterQuery(c)
|
||||
sort := MustGetSortOption(c)
|
||||
|
||||
// get list
|
||||
l, err = d.svc.GetList(query, &mongo.FindOptions{
|
||||
Sort: sort,
|
||||
Skip: pagination.Size * (pagination.Page - 1),
|
||||
Limit: pagination.Size,
|
||||
})
|
||||
if err != nil {
|
||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
} else {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// total count
|
||||
total, err = d.svc.Count(query)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
return l, total, nil
|
||||
}
|
||||
17
core/controllers/delegate_list_action.go
Normal file
17
core/controllers/delegate_list_action.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
)
|
||||
|
||||
func NewListPostActionControllerDelegate(id ControllerId, svc interfaces.ModelBaseService, actions []Action) (d *ListActionControllerDelegate) {
|
||||
return &ListActionControllerDelegate{
|
||||
NewListControllerDelegate(id, svc),
|
||||
NewActionControllerDelegate(id, actions),
|
||||
}
|
||||
}
|
||||
|
||||
type ListActionControllerDelegate struct {
|
||||
ListController
|
||||
ActionController
|
||||
}
|
||||
73
core/controllers/demo.go
Normal file
73
core/controllers/demo.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func getDemoActions() []Action {
|
||||
ctx := newDemoContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/import",
|
||||
HandlerFunc: ctx.import_,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/reimport",
|
||||
HandlerFunc: ctx.reimport,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/cleanup",
|
||||
HandlerFunc: ctx.cleanup,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type demoContext struct {
|
||||
}
|
||||
|
||||
func (ctx *demoContext) import_(c *gin.Context) {
|
||||
if err := utils.ImportDemo(); err != nil {
|
||||
trace.PrintError(err)
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *demoContext) reimport(c *gin.Context) {
|
||||
if err := utils.ReimportDemo(); err != nil {
|
||||
trace.PrintError(err)
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *demoContext) cleanup(c *gin.Context) {
|
||||
if err := utils.ReimportDemo(); err != nil {
|
||||
trace.PrintError(err)
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
var _demoCtx *demoContext
|
||||
|
||||
func newDemoContext() *demoContext {
|
||||
if _demoCtx != nil {
|
||||
return _demoCtx
|
||||
}
|
||||
|
||||
_demoCtx = &demoContext{}
|
||||
|
||||
return _demoCtx
|
||||
}
|
||||
|
||||
var DemoController ActionController
|
||||
57
core/controllers/environment.go
Normal file
57
core/controllers/environment.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/container"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
)
|
||||
|
||||
var EnvironmentController *environmentController
|
||||
|
||||
var EnvironmentActions []Action
|
||||
|
||||
type environmentController struct {
|
||||
ListActionControllerDelegate
|
||||
d ListActionControllerDelegate
|
||||
ctx *environmentContext
|
||||
}
|
||||
|
||||
type environmentContext struct {
|
||||
modelSvc service.ModelService
|
||||
userSvc interfaces.UserService
|
||||
}
|
||||
|
||||
func newEnvironmentContext() *environmentContext {
|
||||
// context
|
||||
ctx := &environmentContext{}
|
||||
|
||||
// dependency injection
|
||||
if err := container.GetContainer().Invoke(func(
|
||||
modelSvc service.ModelService,
|
||||
userSvc interfaces.UserService,
|
||||
) {
|
||||
ctx.modelSvc = modelSvc
|
||||
ctx.userSvc = userSvc
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func newEnvironmentController() *environmentController {
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListPostActionControllerDelegate(ControllerIdEnvironment, modelSvc.GetBaseService(interfaces.ModelIdEnvironment), EnvironmentActions)
|
||||
d := NewListPostActionControllerDelegate(ControllerIdEnvironment, modelSvc.GetBaseService(interfaces.ModelIdEnvironment), EnvironmentActions)
|
||||
ctx := newEnvironmentContext()
|
||||
|
||||
return &environmentController{
|
||||
ListActionControllerDelegate: *ctr,
|
||||
d: *d,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
127
core/controllers/export.go
Normal file
127
core/controllers/export.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/export"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var ExportController ActionController
|
||||
|
||||
func getExportActions() []Action {
|
||||
ctx := newExportContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:type",
|
||||
HandlerFunc: ctx.postExport,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:type/:id",
|
||||
HandlerFunc: ctx.getExport,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:type/:id/download",
|
||||
HandlerFunc: ctx.getExportDownload,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type exportContext struct {
|
||||
csvSvc interfaces.ExportService
|
||||
jsonSvc interfaces.ExportService
|
||||
}
|
||||
|
||||
func (ctx *exportContext) postExport(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportTarget := c.Query("target")
|
||||
exportFilter, _ := GetFilter(c)
|
||||
|
||||
var exportId string
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exportId, err = ctx.csvSvc.Export(exportType, exportTarget, exportFilter)
|
||||
case constants.ExportTypeJson:
|
||||
exportId, err = ctx.jsonSvc.Export(exportType, exportTarget, exportFilter)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, exportId)
|
||||
}
|
||||
|
||||
func (ctx *exportContext) getExport(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportId := c.Param("id")
|
||||
|
||||
var exp interfaces.Export
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exp, err = ctx.csvSvc.GetExport(exportId)
|
||||
case constants.ExportTypeJson:
|
||||
exp, err = ctx.jsonSvc.GetExport(exportId)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, exp)
|
||||
}
|
||||
|
||||
func (ctx *exportContext) getExportDownload(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportId := c.Param("id")
|
||||
|
||||
var exp interfaces.Export
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exp, err = ctx.csvSvc.GetExport(exportId)
|
||||
case constants.ExportTypeJson:
|
||||
exp, err = ctx.jsonSvc.GetExport(exportId)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
c.Header("Content-Type", "text/csv")
|
||||
case constants.ExportTypeJson:
|
||||
c.Header("Content-Type", "text/plain")
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exp.GetDownloadPath()))
|
||||
c.File(exp.GetDownloadPath())
|
||||
}
|
||||
|
||||
func newExportContext() *exportContext {
|
||||
return &exportContext{
|
||||
csvSvc: export.GetCsvService(),
|
||||
jsonSvc: export.GetJsonService(),
|
||||
}
|
||||
}
|
||||
87
core/controllers/export_v2.go
Normal file
87
core/controllers/export_v2.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/export"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PostExport(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportTarget := c.Query("target")
|
||||
exportFilter, _ := GetFilter(c)
|
||||
|
||||
var exportId string
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exportId, err = export.GetCsvService().Export(exportType, exportTarget, exportFilter)
|
||||
case constants.ExportTypeJson:
|
||||
exportId, err = export.GetJsonService().Export(exportType, exportTarget, exportFilter)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, exportId)
|
||||
}
|
||||
|
||||
func GetExport(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportId := c.Param("id")
|
||||
|
||||
var exp interfaces.Export
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exp, err = export.GetCsvService().GetExport(exportId)
|
||||
case constants.ExportTypeJson:
|
||||
exp, err = export.GetJsonService().GetExport(exportId)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccessWithData(c, exp)
|
||||
}
|
||||
|
||||
func GetExportDownload(c *gin.Context) {
|
||||
exportType := c.Param("type")
|
||||
exportId := c.Param("id")
|
||||
|
||||
var exp interfaces.Export
|
||||
var err error
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
exp, err = export.GetCsvService().GetExport(exportId)
|
||||
case constants.ExportTypeJson:
|
||||
exp, err = export.GetJsonService().GetExport(exportId)
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch exportType {
|
||||
case constants.ExportTypeCsv:
|
||||
c.Header("Content-Type", "text/csv")
|
||||
case constants.ExportTypeJson:
|
||||
c.Header("Content-Type", "text/plain")
|
||||
default:
|
||||
HandleErrorBadRequest(c, errors.New(fmt.Sprintf("invalid export type: %s", exportType)))
|
||||
}
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", exp.GetDownloadPath()))
|
||||
c.File(exp.GetDownloadPath())
|
||||
}
|
||||
130
core/controllers/filer.go
Normal file
130
core/controllers/filer.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/imroc/req"
|
||||
"github.com/spf13/viper"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var FilerController ActionController
|
||||
|
||||
func getFilerActions() []Action {
|
||||
filerCtx := newFilerContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "*path",
|
||||
HandlerFunc: filerCtx.do,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "*path",
|
||||
HandlerFunc: filerCtx.do,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPut,
|
||||
Path: "*path",
|
||||
HandlerFunc: filerCtx.do,
|
||||
},
|
||||
{
|
||||
Method: http.MethodDelete,
|
||||
Path: "*path",
|
||||
HandlerFunc: filerCtx.do,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type filerContext struct {
|
||||
endpoint string
|
||||
}
|
||||
|
||||
func (ctx *filerContext) do(c *gin.Context) {
|
||||
// request path
|
||||
requestPath := strings.Replace(c.Request.URL.Path, "/filer", "", 1)
|
||||
|
||||
// request url
|
||||
requestUrl := fmt.Sprintf("%s%s", ctx.endpoint, requestPath)
|
||||
if c.Request.URL.RawQuery != "" {
|
||||
requestUrl += "?" + c.Request.URL.RawQuery
|
||||
}
|
||||
|
||||
// request body
|
||||
bufR := bufio.NewScanner(c.Request.Body)
|
||||
requestBody := req.BodyJSON(bufR.Bytes())
|
||||
|
||||
// request file uploads
|
||||
var requestFileUploads []req.FileUpload
|
||||
form, err := c.MultipartForm()
|
||||
if err == nil {
|
||||
for k, v := range form.File {
|
||||
for _, fh := range v {
|
||||
f, err := fh.Open()
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
requestFileUploads = append(requestFileUploads, req.FileUpload{
|
||||
FileName: fh.Filename,
|
||||
FieldName: k,
|
||||
File: f,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// request header
|
||||
requestHeader := req.Header{}
|
||||
for k, v := range c.Request.Header {
|
||||
if len(v) > 0 {
|
||||
requestHeader[k] = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
// perform request
|
||||
res, err := req.Do(c.Request.Method, requestUrl, requestHeader, requestBody, requestFileUploads)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// status code check
|
||||
statusCode := res.Response().StatusCode
|
||||
if statusCode == http.StatusNotFound {
|
||||
HandleErrorNotFoundNoPrint(c, errors.ErrorControllerFilerNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
for k, v := range res.Response().Header {
|
||||
if len(v) > 0 {
|
||||
c.Header(k, v[0])
|
||||
}
|
||||
}
|
||||
_, _ = c.Writer.Write(res.Bytes())
|
||||
c.AbortWithStatus(statusCode)
|
||||
}
|
||||
|
||||
var _filerCtx *filerContext
|
||||
|
||||
func newFilerContext() *filerContext {
|
||||
if _filerCtx != nil {
|
||||
return _filerCtx
|
||||
}
|
||||
|
||||
ctx := &filerContext{
|
||||
endpoint: "http://localhost:8888",
|
||||
}
|
||||
|
||||
if viper.GetString("fs.filer.proxy") != "" {
|
||||
ctx.endpoint = viper.GetString("fs.filer.proxy")
|
||||
}
|
||||
|
||||
_filerCtx = ctx
|
||||
|
||||
return ctx
|
||||
}
|
||||
100
core/controllers/filter.go
Normal file
100
core/controllers/filter.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/entity"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var FilterController ActionController
|
||||
|
||||
func getFilterActions() []Action {
|
||||
ctx := newFilterContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col",
|
||||
HandlerFunc: ctx.getColFieldOptions,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col/:value",
|
||||
HandlerFunc: ctx.getColFieldOptions,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col/:value/:label",
|
||||
HandlerFunc: ctx.getColFieldOptions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type filterContext struct {
|
||||
}
|
||||
|
||||
func (ctx *filterContext) getColFieldOptions(c *gin.Context) {
|
||||
colName := c.Param("col")
|
||||
value := c.Param("value")
|
||||
if value == "" {
|
||||
value = "_id"
|
||||
}
|
||||
label := c.Param("label")
|
||||
if label == "" {
|
||||
label = "name"
|
||||
}
|
||||
query := MustGetFilterQuery(c)
|
||||
pipelines := mongo2.Pipeline{}
|
||||
if query != nil {
|
||||
pipelines = append(pipelines, bson.D{{"$match", query}})
|
||||
}
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$group",
|
||||
bson.M{
|
||||
"_id": bson.M{
|
||||
"value": "$" + value,
|
||||
"label": "$" + label,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$project",
|
||||
bson.M{
|
||||
"value": "$_id.value",
|
||||
"label": bson.M{"$toString": "$_id.label"},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$sort",
|
||||
bson.D{
|
||||
{"value", 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
var options []entity.FilterSelectOption
|
||||
if err := mongo.GetMongoCol(colName).Aggregate(pipelines, nil).All(&options); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, options)
|
||||
}
|
||||
|
||||
func newFilterContext() *filterContext {
|
||||
return &filterContext{}
|
||||
}
|
||||
69
core/controllers/filter_v2.go
Normal file
69
core/controllers/filter_v2.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/entity"
|
||||
"github.com/crawlab-team/crawlab/db/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func GetFilterColFieldOptions(c *gin.Context) {
|
||||
colName := c.Param("col")
|
||||
value := c.Param("value")
|
||||
if value == "" {
|
||||
value = "_id"
|
||||
}
|
||||
label := c.Param("label")
|
||||
if label == "" {
|
||||
label = "name"
|
||||
}
|
||||
query := MustGetFilterQuery(c)
|
||||
pipelines := mongo2.Pipeline{}
|
||||
if query != nil {
|
||||
pipelines = append(pipelines, bson.D{{"$match", query}})
|
||||
}
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$group",
|
||||
bson.M{
|
||||
"_id": bson.M{
|
||||
"value": "$" + value,
|
||||
"label": "$" + label,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$project",
|
||||
bson.M{
|
||||
"value": "$_id.value",
|
||||
"label": bson.M{"$toString": "$_id.label"},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
pipelines = append(
|
||||
pipelines,
|
||||
bson.D{
|
||||
{
|
||||
"$sort",
|
||||
bson.D{
|
||||
{"value", 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
var options []entity.FilterSelectOption
|
||||
if err := mongo.GetMongoCol(colName).Aggregate(pipelines, nil).All(&options); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, options)
|
||||
}
|
||||
3
core/controllers/git.go
Normal file
3
core/controllers/git.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package controllers
|
||||
|
||||
var GitController ListController
|
||||
16
core/controllers/http.go
Normal file
16
core/controllers/http.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package controllers
|
||||
|
||||
type Response[T any] struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Data T `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type ListResponse[T any] struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Total int `json:"total"`
|
||||
Data []T `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
42
core/controllers/init.go
Normal file
42
core/controllers/init.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
)
|
||||
|
||||
func InitControllers() (err error) {
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
NodeController = newNodeController()
|
||||
ProjectController = newProjectController()
|
||||
SpiderController = newSpiderController()
|
||||
TaskController = newTaskController()
|
||||
UserController = newUserController()
|
||||
TagController = NewListControllerDelegate(ControllerIdTag, modelSvc.GetBaseService(interfaces.ModelIdTag))
|
||||
SettingController = newSettingController()
|
||||
LoginController = NewActionControllerDelegate(ControllerIdLogin, getLoginActions())
|
||||
DataCollectionController = newDataCollectionController()
|
||||
ResultController = NewActionControllerDelegate(ControllerIdResult, getResultActions())
|
||||
ScheduleController = newScheduleController()
|
||||
StatsController = NewActionControllerDelegate(ControllerIdStats, getStatsActions())
|
||||
TokenController = newTokenController()
|
||||
FilerController = NewActionControllerDelegate(ControllerIdFiler, getFilerActions())
|
||||
GitController = NewListControllerDelegate(ControllerIdGit, modelSvc.GetBaseService(interfaces.ModelIdGit))
|
||||
VersionController = NewActionControllerDelegate(ControllerIdVersion, getVersionActions())
|
||||
SystemInfoController = NewActionControllerDelegate(ControllerIdSystemInfo, getSystemInfoActions())
|
||||
DemoController = NewActionControllerDelegate(ControllerIdDemo, getDemoActions())
|
||||
RoleController = NewListControllerDelegate(ControllerIdRole, modelSvc.GetBaseService(interfaces.ModelIdRole))
|
||||
PermissionController = NewListControllerDelegate(ControllerIdPermission, modelSvc.GetBaseService(interfaces.ModelIdPermission))
|
||||
ExportController = NewActionControllerDelegate(ControllerIdExport, getExportActions())
|
||||
NotificationController = NewActionControllerDelegate(ControllerIdNotification, getNotificationActions())
|
||||
FilterController = NewActionControllerDelegate(ControllerIdFilter, getFilterActions())
|
||||
SyncController = NewActionControllerDelegate(ControllerIdSync, getSyncActions())
|
||||
DataSourceController = newDataSourceController()
|
||||
EnvironmentController = newEnvironmentController()
|
||||
|
||||
return nil
|
||||
}
|
||||
64
core/controllers/login.go
Normal file
64
core/controllers/login.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/container"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var LoginController ActionController
|
||||
|
||||
func getLoginActions() []Action {
|
||||
loginCtx := newLoginContext()
|
||||
return []Action{
|
||||
{Method: http.MethodPost, Path: "/login", HandlerFunc: loginCtx.login},
|
||||
{Method: http.MethodPost, Path: "/logout", HandlerFunc: loginCtx.logout},
|
||||
}
|
||||
}
|
||||
|
||||
type loginContext struct {
|
||||
userSvc interfaces.UserService
|
||||
}
|
||||
|
||||
func (ctx *loginContext) login(c *gin.Context) {
|
||||
var u models.User
|
||||
if err := c.ShouldBindJSON(&u); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
token, loggedInUser, err := ctx.userSvc.Login(&interfaces.UserLoginOptions{
|
||||
Username: u.Username,
|
||||
Password: u.Password,
|
||||
})
|
||||
if err != nil {
|
||||
HandleErrorUnauthorized(c, errors.ErrorUserUnauthorized)
|
||||
return
|
||||
}
|
||||
c.Set(constants.UserContextKey, loggedInUser)
|
||||
HandleSuccessWithData(c, token)
|
||||
}
|
||||
|
||||
func (ctx *loginContext) logout(c *gin.Context) {
|
||||
c.Set(constants.UserContextKey, nil)
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func newLoginContext() *loginContext {
|
||||
// context
|
||||
ctx := &loginContext{}
|
||||
|
||||
// dependency injection
|
||||
if err := container.GetContainer().Invoke(func(
|
||||
userSvc interfaces.UserService,
|
||||
) {
|
||||
ctx.userSvc = userSvc
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
36
core/controllers/login_v2.go
Normal file
36
core/controllers/login_v2.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/user"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PostLogin(c *gin.Context) {
|
||||
var payload struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&payload); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
userSvc, err := user.GetUserServiceV2()
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
token, loggedInUser, err := userSvc.Login(payload.Username, payload.Password)
|
||||
if err != nil {
|
||||
HandleErrorUnauthorized(c, errors.ErrorUserUnauthorized)
|
||||
return
|
||||
}
|
||||
c.Set(constants.UserContextKey, loggedInUser)
|
||||
HandleSuccessWithData(c, token)
|
||||
}
|
||||
|
||||
func PostLogout(c *gin.Context) {
|
||||
c.Set(constants.UserContextKey, nil)
|
||||
HandleSuccess(c)
|
||||
}
|
||||
94
core/controllers/node.go
Normal file
94
core/controllers/node.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/constants"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
var NodeController *nodeController
|
||||
|
||||
type nodeController struct {
|
||||
ListControllerDelegate
|
||||
}
|
||||
|
||||
func (ctr *nodeController) Post(c *gin.Context) {
|
||||
var n models.Node
|
||||
if err := c.ShouldBindJSON(&n); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctr._post(c, &n); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctr *nodeController) PostList(c *gin.Context) {
|
||||
// bind
|
||||
var docs []models.Node
|
||||
if err := c.ShouldBindJSON(&docs); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// success ids
|
||||
var ids []primitive.ObjectID
|
||||
|
||||
// iterate nodes
|
||||
for _, n := range docs {
|
||||
if err := ctr._post(c, &n); err != nil {
|
||||
trace.PrintError(err)
|
||||
continue
|
||||
}
|
||||
ids = append(ids, n.Id)
|
||||
}
|
||||
|
||||
// success
|
||||
HandleSuccessWithData(c, docs)
|
||||
}
|
||||
|
||||
func (ctr *nodeController) _post(c *gin.Context, n *models.Node) (err error) {
|
||||
// set default key
|
||||
if n.Key == "" {
|
||||
id, err := uuid.NewUUID()
|
||||
if err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
n.Key = id.String()
|
||||
}
|
||||
|
||||
// set default status
|
||||
if n.Status == "" {
|
||||
n.Status = constants.NodeStatusUnregistered
|
||||
}
|
||||
|
||||
// add
|
||||
if err := delegate.NewModelDelegate(n, GetUserFromContext(c)).Add(); err != nil {
|
||||
return trace.TraceError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNodeController() *nodeController {
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListControllerDelegate(ControllerIdNode, modelSvc.GetBaseService(interfaces.ModelIdNode))
|
||||
|
||||
return &nodeController{
|
||||
ListControllerDelegate: *ctr,
|
||||
}
|
||||
}
|
||||
158
core/controllers/notification.go
Normal file
158
core/controllers/notification.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/notification"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var NotificationController ActionController
|
||||
|
||||
func getNotificationActions() []Action {
|
||||
ctx := newNotificationContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/settings",
|
||||
HandlerFunc: ctx.GetSettingList,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/settings/:id",
|
||||
HandlerFunc: ctx.GetSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/settings",
|
||||
HandlerFunc: ctx.PostSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPut,
|
||||
Path: "/settings/:id",
|
||||
HandlerFunc: ctx.PutSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/settings/:id",
|
||||
HandlerFunc: ctx.DeleteSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/settings/:id/enable",
|
||||
HandlerFunc: ctx.EnableSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/settings/:id/disable",
|
||||
HandlerFunc: ctx.DisableSetting,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type notificationContext struct {
|
||||
svc *notification.Service
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) GetSettingList(c *gin.Context) {
|
||||
query := MustGetFilterQuery(c)
|
||||
pagination := MustGetPagination(c)
|
||||
sort := MustGetSortOption(c)
|
||||
res, total, err := ctx.svc.GetSettingList(query, pagination, sort)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithListData(c, res, total)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) GetSetting(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := ctx.svc.GetSetting(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) PostSetting(c *gin.Context) {
|
||||
var s notification.NotificationSetting
|
||||
if err := c.ShouldBindJSON(&s); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctx.svc.PosSetting(&s); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) PutSetting(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
var s notification.NotificationSetting
|
||||
if err := c.ShouldBindJSON(&s); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctx.svc.PutSetting(id, s); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) DeleteSetting(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctx.svc.DeleteSetting(id); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) EnableSetting(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctx.svc.EnableSetting(id); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *notificationContext) DisableSetting(c *gin.Context) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctx.svc.DisableSetting(id); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func newNotificationContext() *notificationContext {
|
||||
ctx := ¬ificationContext{
|
||||
svc: notification.GetService(),
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
3
core/controllers/permission.go
Normal file
3
core/controllers/permission.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package controllers
|
||||
|
||||
var PermissionController ListController
|
||||
103
core/controllers/project.go
Normal file
103
core/controllers/project.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
var ProjectController *projectController
|
||||
|
||||
type projectController struct {
|
||||
ListControllerDelegate
|
||||
}
|
||||
|
||||
func (ctr *projectController) GetList(c *gin.Context) {
|
||||
// get all if query field "all" is set true
|
||||
all := MustGetFilterAll(c)
|
||||
if all {
|
||||
ctr.getAll(c)
|
||||
return
|
||||
}
|
||||
|
||||
// get list
|
||||
list, total, err := ctr.getList(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data := list.GetModels()
|
||||
|
||||
// check empty list
|
||||
if len(list.GetModels()) == 0 {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// project ids
|
||||
var ids []primitive.ObjectID
|
||||
|
||||
// count cache
|
||||
cache := map[primitive.ObjectID]int{}
|
||||
|
||||
// iterate
|
||||
for _, d := range data {
|
||||
p, ok := d.(*models.Project)
|
||||
if !ok {
|
||||
HandleErrorInternalServerError(c, errors.ErrorControllerInvalidType)
|
||||
return
|
||||
}
|
||||
ids = append(ids, p.Id)
|
||||
cache[p.Id] = 0
|
||||
}
|
||||
|
||||
// spiders
|
||||
modelSvc, err := service.NewService()
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
spiders, err := modelSvc.GetSpiderList(bson.M{
|
||||
"project_id": bson.M{
|
||||
"$in": ids,
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
for _, s := range spiders {
|
||||
_, ok := cache[s.ProjectId]
|
||||
if !ok {
|
||||
HandleErrorInternalServerError(c, errors.ErrorControllerMissingInCache)
|
||||
return
|
||||
}
|
||||
cache[s.ProjectId]++
|
||||
}
|
||||
|
||||
// assign
|
||||
var projects []models.Project
|
||||
for _, d := range data {
|
||||
p := d.(*models.Project)
|
||||
p.Spiders = cache[p.Id]
|
||||
projects = append(projects, *p)
|
||||
}
|
||||
|
||||
HandleSuccessWithListData(c, projects, total)
|
||||
}
|
||||
|
||||
func newProjectController() *projectController {
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListControllerDelegate(ControllerIdProject, modelSvc.GetBaseService(interfaces.ModelIdProject))
|
||||
|
||||
return &projectController{
|
||||
ListControllerDelegate: *ctr,
|
||||
}
|
||||
}
|
||||
150
core/controllers/result.go
Normal file
150
core/controllers/result.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/core/result"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/db/generic"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var ResultController ActionController
|
||||
|
||||
func getResultActions() []Action {
|
||||
var resultCtx = newResultContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
HandlerFunc: resultCtx.getList,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type resultContext struct {
|
||||
modelSvc service.ModelService
|
||||
}
|
||||
|
||||
func (ctx *resultContext) getList(c *gin.Context) {
|
||||
// data collection id
|
||||
dcId, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// data source id
|
||||
var dsId primitive.ObjectID
|
||||
dsIdStr := c.Query("data_source_id")
|
||||
if dsIdStr != "" {
|
||||
dsId, err = primitive.ObjectIDFromHex(dsIdStr)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// data collection
|
||||
dc, err := ctx.modelSvc.GetDataCollectionById(dcId)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// data source
|
||||
ds, err := ctx.modelSvc.GetDataSourceById(dsId)
|
||||
if err != nil {
|
||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
||||
ds = &models.DataSource{}
|
||||
} else {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// spider
|
||||
sq := bson.M{
|
||||
"col_id": dc.Id,
|
||||
"data_source_id": ds.Id,
|
||||
}
|
||||
s, err := ctx.modelSvc.GetSpider(sq, nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// service
|
||||
svc, err := result.GetResultService(s.Id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// params
|
||||
pagination := MustGetPagination(c)
|
||||
query := ctx.getListQuery(c)
|
||||
|
||||
// get results
|
||||
data, err := svc.List(query, &generic.ListOptions{
|
||||
Sort: []generic.ListSort{{"_id", generic.SortDirectionDesc}},
|
||||
Skip: pagination.Size * (pagination.Page - 1),
|
||||
Limit: pagination.Size,
|
||||
})
|
||||
if err != nil {
|
||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
return
|
||||
}
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// validate results
|
||||
if len(data) == 0 {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// total count
|
||||
total, err := svc.Count(query)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
HandleSuccessWithListData(c, data, total)
|
||||
}
|
||||
|
||||
func (ctx *resultContext) getListQuery(c *gin.Context) (q generic.ListQuery) {
|
||||
f, err := GetFilter(c)
|
||||
if err != nil {
|
||||
return q
|
||||
}
|
||||
for _, cond := range f.Conditions {
|
||||
q = append(q, generic.ListQueryCondition{
|
||||
Key: cond.Key,
|
||||
Op: cond.Op,
|
||||
Value: utils.NormalizeObjectId(cond.Value),
|
||||
})
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func newResultContext() *resultContext {
|
||||
// context
|
||||
ctx := &resultContext{}
|
||||
|
||||
var err error
|
||||
ctx.modelSvc, err = service.NewService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
119
core/controllers/result_v2.go
Normal file
119
core/controllers/result_v2.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/crawlab-team/crawlab/core/result"
|
||||
"github.com/crawlab-team/crawlab/core/utils"
|
||||
"github.com/crawlab-team/crawlab/db/generic"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
mongo2 "go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func GetResultList(c *gin.Context) {
|
||||
// data collection id
|
||||
dcId, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// data source id
|
||||
var dsId primitive.ObjectID
|
||||
dsIdStr := c.Query("data_source_id")
|
||||
if dsIdStr != "" {
|
||||
dsId, err = primitive.ObjectIDFromHex(dsIdStr)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// data collection
|
||||
dc, err := service.NewModelServiceV2[models.DataCollectionV2]().GetById(dcId)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// data source
|
||||
ds, err := service.NewModelServiceV2[models.DataSourceV2]().GetById(dsId)
|
||||
if err != nil {
|
||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
||||
ds = &models.DataSourceV2{}
|
||||
} else {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// spider
|
||||
sq := bson.M{
|
||||
"col_id": dc.Id,
|
||||
"data_source_id": ds.Id,
|
||||
}
|
||||
s, err := service.NewModelServiceV2[models.SpiderV2]().GetOne(sq, nil)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// service
|
||||
svc, err := result.GetResultService(s.Id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// params
|
||||
pagination := MustGetPagination(c)
|
||||
query := getResultListQuery(c)
|
||||
|
||||
// get results
|
||||
data, err := svc.List(query, &generic.ListOptions{
|
||||
Sort: []generic.ListSort{{"_id", generic.SortDirectionDesc}},
|
||||
Skip: pagination.Size * (pagination.Page - 1),
|
||||
Limit: pagination.Size,
|
||||
})
|
||||
if err != nil {
|
||||
if err.Error() == mongo2.ErrNoDocuments.Error() {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
return
|
||||
}
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// validate results
|
||||
if len(data) == 0 {
|
||||
HandleSuccessWithListData(c, nil, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// total count
|
||||
total, err := svc.Count(query)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// response
|
||||
HandleSuccessWithListData(c, data, total)
|
||||
}
|
||||
|
||||
func getResultListQuery(c *gin.Context) (q generic.ListQuery) {
|
||||
f, err := GetFilter(c)
|
||||
if err != nil {
|
||||
return q
|
||||
}
|
||||
for _, cond := range f.Conditions {
|
||||
q = append(q, generic.ListQueryCondition{
|
||||
Key: cond.Key,
|
||||
Op: cond.Op,
|
||||
Value: utils.NormalizeObjectId(cond.Value),
|
||||
})
|
||||
}
|
||||
return q
|
||||
}
|
||||
3
core/controllers/role.go
Normal file
3
core/controllers/role.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package controllers
|
||||
|
||||
var RoleController ListController
|
||||
387
core/controllers/router_v2.go
Normal file
387
core/controllers/router_v2.go
Normal file
@@ -0,0 +1,387 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/middlewares"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type RouterGroups struct {
|
||||
AuthGroup *gin.RouterGroup
|
||||
AnonymousGroup *gin.RouterGroup
|
||||
}
|
||||
|
||||
func NewRouterGroups(app *gin.Engine) (groups *RouterGroups) {
|
||||
return &RouterGroups{
|
||||
AuthGroup: app.Group("/", middlewares.AuthorizationMiddlewareV2()),
|
||||
AnonymousGroup: app.Group("/"),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterController[T any](group *gin.RouterGroup, basePath string, ctr *BaseControllerV2[T]) {
|
||||
actionPaths := make(map[string]bool)
|
||||
for _, action := range ctr.actions {
|
||||
group.Handle(action.Method, basePath+action.Path, action.HandlerFunc)
|
||||
path := basePath + action.Path
|
||||
key := action.Method + " - " + path
|
||||
actionPaths[key] = true
|
||||
}
|
||||
registerBuiltinHandler(group, http.MethodGet, basePath+"", ctr.GetList, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodGet, basePath+"/:id", ctr.GetById, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodPost, basePath+"", ctr.Post, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodPut, basePath+"/:id", ctr.PutById, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodPatch, basePath+"", ctr.PatchList, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodDelete, basePath+"/:id", ctr.DeleteById, actionPaths)
|
||||
registerBuiltinHandler(group, http.MethodDelete, basePath+"", ctr.DeleteList, actionPaths)
|
||||
}
|
||||
|
||||
func RegisterActions(group *gin.RouterGroup, basePath string, actions []Action) {
|
||||
for _, action := range actions {
|
||||
group.Handle(action.Method, basePath+action.Path, action.HandlerFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func registerBuiltinHandler(group *gin.RouterGroup, method, path string, handlerFunc gin.HandlerFunc, existingActionPaths map[string]bool) {
|
||||
key := method + " - " + path
|
||||
_, ok := existingActionPaths[key]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
group.Handle(method, path, handlerFunc)
|
||||
}
|
||||
|
||||
func InitRoutes(app *gin.Engine) (err error) {
|
||||
// routes groups
|
||||
groups := NewRouterGroups(app)
|
||||
|
||||
RegisterController(groups.AuthGroup, "/data/collections", NewControllerV2[models.DataCollectionV2]())
|
||||
RegisterController(groups.AuthGroup, "/data-sources", NewControllerV2[models.DataSourceV2]())
|
||||
RegisterController(groups.AuthGroup, "/environments", NewControllerV2[models.EnvironmentV2]())
|
||||
RegisterController(groups.AuthGroup, "/gits", NewControllerV2[models.GitV2]())
|
||||
RegisterController(groups.AuthGroup, "/nodes", NewControllerV2[models.NodeV2]())
|
||||
RegisterController(groups.AuthGroup, "/notifications/settings", NewControllerV2[models.SettingV2]())
|
||||
RegisterController(groups.AuthGroup, "/permissions", NewControllerV2[models.PermissionV2]())
|
||||
RegisterController(groups.AuthGroup, "/projects", NewControllerV2[models.ProjectV2]())
|
||||
RegisterController(groups.AuthGroup, "/roles", NewControllerV2[models.RoleV2]())
|
||||
RegisterController(groups.AuthGroup, "/schedules", NewControllerV2[models.ScheduleV2](
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "",
|
||||
HandlerFunc: PostSchedule,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPut,
|
||||
Path: "/:id",
|
||||
HandlerFunc: PutScheduleById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/enable",
|
||||
HandlerFunc: PostScheduleEnable,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/disable",
|
||||
HandlerFunc: PostScheduleDisable,
|
||||
},
|
||||
))
|
||||
RegisterController(groups.AuthGroup, "/spiders", NewControllerV2[models.SpiderV2](
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
HandlerFunc: GetSpiderById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "",
|
||||
HandlerFunc: GetSpiderList,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "",
|
||||
HandlerFunc: PostSpider,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPut,
|
||||
Path: "/:id",
|
||||
HandlerFunc: PutSpiderById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/:id",
|
||||
HandlerFunc: DeleteSpiderById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodDelete,
|
||||
Path: "",
|
||||
HandlerFunc: DeleteSpiderList,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/files/list",
|
||||
HandlerFunc: GetSpiderListDir,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/files/get",
|
||||
HandlerFunc: GetSpiderFile,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/files/info",
|
||||
HandlerFunc: GetSpiderFileInfo,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/save",
|
||||
HandlerFunc: PostSpiderSaveFile,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/save/batch",
|
||||
HandlerFunc: PostSpiderSaveFiles,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/save/dir",
|
||||
HandlerFunc: PostSpiderSaveDir,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/rename",
|
||||
HandlerFunc: PostSpiderRenameFile,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/:id/files",
|
||||
HandlerFunc: DeleteSpiderFile,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/copy",
|
||||
HandlerFunc: PostSpiderCopyFile,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/files/export",
|
||||
HandlerFunc: PostSpiderExport,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/run",
|
||||
HandlerFunc: PostSpiderRun,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/git",
|
||||
HandlerFunc: GetSpiderGit,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/git/remote-refs",
|
||||
HandlerFunc: GetSpiderGitRemoteRefs,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/git/checkout",
|
||||
HandlerFunc: PostSpiderGitCheckout,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/git/pull",
|
||||
HandlerFunc: PostSpiderGitPull,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/git/commit",
|
||||
HandlerFunc: PostSpiderGitCommit,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/data-source",
|
||||
HandlerFunc: GetSpiderDataSource,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/data-source/:ds_id",
|
||||
HandlerFunc: PostSpiderDataSource,
|
||||
},
|
||||
))
|
||||
RegisterController(groups.AuthGroup, "/tasks", NewControllerV2[models.TaskV2](
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
HandlerFunc: GetTaskById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "",
|
||||
HandlerFunc: GetTaskList,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/:id",
|
||||
HandlerFunc: DeleteTaskById,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodDelete,
|
||||
Path: "",
|
||||
HandlerFunc: DeleteList,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/run",
|
||||
HandlerFunc: PostTaskRun,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/restart",
|
||||
HandlerFunc: PostTaskRestart,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/cancel",
|
||||
HandlerFunc: PostTaskCancel,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/logs",
|
||||
HandlerFunc: GetTaskLogs,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id/data",
|
||||
HandlerFunc: GetTaskData,
|
||||
},
|
||||
))
|
||||
RegisterController(groups.AuthGroup, "/tokens", NewControllerV2[models.TokenV2](
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "",
|
||||
HandlerFunc: PostToken,
|
||||
},
|
||||
))
|
||||
RegisterController(groups.AuthGroup, "/users", NewControllerV2[models.UserV2](
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "",
|
||||
HandlerFunc: PostUser,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/change-password",
|
||||
HandlerFunc: PostUserChangePassword,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodGet,
|
||||
Path: "/me",
|
||||
HandlerFunc: GetUserMe,
|
||||
},
|
||||
Action{
|
||||
Method: http.MethodPut,
|
||||
Path: "/me",
|
||||
HandlerFunc: PutUserById,
|
||||
},
|
||||
))
|
||||
|
||||
RegisterActions(groups.AuthGroup, "/results", []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
HandlerFunc: GetResultList,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AuthGroup, "/export", []Action{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:type",
|
||||
HandlerFunc: PostExport,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:type/:id",
|
||||
HandlerFunc: GetExport,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:type/:id/download",
|
||||
HandlerFunc: GetExportDownload,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AuthGroup, "/filters", []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col",
|
||||
HandlerFunc: GetFilterColFieldOptions,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col/:value",
|
||||
HandlerFunc: GetFilterColFieldOptions,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:col/:value/:label",
|
||||
HandlerFunc: GetFilterColFieldOptions,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AuthGroup, "/settings", []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:id",
|
||||
HandlerFunc: GetSetting,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPut,
|
||||
Path: "/:id",
|
||||
HandlerFunc: PutSetting,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AuthGroup, "/stats", []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/overview",
|
||||
HandlerFunc: GetStatsOverview,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/daily",
|
||||
HandlerFunc: GetStatsDaily,
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/tasks",
|
||||
HandlerFunc: GetStatsTasks,
|
||||
},
|
||||
})
|
||||
|
||||
RegisterActions(groups.AnonymousGroup, "/system-info", []Action{
|
||||
{
|
||||
Path: "",
|
||||
Method: http.MethodGet,
|
||||
HandlerFunc: GetSystemInfo,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AnonymousGroup, "/version", []Action{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "",
|
||||
HandlerFunc: GetVersion,
|
||||
},
|
||||
})
|
||||
RegisterActions(groups.AnonymousGroup, "/", []Action{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/login",
|
||||
HandlerFunc: PostLogin,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/logout",
|
||||
HandlerFunc: PostLogout,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
91
core/controllers/router_v2_test.go
Normal file
91
core/controllers/router_v2_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package controllers_test
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/controllers"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRouterGroups(t *testing.T) {
|
||||
router := gin.Default()
|
||||
groups := controllers.NewRouterGroups(router)
|
||||
|
||||
assertions := []struct {
|
||||
group *gin.RouterGroup
|
||||
name string
|
||||
}{
|
||||
{groups.AuthGroup, "AuthGroup"},
|
||||
{groups.AnonymousGroup, "AnonymousGroup"},
|
||||
}
|
||||
|
||||
for _, a := range assertions {
|
||||
assert.NotNil(t, a.group, a.name+" should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterController_Routes(t *testing.T) {
|
||||
router := gin.Default()
|
||||
groups := controllers.NewRouterGroups(router)
|
||||
ctr := controllers.NewControllerV2[models.TestModel]()
|
||||
basePath := "/testmodels"
|
||||
|
||||
controllers.RegisterController(groups.AuthGroup, basePath, ctr)
|
||||
|
||||
// Check if all routes are registered
|
||||
routes := router.Routes()
|
||||
|
||||
var methodPaths []string
|
||||
for _, route := range routes {
|
||||
methodPaths = append(methodPaths, route.Method+" - "+route.Path)
|
||||
}
|
||||
|
||||
expectedRoutes := []gin.RouteInfo{
|
||||
{Method: "GET", Path: basePath},
|
||||
{Method: "GET", Path: basePath + "/:id"},
|
||||
{Method: "POST", Path: basePath},
|
||||
{Method: "PUT", Path: basePath + "/:id"},
|
||||
{Method: "PATCH", Path: basePath},
|
||||
{Method: "DELETE", Path: basePath + "/:id"},
|
||||
{Method: "DELETE", Path: basePath},
|
||||
}
|
||||
|
||||
assert.Equal(t, len(expectedRoutes), len(routes))
|
||||
for _, route := range expectedRoutes {
|
||||
assert.Contains(t, methodPaths, route.Method+" - "+route.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitRoutes_ProjectsRoute(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
controllers.InitRoutes(router)
|
||||
|
||||
// Check if the projects route is registered
|
||||
routes := router.Routes()
|
||||
|
||||
var methodPaths []string
|
||||
for _, route := range routes {
|
||||
methodPaths = append(methodPaths, route.Method+" - "+route.Path)
|
||||
}
|
||||
|
||||
expectedRoutes := []gin.RouteInfo{
|
||||
{Method: "GET", Path: "/projects"},
|
||||
{Method: "GET", Path: "/projects/:id"},
|
||||
{Method: "POST", Path: "/projects"},
|
||||
{Method: "PUT", Path: "/projects/:id"},
|
||||
{Method: "PATCH", Path: "/projects"},
|
||||
{Method: "DELETE", Path: "/projects/:id"},
|
||||
{Method: "DELETE", Path: "/projects"},
|
||||
}
|
||||
|
||||
for _, route := range expectedRoutes {
|
||||
assert.Contains(t, methodPaths, route.Method+" - "+route.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
m.Run()
|
||||
}
|
||||
221
core/controllers/schedule.go
Normal file
221
core/controllers/schedule.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/crawlab-team/crawlab/core/container"
|
||||
"github.com/crawlab-team/crawlab/core/errors"
|
||||
"github.com/crawlab-team/crawlab/core/interfaces"
|
||||
"github.com/crawlab-team/crawlab/core/models/delegate"
|
||||
"github.com/crawlab-team/crawlab/core/models/models"
|
||||
"github.com/crawlab-team/crawlab/core/models/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var ScheduleController *scheduleController
|
||||
|
||||
func getScheduleActions() []Action {
|
||||
scheduleCtx := newScheduleContext()
|
||||
return []Action{
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/enable",
|
||||
HandlerFunc: scheduleCtx.enable,
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/:id/disable",
|
||||
HandlerFunc: scheduleCtx.disable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type scheduleController struct {
|
||||
ListActionControllerDelegate
|
||||
d ListActionControllerDelegate
|
||||
ctx *scheduleContext
|
||||
}
|
||||
|
||||
func (ctr *scheduleController) Post(c *gin.Context) {
|
||||
var s models.Schedule
|
||||
if err := c.ShouldBindJSON(&s); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if err := delegate.NewModelDelegate(&s, GetUserFromContext(c)).Add(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if s.Enabled {
|
||||
if err := ctr.ctx.scheduleSvc.Enable(&s, GetUserFromContext(c)); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
HandleSuccessWithData(c, s)
|
||||
}
|
||||
|
||||
func (ctr *scheduleController) Put(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
var s models.Schedule
|
||||
if err := c.ShouldBindJSON(&s); err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
if s.GetId() != oid {
|
||||
HandleErrorBadRequest(c, errors.ErrorHttpBadRequest)
|
||||
return
|
||||
}
|
||||
if err := delegate.NewModelDelegate(&s).Save(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if s.Enabled {
|
||||
if err := ctr.ctx.scheduleSvc.Disable(&s, GetUserFromContext(c)); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctr.ctx.scheduleSvc.Enable(&s, GetUserFromContext(c)); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
HandleSuccessWithData(c, s)
|
||||
}
|
||||
|
||||
func (ctr *scheduleController) Delete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
oid, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
s, err := ctr.ctx.modelSvc.GetScheduleById(oid)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctr.ctx.scheduleSvc.Disable(s); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if err := delegate.NewModelDelegate(s, GetUserFromContext(c)).Delete(); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (ctr *scheduleController) DeleteList(c *gin.Context) {
|
||||
payload, err := NewJsonBinder(interfaces.ModelIdSchedule).BindBatchRequestPayload(c)
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
for _, id := range payload.Ids {
|
||||
s, err := ctr.ctx.modelSvc.GetScheduleById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
if err := ctr.ctx.scheduleSvc.Disable(s); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := ctr.ctx.modelSvc.GetBaseService(interfaces.ModelIdSchedule).DeleteList(bson.M{
|
||||
"_id": bson.M{
|
||||
"$in": payload.Ids,
|
||||
},
|
||||
}); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *scheduleContext) enable(c *gin.Context) {
|
||||
s, err := ctx._getSchedule(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := ctx.scheduleSvc.Enable(s, GetUserFromContext(c)); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *scheduleContext) disable(c *gin.Context) {
|
||||
s, err := ctx._getSchedule(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := ctx.scheduleSvc.Disable(s, GetUserFromContext(c)); err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
HandleSuccess(c)
|
||||
}
|
||||
|
||||
func (ctx *scheduleContext) _getSchedule(c *gin.Context) (s *models.Schedule, err error) {
|
||||
id, err := primitive.ObjectIDFromHex(c.Param("id"))
|
||||
if err != nil {
|
||||
HandleErrorBadRequest(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
s, err = ctx.modelSvc.GetScheduleById(id)
|
||||
if err != nil {
|
||||
HandleErrorInternalServerError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type scheduleContext struct {
|
||||
modelSvc service.ModelService
|
||||
scheduleSvc interfaces.ScheduleService
|
||||
}
|
||||
|
||||
func newScheduleContext() *scheduleContext {
|
||||
// context
|
||||
ctx := &scheduleContext{}
|
||||
|
||||
// dependency injection
|
||||
if err := container.GetContainer().Invoke(func(
|
||||
modelSvc service.ModelService,
|
||||
scheduleSvc interfaces.ScheduleService,
|
||||
) {
|
||||
ctx.modelSvc = modelSvc
|
||||
ctx.scheduleSvc = scheduleSvc
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func newScheduleController() *scheduleController {
|
||||
actions := getScheduleActions()
|
||||
modelSvc, err := service.GetService()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctr := NewListPostActionControllerDelegate(ControllerIdSchedule, modelSvc.GetBaseService(interfaces.ModelIdSchedule), actions)
|
||||
d := NewListPostActionControllerDelegate(ControllerIdSchedule, modelSvc.GetBaseService(interfaces.ModelIdSchedule), actions)
|
||||
ctx := newScheduleContext()
|
||||
|
||||
return &scheduleController{
|
||||
ListActionControllerDelegate: *ctr,
|
||||
d: *d,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user