Feature: Support multiple action status per release (#69)

* feat: move release actions to separate table

* chore: update sqlite driver
This commit is contained in:
Ludvig Lundgren 2022-01-08 15:40:31 +01:00 committed by GitHub
parent 2ea2293745
commit e03eac24ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 284 additions and 91 deletions

8
go.mod
View file

@ -22,14 +22,16 @@ require (
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
golang.org/x/tools v0.1.8 // indirect
gopkg.in/ini.v1 v1.64.0 // indirect
gopkg.in/irc.v3 v3.1.4
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.4.0
modernc.org/ccgo/v3 v3.12.73 // indirect
modernc.org/sqlite v1.14.1
modernc.org/ccgo/v3 v3.14.0 // indirect
modernc.org/libc v1.13.2 // indirect
modernc.org/sqlite v1.14.3
)

39
go.sum
View file

@ -876,6 +876,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@ -1033,6 +1034,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1158,9 +1160,12 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@ -1251,6 +1256,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1463,6 +1470,9 @@ modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22 h1:BzShpwCAP7TWzFppM4k2t03RhXhgYqaibROWkrWq7lE=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
@ -1497,6 +1507,17 @@ modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/E
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73 h1:AMk4wEpzWjpODXohKvvnlwLob4Xk8tq3we6CwYh88mA=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.14.0 h1:Zr1Ny9+7r5yAiXpBdgp8XiXqkNA4ARrRphHGHVXeAp0=
modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
@ -1533,6 +1554,18 @@ modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82 h1:CSl/6n4odvPYWKKqBtFb8e0ZWVTjxDqwxTjaoee9V7E=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.13.2 h1:GCFjY9bmwDZ/TJC4OZOUWaNgxIxwb104C/QZrqpcVEA=
modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
@ -1545,14 +1578,20 @@ modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.14.1 h1:jthfQCbWKfbK/lvZSjFEpBk0QzIBN6pQbFdDqBMR490=
modernc.org/sqlite v1.14.1/go.mod h1:04Lqa+3PuAEUhAPAPWeDMljT4UYA31nb2DHTFG47L1g=
modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ=
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.8.13 h1:V0sTNBw0Re86PvXZxuCub3oO9WrSTqALgrwNZNvLFGw=
modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
modernc.org/tcl v1.9.2 h1:YA87dFLOsR2KqMka371a2Xgr+YsyUwo7OmHVSv/kztw=
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao=
modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
modernc.org/z v1.2.20 h1:DyboxM1sJR2NB803j2StnbnL6jcQXz273OhHDGu8dGk=
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -4,6 +4,7 @@ import (
"io"
"os"
"path"
"time"
"github.com/rs/zerolog/log"
@ -24,10 +25,20 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
log.Debug().Msgf("process action: %v for '%v'", action.Name, release.TorrentName)
actionStatus := domain.ReleaseActionStatus{
ReleaseID: release.ID,
Status: domain.ReleasePushStatusPending,
Action: action.Name,
Type: action.Type,
Rejections: []string{},
Timestamp: time.Now(),
}
s.bus.Publish("release:store-action-status", &actionStatus)
switch action.Type {
case domain.ActionTypeTest:
s.test(action.Name)
s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved)
case domain.ActionTypeExec:
if release.TorrentTmpFile == "" {
@ -42,7 +53,6 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
go func(release domain.Release, action domain.Action, tmpFile string) {
s.execCmd(release, action, tmpFile)
s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved)
}(release, action, tmpFile)
case domain.ActionTypeWatchFolder:
@ -56,7 +66,6 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
tmpFile = t.TmpFileName
}
s.watchFolder(action.WatchFolder, tmpFile)
s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved)
case domain.ActionTypeDelugeV1, domain.ActionTypeDelugeV2:
canDownload, err := s.delugeCheckRulesCanDownload(action)
@ -65,7 +74,14 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
continue
}
if !canDownload {
s.bus.Publish("release:update-push-status-rejected", release.ID, "deluge busy")
s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{
ID: actionStatus.ID,
ReleaseID: release.ID,
Status: domain.ReleasePushStatusRejected,
Action: action.Name,
Type: action.Type,
Rejections: []string{"deluge busy"},
})
continue
}
if release.TorrentTmpFile == "" {
@ -83,7 +99,6 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
if err != nil {
log.Error().Stack().Err(err).Msg("error sending torrent to Deluge")
}
s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved)
}(action, tmpFile)
case domain.ActionTypeQbittorrent:
@ -93,7 +108,14 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
continue
}
if !canDownload {
s.bus.Publish("release:update-push-status-rejected", release.ID, "qbittorrent busy")
s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{
ID: actionStatus.ID,
ReleaseID: release.ID,
Status: domain.ReleasePushStatusRejected,
Action: action.Name,
Type: action.Type,
Rejections: []string{"qbittorrent busy"},
})
continue
}
@ -113,7 +135,6 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
if err != nil {
log.Error().Stack().Err(err).Msg("error sending torrent to qBittorrent")
}
s.bus.Publish("release:update-push-status", release.ID, domain.ReleasePushStatusApproved)
}(action, hash, tmpFile)
case domain.ActionTypeRadarr:
@ -146,6 +167,15 @@ func (s *service) RunActions(actions []domain.Action, release domain.Release) er
default:
log.Warn().Msgf("unsupported action: %v type: %v", action.Name, action.Type)
}
s.bus.Publish("release:store-action-status", &domain.ReleaseActionStatus{
ID: actionStatus.ID,
ReleaseID: release.ID,
Status: domain.ReleasePushStatusApproved,
Action: action.Name,
Type: action.Type,
Rejections: []string{},
})
}
// safe to delete tmp file

View file

@ -183,7 +183,8 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e
WHERE i.identifier = ?
AND f.enabled = true`, indexer)
if err != nil {
log.Fatal().Err(err)
log.Error().Stack().Err(err).Msg("error querying filter row")
return nil, err
}
defer rows.Close()
@ -199,8 +200,6 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), &years, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &createdAt, &updatedAt); err != nil {
log.Error().Stack().Err(err).Msg("error scanning data to struct")
}
if err != nil {
return nil, err
}

View file

@ -143,7 +143,6 @@ CREATE TABLE "release"
(
id INTEGER PRIMARY KEY,
filter_status TEXT,
push_status TEXT,
rejections TEXT [] DEFAULT '{}' NOT NULL,
indexer TEXT,
filter TEXT,
@ -190,6 +189,20 @@ CREATE TABLE "release"
uploader TEXT,
pre_time TEXT
);
CREATE TABLE release_action_status
(
id INTEGER PRIMARY KEY,
status TEXT,
action TEXT NOT NULL,
type TEXT NOT NULL,
rejections TEXT [] DEFAULT '{}' NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
raw TEXT,
log TEXT,
release_id INTEGER NOT NULL,
FOREIGN KEY (release_id) REFERENCES "release"(id)
);
`
var migrations = []string{
@ -247,6 +260,27 @@ var migrations = []string{
pre_time TEXT
);
`,
`
CREATE TABLE release_action_status
(
id INTEGER PRIMARY KEY,
status TEXT,
action TEXT NOT NULL,
type TEXT NOT NULL,
rejections TEXT [] DEFAULT '{}' NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
raw TEXT,
log TEXT,
release_id INTEGER NOT NULL,
FOREIGN KEY (release_id) REFERENCES "release"(id)
);
INSERT INTO "release_action_status" (status, action, type, timestamp, release_id)
SELECT push_status, 'DEFAULT', 'QBITTORRENT', timestamp, id FROM "release";
ALTER TABLE "release"
DROP COLUMN push_status;
`,
}
func Migrate(db *sql.DB) error {

View file

@ -23,8 +23,8 @@ func NewReleaseRepo(db *sql.DB) domain.ReleaseRepo {
func (repo *ReleaseRepo) Store(ctx context.Context, r *domain.Release) (*domain.Release, error) {
query, args, err := sq.
Insert("release").
Columns("filter_status", "push_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "raw", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "audio", "release_group", "region", "language", "edition", "unrated", "hybrid", "proper", "repack", "website", "artists", "type", "format", "bitrate", "log_score", "has_log", "has_cue", "is_scene", "origin", "tags", "freeleech", "freeleech_percent", "uploader", "pre_time").
Values(r.FilterStatus, r.PushStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Raw, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, r.Codec, r.Container, r.HDR, r.Audio, r.Group, r.Region, r.Language, r.Edition, r.Unrated, r.Hybrid, r.Proper, r.Repack, r.Website, pq.Array(r.Artists), r.Type, r.Format, r.Bitrate, r.LogScore, r.HasLog, r.HasCue, r.IsScene, r.Origin, pq.Array(r.Tags), r.Freeleech, r.FreeleechPercent, r.Uploader, r.PreTime).
Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "raw", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "audio", "release_group", "region", "language", "edition", "unrated", "hybrid", "proper", "repack", "website", "artists", "type", "format", "bitrate", "log_score", "has_log", "has_cue", "is_scene", "origin", "tags", "freeleech", "freeleech_percent", "uploader", "pre_time").
Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Raw, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, r.Codec, r.Container, r.HDR, r.Audio, r.Group, r.Region, r.Language, r.Edition, r.Unrated, r.Hybrid, r.Proper, r.Repack, r.Website, pq.Array(r.Artists), r.Type, r.Format, r.Bitrate, r.LogScore, r.HasLog, r.HasCue, r.IsScene, r.Origin, pq.Array(r.Tags), r.Freeleech, r.FreeleechPercent, r.Uploader, r.PreTime).
ToSql()
res, err := repo.db.ExecContext(ctx, query, args...)
@ -41,37 +41,42 @@ func (repo *ReleaseRepo) Store(ctx context.Context, r *domain.Release) (*domain.
return r, nil
}
func (repo *ReleaseRepo) UpdatePushStatus(ctx context.Context, id int64, status domain.ReleasePushStatus) error {
query, args, err := sq.Update("release").Set("push_status", status).Where("id = ?", id).ToSql()
func (repo *ReleaseRepo) StoreReleaseActionStatus(ctx context.Context, a *domain.ReleaseActionStatus) error {
_, err = repo.db.ExecContext(ctx, query, args...)
if err != nil {
log.Error().Stack().Err(err).Msg("error updating status of release")
return err
if a.ID != 0 {
query, args, err := sq.
Update("release_action_status").
Set("status", a.Status).
Set("rejections", pq.Array(a.Rejections)).
Set("timestamp", time.Now().Format(time.RFC3339)).
Where("id = ?", a.ID).
Where("release_id = ?", a.ReleaseID).
ToSql()
_, err = repo.db.ExecContext(ctx, query, args...)
if err != nil {
log.Error().Stack().Err(err).Msg("error updating status of release")
return err
}
} else {
query, args, err := sq.
Insert("release_action_status").
Columns("status", "action", "type", "rejections", "timestamp", "release_id").
Values(a.Status, a.Action, a.Type, pq.Array(a.Rejections), a.Timestamp, a.ReleaseID).
ToSql()
res, err := repo.db.ExecContext(ctx, query, args...)
if err != nil {
log.Error().Stack().Err(err).Msg("error inserting status of release")
return err
}
resId, _ := res.LastInsertId()
a.ID = resId
}
log.Trace().Msgf("release.update_push_status: id %+v", id)
return nil
}
func (repo *ReleaseRepo) UpdatePushStatusRejected(ctx context.Context, id int64, rejections string) error {
r := []string{rejections}
query, args, err := sq.
Update("release").
Set("push_status", domain.ReleasePushStatusRejected).
Set("rejections", pq.Array(r)).
Where("id = ?", id).
ToSql()
_, err = repo.db.ExecContext(ctx, query, args...)
if err != nil {
log.Error().Stack().Err(err).Msg("error updating status of release")
return err
}
log.Trace().Msgf("release.update_push_status_rejected: id %+v", id)
log.Trace().Msgf("release.store_release_action_status: %+v", a)
return nil
}
@ -79,7 +84,7 @@ func (repo *ReleaseRepo) UpdatePushStatusRejected(ctx context.Context, id int64,
func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([]domain.Release, int64, int64, error) {
queryBuilder := sq.
Select("id", "filter_status", "push_status", "rejections", "indexer", "filter", "protocol", "title", "torrent_name", "size", "timestamp", "COUNT() OVER() AS total_count").
Select("id", "filter_status", "rejections", "indexer", "filter", "protocol", "title", "torrent_name", "size", "timestamp", "COUNT() OVER() AS total_count").
From("release").
OrderBy("timestamp DESC")
@ -132,7 +137,7 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([
var indexer, filter sql.NullString
var timestamp string
if err := rows.Scan(&rls.ID, &rls.FilterStatus, &rls.PushStatus, pq.Array(&rls.Rejections), &indexer, &filter, &rls.Protocol, &rls.Title, &rls.TorrentName, &rls.Size, &timestamp, &countItems); err != nil {
if err := rows.Scan(&rls.ID, &rls.FilterStatus, pq.Array(&rls.Rejections), &indexer, &filter, &rls.Protocol, &rls.Title, &rls.TorrentName, &rls.Size, &timestamp, &countItems); err != nil {
log.Error().Stack().Err(err).Msg("release.find: error scanning data to struct")
return res, 0, 0, err
}
@ -143,6 +148,15 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([
ca, _ := time.Parse(time.RFC3339, timestamp)
rls.Timestamp = ca
// get action status
actionStatus, err := repo.GetActionStatusByReleaseID(ctx, rls.ID)
if err != nil {
log.Error().Stack().Err(err).Msg("release.find: error getting action status")
return res, 0, 0, err
}
rls.ActionStatus = actionStatus
res = append(res, rls)
}
@ -156,13 +170,57 @@ func (repo *ReleaseRepo) Find(ctx context.Context, params domain.QueryParams) ([
return res, nextCursor, countItems, nil
}
func (repo *ReleaseRepo) GetActionStatusByReleaseID(ctx context.Context, releaseID int64) ([]domain.ReleaseActionStatus, error) {
queryBuilder := sq.
Select("id", "status", "action", "type", "rejections", "timestamp").
From("release_action_status").
Where("release_id = ?", releaseID)
query, args, err := queryBuilder.ToSql()
res := make([]domain.ReleaseActionStatus, 0)
rows, err := repo.db.QueryContext(ctx, query, args...)
if err != nil {
log.Error().Stack().Err(err).Msg("error fetching releases")
return res, nil
}
defer rows.Close()
if err := rows.Err(); err != nil {
log.Error().Stack().Err(err)
return res, err
}
for rows.Next() {
var rls domain.ReleaseActionStatus
var timestamp string
if err := rows.Scan(&rls.ID, &rls.Status, &rls.Action, &rls.Type, pq.Array(&rls.Rejections), &timestamp); err != nil {
log.Error().Stack().Err(err).Msg("release.find: error scanning data to struct")
return res, err
}
ca, _ := time.Parse(time.RFC3339, timestamp)
rls.Timestamp = ca
res = append(res, rls)
}
return res, nil
}
func (repo *ReleaseRepo) Stats(ctx context.Context) (*domain.ReleaseStats, error) {
query := `SELECT
COUNT(*) total,
IFNULL(SUM(CASE WHEN push_status = 'PUSH_APPROVED' THEN 1 ELSE 0 END), 0) push_approved_count,
IFNULL(SUM(CASE WHEN push_status = 'PUSH_REJECTED' THEN 1 ELSE 0 END), 0) push_rejected_count,
IFNULL(SUM(CASE WHEN filter_status = 'FILTER_APPROVED' THEN 1 ELSE 0 END), 0) filtered_count,
IFNULL(SUM(CASE WHEN filter_status = 'FILTER_REJECTED' THEN 1 ELSE 0 END), 0) filter_rejected_count
query := `SELECT COUNT(*) total,
IFNULL(SUM(CASE WHEN filter_status = 'FILTER_APPROVED' THEN 1 ELSE 0 END), 0) filtered_count,
IFNULL(SUM(CASE WHEN filter_status = 'FILTER_REJECTED' THEN 1 ELSE 0 END), 0) filter_rejected_count,
(SELECT IFNULL(SUM(CASE WHEN status = 'PUSH_APPROVED' THEN 1 ELSE 0 END), 0)
FROM "release_action_status") AS push_approved_count,
(SELECT IFNULL(SUM(CASE WHEN status = 'PUSH_REJECTED' THEN 1 ELSE 0 END), 0)
FROM "release_action_status") AS push_rejected_count
FROM "release";`
row := repo.db.QueryRowContext(ctx, query)

View file

@ -27,15 +27,14 @@ import (
type ReleaseRepo interface {
Store(ctx context.Context, release *Release) (*Release, error)
Find(ctx context.Context, params QueryParams) (res []Release, nextCursor int64, count int64, err error)
GetActionStatusByReleaseID(ctx context.Context, releaseID int64) ([]ReleaseActionStatus, error)
Stats(ctx context.Context) (*ReleaseStats, error)
UpdatePushStatus(ctx context.Context, id int64, status ReleasePushStatus) error
UpdatePushStatusRejected(ctx context.Context, id int64, rejections string) error
StoreReleaseActionStatus(ctx context.Context, actionStatus *ReleaseActionStatus) error
}
type Release struct {
ID int64 `json:"id"`
FilterStatus ReleaseFilterStatus `json:"filter_status"`
PushStatus ReleasePushStatus `json:"push_status"`
Rejections []string `json:"rejections"`
Indexer string `json:"indexer"`
FilterName string `json:"filter"`
@ -89,6 +88,17 @@ type Release struct {
AdditionalSizeCheckRequired bool `json:"-"`
FilterID int `json:"-"`
Filter *Filter `json:"-"`
ActionStatus []ReleaseActionStatus `json:"action_status"`
}
type ReleaseActionStatus struct {
ID int64 `json:"id"`
Status ReleasePushStatus `json:"status"`
Action string `json:"action"`
Type ActionType `json:"type"`
Rejections []string `json:"rejections"`
Timestamp time.Time `json:"timestamp"`
ReleaseID int64 `json:"-"`
}
func NewRelease(indexer string, line string) (*Release, error) {
@ -96,7 +106,6 @@ func NewRelease(indexer string, line string) (*Release, error) {
Indexer: indexer,
Raw: line,
FilterStatus: ReleaseStatusFilterPending,
PushStatus: ReleasePushStatusPending,
Rejections: []string{},
Protocol: ReleaseProtocolTorrent,
Implementation: ReleaseImplementationIRC,

View file

@ -24,23 +24,14 @@ func NewSubscribers(eventbus EventBus.Bus, releaseSvc release.Service) Subscribe
}
func (s Subscriber) Register() {
s.eventbus.Subscribe("release:update-push-status", s.releaseUpdatePushStatus)
s.eventbus.Subscribe("release:update-push-status-rejected", s.releaseUpdatePushStatusRejected)
s.eventbus.Subscribe("release:store-action-status", s.releaseActionStatus)
}
func (s Subscriber) releaseUpdatePushStatus(id int64, status domain.ReleasePushStatus) {
log.Trace().Msgf("event: 'release:update-push-status' release ID '%v' update push status: '%v'", id, status)
func (s Subscriber) releaseActionStatus(actionStatus *domain.ReleaseActionStatus) {
log.Trace().Msgf("events: 'release:store-action-status' '%+v'", actionStatus)
err := s.releaseSvc.UpdatePushStatus(context.Background(), id, status)
err := s.releaseSvc.StoreReleaseActionStatus(context.Background(), actionStatus)
if err != nil {
log.Error().Err(err).Msgf("events: error")
}
}
func (s Subscriber) releaseUpdatePushStatusRejected(id int64, rejections string) {
log.Trace().Msgf("event: 'release:update-push-status-rejected' release ID '%v' update push status rejected rejections: '%v'", id, rejections)
err := s.releaseSvc.UpdatePushStatusRejected(context.Background(), id, rejections)
if err != nil {
log.Error().Err(err).Msgf("events: error")
log.Error().Err(err).Msgf("events: 'release:store-action-status' error")
}
}

View file

@ -14,8 +14,7 @@ type Service interface {
Find(ctx context.Context, query domain.QueryParams) (res []domain.Release, nextCursor int64, count int64, err error)
Stats(ctx context.Context) (*domain.ReleaseStats, error)
Store(ctx context.Context, release *domain.Release) error
UpdatePushStatus(ctx context.Context, id int64, status domain.ReleasePushStatus) error
UpdatePushStatusRejected(ctx context.Context, id int64, rejections string) error
StoreReleaseActionStatus(ctx context.Context, actionStatus *domain.ReleaseActionStatus) error
Process(release domain.Release) error
}
@ -36,6 +35,7 @@ func (s *service) Find(ctx context.Context, query domain.QueryParams) (res []dom
if err != nil {
return
}
return
}
@ -57,17 +57,8 @@ func (s *service) Store(ctx context.Context, release *domain.Release) error {
return nil
}
func (s *service) UpdatePushStatus(ctx context.Context, id int64, status domain.ReleasePushStatus) error {
err := s.repo.UpdatePushStatus(ctx, id, status)
if err != nil {
return err
}
return nil
}
func (s *service) UpdatePushStatusRejected(ctx context.Context, id int64, rejections string) error {
err := s.repo.UpdatePushStatusRejected(ctx, id, rejections)
func (s *service) StoreReleaseActionStatus(ctx context.Context, actionStatus *domain.ReleaseActionStatus) error {
err := s.repo.StoreReleaseActionStatus(ctx, actionStatus)
if err != nil {
return err
}

View file

@ -177,7 +177,6 @@ export interface Config {
export interface Release {
id: number;
filter_status: string;
push_status: string;
rejections: string[];
indexer: string;
filter: string;
@ -186,6 +185,17 @@ export interface Release {
size: number;
raw: string;
timestamp: Date
action_status: ReleaseActionStatus[]
}
export interface ReleaseActionStatus {
id: number;
status: string;
action: string;
type: string;
rejections: string[];
timestamp: Date
// raw: string;
}
export interface ReleaseFindResponse {

View file

@ -6,6 +6,7 @@ import APIClient from '../api/APIClient'
import { useQuery } from 'react-query'
import { ReleaseFindResponse, ReleaseStats } from '../domain/interfaces'
import { EmptyListState } from '../components/emptystates'
import { ReleaseStatusCell } from './Releases'
export function Dashboard() {
return (
@ -660,9 +661,9 @@ function DataTablee() {
// Cell: StatusPill,
// },
{
Header: "Push Status",
accessor: 'push_status',
Cell: StatusPill,
Header: "Actions",
accessor: 'action_status',
Cell: ReleaseStatusCell,
},
{
Header: "Indexer",

View file

@ -1,10 +1,13 @@
import { ChevronDoubleLeftIcon, ChevronLeftIcon, ChevronRightIcon, ChevronDoubleRightIcon } from "@heroicons/react/solid"
import { ExclamationCircleIcon } from "@heroicons/react/outline"
import ClockIcon from "@heroicons/react/outline/ClockIcon"
import { ChevronDoubleLeftIcon, ChevronLeftIcon, ChevronRightIcon, ChevronDoubleRightIcon, CheckIcon } from "@heroicons/react/solid"
import { formatDistanceToNowStrict } from "date-fns"
import React from "react"
import { useQuery } from "react-query"
import { useTable, useSortBy, usePagination } from "react-table"
import APIClient from "../api/APIClient"
import { EmptyListState } from "../components/emptystates"
import { ReleaseActionStatus } from "../domain/interfaces"
import { classNames } from "../utils"
export function Releases() {
@ -98,6 +101,7 @@ export function StatusPill({ value }: any) {
);
};
export function AgeCell({ value, column, row }: any) {
const formatDate = formatDistanceToNowStrict(
@ -116,6 +120,31 @@ export function ReleaseCell({ value, column, row }: any) {
)
}
interface ReleaseStatusCellProps {
value: ReleaseActionStatus[];
column: any;
row: any;
}
export function ReleaseStatusCell({ value, column, row }: ReleaseStatusCellProps) {
const statusMap: any = {
"PUSH_REJECTED": <span className="mr-1 inline-flex items-center rounded text-xs font-semibold uppercase bg-pink-100 text-pink-800 hover:bg-pink-300">
<ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />
</span>,
"PUSH_APPROVED": <span className="mr-1 inline-flex items-center rounded text-xs font-semibold uppercase bg-green-100 text-green-800 hover:bg-green-300">
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>,
"PENDING": <span className="mr-1 inline-flex items-center rounded text-xs font-semibold uppercase bg-yellow-100 text-yellow-800 hover:bg-yellow-200">
<ClockIcon className="h-5 w-5" aria-hidden="true" />
</span>,
}
return (
<div className="flex text-sm font-medium text-gray-900 dark:text-gray-300">
{value.map(v => <div title={`action: ${v.action}, type: ${v.type}, status: ${v.status}, ;time: ${v.timestamp}`}>{statusMap[v.status]}</div>)}
</div>
)
}
const initialState = {
queryPageIndex: 0,
queryPageSize: 10,
@ -166,9 +195,9 @@ function Table() {
// Cell: StatusPill,
// },
{
Header: "Push Status",
accessor: 'push_status',
Cell: StatusPill,
Header: "Actions",
accessor: 'action_status',
Cell: ReleaseStatusCell,
},
{
Header: "Indexer",