]> git.feebdaed.xyz Git - lgtm-rsvp.git/commitdiff
add: mysql connection
authorseantywork <seantywork@gmail.com>
Wed, 23 Jul 2025 13:49:14 +0000 (13:49 +0000)
committerseantywork <seantywork@gmail.com>
Wed, 23 Jul 2025 13:49:14 +0000 (13:49 +0000)
13 files changed:
.gitignore
go.mod
go.sum
main.go
pkg/db/db.go
pkg/db/db_comment.go
pkg/db/db_media.go
pkg/glob/glob.go
pkg/glob/glob_conf.go
pkg/server/api/api_comment.go
pkg/server/server.go
run.sh
run_dump.sh [new file with mode: 0755]

index d95c76d72513fbc49723e57c7ebf3d01346882ea..82beff3bdd9091c371e292eeee4533c740827f18 100644 (file)
@@ -13,4 +13,5 @@ album.zip
 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
diff --git a/go.mod b/go.mod
index 2ef8c1e0452409c90d2a600c28fecb251a047ab1..0c4d606afccce737f2e2b9611fdd6293afbef1b9 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
 
 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
@@ -24,6 +25,7 @@ require (
        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
diff --git a/go.sum b/go.sum
index c85989ea0e49cb2eff73fe8fd5bf345cd3b533a1..ef284b2d4a02b000bd23d7e30194bbf40d991dc8 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 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=
@@ -33,6 +35,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
 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=
diff --git a/main.go b/main.go
index 85b011062f931a1ba4628633417f5c423dd4636a..d35f919f74439f326f635da6ab81d81109937877 100644 (file)
--- a/main.go
+++ b/main.go
 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()
@@ -19,6 +141,48 @@ func main() {
                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 {
@@ -45,7 +209,7 @@ func main() {
 
        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)
        }
index c19e31813c9d1d99af6290088187893b95efffa2..45a9ebd8a4e9e8b02f1c0771e60310ea03a37936 100644 (file)
@@ -1,19 +1,28 @@
 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
 }
 
@@ -52,35 +61,121 @@ func exec(query string, args []any) error {
 
 }
 
-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{}
 
@@ -95,7 +190,7 @@ func Init(initfile string, adminId string, adminPw string) error {
 
        for res.Next() {
 
-               t := SqliteMaster{}
+               t := SqlMaster{}
 
                err = res.Scan(&t.TblName)
 
@@ -204,7 +299,7 @@ func createFromInitSql(initfile string) error {
                return fmt.Errorf("failed to init: %v", err)
        }
 
-       tables := make([]SqliteMaster, 0)
+       tables := make([]SqlMaster, 0)
 
        q = `
        
@@ -229,7 +324,7 @@ func createFromInitSql(initfile string) error {
 
        for res.Next() {
 
-               t := SqliteMaster{}
+               t := SqlMaster{}
 
                err = res.Scan(&t.TblName)
 
index efd97e5641729700af2e4b8cb88e79d7b32b4919..d9ad21679b4348ce89d19eaed0d58dfa16e1070b 100644 (file)
@@ -22,6 +22,7 @@ func ListApprovedComments() ([]Comment, error) {
        q := `
        
        SELECT
+               id,
                title,
                content,
                timestamp_registered
@@ -49,6 +50,7 @@ func ListApprovedComments() ([]Comment, error) {
                comment := Comment{}
 
                err = res.Scan(
+                       &comment.Id,
                        &comment.Title,
                        &comment.Content,
                        &comment.TimestampRegistered,
@@ -158,7 +160,7 @@ func RegisterComment(id string, title string, content string, tr string) error {
 
        if err != nil {
 
-               return fmt.Errorf("faield to register comment: %v", err)
+               return fmt.Errorf("failed to register comment: %v", err)
        }
 
        return nil
@@ -214,3 +216,43 @@ func ApproveComment(id string, ta string) error {
 
        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
+}
index ae00bbac1b52d897195c49de40b2cbe9cb74dd22..d3dc544cac975926c709d7ad13b238e694bb0f7f 100644 (file)
@@ -5,10 +5,12 @@ import (
        "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 {
 
index 2cfc9ac8272a50da4da5d9926607efbc964ccf66..6315d86b5223a3c72fbea17e81c3890854933fe3 100644 (file)
@@ -34,3 +34,19 @@ type Config struct {
 }
 
 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"
index af2a596765837db9fd897265b28ab1203add9a1b..907efaf72d227f0e195913f8886fada581c0d930 100644 (file)
@@ -10,7 +10,7 @@ func LoadConfig() error {
 
        conf := Config{}
 
-       file_b, err := os.ReadFile("./config/config.yaml")
+       file_b, err := os.ReadFile(G_CONFIG_PATH)
 
        if err != nil {
 
index 7b5ab56f4713d4b809be8a2f1c8ad3febe8170ad..6f1ef43c7c3b44b136f78f7ca7d946ef464774f7 100644 (file)
@@ -25,10 +25,12 @@ type CommentInfo struct {
        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) {
@@ -194,19 +196,6 @@ func StartMailer(reterr chan error) {
 
        _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
@@ -215,12 +204,13 @@ func StartMailer(reterr chan error) {
 
                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 {
@@ -262,3 +252,38 @@ func sendMail(commentId string, title string, content string) error {
 
        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
+
+}
index dbcaa0393ef2f845ef15f9b0c89f0565bebb9e9b..48d6261f21f17dc778c1053441a196cf5c09286e 100644 (file)
@@ -79,7 +79,7 @@ func CreateServerFromConfig() (*gin.Engine, error) {
 
 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)
diff --git a/run.sh b/run.sh
index 8f6b90cf942021b573d52a327e06691fe868921e..6113c7b96491b33a7d9bbb8180d1a64f9d2dcb61 100755 (executable)
--- a/run.sh
+++ b/run.sh
@@ -13,6 +13,13 @@ then
 
 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
+
 
 
diff --git a/run_dump.sh b/run_dump.sh
new file mode 100755 (executable)
index 0000000..d744aca
--- /dev/null
@@ -0,0 +1,12 @@
+#!/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