329 lines
10 KiB
Go
329 lines
10 KiB
Go
package common
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
|
|
//"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
qgen "git.tuxpa.in/a/gosora/query_gen"
|
|
)
|
|
|
|
var Attachments AttachmentStore
|
|
|
|
var ErrCorruptAttachPath = errors.New("corrupt attachment path")
|
|
|
|
type MiniAttachment struct {
|
|
ID int
|
|
SectionID int
|
|
OriginID int
|
|
UploadedBy int
|
|
Path string
|
|
Extra string
|
|
|
|
Image bool
|
|
Ext string
|
|
}
|
|
|
|
type Attachment struct {
|
|
ID int
|
|
SectionTable string
|
|
SectionID int
|
|
OriginTable string
|
|
OriginID int
|
|
UploadedBy int
|
|
Path string
|
|
Extra string
|
|
|
|
Image bool
|
|
Ext string
|
|
}
|
|
|
|
type AttachmentStore interface {
|
|
GetForRenderRoute(filename string, sid int, sectionTable string) (*Attachment, error)
|
|
FGet(id int) (*Attachment, error)
|
|
Get(id int) (*MiniAttachment, error)
|
|
MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error)
|
|
BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error)
|
|
Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error)
|
|
MoveTo(sectionID, originID int, originTable string) error
|
|
MoveToByExtra(sectionID int, originTable, extra string) error
|
|
Count() int
|
|
CountIn(originTable string, oid int) int
|
|
CountInPath(path string) int
|
|
Delete(id int) error
|
|
|
|
AddLinked(otable string, oid int) (err error)
|
|
RemoveLinked(otable string, oid int) (err error)
|
|
}
|
|
|
|
type DefaultAttachmentStore struct {
|
|
getForRenderRoute *sql.Stmt
|
|
|
|
fget *sql.Stmt
|
|
get *sql.Stmt
|
|
getByObj *sql.Stmt
|
|
add *sql.Stmt
|
|
count *sql.Stmt
|
|
countIn *sql.Stmt
|
|
countInPath *sql.Stmt
|
|
move *sql.Stmt
|
|
moveByExtra *sql.Stmt
|
|
delete *sql.Stmt
|
|
|
|
replyUpdateAttachs *sql.Stmt
|
|
topicUpdateAttachs *sql.Stmt
|
|
}
|
|
|
|
func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmentStore, error) {
|
|
a := "attachments"
|
|
return &DefaultAttachmentStore{
|
|
getForRenderRoute: acc.Select(a).Columns("sectionTable, originID, originTable, uploadedBy, path").Where("path=? AND sectionID=? AND sectionTable=?").Prepare(),
|
|
|
|
fget: acc.Select(a).Columns("originTable, originID, sectionTable, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(),
|
|
get: acc.Select(a).Columns("originID, sectionID, uploadedBy, path, extra").Where("attachID=?").Prepare(),
|
|
getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable=? AND originID=?").Prepare(),
|
|
add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(),
|
|
count: acc.Count(a).Prepare(),
|
|
countIn: acc.Count(a).Where("originTable=? and originID=?").Prepare(),
|
|
countInPath: acc.Count(a).Where("path=?").Prepare(),
|
|
move: acc.Update(a).Set("sectionID=?").Where("originID=? AND originTable=?").Prepare(),
|
|
moveByExtra: acc.Update(a).Set("sectionID=?").Where("originTable=? AND extra=?").Prepare(),
|
|
delete: acc.Delete(a).Where("attachID=?").Prepare(),
|
|
|
|
// TODO: Less race-y attachment count updates
|
|
replyUpdateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
|
|
topicUpdateAttachs: acc.Update("topics").Set("attachCount=?").Where("tid=?").Prepare(),
|
|
}, acc.FirstError()
|
|
}
|
|
|
|
// TODO: Revamp this to make it less of a copy-paste from the original code in the route
|
|
// ! Lacks some attachment initialisation code
|
|
func (s *DefaultAttachmentStore) GetForRenderRoute(filename string, sid int, sectionTable string) (*Attachment, error) {
|
|
a := &Attachment{SectionID: sid}
|
|
e := s.getForRenderRoute.QueryRow(filename, sid, sectionTable).Scan(&a.SectionTable, &a.OriginID, &a.OriginTable, &a.UploadedBy, &a.Path)
|
|
// TODO: Initialise attachment struct fields?
|
|
return a, e
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error) {
|
|
rows, err := s.getByObj.Query(originTable, originID)
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
a := &MiniAttachment{OriginID: originID}
|
|
err := rows.Scan(&a.ID, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
|
|
if len(a.Ext) == 0 {
|
|
return nil, ErrCorruptAttachPath
|
|
}
|
|
a.Image = ImageFileExts.Contains(a.Ext)
|
|
alist = append(alist, a)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(alist) == 0 {
|
|
err = sql.ErrNoRows
|
|
}
|
|
return alist, err
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error) {
|
|
if len(ids) == 0 {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
if len(ids) == 1 {
|
|
res, err := s.MiniGetList(originTable, ids[0])
|
|
return map[int][]*MiniAttachment{ids[0]: res}, err
|
|
}
|
|
|
|
amap = make(map[int][]*MiniAttachment)
|
|
var buffer []*MiniAttachment
|
|
var currentID int
|
|
rows, err := qgen.NewAcc().Select("attachments").Columns("attachID,sectionID,originID,uploadedBy,path").Where("originTable=?").In("originID", ids).Orderby("originID ASC").Query(originTable)
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
a := &MiniAttachment{}
|
|
err := rows.Scan(&a.ID, &a.SectionID, &a.OriginID, &a.UploadedBy, &a.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
|
|
if len(a.Ext) == 0 {
|
|
return nil, ErrCorruptAttachPath
|
|
}
|
|
a.Image = ImageFileExts.Contains(a.Ext)
|
|
if currentID == 0 {
|
|
currentID = a.OriginID
|
|
}
|
|
if a.OriginID != currentID {
|
|
if len(buffer) > 0 {
|
|
amap[currentID] = buffer
|
|
currentID = a.OriginID
|
|
buffer = nil
|
|
}
|
|
}
|
|
buffer = append(buffer, a)
|
|
}
|
|
if len(buffer) > 0 {
|
|
amap[currentID] = buffer
|
|
}
|
|
return amap, rows.Err()
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) FGet(id int) (*Attachment, error) {
|
|
a := &Attachment{ID: id}
|
|
e := s.fget.QueryRow(id).Scan(&a.OriginTable, &a.OriginID, &a.SectionTable, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
|
|
if len(a.Ext) == 0 {
|
|
return nil, ErrCorruptAttachPath
|
|
}
|
|
a.Image = ImageFileExts.Contains(a.Ext)
|
|
return a, nil
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) Get(id int) (*MiniAttachment, error) {
|
|
a := &MiniAttachment{ID: id}
|
|
err := s.get.QueryRow(id).Scan(&a.OriginID, &a.SectionID, &a.UploadedBy, &a.Path, &a.Extra)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.Ext = strings.TrimPrefix(filepath.Ext(a.Path), ".")
|
|
if len(a.Ext) == 0 {
|
|
return nil, ErrCorruptAttachPath
|
|
}
|
|
a.Image = ImageFileExts.Contains(a.Ext)
|
|
return a, nil
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error) {
|
|
res, err := s.add.Exec(sectionID, sectionTable, originID, originTable, uploadedBy, path, extra)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
lid, err := res.LastInsertId()
|
|
return int(lid), err
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) MoveTo(sectionID, originID int, originTable string) error {
|
|
_, err := s.move.Exec(sectionID, originID, originTable)
|
|
return err
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable, extra string) error {
|
|
_, err := s.moveByExtra.Exec(sectionID, originTable, extra)
|
|
return err
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) Count() (count int) {
|
|
e := s.count.QueryRow().Scan(&count)
|
|
if e != nil {
|
|
LogError(e)
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) CountIn(originTable string, oid int) (count int) {
|
|
e := s.countIn.QueryRow(originTable, oid).Scan(&count)
|
|
if e != nil {
|
|
LogError(e)
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) CountInPath(path string) (count int) {
|
|
e := s.countInPath.QueryRow(path).Scan(&count)
|
|
if e != nil {
|
|
LogError(e)
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *DefaultAttachmentStore) Delete(id int) error {
|
|
_, e := s.delete.Exec(id)
|
|
return e
|
|
}
|
|
|
|
// TODO: Split this out of this store
|
|
func (s *DefaultAttachmentStore) AddLinked(otable string, oid int) (err error) {
|
|
switch otable {
|
|
case "topics":
|
|
_, err = s.topicUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = Topics.Reload(oid)
|
|
case "replies":
|
|
_, err = s.replyUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = Rstore.GetCache().Remove(oid)
|
|
}
|
|
if err == sql.ErrNoRows {
|
|
err = nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// TODO: Split this out of this store
|
|
func (s *DefaultAttachmentStore) RemoveLinked(otable string, oid int) (err error) {
|
|
switch otable {
|
|
case "topics":
|
|
_, err = s.topicUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tc := Topics.GetCache(); tc != nil {
|
|
tc.Remove(oid)
|
|
}
|
|
case "replies":
|
|
_, err = s.replyUpdateAttachs.Exec(s.CountIn(otable, oid), oid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = Rstore.GetCache().Remove(oid)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// TODO: Add a table for the files and lock the file row when performing tasks related to the file
|
|
func DeleteAttachment(aid int) error {
|
|
a, err := Attachments.FGet(aid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = deleteAttachment(a)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_ = Attachments.RemoveLinked(a.OriginTable, a.OriginID)
|
|
return nil
|
|
}
|
|
|
|
func deleteAttachment(a *Attachment) error {
|
|
err := Attachments.Delete(a.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
count := Attachments.CountInPath(a.Path)
|
|
if count == 0 {
|
|
err := os.Remove("./attachs/" + a.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|