commit
9fc1f83003
@ -0,0 +1,46 @@ |
|||||||
|
root = "." |
||||||
|
testdata_dir = "testdata" |
||||||
|
tmp_dir = "tmp" |
||||||
|
|
||||||
|
[build] |
||||||
|
args_bin = [] |
||||||
|
bin = "tmp\\main.exe" |
||||||
|
cmd = "go build -o ./tmp/main.exe ." |
||||||
|
delay = 1000 |
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"] |
||||||
|
exclude_file = [] |
||||||
|
exclude_regex = ["_test.go", ".html"] |
||||||
|
exclude_unchanged = false |
||||||
|
follow_symlink = false |
||||||
|
full_bin = "" |
||||||
|
include_dir = [] |
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"] |
||||||
|
include_file = [] |
||||||
|
kill_delay = "0s" |
||||||
|
log = "build-errors.log" |
||||||
|
poll = false |
||||||
|
poll_interval = 0 |
||||||
|
post_cmd = [] |
||||||
|
pre_cmd = [] |
||||||
|
rerun = false |
||||||
|
rerun_delay = 500 |
||||||
|
send_interrupt = false |
||||||
|
stop_on_error = false |
||||||
|
|
||||||
|
[color] |
||||||
|
app = "" |
||||||
|
build = "yellow" |
||||||
|
main = "magenta" |
||||||
|
runner = "green" |
||||||
|
watcher = "cyan" |
||||||
|
|
||||||
|
[log] |
||||||
|
main_only = false |
||||||
|
time = false |
||||||
|
|
||||||
|
[misc] |
||||||
|
clean_on_exit = false |
||||||
|
|
||||||
|
[screen] |
||||||
|
clear_on_rebuild = false |
||||||
|
keep_scroll = true |
@ -0,0 +1,2 @@ |
|||||||
|
/tmp |
||||||
|
*.db |
@ -0,0 +1,8 @@ |
|||||||
|
# 默认忽略的文件 |
||||||
|
/shelf/ |
||||||
|
/workspace.xml |
||||||
|
# 基于编辑器的 HTTP 客户端请求 |
||||||
|
/httpRequests/ |
||||||
|
# Datasource local storage ignored files |
||||||
|
/dataSources/ |
||||||
|
/dataSources.local.xml |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> |
||||||
|
<data-source source="LOCAL" name="devops" uuid="92895df0-7d2d-44e4-b71f-0ff2923933d6"> |
||||||
|
<driver-ref>sqlite.xerial</driver-ref> |
||||||
|
<synchronize>true</synchronize> |
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver> |
||||||
|
<jdbc-url>jdbc:sqlite:C:\Users\hupeh\Desktop\projects\devops\devops.db</jdbc-url> |
||||||
|
<working-dir>$ProjectFileDir$</working-dir> |
||||||
|
</data-source> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,9 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<module type="WEB_MODULE" version="4"> |
||||||
|
<component name="Go" enabled="true" /> |
||||||
|
<component name="NewModuleRootManager"> |
||||||
|
<content url="file://$MODULE_DIR$" /> |
||||||
|
<orderEntry type="inheritedJdk" /> |
||||||
|
<orderEntry type="sourceFolder" forTests="false" /> |
||||||
|
</component> |
||||||
|
</module> |
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="ProjectModuleManager"> |
||||||
|
<modules> |
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/devops.iml" filepath="$PROJECT_DIR$/.idea/devops.iml" /> |
||||||
|
</modules> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="VcsDirectoryMappings"> |
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,10 @@ |
|||||||
|
#! /bin/sh |
||||||
|
|
||||||
|
kill -9 $(pgrep webserver) |
||||||
|
mkdir -p ./www |
||||||
|
cd ./www |
||||||
|
echo "cd ./www" |
||||||
|
git clone https://github.com/zestack/slim.git |
||||||
|
cd ./slim |
||||||
|
go mod tidy |
||||||
|
./webserver & |
@ -0,0 +1,28 @@ |
|||||||
|
name: Deploy |
||||||
|
|
||||||
|
vcs: |
||||||
|
branch: main |
||||||
|
|
||||||
|
steps: |
||||||
|
- name: Clone repository |
||||||
|
uses: actions/checkout@v3 |
||||||
|
|
||||||
|
- name: Install Deno |
||||||
|
uses: denoland/setup-deno@v1 |
||||||
|
with: |
||||||
|
deno-version: v1.x |
||||||
|
|
||||||
|
- name: Install Node.js |
||||||
|
uses: actions/setup-node@v3 |
||||||
|
with: |
||||||
|
node-version: lts/* |
||||||
|
|
||||||
|
- name: Build step |
||||||
|
run: "npm install && npm run build" |
||||||
|
|
||||||
|
- name: Upload to Deno Deploy |
||||||
|
uses: denoland/deployctl@v1 |
||||||
|
with: |
||||||
|
project: "zestack" |
||||||
|
entrypoint: "https://raw.githubusercontent.com/zestack/zestack/main/server.ts" |
||||||
|
root: "build" |
@ -0,0 +1,32 @@ |
|||||||
|
module devops |
||||||
|
|
||||||
|
go 1.23 |
||||||
|
|
||||||
|
toolchain go1.23.0 |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/gorilla/sessions v1.4.0 |
||||||
|
github.com/gorilla/websocket v1.5.3 |
||||||
|
github.com/wader/gormstore/v2 v2.0.3 |
||||||
|
gopkg.in/yaml.v3 v3.0.1 |
||||||
|
gorm.io/driver/sqlite v1.5.6 |
||||||
|
gorm.io/gorm v1.25.11 |
||||||
|
zestack.dev/misc v0.0.0-20240815120320-73293b130b9f |
||||||
|
zestack.dev/slim v0.0.0-20240815120229-098d3294fc1a |
||||||
|
) |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/gorilla/securecookie v1.1.2 // indirect |
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect |
||||||
|
github.com/jinzhu/now v1.1.5 // indirect |
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect |
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect |
||||||
|
github.com/rs/xid v1.6.0 // indirect |
||||||
|
golang.org/x/crypto v0.23.0 // indirect |
||||||
|
golang.org/x/net v0.21.0 // indirect |
||||||
|
golang.org/x/sync v0.6.0 // indirect |
||||||
|
golang.org/x/sys v0.20.0 // indirect |
||||||
|
golang.org/x/text v0.15.0 // indirect |
||||||
|
zestack.dev/color v0.0.0-20240522040239-8edfb0bd027f // indirect |
||||||
|
zestack.dev/log v0.0.0-20240523001421-24d58305cd03 // indirect |
||||||
|
) |
@ -0,0 +1,236 @@ |
|||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= |
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= |
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= |
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= |
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= |
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= |
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= |
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= |
||||||
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= |
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= |
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= |
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= |
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= |
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= |
||||||
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= |
||||||
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= |
||||||
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= |
||||||
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= |
||||||
|
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= |
||||||
|
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= |
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= |
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||||
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= |
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= |
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= |
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= |
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= |
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= |
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= |
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= |
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= |
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= |
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= |
||||||
|
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= |
||||||
|
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= |
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= |
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= |
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= |
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= |
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= |
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= |
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= |
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= |
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= |
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= |
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= |
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= |
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= |
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= |
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= |
||||||
|
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= |
||||||
|
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= |
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= |
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= |
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= |
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= |
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= |
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= |
||||||
|
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= |
||||||
|
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= |
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= |
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= |
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= |
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= |
||||||
|
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= |
||||||
|
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= |
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= |
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= |
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= |
||||||
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= |
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= |
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= |
||||||
|
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= |
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= |
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= |
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= |
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= |
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= |
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= |
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= |
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= |
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= |
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= |
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= |
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= |
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= |
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= |
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= |
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= |
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= |
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= |
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= |
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= |
||||||
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= |
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= |
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= |
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= |
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= |
||||||
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= |
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= |
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= |
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= |
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= |
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= |
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= |
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= |
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= |
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= |
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= |
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= |
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= |
||||||
|
github.com/wader/gormstore/v2 v2.0.3 h1:/29GWPauY8xZkpLnB8hsp+dZfP3ivA9fiDw1YVNTp6U= |
||||||
|
github.com/wader/gormstore/v2 v2.0.3/go.mod h1:sr3N3a8F1+PBc3fHoKaphFqDXLRJ9Oe6Yow0HxKFbbg= |
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= |
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= |
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= |
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= |
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= |
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= |
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= |
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= |
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= |
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= |
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= |
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= |
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= |
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= |
||||||
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= |
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= |
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= |
||||||
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= |
||||||
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= |
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= |
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= |
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= |
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
||||||
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= |
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= |
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||||
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= |
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= |
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= |
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= |
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= |
||||||
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= |
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= |
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= |
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= |
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= |
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= |
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= |
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gorm.io/driver/mysql v1.4.0 h1:P+gpa0QGyNma39khn1vZMS/eXEJxTwHz4Q26NR4C8fw= |
||||||
|
gorm.io/driver/mysql v1.4.0/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= |
||||||
|
gorm.io/driver/postgres v1.4.1 h1:DutsKq2LK2Ag65q/+VygWth0/L4GAVOp+sCtg6WzZjs= |
||||||
|
gorm.io/driver/postgres v1.4.1/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw= |
||||||
|
gorm.io/driver/sqlite v1.4.1/go.mod h1:AKZZCAoFfOWHF7Nd685Iq8Uywc0i9sWJlzpoE/INzsw= |
||||||
|
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= |
||||||
|
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= |
||||||
|
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= |
||||||
|
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= |
||||||
|
gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= |
||||||
|
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= |
||||||
|
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= |
||||||
|
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= |
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= |
||||||
|
zestack.dev/color v0.0.0-20240522040239-8edfb0bd027f h1:5tSc5qVy2f6vyG5LVRETRe8tAFeZKxcjFsl0Jy7rRUQ= |
||||||
|
zestack.dev/color v0.0.0-20240522040239-8edfb0bd027f/go.mod h1:lHvP85VRaqzm4E/9Q9kW/WmEnEHL4PQ9Fls5xDah3zI= |
||||||
|
zestack.dev/log v0.0.0-20240523001421-24d58305cd03 h1:ZNnBVHIiV4R2SBypa24SLuhkrc9unlvPdXg/u0ZqSro= |
||||||
|
zestack.dev/log v0.0.0-20240523001421-24d58305cd03/go.mod h1:1ZoaLtIYHxx/J7jFoQpNLxsCIcpBZ1NQWFjafTEBzN8= |
||||||
|
zestack.dev/misc v0.0.0-20240815120320-73293b130b9f h1:77RL7xP7l0kV8s12EDo/JcQDbYuuwfbBO3JhYu9V8MI= |
||||||
|
zestack.dev/misc v0.0.0-20240815120320-73293b130b9f/go.mod h1:dfX3xtBz37zBEsTnO1u/XPpXeq7ZFZHDXqoz9vllfDw= |
||||||
|
zestack.dev/slim v0.0.0-20240815120229-098d3294fc1a h1:f7KGs9bZSZyZRiFZ+C9YbEYk/AvgsDmo8CzWlqAmarE= |
||||||
|
zestack.dev/slim v0.0.0-20240815120229-098d3294fc1a/go.mod h1:QhpKfYJGCC13b11uqWOjxgEGLJUUwLyJ6XPsGBLcjC0= |
@ -0,0 +1,145 @@ |
|||||||
|
<!doctype html> |
||||||
|
<html lang="zh-Hans"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<meta name="viewport" |
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> |
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge"> |
||||||
|
<title>Document</title> |
||||||
|
<style> |
||||||
|
:root { |
||||||
|
--color-primary: 139 69 19; |
||||||
|
} |
||||||
|
|
||||||
|
html, |
||||||
|
body { |
||||||
|
background: #f7f7f7; |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
* { |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
form { |
||||||
|
width: 360px; |
||||||
|
margin: 150px auto 24px; |
||||||
|
padding: 48px 48px 56px; |
||||||
|
border-radius: 12px; |
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgb(var(--color-primary)); |
||||||
|
border: none; |
||||||
|
} |
||||||
|
|
||||||
|
form:focus-within { |
||||||
|
/*box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2), inset 0 0 0 2px rgb(var(--color-primary));*/ |
||||||
|
} |
||||||
|
|
||||||
|
hr { |
||||||
|
margin-bottom: 36px; |
||||||
|
opacity: 0.3; |
||||||
|
} |
||||||
|
|
||||||
|
fieldset { |
||||||
|
padding: 0; |
||||||
|
border: 0; |
||||||
|
} |
||||||
|
|
||||||
|
legend { |
||||||
|
padding: 0; |
||||||
|
font-size: 140%; |
||||||
|
} |
||||||
|
|
||||||
|
section:not(:last-of-type) { |
||||||
|
margin-bottom: 24px; |
||||||
|
} |
||||||
|
|
||||||
|
input, |
||||||
|
button { |
||||||
|
appearance: none; |
||||||
|
width: 100%; |
||||||
|
padding-inline: 12px; |
||||||
|
border-radius: 6px; |
||||||
|
height: 40px; |
||||||
|
border-width: 1px; |
||||||
|
border-style: solid; |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
margin-top: 8px; |
||||||
|
} |
||||||
|
|
||||||
|
input:focus { |
||||||
|
border-color: rgb(var(--color-primary)); |
||||||
|
box-shadow: 0 0 0 3px rgb(var(--color-primary) / 50%); |
||||||
|
} |
||||||
|
|
||||||
|
button:focus { |
||||||
|
border-color: rgb(var(--color-primary)); |
||||||
|
background-color: rgb(var(--color-primary)); |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
.error { |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
left: 50%; |
||||||
|
transform: translateX(-50%); |
||||||
|
color: #fff; |
||||||
|
background: red; |
||||||
|
border: 1px solid #f00; |
||||||
|
text-align: center; |
||||||
|
padding: 6px 12px; |
||||||
|
border-radius: 0 0 6px 6px; |
||||||
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); |
||||||
|
} |
||||||
|
</style> |
||||||
|
<script> |
||||||
|
var legend = null |
||||||
|
var timer = null |
||||||
|
|
||||||
|
function showError(message) { |
||||||
|
if (timer) { |
||||||
|
clearTimeout(timer) |
||||||
|
} |
||||||
|
if (legend) { |
||||||
|
legend.innerText = message |
||||||
|
} else { |
||||||
|
legend = document.createElement("div") |
||||||
|
legend.className = "error" |
||||||
|
legend.innerText = message |
||||||
|
document.body.appendChild(legend) |
||||||
|
} |
||||||
|
timer = setTimeout(() => { |
||||||
|
timer = null |
||||||
|
legend.remove() |
||||||
|
}, 5000); |
||||||
|
} |
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", function () { |
||||||
|
var params = new URLSearchParams(location.search) |
||||||
|
var error = params.get("error") |
||||||
|
error && showError(error) |
||||||
|
}, false) |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<form method="post"> |
||||||
|
<fieldset> |
||||||
|
<legend>登录</legend> |
||||||
|
<hr/> |
||||||
|
<section> |
||||||
|
<label for="username">账号</label> |
||||||
|
<input name="username" id="username" placeholder="请输入账号"> |
||||||
|
</section> |
||||||
|
<section> |
||||||
|
<label for="password">登录密码</label> |
||||||
|
<input name="password" type="password" id="password" placeholder="登录密码"> |
||||||
|
</section> |
||||||
|
<section> |
||||||
|
<label style="opacity: 0; color: transparent">登录</label> |
||||||
|
<button type="submit">确定</button> |
||||||
|
</section> |
||||||
|
</fieldset> |
||||||
|
</form> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,568 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"database/sql" |
||||||
|
"database/sql/driver" |
||||||
|
"encoding/gob" |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"gopkg.in/yaml.v3" |
||||||
|
"html/template" |
||||||
|
"io" |
||||||
|
"log" |
||||||
|
"net/http" |
||||||
|
"os" |
||||||
|
"os/exec" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/gorilla/sessions" |
||||||
|
"github.com/gorilla/websocket" |
||||||
|
"github.com/wader/gormstore/v2" |
||||||
|
"gorm.io/driver/sqlite" |
||||||
|
"gorm.io/gorm" |
||||||
|
"zestack.dev/misc" |
||||||
|
"zestack.dev/slim" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// Time allowed to write a message to the peer.
|
||||||
|
writeWait = 10 * time.Second |
||||||
|
|
||||||
|
// Time allowed to read the next pong message from the peer.
|
||||||
|
pongWait = 60 * time.Second |
||||||
|
|
||||||
|
// Send pings to peer with this period. Must be less than pongWait.
|
||||||
|
pingPeriod = (pongWait * 9) / 10 |
||||||
|
|
||||||
|
// Maximum message size allowed from peer.
|
||||||
|
maxMessageSize = 512 |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
newline = []byte{'\n'} |
||||||
|
space = []byte{' '} |
||||||
|
|
||||||
|
upgrader *websocket.Upgrader |
||||||
|
clients *sync.Map |
||||||
|
broadcast chan []byte |
||||||
|
|
||||||
|
store sessions.Store |
||||||
|
db *gorm.DB |
||||||
|
) |
||||||
|
|
||||||
|
type NullString sql.NullString |
||||||
|
|
||||||
|
// Scan implements the [Scanner] interface.
|
||||||
|
func (ns *NullString) Scan(value any) error { |
||||||
|
ss := new(sql.NullString) |
||||||
|
err := ss.Scan(value) |
||||||
|
ns.String = ss.String |
||||||
|
ns.Valid = ss.Valid |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Value implements the [driver.Valuer] interface.
|
||||||
|
func (ns NullString) Value() (driver.Value, error) { |
||||||
|
if !ns.Valid { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
return ns.String, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (ns NullString) MarshalJSON() ([]byte, error) { |
||||||
|
if ns.Valid { |
||||||
|
return json.Marshal(ns.String) |
||||||
|
} |
||||||
|
return json.Marshal(nil) |
||||||
|
} |
||||||
|
|
||||||
|
func (ns *NullString) UnmarshalJSON(b []byte) error { |
||||||
|
if string(b) == "null" { |
||||||
|
ns.Valid = false |
||||||
|
return nil |
||||||
|
} |
||||||
|
err := json.Unmarshal(b, &ns.String) |
||||||
|
if err == nil { |
||||||
|
ns.Valid = true |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
type NullTime sql.NullTime |
||||||
|
|
||||||
|
func (nt *NullTime) Scan(value any) error { |
||||||
|
st := new(sql.NullTime) |
||||||
|
err := st.Scan(value) |
||||||
|
nt.Time = st.Time |
||||||
|
nt.Valid = st.Valid |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func (nt NullTime) Value() (driver.Value, error) { |
||||||
|
if !nt.Valid { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
return nt.Time, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (nt NullTime) MarshalJSON() ([]byte, error) { |
||||||
|
if nt.Valid { |
||||||
|
return nt.Time.MarshalJSON() |
||||||
|
} |
||||||
|
return json.Marshal(nil) |
||||||
|
} |
||||||
|
|
||||||
|
func (nt *NullTime) UnmarshalJSON(b []byte) error { |
||||||
|
if string(b) == "null" { |
||||||
|
nt.Valid = false |
||||||
|
return nil |
||||||
|
} |
||||||
|
err := json.Unmarshal(b, &nt.Time) |
||||||
|
if err == nil { |
||||||
|
nt.Valid = true |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
type NullUint struct { |
||||||
|
V uint |
||||||
|
Valid bool |
||||||
|
} |
||||||
|
|
||||||
|
func (nu *NullUint) Scan(value any) error { |
||||||
|
sn := new(sql.Null[uint]) |
||||||
|
err := sn.Scan(value) |
||||||
|
nu.V = sn.V |
||||||
|
nu.Valid = sn.Valid |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
func (nu NullUint) Value() (driver.Value, error) { |
||||||
|
if !nu.Valid { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
// 注意:driver.Value 不支持 uint
|
||||||
|
return int64(nu.V), nil |
||||||
|
} |
||||||
|
|
||||||
|
func (nu NullUint) MarshalJSON() ([]byte, error) { |
||||||
|
if nu.Valid { |
||||||
|
return json.Marshal(nu.V) |
||||||
|
} |
||||||
|
return json.Marshal(nil) |
||||||
|
} |
||||||
|
|
||||||
|
func (nu *NullUint) UnmarshalJSON(b []byte) error { |
||||||
|
if string(b) == "null" { |
||||||
|
nu.Valid = false |
||||||
|
return nil |
||||||
|
} |
||||||
|
err := json.Unmarshal(b, &nu.V) |
||||||
|
if err == nil { |
||||||
|
nu.Valid = true |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// User 用户
|
||||||
|
type User struct { |
||||||
|
ID uint `json:"id" gorm:"primaryKey"` |
||||||
|
Username string `json:"username" gorm:"unique"` |
||||||
|
RawPassword string `json:"-" gorm:"-"` // 原始密码
|
||||||
|
Password string `json:"password"` |
||||||
|
Disabled bool `json:"disabled" gorm:"default:false"` |
||||||
|
CreatedAt time.Time `json:"createdAt"` |
||||||
|
UpdatedAt time.Time `json:"updatedAt"` |
||||||
|
} |
||||||
|
|
||||||
|
func (u *User) BeforeCreate(_ *gorm.DB) error { |
||||||
|
if u.RawPassword != "" { |
||||||
|
hash, err := misc.PasswordHash(u.RawPassword) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
u.Password = hash |
||||||
|
return nil |
||||||
|
} |
||||||
|
return errors.New("缺少密码") |
||||||
|
} |
||||||
|
|
||||||
|
// Project 项目
|
||||||
|
type Project struct { |
||||||
|
// 项目编号,推荐使用 ObjectId 算法生成,也可以使用 UUID/NanoID 等。
|
||||||
|
ID string `json:"id" gorm:"primaryKey"` |
||||||
|
// 项目名称
|
||||||
|
Name string `json:"name" gorm:"unique"` |
||||||
|
// 项目介绍
|
||||||
|
Intro string `json:"intro"` |
||||||
|
// 项目网站
|
||||||
|
Website string `json:"website"` |
||||||
|
// 项目的仓库地址,比如 GitHub、Gitee、Gitlab、Gitea 或自建的仓库,
|
||||||
|
// 如果是私有仓库,应该携带授权信息(建议使用私有令牌而不是账号密码)。
|
||||||
|
RepositoryURL string `json:"repositoryURL"` |
||||||
|
// 用于构建的仓库分支,若置空则使用主分支
|
||||||
|
Branch string `json:"branch"` |
||||||
|
// 创建项目的用户编号
|
||||||
|
CreatedBy uint `json:"createdBy"` |
||||||
|
// 创建项目时的时间
|
||||||
|
CreatedAt time.Time `json:"createdAt"` |
||||||
|
// 上一次修改项目信息的用户编号
|
||||||
|
UpdatedBy NullUint `json:"updatedBy"` |
||||||
|
// 上一次修改项目信息的时间
|
||||||
|
UpdatedAt NullTime `json:"updatedAt" gorm:"autoUpdateTime:false"` |
||||||
|
} |
||||||
|
|
||||||
|
// BuildStatus 构建状态
|
||||||
|
type BuildStatus string |
||||||
|
|
||||||
|
const ( |
||||||
|
BuildRunning BuildStatus = "running" // 正在运行
|
||||||
|
BuildSuccess BuildStatus = "success" // 构建成功
|
||||||
|
BuildFailure BuildStatus = "failure" // 构建失败
|
||||||
|
BuildAborted BuildStatus = "aborted" // 构建中断
|
||||||
|
) |
||||||
|
|
||||||
|
// Build 项目构建
|
||||||
|
type Build struct { |
||||||
|
// 构建编号
|
||||||
|
ID string `json:"id" gorm:"primaryKey"` |
||||||
|
// 构建项目
|
||||||
|
ProjectID string `json:"projectId"` |
||||||
|
// 关联项目,如果项目删除也删除
|
||||||
|
Project *Project `json:"project,omitempty" gorm:"foreignKey:ProjectID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` |
||||||
|
// 构建时,项目最后一次提交的 commit 标识
|
||||||
|
CommitId string `json:"commitId"` |
||||||
|
// 构建时,项目最后一次提交的 commit 信息
|
||||||
|
CommitText string `json:"commitText"` |
||||||
|
// 非构建步骤失败时的错误信息
|
||||||
|
Error NullString `json:"startedError"` |
||||||
|
// 启动构建程序的用户的编号,如果是通过 webhook 修改,会被置空
|
||||||
|
StartedBy NullUint `json:"startedBy"` |
||||||
|
// 启动构建项目时的时间
|
||||||
|
StartedAt time.Time `json:"startedAt"` |
||||||
|
// 手动停止或自动完成该项目构建时的时间
|
||||||
|
StoppedAt NullTime `json:"stoppedAt"` |
||||||
|
// 手动停止该项目构建的用户的编号,自动完成会将该值置空。
|
||||||
|
StoppedBy NullUint `json:"stoppedBy"` |
||||||
|
// 当前所处状态
|
||||||
|
Status BuildStatus `json:"status"` |
||||||
|
|
||||||
|
// 步骤日志
|
||||||
|
Logs []BuildLog `json:"logs"` |
||||||
|
|
||||||
|
// 创建构建步骤的用户编号,如果是通过 webhook 修改,会被置空
|
||||||
|
CreatedBy NullUint `json:"createdBy"` |
||||||
|
// 创建构建步骤时的时间
|
||||||
|
CreatedAt time.Time `json:"createdAt"` |
||||||
|
// 上一次修改构建步骤信息的用户编号,如果是通过 webhook 修改,会被置空
|
||||||
|
UpdatedBy NullUint `json:"updatedBy"` |
||||||
|
// 上一次修改构建步骤信息的时间
|
||||||
|
UpdatedAt NullTime `json:"updatedAt" gorm:"autoUpdateTime:false"` |
||||||
|
} |
||||||
|
|
||||||
|
// BuildLog 构建步骤日志
|
||||||
|
type BuildLog struct { |
||||||
|
// 日志编号
|
||||||
|
Id uint `json:"id" gorm:"primaryKey"` |
||||||
|
// 归属的构建编号
|
||||||
|
BuildID string `json:"buildId"` |
||||||
|
// 关联项目,如果项目删除也删除
|
||||||
|
Build *Build `json:"build,omitempty" gorm:"foreignKey:BuildID;references:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` |
||||||
|
// 构建步骤名称
|
||||||
|
Name string `json:"name"` |
||||||
|
// 该构建步骤使用的程序
|
||||||
|
Uses string `json:"uses"` |
||||||
|
// 该构建步骤时传递给程序的参数
|
||||||
|
With map[string]string `json:"with" gorm:"serializer:json"` |
||||||
|
// 程序的运行日志
|
||||||
|
Logs []string `json:"logs" gorm:"serializer:json"` |
||||||
|
// 当前所处状态
|
||||||
|
Status BuildStatus `json:"status"` |
||||||
|
// 开始该步骤时的时间
|
||||||
|
StartedAt time.Time `json:"startedAt"` |
||||||
|
// 结束该步骤时的时间
|
||||||
|
StoppedAt time.Time `json:"stoppedAt"` |
||||||
|
} |
||||||
|
|
||||||
|
type BuildConfig struct { |
||||||
|
Name string `json:"name" yaml:"name"` |
||||||
|
VCS VCSConfig `json:"vcs" yaml:"vcs"` |
||||||
|
Steps []StepConfig `json:"steps" yaml:"steps"` |
||||||
|
} |
||||||
|
|
||||||
|
type VCSConfig struct { |
||||||
|
Branch string `json:"branch" yaml:"branch"` |
||||||
|
} |
||||||
|
|
||||||
|
type StepConfig struct { |
||||||
|
Name string `json:"name" yaml:"name"` |
||||||
|
Uses string `json:"uses" yaml:"uses"` |
||||||
|
With map[string]string `json:"with" yaml:"with"` |
||||||
|
} |
||||||
|
|
||||||
|
func Fail(c slim.Context, msg string, code ...int) error { |
||||||
|
statusCode := http.StatusBadRequest |
||||||
|
if len(code) > 0 { |
||||||
|
statusCode = code[0] |
||||||
|
} |
||||||
|
return c.JSON(statusCode, slim.Map{ |
||||||
|
"ok": false, |
||||||
|
"msg": msg, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func AuthSession(c slim.Context, next slim.HandlerFunc) error { |
||||||
|
sess, err := store.Get(c.Request(), "session-key") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
user, ok := sess.Values["user"].(*User) |
||||||
|
if !ok { |
||||||
|
return Fail(c, "请登录", http.StatusUnauthorized) |
||||||
|
} |
||||||
|
c.Set("user", user) |
||||||
|
return next(c) |
||||||
|
} |
||||||
|
|
||||||
|
func handleLogin(c slim.Context) error { |
||||||
|
var req struct { |
||||||
|
Username string `json:"username" form:"username"` |
||||||
|
Password string `json:"password" form:"password"` |
||||||
|
} |
||||||
|
if err := c.Bind(&req); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if req.Username == "" { |
||||||
|
return c.Redirect(301, "/login?error=用户名不能为空") |
||||||
|
} |
||||||
|
if req.Password == "" { |
||||||
|
return c.Redirect(301, "/login?error=登录密码不能为空") |
||||||
|
} |
||||||
|
var user User |
||||||
|
err := db.Model(&User{}).Where("username", req.Username).First(&user).Error |
||||||
|
if err != nil { |
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) { |
||||||
|
return c.Redirect(301, "/login?error=用户名或密码错误") |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
if !misc.PasswordVerify(req.Password, user.Password) { |
||||||
|
return c.Redirect(301, "/login?error=用户名或密码错误") |
||||||
|
} |
||||||
|
sess, err := store.Get(c.Request(), "session-key") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
sess.Values["user"] = &user |
||||||
|
err = sess.Save(c.Request(), c.Response()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return c.Redirect(http.StatusMovedPermanently, "/") |
||||||
|
} |
||||||
|
|
||||||
|
func handleLogout(c slim.Context) error { |
||||||
|
// Get a session. We're ignoring the error resulted from decoding an
|
||||||
|
// existing session: Get() always returns a session, even if empty.
|
||||||
|
session, _ := store.Get(c.Request(), "session-name") |
||||||
|
// Set some session values.
|
||||||
|
session.Values["foo"] = nil |
||||||
|
// Save it before we write to the response/return from the handler.
|
||||||
|
err := session.Save(c.Request(), c.Response()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return c.Redirect(http.StatusMovedPermanently, "/") |
||||||
|
} |
||||||
|
|
||||||
|
func handleEcho(c slim.Context) error { |
||||||
|
conn, err := upgrader.Upgrade(c.Response(), c.Request(), nil) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
clients.Store(conn, struct{}{}) |
||||||
|
defer clients.Delete(conn) |
||||||
|
|
||||||
|
// TODO 使用 pool 实现复用
|
||||||
|
exit := make(chan error) |
||||||
|
defer close(exit) |
||||||
|
|
||||||
|
stop := func(err error) { |
||||||
|
select { |
||||||
|
case <-exit: |
||||||
|
default: |
||||||
|
exit <- err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
go func() { |
||||||
|
conn.SetReadLimit(maxMessageSize) |
||||||
|
conn.SetReadDeadline(time.Now().Add(pongWait)) |
||||||
|
conn.SetPongHandler(func(string) error { |
||||||
|
conn.SetReadDeadline(time.Now().Add(pongWait)) |
||||||
|
return nil |
||||||
|
}) |
||||||
|
for { |
||||||
|
_, message, ex := conn.ReadMessage() |
||||||
|
if ex != nil { |
||||||
|
if websocket.IsUnexpectedCloseError(ex, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { |
||||||
|
stop(ex) |
||||||
|
} else { |
||||||
|
stop(nil) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1)) |
||||||
|
broadcast <- message |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
go func() { |
||||||
|
ticker := time.NewTicker(pingPeriod) |
||||||
|
defer func() { |
||||||
|
ticker.Stop() |
||||||
|
conn.Close() |
||||||
|
}() |
||||||
|
|
||||||
|
for { |
||||||
|
for { |
||||||
|
select { |
||||||
|
case message, ok := <-broadcast: |
||||||
|
conn.SetWriteDeadline(time.Now().Add(writeWait)) |
||||||
|
if !ok { |
||||||
|
// The hub closed the channel.
|
||||||
|
conn.WriteMessage(websocket.CloseMessage, []byte{}) |
||||||
|
stop(nil) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
w, err2 := conn.NextWriter(websocket.TextMessage) |
||||||
|
if err2 != nil { |
||||||
|
stop(err2) |
||||||
|
return |
||||||
|
} |
||||||
|
w.Write(message) |
||||||
|
|
||||||
|
// Add queued chat messages to the current websocket message.
|
||||||
|
n := len(broadcast) |
||||||
|
for i := 0; i < n; i++ { |
||||||
|
w.Write(newline) |
||||||
|
w.Write(<-broadcast) |
||||||
|
} |
||||||
|
|
||||||
|
if err3 := w.Close(); err3 != nil { |
||||||
|
stop(err3) |
||||||
|
return |
||||||
|
} |
||||||
|
case <-ticker.C: |
||||||
|
conn.SetWriteDeadline(time.Now().Add(writeWait)) |
||||||
|
if err4 := conn.WriteMessage(websocket.PingMessage, nil); err4 != nil { |
||||||
|
stop(err4) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
return <-exit |
||||||
|
} |
||||||
|
|
||||||
|
func handleStart(c slim.Context) error { |
||||||
|
cmd := exec.Command("go") |
||||||
|
cmd.Stdout = c.Response() |
||||||
|
cmd.Stderr = c.Response() |
||||||
|
cmd.Start() |
||||||
|
cmd.Wait() |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func handleAbort(c slim.Context) error { |
||||||
|
return c.String(200, "ok") |
||||||
|
} |
||||||
|
|
||||||
|
func handleHome(c slim.Context) error { |
||||||
|
return c.Render(http.StatusOK, "index.gohtml", "ws://"+c.Request().Host+"/echo") |
||||||
|
} |
||||||
|
|
||||||
|
type Template struct { |
||||||
|
templates *template.Template |
||||||
|
} |
||||||
|
|
||||||
|
func (t *Template) Render(c slim.Context, w io.Writer, name string, data any) error { |
||||||
|
return t.templates.ExecuteTemplate(w, name, data) |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
var err error |
||||||
|
db, err = gorm.Open(sqlite.Open("devops.db"), &gorm.Config{}) |
||||||
|
if err != nil { |
||||||
|
log.Fatalln(err) |
||||||
|
} |
||||||
|
err = db.AutoMigrate( |
||||||
|
&User{}, |
||||||
|
&Project{}, |
||||||
|
&Build{}, |
||||||
|
&BuildLog{}, |
||||||
|
) |
||||||
|
if err != nil { |
||||||
|
log.Fatalln(err) |
||||||
|
} |
||||||
|
err = db.Model(&User{}).FirstOrCreate(&User{ |
||||||
|
ID: 1, |
||||||
|
Username: "admin", |
||||||
|
RawPassword: "111111", |
||||||
|
}, User{ID: 1}).Error |
||||||
|
if err != nil { |
||||||
|
log.Fatalln(err) |
||||||
|
} |
||||||
|
|
||||||
|
upgrader = &websocket.Upgrader{} |
||||||
|
clients = new(sync.Map) |
||||||
|
broadcast = make(chan []byte) |
||||||
|
store = gormstore.New(db, []byte("secret")) |
||||||
|
|
||||||
|
gob.Register(&User{}) |
||||||
|
|
||||||
|
s := slim.New() |
||||||
|
s.Debug = true |
||||||
|
s.Renderer = &Template{templates: template.Must(template.ParseGlob("views/*.gohtml"))} |
||||||
|
s.Use(slim.Logging()) |
||||||
|
s.Use(slim.Recovery()) |
||||||
|
s.Use(slim.Static("public")) |
||||||
|
|
||||||
|
s.File("/login", "login.html") |
||||||
|
s.POST("/login", handleLogin) |
||||||
|
s.GET("/logout", handleLogout) |
||||||
|
s.GET("/echo", handleEcho) |
||||||
|
s.GET("/", handleHome) |
||||||
|
s.Route("/p/", func(sub slim.RouteCollector) { |
||||||
|
sub.Use(AuthSession) |
||||||
|
sub.POST(":id/start", handleStart) |
||||||
|
sub.POST(":id/abort", handleAbort) |
||||||
|
sub.GET("*", handleStart) |
||||||
|
}) |
||||||
|
|
||||||
|
s.GET("/~", func(c slim.Context) error { |
||||||
|
file, err := os.ReadFile("deploy.yml") |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
var config BuildConfig |
||||||
|
err = yaml.Unmarshal(file, &config) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
return c.JSONPretty(200, config, " ") |
||||||
|
}) |
||||||
|
|
||||||
|
//http.HandleFunc("/echo", handleWebsocket)
|
||||||
|
//http.HandleFunc("/restart", restart)
|
||||||
|
//http.ListenAndServe(":5000", nil)
|
||||||
|
log.Fatalln(s.Start(":5000")) |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="zh-Hans"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<title></title> |
||||||
|
<script> |
||||||
|
window.addEventListener("load", function() { |
||||||
|
var output = document.getElementById("output"); |
||||||
|
var input = document.getElementById("input"); |
||||||
|
var ws; |
||||||
|
|
||||||
|
var print = function(message) { |
||||||
|
var d = document.createElement("div"); |
||||||
|
d.textContent = message; |
||||||
|
output.appendChild(d); |
||||||
|
output.scroll(0, output.scrollHeight); |
||||||
|
}; |
||||||
|
|
||||||
|
document.getElementById("open").onclick = function(evt) { |
||||||
|
if (ws) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
ws = new WebSocket("{{.}}"); |
||||||
|
ws.onopen = function(evt) { |
||||||
|
print("OPEN"); |
||||||
|
} |
||||||
|
ws.onclose = function(evt) { |
||||||
|
print("CLOSE"); |
||||||
|
ws = null; |
||||||
|
} |
||||||
|
ws.onmessage = function(evt) { |
||||||
|
print("RESPONSE: " + evt.data); |
||||||
|
} |
||||||
|
ws.onerror = function(evt) { |
||||||
|
print("ERROR: " + evt.data); |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
document.getElementById("send").onclick = function(evt) { |
||||||
|
if (!ws) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
print("SEND: " + input.value); |
||||||
|
ws.send(input.value); |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
document.getElementById("close").onclick = function(evt) { |
||||||
|
if (!ws) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
ws.close(); |
||||||
|
return false; |
||||||
|
}; |
||||||
|
}); |
||||||
|
</script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<table> |
||||||
|
<tr><td valign="top" width="50%"> |
||||||
|
<p>Click "Open" to create a connection to the server, |
||||||
|
"Send" to send a message to the server and "Close" to close the connection. |
||||||
|
You can change the message and send multiple times. |
||||||
|
<p> |
||||||
|
<form> |
||||||
|
<button id="open">Open</button> |
||||||
|
<button id="close">Close</button> |
||||||
|
<p><input id="input" type="text" value="Hello world!"> |
||||||
|
<button id="send">Send</button> |
||||||
|
</form> |
||||||
|
</td><td valign="top" width="50%"> |
||||||
|
<div id="output" style="max-height: 70vh;overflow-y: scroll;"></div> |
||||||
|
</td></tr></table> |
||||||
|
</body> |
||||||
|
</html> |
Loading…
Reference in new issue