...

Source file src/codeberg.org/tslocum/sriracha/internal/database/database_board.go

Documentation: codeberg.org/tslocum/sriracha/internal/database

     1  package database
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"strings"
     7  
     8  	. "codeberg.org/tslocum/sriracha/model"
     9  	"github.com/jackc/pgx/v5"
    10  )
    11  
    12  // boardCache is a cache of all boards.
    13  var boardCache []*Board
    14  
    15  func (db *DB) setBoardAttributes(b *Board) {
    16  	rows, err := db.conn.Query(context.Background(), "SELECT upload FROM board_upload WHERE board = $1", b.ID)
    17  	if err != nil {
    18  		log.Fatalf("failed to select board uploads: %s", err)
    19  	}
    20  	b.Uploads = nil
    21  	for rows.Next() {
    22  		var mimeType string
    23  		err := rows.Scan(&mimeType)
    24  		if err != nil {
    25  			log.Fatalf("failed to select board uploads: %s", err)
    26  		}
    27  		for _, u := range db.config.UploadTypes() {
    28  			if u.MIME == mimeType {
    29  				b.Uploads = append(b.Uploads, u.MIME)
    30  				break
    31  			}
    32  		}
    33  	}
    34  
    35  	rows, err = db.conn.Query(context.Background(), "SELECT embed FROM board_embed WHERE board = $1", b.ID)
    36  	if err != nil {
    37  		log.Fatalf("failed to select board embeds: %s", err)
    38  	}
    39  	b.Embeds = nil
    40  	for rows.Next() {
    41  		var name string
    42  		err := rows.Scan(&name)
    43  		if err != nil {
    44  			log.Fatalf("failed to select board embeds: %s", err)
    45  		}
    46  		b.Embeds = append(b.Embeds, name)
    47  	}
    48  }
    49  
    50  func (db *DB) AddBoard(b *Board) {
    51  	boardCache = nil
    52  
    53  	var reports int
    54  	if b.Reports {
    55  		reports = 1
    56  	}
    57  	var oekaki int
    58  	if b.Oekaki {
    59  		oekaki = 1
    60  	}
    61  	var backlinks int
    62  	if b.Backlinks {
    63  		backlinks = 1
    64  	}
    65  	var gallery int
    66  	if b.Gallery {
    67  		gallery = 1
    68  	}
    69  	_, err := db.conn.Exec(context.Background(), "INSERT INTO board VALUES (DEFAULT, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39)",
    70  		b.Dir,
    71  		b.Name,
    72  		b.Description,
    73  		b.Type,
    74  		b.Lock,
    75  		b.Approval,
    76  		reports,
    77  		b.Style,
    78  		b.Locale,
    79  		b.Delay,
    80  		b.MinName,
    81  		b.MaxName,
    82  		b.MinEmail,
    83  		b.MaxEmail,
    84  		b.MinSubject,
    85  		b.MaxSubject,
    86  		b.MinMessage,
    87  		b.MaxMessage,
    88  		b.MinSizeThread,
    89  		b.MaxSizeThread,
    90  		b.MinSizeReply,
    91  		b.MaxSizeReply,
    92  		b.ThumbWidth,
    93  		b.ThumbHeight,
    94  		b.DefaultName,
    95  		b.WordBreak,
    96  		b.Truncate,
    97  		b.Threads,
    98  		b.Replies,
    99  		b.MaxThreads,
   100  		b.MaxReplies,
   101  		oekaki,
   102  		strings.Join(b.Rules, "|||"),
   103  		b.Hide,
   104  		backlinks,
   105  		b.Instances,
   106  		b.Identifiers,
   107  		b.Files,
   108  		gallery,
   109  	)
   110  	if err != nil {
   111  		log.Fatalf("failed to insert board: %s", err)
   112  	}
   113  	err = db.conn.QueryRow(context.Background(), "SELECT id FROM board WHERE dir = $1", b.Dir).Scan(&b.ID)
   114  	if err != nil || b.ID == 0 {
   115  		log.Fatalf("failed to select id of inserted board: %s", err)
   116  	}
   117  	for _, upload := range b.Uploads {
   118  		_, err := db.conn.Exec(context.Background(), "INSERT INTO board_upload VALUES ($1, $2)", b.ID, upload)
   119  		if err != nil {
   120  			log.Fatalf("failed to insert board uploads: %s", err)
   121  		}
   122  	}
   123  	for _, embed := range b.Embeds {
   124  		_, err := db.conn.Exec(context.Background(), "INSERT INTO board_embed VALUES ($1, $2)", b.ID, embed)
   125  		if err != nil {
   126  			log.Fatalf("failed to insert board embeds: %s", err)
   127  		}
   128  	}
   129  }
   130  
   131  func (db *DB) AllBoards() []*Board {
   132  	if boardCache != nil {
   133  		return boardCache
   134  	}
   135  	boardCache = []*Board{}
   136  
   137  	rows, err := db.conn.Query(context.Background(), "SELECT * FROM board ORDER BY dir ASC")
   138  	if err != nil {
   139  		log.Fatalf("failed to select all boards: %s", err)
   140  	}
   141  	for rows.Next() {
   142  		b := &Board{}
   143  		err := scanBoard(b, rows)
   144  		if err != nil {
   145  			log.Fatalf("failed to select all boards: %s", err)
   146  		}
   147  		boardCache = append(boardCache, b)
   148  	}
   149  	for _, b := range boardCache {
   150  		db.setBoardAttributes(b)
   151  	}
   152  
   153  	return boardCache
   154  }
   155  
   156  func (db *DB) BoardByID(id int) *Board {
   157  	// Search cached boards.
   158  	for _, b := range db.AllBoards() {
   159  		if b.ID == id {
   160  			return b
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  func (db *DB) BoardByDir(dir string) *Board {
   167  	// Search cached boards.
   168  	for _, b := range db.AllBoards() {
   169  		if b.Dir == dir {
   170  			return b
   171  		}
   172  	}
   173  	return nil
   174  }
   175  
   176  func (db *DB) UniqueUserPosts(b *Board) int {
   177  	var count int
   178  	var err error
   179  	if b == nil {
   180  		err = db.conn.QueryRow(context.Background(), "SELECT COUNT(DISTINCT ip) FROM post").Scan(&count)
   181  	} else {
   182  		err = db.conn.QueryRow(context.Background(), "SELECT COUNT(DISTINCT ip) FROM post WHERE board = $1", b.ID).Scan(&count)
   183  	}
   184  	if err == pgx.ErrNoRows {
   185  		return 0
   186  	} else if err != nil {
   187  		log.Fatalf("failed to select unique user posts: %s", err)
   188  	}
   189  	return count
   190  }
   191  
   192  func (db *DB) UpdateBoard(b *Board) {
   193  	if b.ID <= 0 {
   194  		log.Fatalf("invalid board ID %d", b.ID)
   195  	}
   196  	boardCache = nil
   197  
   198  	var reports int
   199  	if b.Reports {
   200  		reports = 1
   201  	}
   202  	var oekaki int
   203  	if b.Oekaki {
   204  		oekaki = 1
   205  	}
   206  	var backlinks int
   207  	if b.Backlinks {
   208  		backlinks = 1
   209  	}
   210  	var gallery int
   211  	if b.Gallery {
   212  		gallery = 1
   213  	}
   214  	_, err := db.conn.Exec(context.Background(), "UPDATE board SET dir = $1, name = $2, description = $3, type = $4, lock = $5, approval = $6, reports = $7, style = $8, locale = $9, delay = $10, minname = $11, maxname = $12, minemail = $13, maxemail = $14, minsubject = $15, maxsubject = $16, minmessage = $17, maxmessage = $18, minsizethread = $19, maxsizethread = $20, minsizereply = $21, maxsizereply = $22, thumbwidth = $23, thumbheight = $24, defaultname = $25, wordbreak = $26, truncate = $27, threads = $28, replies = $29, maxthreads = $30, maxreplies = $31, oekaki = $32, rules = $33, hide = $34, backlinks = $35, instances = $36, identifiers = $37, files = $38, gallery = $39 WHERE id = $40",
   215  		b.Dir,
   216  		b.Name,
   217  		b.Description,
   218  		b.Type,
   219  		b.Lock,
   220  		b.Approval,
   221  		reports,
   222  		b.Style,
   223  		b.Locale,
   224  		b.Delay,
   225  		b.MinName,
   226  		b.MaxName,
   227  		b.MinEmail,
   228  		b.MaxEmail,
   229  		b.MinSubject,
   230  		b.MaxSubject,
   231  		b.MinMessage,
   232  		b.MaxMessage,
   233  		b.MinSizeThread,
   234  		b.MaxSizeThread,
   235  		b.MinSizeReply,
   236  		b.MaxSizeReply,
   237  		b.ThumbWidth,
   238  		b.ThumbHeight,
   239  		b.DefaultName,
   240  		b.WordBreak,
   241  		b.Truncate,
   242  		b.Threads,
   243  		b.Replies,
   244  		b.MaxThreads,
   245  		b.MaxReplies,
   246  		oekaki,
   247  		strings.Join(b.Rules, "|||"),
   248  		b.Hide,
   249  		backlinks,
   250  		b.Instances,
   251  		b.Identifiers,
   252  		b.Files,
   253  		gallery,
   254  		b.ID,
   255  	)
   256  	if err != nil {
   257  		log.Fatalf("failed to update board: %s", err)
   258  	}
   259  
   260  	_, err = db.conn.Exec(context.Background(), "DELETE FROM board_upload WHERE board = $1", b.ID)
   261  	if err != nil {
   262  		log.Fatalf("failed to delete board uploads: %s", err)
   263  	}
   264  	for _, upload := range b.Uploads {
   265  		_, err := db.conn.Exec(context.Background(), "INSERT INTO board_upload VALUES ($1, $2)", b.ID, upload)
   266  		if err != nil {
   267  			log.Fatalf("failed to insert board uploads: %s", err)
   268  		}
   269  	}
   270  
   271  	_, err = db.conn.Exec(context.Background(), "DELETE FROM board_embed WHERE board = $1", b.ID)
   272  	if err != nil {
   273  		log.Fatalf("failed to delete board embeds: %s", err)
   274  	}
   275  	for _, embed := range b.Embeds {
   276  		_, err := db.conn.Exec(context.Background(), "INSERT INTO board_embed VALUES ($1, $2)", b.ID, embed)
   277  		if err != nil {
   278  			log.Fatalf("failed to insert board embeds: %s", err)
   279  		}
   280  	}
   281  }
   282  
   283  func (db *DB) DeleteBoard(id int) {
   284  	if id == 0 {
   285  		return
   286  	}
   287  	boardCache = nil
   288  
   289  	_, err := db.conn.Exec(context.Background(), "DELETE FROM board WHERE id = $1", id)
   290  	if err != nil {
   291  		log.Fatalf("failed to delete board: %s", err)
   292  	}
   293  	db.DeleteSubscriptionsByBoard(id)
   294  }
   295  
   296  func (db *DB) ClearBoardCache() {
   297  	boardCache = nil
   298  }
   299  
   300  func scanBoard(b *Board, row pgx.Row) error {
   301  	var (
   302  		reports   int
   303  		oekaki    int
   304  		rules     string
   305  		backlinks int
   306  		gallery   int
   307  	)
   308  	err := row.Scan(
   309  		&b.ID,
   310  		&b.Dir,
   311  		&b.Name,
   312  		&b.Description,
   313  		&b.Type,
   314  		&b.Lock,
   315  		&b.Approval,
   316  		&reports,
   317  		&b.Style,
   318  		&b.Locale,
   319  		&b.Delay,
   320  		&b.MinName,
   321  		&b.MaxName,
   322  		&b.MinEmail,
   323  		&b.MaxEmail,
   324  		&b.MinSubject,
   325  		&b.MaxSubject,
   326  		&b.MinMessage,
   327  		&b.MaxMessage,
   328  		&b.MinSizeThread,
   329  		&b.MaxSizeThread,
   330  		&b.MinSizeReply,
   331  		&b.MaxSizeReply,
   332  		&b.ThumbWidth,
   333  		&b.ThumbHeight,
   334  		&b.DefaultName,
   335  		&b.WordBreak,
   336  		&b.Truncate,
   337  		&b.Threads,
   338  		&b.Replies,
   339  		&b.MaxThreads,
   340  		&b.MaxReplies,
   341  		&oekaki,
   342  		&rules,
   343  		&b.Hide,
   344  		&backlinks,
   345  		&b.Instances,
   346  		&b.Identifiers,
   347  		&b.Files,
   348  		&gallery,
   349  	)
   350  	if err != nil {
   351  		return err
   352  	}
   353  	b.Reports = reports == 1
   354  	b.Oekaki = oekaki == 1
   355  	if rules != "" {
   356  		b.Rules = strings.Split(rules, "|||")
   357  	}
   358  	b.Backlinks = backlinks == 1
   359  	b.Gallery = gallery == 1
   360  	return nil
   361  }
   362  

View as plain text