PostgreSQL compatibility #5

Merged
arch merged 2 commits from postgres-database into main 2024-04-30 23:57:38 +01:00
6 changed files with 58 additions and 41 deletions

View file

@ -23,6 +23,7 @@ import (
"github.com/go-enry/go-enry/v2" "github.com/go-enry/go-enry/v2"
"github.com/google/uuid" "github.com/google/uuid"
_ "github.com/lib/pq"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"github.com/niklasfasching/go-org/org" "github.com/niklasfasching/go-org/org"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
@ -35,11 +36,12 @@ import (
) )
var ( var (
hostname = flag.String("hostname", envOr("TSNET_HOSTNAME", "paste"), "hostname to use on your tailnet, TSNET_HOSTNAME in the environment") hostname = flag.String("hostname", envOr("TSNET_HOSTNAME", "paste"), "hostname to use on your tailnet, TSNET_HOSTNAME in the environment")
dataDir = flag.String("data-location", dataLocation(), "where data is stored, defaults to DATA_DIR or ~/.config/tailscale/paste") dataDir = flag.String("data-location", dataLocation(), "where data is stored, defaults to DATA_DIR or ~/.config/tailscale/paste")
tsnetLogVerbose = flag.Bool("tsnet-verbose", hasEnv("TSNET_VERBOSE"), "if set, have tsnet log verbosely to standard error") tsnetLogVerbose = flag.Bool("tsnet-verbose", hasEnv("TSNET_VERBOSE"), "if set, have tsnet log verbosely to standard error")
useFunnel = flag.Bool("use-funnel", hasEnv("USE_FUNNEL"), "if set, expose individual pastes to the public internet with Funnel, USE_FUNNEL in the environment") useFunnel = flag.Bool("use-funnel", hasEnv("USE_FUNNEL"), "if set, expose individual pastes to the public internet with Funnel, USE_FUNNEL in the environment")
hidePasteUserInfo = flag.Bool("hide-funnel-users", hasEnv("HIDE_FUNNEL_USERS"), "if set, display the username and profile picture of the user who created the paste in funneled pastes") hidePasteUserInfo = flag.Bool("hide-funnel-users", hasEnv("HIDE_FUNNEL_USERS"), "if set, display the username and profile picture of the user who created the paste in funneled pastes")
databaseUrl = flag.String("database-url", envOr("DATABASE_URL", ""), "optional database url if you'd rather use Postgres instead of sqlite")
//go:embed schema.sql //go:embed schema.sql
sqlSchema string sqlSchema string
@ -105,7 +107,7 @@ SELECT p.id
FROM pastes p FROM pastes p
INNER JOIN users u INNER JOIN users u
ON p.user_id = u.id ON p.user_id = u.id
ORDER BY p.rowid DESC ORDER BY p.created_at DESC
LIMIT 5 LIMIT 5
` `
@ -239,11 +241,11 @@ INSERT INTO pastes
, data , data
) )
VALUES VALUES
( ?1 ( $1
, ?2 , $2
, ?3 , $3
, ?4 , $4
, ?5 , $5
)` )`
_, err = s.db.ExecContext( _, err = s.db.ExecContext(
@ -296,9 +298,9 @@ SELECT p.id
FROM pastes p FROM pastes p
INNER JOIN users u INNER JOIN users u
ON p.user_id = u.id ON p.user_id = u.id
ORDER BY p.rowid DESC ORDER BY p.created_at DESC
LIMIT 25 LIMIT 25
OFFSET ?1 OFFSET $1
` `
uq := r.URL.Query() uq := r.URL.Query()
@ -434,7 +436,7 @@ func (s *Server) TailnetDeletePost(w http.ResponseWriter, r *http.Request) {
q := ` q := `
SELECT p.user_id SELECT p.user_id
FROM pastes p FROM pastes p
WHERE p.id = ?1` WHERE p.id = $1`
row := s.db.QueryRowContext(r.Context(), q, id) row := s.db.QueryRowContext(r.Context(), q, id)
var userIDOfPaste int64 var userIDOfPaste int64
@ -450,7 +452,7 @@ WHERE p.id = ?1`
q = ` q = `
DELETE FROM pastes DELETE FROM pastes
WHERE id = ?1 AND user_id = ?2 WHERE id = $1 AND user_id = $2
` `
if _, err := s.db.ExecContext(r.Context(), q, id, ui.UserProfile.ID); err != nil { if _, err := s.db.ExecContext(r.Context(), q, id, ui.UserProfile.ID); err != nil {
@ -502,7 +504,7 @@ SELECT p.filename
FROM pastes p FROM pastes p
INNER JOIN users u INNER JOIN users u
ON p.user_id = u.id ON p.user_id = u.id
WHERE p.id = ?1` WHERE p.id = $1`
row := s.db.QueryRowContext(r.Context(), q, id) row := s.db.QueryRowContext(r.Context(), q, id)
var fname, data, userLoginName, userDisplayName, userProfilePicURL string var fname, data, userLoginName, userDisplayName, userProfilePicURL string
@ -684,7 +686,7 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
db, err := openDB(*dataDir) db, err := openDB(*dataDir, *databaseUrl)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -822,8 +824,15 @@ func (mch MixedCriticalityHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
panic("unknown security level") panic("unknown security level")
} }
func openDB(dir string) (*sql.DB, error) { func openDB(dir string, databaseUrl string) (*sql.DB, error) {
db, err := sql.Open("sqlite", "file:"+filepath.Join(dir, "data.db")) dbtype := "sqlite"
dbpath := "file:" + filepath.Join(dir, "data.db")
if databaseUrl != "" {
dbtype = "postgres"
dbpath = databaseUrl
}
db, err := sql.Open(dbtype, dbpath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -833,6 +842,14 @@ func openDB(dir string) (*sql.DB, error) {
return nil, err return nil, err
} }
// Enable WAL for SQLite + Litestream
if dbtype == "sqlite" {
_, err := db.Exec("PRAGMA journal_mode=WAL;")
if err != nil {
return nil, err
}
}
if _, err := db.Exec(sqlSchema); err != nil { if _, err := db.Exec(sqlSchema); err != nil {
return nil, err return nil, err
} }
@ -866,16 +883,16 @@ INSERT INTO users
, profile_pic_url , profile_pic_url
) )
VALUES VALUES
( ?1 ( $1
, ?2 , $2
, ?3 , $3
, ?4 , $4
) )
ON CONFLICT DO ON CONFLICT (id) DO
UPDATE SET UPDATE SET
login_name = ?2 login_name = $2
, display_name = ?3 , display_name = $3
, profile_pic_url = ?4 , profile_pic_url = $4
` `
_, err = db.ExecContext( _, err = db.ExecContext(

View file

@ -1,5 +1,10 @@
-- Enable WAL mode to make backups easier with Litestream -- Correlates to tailcfg.UserProfile
PRAGMA journal_mode=WAL; CREATE TABLE IF NOT EXISTS users
( id TEXT PRIMARY KEY NOT NULL
, login_name TEXT NOT NULL
, display_name TEXT NOT NULL
, profile_pic_url TEXT NOT NULL
);
-- Paste data -- Paste data
CREATE TABLE IF NOT EXISTS pastes CREATE TABLE IF NOT EXISTS pastes
@ -10,11 +15,3 @@ CREATE TABLE IF NOT EXISTS pastes
, data TEXT NOT NULL , data TEXT NOT NULL
, FOREIGN KEY(user_id) REFERENCES users(id) , FOREIGN KEY(user_id) REFERENCES users(id)
); );
-- Correlates to tailcfg.UserProfile
CREATE TABLE IF NOT EXISTS users
( id TEXT PRIMARY KEY NOT NULL
, login_name TEXT NOT NULL
, display_name TEXT NOT NULL
, profile_pic_url TEXT NOT NULL
);

View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1711703276, "lastModified": 1714253743,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -24,7 +24,7 @@
go = pkgs.go; go = pkgs.go;
src = ./.; src = ./.;
subPackages = "cmd/tclipd"; subPackages = "cmd/tclipd";
vendorHash = "sha256-7iOEp0NrcmvBNrxl5kjrJOAhVfKYPNpI4ssNxaf6g3M="; vendorHash = "sha256-Ho39aNWTbygA46/9lkXRVLV6FLkWenAJKxOE3MJUb2M=";
}; };
tclip = pkgs.buildGo122Module { tclip = pkgs.buildGo122Module {

1
go.mod
View file

@ -7,6 +7,7 @@ toolchain go1.22.1
require ( require (
github.com/go-enry/go-enry/v2 v2.8.7 github.com/go-enry/go-enry/v2 v2.8.7
github.com/google/uuid v1.5.0 github.com/google/uuid v1.5.0
github.com/lib/pq v1.10.9
github.com/microcosm-cc/bluemonday v1.0.26 github.com/microcosm-cc/bluemonday v1.0.26
github.com/niklasfasching/go-org v1.7.0 github.com/niklasfasching/go-org v1.7.0
github.com/russross/blackfriday v1.6.0 github.com/russross/blackfriday v1.6.0

2
go.sum
View file

@ -125,6 +125,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=