public/images/album
public/images/album.zip
log
-config.yaml
\ No newline at end of file
+config.yaml
+rsvpdump.sql
\ No newline at end of file
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
+ filippo.io/edwards25519 v1.1.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
+ github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/gorilla/context v1.1.2 // indirect
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
+github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
package main
import (
+ "encoding/json"
+ "fmt"
pkgdb "lgtm-rsvp/pkg/db"
pkgglob "lgtm-rsvp/pkg/glob"
pkgserver "lgtm-rsvp/pkg/server"
+ pkgserverapi "lgtm-rsvp/pkg/server/api"
+ pkgutils "lgtm-rsvp/pkg/utils"
"log"
"os"
+ "time"
+
+ "github.com/microcosm-cc/bluemonday"
)
+func printHelp() {
+ fmt.Println("help: print help")
+ fmt.Println("coli y: allow all comment from comment-list.json, force overide")
+ fmt.Println("coli n: block all comments approved from comment-list.json, key is title")
+}
+
+var _doColi bool = false
+var _doColiAct bool = false
+
+func doColi() error {
+
+ err := pkgdb.OpenDB(pkgglob.G_CONF.Db.Addr)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to open db: %s", err.Error())
+
+ }
+
+ err = pkgdb.Init(pkgglob.G_CONF.Db.InitFile, pkgglob.G_CONF.Admin.Id, pkgglob.G_CONF.Admin.Pw)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to init db: %s", err.Error())
+
+ }
+
+ fb, err := os.ReadFile(pkgglob.G_COMMENT_LIST_PATH)
+
+ if err != nil {
+ return fmt.Errorf("failed to read comment list: %v", err)
+ }
+
+ var coli pkgserverapi.CommntDataList
+
+ err = json.Unmarshal(fb, &coli)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to unmarshal comment list: %v", err)
+ }
+
+ clen := len(coli)
+
+ if _doColiAct {
+
+ log.Printf("allowing all data in comment list...\n")
+
+ for i := 0; i < clen; i++ {
+
+ c, err := pkgdb.GetCommentById(coli[i].CommentId)
+
+ id := ""
+ title := ""
+
+ if err != nil {
+ log.Printf(" - comment by id doesn't exit: %v\n", err)
+ comment_id, _ := pkgutils.GetRandomHex(32)
+ now := time.Now().UTC()
+ p := bluemonday.UGCPolicy()
+ title_san := p.Sanitize(coli[i].Title)
+ content_san := p.Sanitize(coli[i].Content)
+ if title_san == "" {
+ log.Printf(" - register comment: invalid title\n")
+ continue
+ }
+ if content_san == "" {
+ log.Printf(" - register comment: invalid content\n")
+ continue
+ }
+ timeRegistered := now.Format("2006-01-02-15-04-05")
+ err = pkgdb.RegisterComment(comment_id, title_san, content_san, timeRegistered)
+ if err != nil {
+ log.Printf(" - register comment failed: %v\n", err)
+ } else {
+ log.Printf(" - register comment success: %s\n", title_san)
+ }
+ id = comment_id
+ title = title_san
+ } else {
+ id = c.Id
+ title = c.Title
+ }
+
+ now := time.Now().UTC()
+ timeApproved := now.Format("2006-01-02-15-04-05")
+
+ err = pkgdb.ApproveComment(id, timeApproved)
+
+ if err != nil {
+ log.Printf(" - approve comment failed: %v\n", err)
+ } else {
+ log.Printf(" - approve comment success: %s\n", title)
+ }
+ }
+
+ } else {
+
+ log.Printf("blocking all data in comment list...\n")
+
+ for i := 0; i < clen; i++ {
+ err := pkgdb.DisapproveCommentByTitle(coli[i].Title)
+ if err != nil {
+ log.Printf(" - disapprove by title failed: %v\n", err)
+ } else {
+ log.Printf(" - disapprove by title success: %s\n", coli[i].Title)
+ }
+
+ }
+
+ }
+
+ return nil
+}
+
func main() {
err := pkgglob.LoadConfig()
os.Exit(-1)
}
+ if len(os.Args) > 1 {
+
+ if os.Args[1] == "help" {
+ printHelp()
+ os.Exit(0)
+ }
+ if os.Args[1] == "coli" {
+ if len(os.Args) != 3 {
+ fmt.Printf("invalid coli command count: %v\n", os.Args[1:])
+ printHelp()
+ os.Exit(-1)
+ } else if os.Args[2] == "y" {
+ _doColi = true
+ _doColiAct = true
+ } else if os.Args[2] == "n" {
+ _doColi = true
+ _doColiAct = false
+
+ } else {
+ fmt.Printf("invalid command coli: %v\n", os.Args[1:])
+ printHelp()
+ os.Exit(-1)
+ }
+
+ } else {
+ fmt.Printf("invalid command: %v\n", os.Args[1:])
+ printHelp()
+ os.Exit(-1)
+ }
+
+ }
+
+ if _doColi {
+ err := doColi()
+ if err != nil {
+ log.Printf("failed to do comment list action: %v\n", err)
+ os.Exit(-1)
+ } else {
+ os.Exit(0)
+ }
+ }
+
srv, err := pkgserver.CreateServerFromConfig()
if err != nil {
if err != nil {
- log.Printf("failed to open db: %s\n", err.Error())
+ log.Printf("failed to init db: %s\n", err.Error())
os.Exit(-1)
}
package db
import (
+ "crypto/tls"
+ "crypto/x509"
"database/sql"
"fmt"
"log"
"os"
+ "strings"
+ pkgglob "lgtm-rsvp/pkg/glob"
+
+ "github.com/go-sql-driver/mysql"
+ _ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"
)
var _db *sql.DB
+var _ismyql bool = false
+
var initiated bool = false
-type SqliteMaster struct {
+type SqlMaster struct {
TblName string
}
}
-func OpenDB(addr string) error {
+func createTLSConf() (*tls.Config, error) {
- db, err := sql.Open("sqlite3", addr)
+ rootCertPool := x509.NewCertPool()
+ pem, err := os.ReadFile(pkgglob.G_DB_CA_CERT)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read ca cert: %v", err)
+ }
+ if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
+ return nil, fmt.Errorf("failed to add pem")
+ }
+ clientCert := make([]tls.Certificate, 0, 1)
+ certs, err := tls.LoadX509KeyPair(pkgglob.G_DB_CLIENT_CERT, pkgglob.G_DB_CLIENT_KEY)
if err != nil {
+ return nil, fmt.Errorf("failed to load key pair")
+ }
- return err
+ clientCert = append(clientCert, certs)
+
+ c := tls.Config{
+ RootCAs: rootCertPool,
+ Certificates: clientCert,
+ InsecureSkipVerify: true,
}
- _db = db
+ return &c, nil
+}
+
+func OpenDB(addr string) error {
+
+ if strings.HasPrefix(addr, pkgglob.G_DB_MYSQL_PREFIX) {
+
+ log.Printf("using mysql\n")
+
+ _ismyql = true
+
+ idPwAddrDb := strings.ReplaceAll(addr, pkgglob.G_DB_MYSQL_PREFIX, "")
+
+ li1 := strings.SplitN(idPwAddrDb, "@", 2)
+
+ if len(li1) != 2 {
+ return fmt.Errorf("invalid db info")
+ }
+
+ li2 := strings.SplitN(li1[1], "/", 2)
+
+ if len(li2) != 2 {
+ return fmt.Errorf("invalid db info")
+ }
+
+ connnInfo := fmt.Sprintf("%s@tcp(%s)/%s?tls=custom", li1[0], li2[0], li2[1])
+
+ tc, err := createTLSConf()
+
+ if err != nil {
+ return fmt.Errorf("failed to create tls conf: %v", err)
+ }
+ err = mysql.RegisterTLSConfig("custom", tc)
+
+ if err != nil {
+ return fmt.Errorf("failed to register tls conf: %v", err)
+ }
+ db, err := sql.Open("mysql", connnInfo)
+
+ if err != nil {
+ return fmt.Errorf("failed to open mysql connection: %v", err)
+ }
+
+ _db = db
+
+ } else {
+
+ log.Printf("using sqlite3\n")
+
+ db, err := sql.Open("sqlite3", addr)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to open sqlite3 connection: %v", err)
+ }
+
+ _db = db
+
+ }
return nil
}
func Init(initfile string, adminId string, adminPw string) error {
- tables := make([]SqliteMaster, 0)
+ tables := make([]SqlMaster, 0)
admins := make([]Admin, 0)
- q := `
-
- SELECT
- tbl_name
- FROM
- sqlite_master
- WHERE
- type='table'
- `
+ q := ""
+
+ if _ismyql {
+ q = `
+
+ SELECT
+ table_name
+ FROM
+ information_schema.tables
+ `
+ } else {
+ q = `
+
+ SELECT
+ tbl_name
+ FROM
+ sqlite_master
+ WHERE
+ type='table'
+ `
+ }
a := []any{}
for res.Next() {
- t := SqliteMaster{}
+ t := SqlMaster{}
err = res.Scan(&t.TblName)
return fmt.Errorf("failed to init: %v", err)
}
- tables := make([]SqliteMaster, 0)
+ tables := make([]SqlMaster, 0)
q = `
for res.Next() {
- t := SqliteMaster{}
+ t := SqlMaster{}
err = res.Scan(&t.TblName)
q := `
SELECT
+ id,
title,
content,
timestamp_registered
comment := Comment{}
err = res.Scan(
+ &comment.Id,
&comment.Title,
&comment.Content,
&comment.TimestampRegistered,
if err != nil {
- return fmt.Errorf("faield to register comment: %v", err)
+ return fmt.Errorf("failed to register comment: %v", err)
}
return nil
return nil
}
+
+func DisapproveCommentByTitle(title string) error {
+
+ comments, err := ListApprovedComments()
+
+ if err != nil {
+
+ return fmt.Errorf("disapprove comment: list approved comments: %s", err.Error())
+ }
+
+ for i := 0; i < len(comments); i++ {
+
+ if comments[i].Title == title {
+ fmt.Printf("hit: %s\n", comments[i].Id)
+ q := `
+
+ UPDATE
+ comment
+ SET
+ timestamp_approved = NULL
+ WHERE
+ id = ?
+
+ `
+ a := []any{
+ comments[i].Id,
+ }
+
+ err = exec(q, a)
+
+ if err != nil {
+
+ return fmt.Errorf("failed to disapprove comment: %v", err)
+ }
+ }
+
+ }
+
+ return nil
+}
"mime/multipart"
"os"
+ pkgglob "lgtm-rsvp/pkg/glob"
+
"github.com/gin-gonic/gin"
)
-var mediaPath = "./data/media/"
+var mediaPath = pkgglob.G_MEDIA_PATH
func UploadMedia(c *gin.Context, file *multipart.FileHeader, new_filename string) error {
}
var G_CONF *Config
+
+var G_CONFIG_PATH = "config/config.yaml"
+
+var G_MEDIA_PATH = "data/media/"
+
+var G_MAIL_ERR_PATH = "data/comment-list.json"
+
+var G_ALBUM_PATH = "public/images/album"
+
+var G_DB_MYSQL_PREFIX = "mysql://"
+
+var G_DB_CA_CERT = "data/server-ca.pem"
+var G_DB_CLIENT_CERT = "data/client-cert.pem"
+var G_DB_CLIENT_KEY = "data/client-key.pem"
+
+var G_COMMENT_LIST_PATH = "comment-list.json"
conf := Config{}
- file_b, err := os.ReadFile("./config/config.yaml")
+ file_b, err := os.ReadFile(G_CONFIG_PATH)
if err != nil {
Content string `json:"content"`
}
+type CommntDataList []CommentData
+
type CommentData struct {
- CommentId string
- Title string
- Content string
+ CommentId string `json:"commentid"`
+ Title string `json:"title"`
+ Content string `json:"content"`
}
func GetApprovedComments(c *gin.Context) {
_comment = make(chan CommentData)
- f, err := os.OpenFile("data/mailerr.txt", os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
-
- if err != nil {
-
- reterr <- fmt.Errorf("failed to create mailerr.txt")
-
- return
- }
-
- reterr <- nil
-
- defer f.Close()
-
for {
c := <-_comment
if err != nil {
- msg := "==============================\n"
- msg += "id: " + c.CommentId + "\n"
- msg += "title: " + c.Title + "\n"
- msg += "content: " + c.Content + "\n\n"
+ err = writeMailErr(c)
- f.Write([]byte(msg))
+ if err != nil {
+ log.Printf("CRIT: failed to write mail err\n")
+ fmt.Println(c)
+ log.Printf("------------------------------\n")
+ }
log.Printf("send mail failed: %s\n", c.CommentId)
} else {
return nil
}
+
+func writeMailErr(c CommentData) error {
+
+ f, err := os.ReadFile(pkgglob.G_MAIL_ERR_PATH)
+
+ if err != nil {
+ return fmt.Errorf("failed to read mail err path: %v", err)
+ }
+
+ errCommData := make(CommntDataList, 0)
+
+ err = json.Unmarshal(f, &errCommData)
+
+ if err != nil {
+ return fmt.Errorf("failed to unmarshal mail err: %v", err)
+ }
+
+ errCommData = append(errCommData, c)
+
+ eb, err := json.Marshal(errCommData)
+
+ if err != nil {
+ return fmt.Errorf("failed to marshal mail err: %v", err)
+ }
+
+ err = os.WriteFile(pkgglob.G_MAIL_ERR_PATH, eb, 0644)
+
+ if err != nil {
+ fmt.Println(string(eb))
+ return fmt.Errorf("failed to write mail err: %v", err)
+ }
+
+ return nil
+
+}
func configureServer(e *gin.Engine) error {
- albumPath := "./public/images/album"
+ albumPath := pkgglob.G_ALBUM_PATH
if _, err := os.Stat(albumPath); err != nil {
return fmt.Errorf("album addr not found: %v", err)
fi
-./rsvp.out
+if [ "$1" == "filelog" ]
+then
+ currdate=$(date '+%Y-%m-%d-%H-%M-%S')
+ ./rsvp.out >> "data/$currdate" 2>&1
+else
+ ./rsvp.out
+fi
+
--- /dev/null
+#!/bin/bash
+
+
+sqlite3 data/rsvp.db .dump > rsvpdump.sql
+
+
+sed -i -e 's/PRAGMA foreign_keys=OFF;//g' rsvpdump.sql
+sed -i -e 's/BEGIN TRANSACTION;//g' rsvpdump.sql
+sed -i -e 's/INTEGER PRIMARY KEY/INT NOT NULL AUTO_INCREMENT/g' rsvpdump.sql
+sed -i -e 's/pw TEXT/pw TEXT,\n PRIMARY KEY(admin_id)/g' rsvpdump.sql
+sed -i -e '0,/content TEXT/ s//content TEXT,\n PRIMARY KEY(story_id)/' rsvpdump.sql
+sed -i -e 's/timestamp_approved TEXT/timestamp_approved TEXT,\n PRIMARY KEY(comment_id)/g' rsvpdump.sql