mirror of
https://github.com/ChronosX88/yans.git
synced 2024-09-07 22:46:17 +00:00
Implement attachments for article
This commit is contained in:
parent
537c3abe82
commit
56205ffdd1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.db
|
||||
.config.toml
|
||||
.idea
|
||||
.upload
|
10
README.md
10
README.md
@ -9,9 +9,9 @@
|
||||
- :heavy_check_mark: Wildmat support
|
||||
- :heavy_check_mark: Database (SQLite)
|
||||
- :heavy_check_mark: Basic article posting
|
||||
- :construction: Article retrieving
|
||||
- :construction: Multipart article support
|
||||
- :x: Transit mode
|
||||
- :heavy_check_mark: Article retrieving
|
||||
- :heavy_check_mark: Multipart article support
|
||||
- :construction: Transit mode
|
||||
- :x: Authentication
|
||||
|
||||
#### Commands
|
||||
@ -21,8 +21,8 @@
|
||||
- :heavy_check_mark: `CAPABILITIES`
|
||||
- :heavy_check_mark: `QUIT`
|
||||
- :construction: Article posting
|
||||
- :construction: `POST`
|
||||
- :x: `IHAVE`
|
||||
- :heavy_check_mark: `POST`
|
||||
- :construction: `IHAVE`
|
||||
- :heavy_check_mark: Article retrieving
|
||||
- :heavy_check_mark: `ARTICLE`
|
||||
- :heavy_check_mark: `HEAD`
|
||||
|
11
internal/backend/sqlite/migrations/002_attachments.sql
Normal file
11
internal/backend/sqlite/migrations/002_attachments.sql
Normal file
@ -0,0 +1,11 @@
|
||||
-- +goose Up
|
||||
|
||||
CREATE TABLE IF NOT EXISTS attachments_articles_mapping (
|
||||
article_id INTEGER REFERENCES articles(id),
|
||||
content_type TEXT NOT NULL,
|
||||
attachment_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
DROP TABLE IF EXISTS attachments_articles_mapping;
|
@ -124,6 +124,15 @@ func (sb *SQLiteBackend) SaveArticle(a models.Article, groups []string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// save attachments into db
|
||||
for _, v := range a.Attachments {
|
||||
_, err = sb.db.Exec("INSERT INTO attachments_articles_mapping (article_id, content_type, attachment_id) VALUES (?, ?, ?)", articleID, v.ContentType, v.FileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -135,6 +144,9 @@ func (sb *SQLiteBackend) GetArticle(messageID string) (models.Article, error) {
|
||||
if err := sb.db.Get(&a.ArticleNumber, "SELECT article_number FROM articles_to_groups WHERE article_id = ?", a.ID); err != nil {
|
||||
return a, err
|
||||
}
|
||||
if err := sb.db.Select(&a.Attachments, "SELECT content_type, attachment_id FROM attachments_articles_mapping WHERE article_id = ?", a.ID); err != nil {
|
||||
return a, err
|
||||
}
|
||||
return a, json.Unmarshal([]byte(a.HeaderRaw), &a.Header)
|
||||
}
|
||||
|
||||
@ -144,6 +156,9 @@ func (sb *SQLiteBackend) GetArticleByNumber(g *models.Group, num int) (models.Ar
|
||||
return a, err
|
||||
}
|
||||
a.ArticleNumber = num
|
||||
if err := sb.db.Select(&a.Attachments, "SELECT content_type, attachment_id FROM attachments_articles_mapping WHERE article_id = ?", a.ID); err != nil {
|
||||
return a, err
|
||||
}
|
||||
return a, json.Unmarshal([]byte(a.HeaderRaw), &a.Header)
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ type Config struct {
|
||||
BackendType string `toml:"backend_type"`
|
||||
Domain string `toml:"domain"`
|
||||
SQLite SQLiteBackendConfig `toml:"sqlite"`
|
||||
UploadPath string `toml:"upload_path"`
|
||||
}
|
||||
|
||||
type SQLiteBackendConfig struct {
|
||||
|
@ -17,4 +17,10 @@ type Article struct {
|
||||
Header textproto.MIMEHeader `db:"-"`
|
||||
Envelope *enmime.Envelope `db:"-"`
|
||||
ArticleNumber int `db:"-"`
|
||||
Attachments []Attachment
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
ContentType string `db:"content_type"`
|
||||
FileName string `db:"attachment_id"`
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import (
|
||||
"github.com/ChronosX88/yans/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"io/ioutil"
|
||||
"net/mail"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -22,9 +24,10 @@ type Handler struct {
|
||||
handlers map[string]func(s *Session, command string, arguments []string, id uint) error
|
||||
backend backend.StorageBackend
|
||||
serverDomain string
|
||||
uploadPath string
|
||||
}
|
||||
|
||||
func NewHandler(b backend.StorageBackend, serverDomain string) *Handler {
|
||||
func NewHandler(b backend.StorageBackend, serverDomain, uploadPath string) *Handler {
|
||||
h := &Handler{}
|
||||
h.backend = b
|
||||
h.handlers = map[string]func(s *Session, command string, arguments []string, id uint) error{
|
||||
@ -49,6 +52,7 @@ func NewHandler(b backend.StorageBackend, serverDomain string) *Handler {
|
||||
protocol.CommandXover: h.handleOver,
|
||||
}
|
||||
h.serverDomain = serverDomain
|
||||
h.uploadPath = uploadPath
|
||||
return h
|
||||
}
|
||||
|
||||
@ -347,13 +351,29 @@ func (h *Handler) handlePost(s *Session, command string, arguments []string, id
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !parentMessage.Thread.Valid {
|
||||
var parentHeader mail.Header
|
||||
err = json.Unmarshal([]byte(parentMessage.HeaderRaw), &parentHeader)
|
||||
parentMessageID := parentHeader.Get("Message-ID")
|
||||
a.Thread = sql.NullString{String: parentMessageID, Valid: true}
|
||||
} else {
|
||||
a.Thread = parentMessage.Thread
|
||||
var parentHeader mail.Header
|
||||
err = json.Unmarshal([]byte(parentMessage.HeaderRaw), &parentHeader)
|
||||
parentMessageID := parentHeader.Get("Message-ID")
|
||||
a.Thread = sql.NullString{String: parentMessageID, Valid: true}
|
||||
}
|
||||
|
||||
if len(envelope.Attachments) > 0 {
|
||||
// save attachments
|
||||
for _, v := range envelope.Attachments {
|
||||
if v.ContentType != "image/jpeg" && v.ContentType != "image/png" && v.ContentType != "image/gif" {
|
||||
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 441, Message: "disallowed attachment type"}.String())
|
||||
}
|
||||
ext_ := strings.Split(v.FileName, ".")
|
||||
ext := ext_[len(ext_)-1]
|
||||
fileName := uuid.New().String() + "." + ext
|
||||
err = ioutil.WriteFile(path.Join(h.uploadPath, fileName), v.Content, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Attachments = append(a.Attachments, models.Attachment{
|
||||
ContentType: v.ContentType,
|
||||
FileName: fileName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,7 +513,10 @@ func (h *Handler) handleArticle(s *Session, command string, arguments []string,
|
||||
builder = builder.Header(k, j)
|
||||
}
|
||||
}
|
||||
builder = builder.Text([]byte(a.Body)) // FIXME currently only plain text is supported
|
||||
builder = builder.Text([]byte(a.Body))
|
||||
for _, v := range a.Attachments {
|
||||
builder = builder.AddFileAttachment(path.Join(h.uploadPath, v.FileName))
|
||||
}
|
||||
p, err := builder.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -97,7 +97,7 @@ func (ns *NNTPServer) Start() error {
|
||||
|
||||
id, _ := uuid.NewUUID()
|
||||
closed := make(chan bool)
|
||||
session, err := NewSession(ctx, conn, Capabilities, id.String(), closed, NewHandler(ns.backend, ns.cfg.Domain))
|
||||
session, err := NewSession(ctx, conn, Capabilities, id.String(), closed, NewHandler(ns.backend, ns.cfg.Domain, ns.cfg.UploadPath))
|
||||
ns.sessionPoolMutex.Lock()
|
||||
ns.sessionPool[id.String()] = session
|
||||
ns.sessionPoolMutex.Unlock()
|
||||
|
Loading…
Reference in New Issue
Block a user