Skip to content

Commit 98097c3

Browse files
authored
Merge pull request #4 from prinzpiuz/prinz/like_counter
feat(likes counter): Implementing Likes Counter
2 parents d67b273 + 674a859 commit 98097c3

35 files changed

+1490
-84
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ __debug_*
3131
# DB files
3232
BloTils.db
3333
*.db
34+
*.session.sql

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# BloTils
1+
# **BLOTILS** aka Blog Utils
22

3-
Minimal go server to count clapping
3+
Utils for your static blog or pages

config.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"DBLocation": "./BloTils.db",
77
"Vacuum": "full",
88
"ForeignKeys": true
9-
}
9+
},
10+
"StaticFiles": "static"
1011
},
1112

1213
"Name": "BloTils",

go.mod

+10
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,19 @@ require (
77
github.com/mattn/go-sqlite3 v1.14.22
88
)
99

10+
require (
11+
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
12+
github.com/didip/tollbooth v4.0.2+incompatible // indirect
13+
github.com/didip/tollbooth/v8 v8.0.1 // indirect
14+
github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect
15+
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
16+
golang.org/x/time v0.9.0 // indirect
17+
)
18+
1019
require (
1120
github.com/charmbracelet/x/ansi v0.1.4 // indirect
1221
github.com/felixge/httpsnoop v1.0.3 // indirect
22+
github.com/rs/cors v1.11.1
1323
)
1424

1525
require (

go.sum

+15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1+
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
2+
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
13
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
24
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
35
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
46
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
57
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
68
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
9+
github.com/didip/tollbooth v4.0.2+incompatible h1:fVSa33JzSz0hoh2NxpwZtksAzAgd7zjmGO20HCZtF4M=
10+
github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++elm/CsuWBMa5Jv4WY0PEY=
11+
github.com/didip/tollbooth/v8 v8.0.1 h1:VAAapTo1t4Bn6bbpcHjuovwoa9u3JH++wgjbpWv+rB8=
12+
github.com/didip/tollbooth/v8 v8.0.1/go.mod h1:oEd9l+ep373d7DmvKLc0a5gasPOev2mTewi6KPQBGJ4=
713
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
814
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
15+
github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw=
16+
github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec=
917
github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA=
1018
github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
1119
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
1220
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
1321
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
1422
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
23+
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
1524
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
1625
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
1726
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -22,9 +31,15 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
2231
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
2332
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
2433
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
34+
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
35+
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
2536
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
2637
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
2738
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
39+
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
40+
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
2841
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2942
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
3043
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
44+
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
45+
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=

src/app/app.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"os"
99
"path/filepath"
10+
fp "path/filepath"
1011
)
1112

1213
type App struct {
@@ -15,10 +16,11 @@ type App struct {
1516
}
1617

1718
type Config struct {
18-
ServerConfig server.ServerConfig
19-
Name string
20-
Version string
21-
Env string
19+
ServerConfig server.ServerConfig
20+
Name string
21+
Version string
22+
Env string
23+
BaseDirectory string
2224
}
2325

2426
func (app *App) Start() {
@@ -32,6 +34,13 @@ func LoadConfig(filepath string) Config {
3234
fmt.Println(err)
3335
return Config{}
3436
}
37+
baseDir, err := os.Getwd()
38+
if err != nil {
39+
panic(err)
40+
}
41+
config.BaseDirectory = baseDir
42+
config.ServerConfig.BaseDirectory = baseDir
43+
config.ServerConfig.DB.DBBaseDirectory = fp.Join(baseDir, "src/db")
3544
return config
3645
}
3746

@@ -65,5 +74,6 @@ func New(config Config) *App {
6574
Config: config,
6675
}
6776
routes.RegisterRoutes(server)
77+
routes.ServeStaticFiles(server)
6878
return app
6979
}

src/app/app_logo.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ import (
99
var logoStyle = lipgloss.NewStyle().
1010
Bold(true).
1111
Foreground(lipgloss.Color("#7D56F4"))
12-
var subtitleStyle = lipgloss.NewStyle().
13-
Padding(0, 22).
14-
Bold(true)
1512

1613
const appLogo = `
1714
██████ ██ ██████ ██████ ██ ██ ███████
@@ -21,11 +18,20 @@ const appLogo = `
2118
██████ █████ ██████ ██ ██ █████ ███████
2219
`
2320

21+
func getversion(c Config) string {
22+
return fmt.Sprintf("Version : %s\n", c.Version)
23+
}
24+
25+
func getPort(c Config) string {
26+
return fmt.Sprintf("Server Running On %s:%d\n", c.ServerConfig.Host, c.ServerConfig.Port)
27+
}
28+
2429
func logo(c Config) {
2530
fmt.Print(logoStyle.Render(appLogo))
2631
fmt.Println()
27-
fmt.Printf("Version : %s", c.Version)
28-
fmt.Print(subtitleStyle.Render(fmt.Sprintf("Server Running On %s:%d", c.ServerConfig.Host, c.ServerConfig.Port)))
32+
fmt.Print("Utilities For Your Blog Engine\n")
33+
fmt.Print(getversion(c))
34+
fmt.Print(getPort(c))
2935
fmt.Println()
3036
fmt.Println()
3137
}

src/db/db.go

+17-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package db provides functionality for interacting with the application's database.
12
package db
23

34
import (
@@ -6,6 +7,7 @@ import (
67
"fmt"
78
"log"
89
"os"
10+
"path/filepath"
911

1012
"github.com/golang-migrate/migrate"
1113
"github.com/golang-migrate/migrate/database/sqlite3"
@@ -15,10 +17,11 @@ import (
1517
)
1618

1719
type DB struct {
18-
DBLocation string
19-
Vacuum string
20-
ForeignKeys bool
21-
Connection *sql.DB
20+
DBLocation string
21+
Vacuum string
22+
ForeignKeys bool
23+
Connection *sql.DB
24+
DBBaseDirectory string
2225
}
2326

2427
func closeDB(new_db *sql.DB) {
@@ -32,7 +35,7 @@ func closeDB(new_db *sql.DB) {
3235
func closeFile(fSrc source.Driver) {
3336
defer func() {
3437
if err := fSrc.Close(); err != nil {
35-
log.Printf("Error Closing Migration Files %s", err)
38+
log.Printf("Error Closing Migration Files: %s", err)
3639
}
3740
}()
3841
}
@@ -56,7 +59,8 @@ func (db *DB) Initialize() error {
5659
closeDB(new_db)
5760
return err
5861
}
59-
err = runMigrations(new_db)
62+
migrationFiles := filepath.Join(db.DBBaseDirectory, "migrations")
63+
err = runMigrations(new_db, migrationFiles)
6064
if err != nil {
6165
log.Println("Error Running Migrations")
6266
closeDB(new_db)
@@ -66,32 +70,33 @@ func (db *DB) Initialize() error {
6670
return nil
6771
}
6872

69-
func runMigrations(db *sql.DB) error {
73+
func runMigrations(db *sql.DB, migrationFiles string) error {
7074
instance, err := sqlite3.WithInstance(db, &sqlite3.Config{})
7175
if err != nil {
72-
log.Printf("Error Connecting With SQLite Instance %s", err)
76+
log.Printf("Error Connecting With SQLite Instance: %s", err)
7377
return err
7478
}
7579

76-
fSrc, err := (&file.File{}).Open("./src/db/migrations")
80+
fSrc, err := (&file.File{}).Open(migrationFiles)
81+
print()
7782
if err != nil {
7883
closeFile(fSrc)
79-
log.Printf("Error Getting Migration Files %s", err)
84+
log.Printf("Error Getting Migration Files: %s", err)
8085
return err
8186
}
8287

8388
m, err := migrate.NewWithInstance("file", fSrc, "sqlite3", instance)
8489
if err != nil {
8590
closeFile(fSrc)
86-
log.Printf("Error Creating Migration Instance %s", err)
91+
log.Printf("Error Creating Migration Instance: %s", err)
8792
return err
8893
}
8994
if err := m.Up(); err != nil {
9095
if err == migrate.ErrNoChange {
9196
log.Println("No Migrations To Run")
9297
} else {
9398
closeFile(fSrc)
94-
log.Printf("Error While Running UP Migrations %s", err)
99+
log.Printf("Error While Running UP Migrations: %s", err)
95100
return err
96101
}
97102
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
DROP TABLE IF EXISTS sources;
2-
DROP TABLE IF EXISTS liked_ips;
3-
DROP TABLE IF EXISTS likes;
1+
DROP TABLE IF EXISTS DomainSettings;
2+
DROP TABLE IF EXISTS Domain;
3+
DROP TABLE IF EXISTS Liked_IPs;
4+
DROP TABLE IF EXISTS Likes;
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
1-
CREATE TABLE IF NOT EXISTS sources (
2-
ID INTEGER PRIMARY KEY AUTOINCREMENT,
3-
Source TEXT,
4-
Likes NUMERIC,
5-
Comments NUMERIC,
6-
CreatedTime DATETIME DEFAULT CURRENT_TIMESTAMP
1+
CREATE TABLE IF NOT EXISTS DomainSettings (
2+
id INTEGER PRIMARY KEY AUTOINCREMENT,
3+
likes NUMERIC check(
4+
likes = 0
5+
or likes = 1
6+
),
7+
comments NUMERIC check(
8+
comments = 0
9+
or comments = 1
10+
),
11+
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
712
);
8-
CREATE TABLE IF NOT EXISTS liked_ips (
9-
ID INTEGER PRIMARY KEY AUTOINCREMENT,
10-
IP TEXT,
11-
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
13+
CREATE TABLE IF NOT EXISTS Domain (
14+
id INTEGER PRIMARY KEY AUTOINCREMENT,
15+
settings_id INTEGER,
16+
domain VARCHAR(255) UNIQUE,
17+
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
18+
FOREIGN KEY (settings_id) REFERENCES DomainSettings(id)
1219
);
13-
CREATE TABLE IF NOT EXISTS likes (
14-
ID INTEGER PRIMARY KEY AUTOINCREMENT,
15-
Uri TEXT,
16-
Count INTEGER,
17-
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
20+
CREATE TABLE IF NOT EXISTS Liked_IPs (
21+
id INTEGER PRIMARY KEY AUTOINCREMENT,
22+
ip VARCHAR(255) UNIQUE,
23+
count INTEGER check(count >= 0),
24+
domain VARCHAR(255),
25+
path TEXT,
26+
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
27+
);
28+
CREATE TABLE IF NOT EXISTS Likes (
29+
id INTEGER PRIMARY KEY AUTOINCREMENT,
30+
uri TEXT UNIQUE,
31+
count INTEGER check(count >= 0),
32+
domain_id INTEGER,
33+
FOREIGN KEY (domain_id) REFERENCES Domain(id)
1834
);

src/db/models.go

+62-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,76 @@
1+
// Package db provides functionality for interacting with the application's database.
12
package db
23

34
import "time"
45

5-
type Source struct {
6+
// DomainSettings represents the settings for a domain, including whether likes and comments are enabled,
7+
// and the timestamp of the last update.
8+
type DomainSettings struct {
69
id int
7-
source string
8-
like bool
9-
comment bool
10+
likes bool
11+
comments bool
1012
timestamp time.Time
1113
}
1214

13-
type LikedIP struct {
14-
id int
15-
ip string
15+
// Domain represents a domain and its associated settings.
16+
// The id field is the unique identifier for the domain.
17+
// The settings field contains the domain-specific settings.
18+
// The domain field contains the domain name.
19+
// The timestamp field contains the timestamp for when the domain was created or updated.
20+
type Domain struct {
21+
ID int
22+
settings DomainSettings
23+
domain string
1624
timestamp time.Time
1725
}
1826

19-
type Like struct {
27+
// IsEmpty returns true if the Domain is the zero value.
28+
func (domain Domain) IsEmpty() bool {
29+
return domain == Domain{}
30+
}
31+
32+
// LikesEnabled returns whether likes are enabled for the given Domain.
33+
func (domain Domain) LikesEnabled() bool {
34+
return domain.settings.likes
35+
}
36+
37+
// CommentsEnabled returns whether comments are enabled for the given Domain.
38+
func (domain Domain) CommentsEnabled() bool {
39+
return domain.settings.comments
40+
}
41+
42+
// Like represents a like for a domain and URI.
43+
// The id field is the unique identifier for the like.
44+
// The domain field is the domain the like is for.
45+
// The uri field is the URI the like is for.
46+
// The count field is the number of likes for the domain and URI.
47+
// The timestamp field is the timestamp of when the like was created.
48+
type Likes struct {
49+
id int
50+
domain_id int
51+
Domain Domain
52+
URI string
53+
Count int
54+
}
55+
56+
func (likes Likes) IsEmpty() bool {
57+
return likes == Likes{}
58+
}
59+
60+
func (likes Likes) GetDomainID() int {
61+
return likes.Domain.ID
62+
}
63+
64+
// LikedIPs represents a record of an IP address that has liked something, along with the count of likes and the timestamp of the last like.
65+
type LikedIPs struct {
2066
id int
21-
uri string
22-
count int
67+
IP string
68+
Count int
69+
Domain string
70+
Path string
2371
timestamp time.Time
2472
}
73+
74+
func (likedIPs LikedIPs) IsEmpty() bool {
75+
return likedIPs == LikedIPs{}
76+
}

0 commit comments

Comments
 (0)