mirror of
https://github.com/tinode/chat.git
synced 2025-03-14 10:05:07 +00:00
Fixed docs (added info about PostgreSQL), added cache functions in adapter and little refactoring
This commit is contained in:
18
INSTALL.md
18
INSTALL.md
@ -6,7 +6,7 @@ The config file [`tinode.conf`](./server/tinode.conf) contains extensive instruc
|
||||
|
||||
1. Visit the [Releases page](https://github.com/tinode/chat/releases/), choose the latest or otherwise the most suitable release. From the list of binaries download the one for your database and platform. Once the binary is downloaded, unpack it to a directory of your choosing, `cd` to that directory.
|
||||
|
||||
2. Make sure your database is running. Make sure it's configured to accept connections from `localhost`. In case of MySQL, Tinode will try to connect as `root` without the password. See notes below (_Building from Source_, section 4) on how to configure Tinode to use a different user or a password. MySQL 5.7 or above is required. MySQL 5.6 or below **will not work**.
|
||||
2. Make sure your database is running. Make sure it's configured to accept connections from `localhost`. In case of MySQL, Tinode will try to connect as `root` without the password. In case of PostgreSQL, Tinode will try connect as `postgres` with the password `postgres`. See notes below (_Building from Source_, section 4) on how to configure Tinode to use a different user or a password. MySQL 5.7 or above is required. MySQL 5.6 or below **will not work**. PostgreSQL 13 or above is required. PostgreSQL 12 or below **will not work**.
|
||||
|
||||
3. Run the database initializer `init-db` (or `init-db.exe` on Windows):
|
||||
```
|
||||
@ -36,6 +36,7 @@ See [instructions](./docker/README.md)
|
||||
* MySQL 5.7 or above. MySQL 5.6 or below **will not work**.
|
||||
* MongoDB 4.0 or above.
|
||||
* RethinkDB.
|
||||
* PostgreSQL 13 or above. PostgreSQL 12 or below **will not work**.
|
||||
|
||||
4. Fetch, build Tinode server and tinode-db database initializer:
|
||||
- **MySQL**:
|
||||
@ -53,15 +54,20 @@ See [instructions](./docker/README.md)
|
||||
go install -tags rethinkdb github.com/tinode/chat/server@latest
|
||||
go install -tags rethinkdb github.com/tinode/chat/tinode-db@latest
|
||||
```
|
||||
- **PostgreSQL**:
|
||||
```
|
||||
go install -tags postgres github.com/tinode/chat/server@latest
|
||||
go install -tags postgres github.com/tinode/chat/tinode-db@latest
|
||||
```
|
||||
- **All** (bundle all of the above DB adapters):
|
||||
```
|
||||
go install -tags "mysql rethinkdb mongodb" github.com/tinode/chat/server@latest
|
||||
go install -tags "mysql rethinkdb mongodb" github.com/tinode/chat/tinode-db@latest
|
||||
go install -tags "mysql rethinkdb mongodb postgres" github.com/tinode/chat/server@latest
|
||||
go install -tags "mysql rethinkdb mongodb postgres" github.com/tinode/chat/tinode-db@latest
|
||||
```
|
||||
|
||||
The steps above install Tinode binaries at `$GOPATH/bin/`, sorces and supporting files are located at `$GOPATH/pkg/mod/github.com/tinode/chat@vX.XX.X/` where `X.XX.X` is the version you installed, such as `0.19.1`.
|
||||
|
||||
Note the required **`-tags rethinkdb`**, **`-tags mysql`** or **`-tags mongodb`** build option.
|
||||
Note the required **`-tags rethinkdb`**, **`-tags mysql`**, **`-tags mongodb`** or **`-tags postgres`** build option.
|
||||
|
||||
You may also optionally define `main.buildstamp` for the server by adding a build option, for instance, with a timestamp:
|
||||
```
|
||||
@ -118,6 +124,10 @@ MongoDB should run as single node replicaset. See https://docs.mongodb.com/manua
|
||||
```
|
||||
rethinkdb --bind all --daemon
|
||||
```
|
||||
- **PostgreSQL**: https://www.postgresql.org/docs/current/app-pg-ctl.html
|
||||
```
|
||||
pg_ctl start
|
||||
```
|
||||
|
||||
2. Run DB initializer
|
||||
```
|
||||
|
@ -34,7 +34,13 @@ All images are available at https://hub.docker.com/r/tinode/
|
||||
```
|
||||
See [instructions](https://hub.docker.com/_/mongo/) for more options. MongoDB 4.2 or above is required.
|
||||
|
||||
The name `rethinkdb`, `mysql` or `mongodb` in the `--name` assignment is important. It's used by other containers as a database's host name.
|
||||
4. **PostgreSQL**: If you've decided to use PostgreSQL backend, run the official PostgreSQL Docker container:
|
||||
```
|
||||
$ docker run --name postgres --network tinode-net --restart always --env POSTGRES_PASSWORD=postgres -d postgres:13
|
||||
```
|
||||
See [instructions](https://hub.docker.com/_/postgres/) for more options. PostgresSQL 13 or above is required.
|
||||
|
||||
The name `rethinkdb`, `mysql`, `mongodb` or `postgres` in the `--name` assignment is important. It's used by other containers as a database's host name.
|
||||
|
||||
4. Run the Tinode container for the appropriate database:
|
||||
|
||||
@ -53,6 +59,11 @@ All images are available at https://hub.docker.com/r/tinode/
|
||||
$ docker run -p 6060:6060 -d --name tinode-srv --network tinode-net tinode/tinode-mongodb:latest
|
||||
```
|
||||
|
||||
4. **PostgreSQL**:
|
||||
```
|
||||
$ docker run -p 6060:6060 -d --name tinode-srv --network tinode-net tinode/tinode-postgresql:latest
|
||||
```
|
||||
|
||||
You can also run Tinode with the `tinode/tinode` image (which has all of the above DB adapters compiled in). You will need to specify the database adapter via `STORE_USE_ADAPTER` environment variable. E.g. for `mysql`, the command line will look like
|
||||
```
|
||||
$ docker run -p 6060:6060 -d -e STORE_USE_ADAPTER mysql --name tinode-srv --network tinode-net tinode/tinode:latest
|
||||
@ -66,6 +77,7 @@ All images are available at https://hub.docker.com/r/tinode/
|
||||
* [MySQL tags](https://hub.docker.com/r/tinode/tinode-mysql/tags/)
|
||||
* [RethinkDB tags](https://hub.docker.com/r/tinode/tinode-rethink/tags/)
|
||||
* [MongoDB tags](https://hub.docker.com/r/tinode/tinode-mongodb/tags/)
|
||||
* [PostgreSQL tags](https://hub.docker.com/r/tinode/tinode-postgresql/tags/) (comming soon)
|
||||
* [All bundle tags](https://hub.docker.com/r/tinode/tinode/tags/) (comming soon)
|
||||
|
||||
5. Test the installation by pointing your browser to [http://localhost:6060/](http://localhost:6060/).
|
||||
|
@ -168,11 +168,6 @@ func (a *adapter) Open(jsonconfig json.RawMessage) error {
|
||||
// Actually opening the network connection.
|
||||
err = a.db.Ping(ctx)
|
||||
|
||||
// if isMissingDb(err) {
|
||||
// // Ignore missing database here. If we are initializing the database
|
||||
// // missing DB is OK.
|
||||
// err = nil
|
||||
// }
|
||||
if err == nil {
|
||||
if config.MaxOpenConns > 0 {
|
||||
|
||||
@ -221,7 +216,7 @@ func (a *adapter) GetDbVersion() (int, error) {
|
||||
defer cancel()
|
||||
}
|
||||
var vers string
|
||||
err := a.db.QueryRow(ctx, `SELECT value FROM kvmeta WHERE key = $1`, "version").Scan(&vers)
|
||||
err := a.db.QueryRow(ctx, "SELECT value FROM kvmeta WHERE key = $1", "version").Scan(&vers)
|
||||
|
||||
if err != nil {
|
||||
if isMissingDb(err) || isMissingTable(err) || err == pgx.ErrNoRows {
|
||||
@ -241,7 +236,7 @@ func (a *adapter) updateDbVersion(v int) error {
|
||||
defer cancel()
|
||||
}
|
||||
a.version = -1
|
||||
if _, err := a.db.Exec(ctx, `UPDATE kvmeta SET value = $1 WHERE key = $2`, strconv.Itoa(v), "version"); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "UPDATE kvmeta SET value = $1 WHERE key = $2", strconv.Itoa(v), "version"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -306,10 +301,8 @@ func (a *adapter) CreateDb(reset bool) error {
|
||||
if a.db != nil {
|
||||
a.db.Close()
|
||||
}
|
||||
// // This DSN has been parsed before and produced no error, not checking for errors here.
|
||||
// cfg, _ := pgxpool.ParseConfig(a.dsn)
|
||||
|
||||
// // Create default database name
|
||||
// Create default database name
|
||||
a.poolConfig.ConnConfig.Database = "postgres"
|
||||
|
||||
a.db, err = pgxpool.ConnectConfig(ctx, a.poolConfig)
|
||||
@ -616,15 +609,15 @@ func (a *adapter) UpgradeDb() error {
|
||||
if a.version == 106 {
|
||||
// Perform database upgrade from version 106 to version 107.
|
||||
|
||||
if _, err := a.db.Exec(ctx, `CREATE UNIQUE INDEX usertags_userid_tag ON usertags(userid, tag)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "CREATE UNIQUE INDEX usertags_userid_tag ON usertags(userid, tag)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `CREATE UNIQUE INDEX topictags_userid_tag ON topictags(topic, tag)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "CREATE UNIQUE INDEX topictags_userid_tag ON topictags(topic, tag)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE credentials ADD deletedat TIMESTAMP(3) AFTER updatedat`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE credentials ADD deletedat TIMESTAMP(3) AFTER updatedat"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -669,7 +662,7 @@ func (a *adapter) UpgradeDb() error {
|
||||
|
||||
if a.version == 109 {
|
||||
// Perform database upgrade from version 109 to version 110.
|
||||
if _, err := a.db.Exec(ctx, `UPDATE topics SET touchedat=updatedat WHERE touchedat IS NULL`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "UPDATE topics SET touchedat=updatedat WHERE touchedat IS NULL"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -680,47 +673,47 @@ func (a *adapter) UpgradeDb() error {
|
||||
|
||||
if a.version == 110 {
|
||||
// Users
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE users MODIFY state SMALLINT NOT NULL DEFAULT 0 AFTER updatedat`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE users MODIFY state SMALLINT NOT NULL DEFAULT 0 AFTER updatedat"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE users CHANGE deletedat stateat TIMESTAMP(3)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE users CHANGE deletedat stateat TIMESTAMP(3)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE users DROP INDEX users_deletedat`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE users DROP INDEX users_deletedat"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add status to formerly soft-deleted users.
|
||||
if _, err := a.db.Exec(ctx, `UPDATE users SET state=$1 WHERE stateat IS NOT NULL`, t.StateDeleted); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "UPDATE users SET state=$1 WHERE stateat IS NOT NULL", t.StateDeleted); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE users ADD INDEX users_state(state)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE users ADD INDEX users_state(state)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Topics
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE topics ADD state SMALLINT NOT NULL DEFAULT 0 AFTER updatedat`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE topics ADD state SMALLINT NOT NULL DEFAULT 0 AFTER updatedat"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE topics CHANGE deletedat stateat TIMESTAMP(3)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE topics CHANGE deletedat stateat TIMESTAMP(3)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add status to formerly soft-deleted topics.
|
||||
if _, err := a.db.Exec(ctx, `UPDATE topics SET state=$1 WHERE stateat IS NOT NULL`, t.StateDeleted); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "UPDATE topics SET state=$1 WHERE stateat IS NOT NULL", t.StateDeleted); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE topics ADD INDEX topics_state(state)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE topics ADD INDEX topics_state(state)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Subscriptions
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE subscriptions ADD INDEX topics_deletedat(deletedat)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE subscriptions ADD INDEX topics_deletedat(deletedat)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -731,41 +724,41 @@ func (a *adapter) UpgradeDb() error {
|
||||
|
||||
if a.version == 111 {
|
||||
// Perform database upgrade from version 111 to version 112.
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE users ADD trusted JSON AFTER public`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE users ADD trusted JSON AFTER public"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE topics ADD trusted JSON AFTER public`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE topics ADD trusted JSON AFTER public"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove NOT NULL constraint, so an avatar upload can be done at registration.
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE fileuploads MODIFY userid BIGINT`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE fileuploads MODIFY userid BIGINT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE fileuploads ADD INDEX fileuploads_status(status)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE fileuploads ADD INDEX fileuploads_status(status)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove NOT NULL constraint to enable links to users and topics.
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE filepgglinks MODIFY pggid INT`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE filepgglinks MODIFY pggid INT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE filepgglinks ADD topic CHAR(25)`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE filepgglinks ADD topic CHAR(25)"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE filepgglinks ADD userid BIGINT`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE filepgglinks ADD userid BIGINT"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE filepgglinks ADD FOREIGN KEY(topic) REFERENCES topics(name) ON DELETE CASCADE`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE filepgglinks ADD FOREIGN KEY(topic) REFERENCES topics(name) ON DELETE CASCADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := a.db.Exec(ctx, `ALTER TABLE filepgglinks ADD FOREIGN KEY(userid) REFERENCES users(id) ON DELETE CASCADE`); err != nil {
|
||||
if _, err := a.db.Exec(ctx, "ALTER TABLE filepgglinks ADD FOREIGN KEY(userid) REFERENCES users(id) ON DELETE CASCADE"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -847,7 +840,7 @@ func (a *adapter) UserCreate(user *t.User) error {
|
||||
|
||||
decoded_uid := store.DecodeUid(user.Uid())
|
||||
if _, err = tx.Exec(ctx,
|
||||
`INSERT INTO users(id,createdat,updatedat,state,access,public,trusted,tags) VALUES($1,$2,$3,$4,$5,$6,$7,$8);`,
|
||||
"INSERT INTO users(id,createdat,updatedat,state,access,public,trusted,tags) VALUES($1,$2,$3,$4,$5,$6,$7,$8);",
|
||||
decoded_uid,
|
||||
user.CreatedAt,
|
||||
user.UpdatedAt,
|
||||
@ -1121,7 +1114,7 @@ func (a *adapter) UserDelete(uid t.Uid, hard bool) error {
|
||||
}
|
||||
|
||||
// Delete records of messages soft-deleted for the user.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM dellog WHERE deletedfor=$1;`, decoded_uid); err != nil {
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM dellog WHERE deletedfor=$1", decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1131,34 +1124,34 @@ func (a *adapter) UserDelete(uid t.Uid, hard bool) error {
|
||||
// Delete topics where the user is the owner.
|
||||
|
||||
// First delete all messages in those topics.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM dellog USING topics WHERE topics.name=dellog.topic AND topics.owner=$1;`,
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM dellog USING topics WHERE topics.name=dellog.topic AND topics.owner=$1",
|
||||
decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM messages USING topics WHERE topics.name=messages.topic AND topics.owner=$1;`,
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM messages USING topics WHERE topics.name=messages.topic AND topics.owner=$1",
|
||||
decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete all subscriptions.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM subscriptions USING topics WHERE topics.name=subscriptions.topic AND topics.owner=$1;`,
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM subscriptions USING topics WHERE topics.name=subscriptions.topic AND topics.owner=$1",
|
||||
decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete topic tags.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM topictags USING topics WHERE topics.name=topictags.topic AND topics.owner=$1;`,
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM topictags USING topics WHERE topics.name=topictags.topic AND topics.owner=$1",
|
||||
decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// And finally delete the topics.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM topics WHERE owner=$1;`, decoded_uid); err != nil {
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM topics WHERE owner=$1", decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete user's authentication records.
|
||||
if _, err = tx.Exec(ctx, `DELETE FROM auth WHERE userid=$1;`, decoded_uid); err != nil {
|
||||
if _, err = tx.Exec(ctx, "DELETE FROM auth WHERE userid=$1", decoded_uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1601,8 +1594,6 @@ func (a *adapter) TopicGet(topic string) (*t.Topic, error) {
|
||||
}
|
||||
|
||||
tt.Owner = store.EncodeUid(owner).String()
|
||||
// tt.Public = fromJSON(tt.Public)
|
||||
// tt.Trusted = fromJSON(tt.Trusted)
|
||||
|
||||
return tt, nil
|
||||
}
|
||||
@ -1672,8 +1663,6 @@ func (a *adapter) TopicsForUser(uid t.Uid, keepDeleted bool, opts *t.QueryOpt) (
|
||||
&sub.RecvSeqId, &sub.ReadSeqId, &modeWant, &modeGiven, &sub.Private); err != nil {
|
||||
break
|
||||
}
|
||||
// modeWant = bytes.Trim(modeWant, " ")
|
||||
// modeGiven = bytes.Trim(modeGiven, " ")
|
||||
sub.ModeWant.Scan(modeWant)
|
||||
sub.ModeGiven.Scan(modeGiven)
|
||||
tname := sub.Topic
|
||||
@ -1922,13 +1911,10 @@ func (a *adapter) UsersForTopic(topic string, keepDeleted bool, opts *t.QueryOpt
|
||||
break
|
||||
}
|
||||
|
||||
// sub.SetUid(store.EncodeUid(userId))
|
||||
sub.User = store.EncodeUid(userId).String()
|
||||
sub.SetPublic(public)
|
||||
sub.SetTrusted(trusted)
|
||||
sub.SetLastSeenAndUA(lastSeen, userAgent)
|
||||
// modeWant = bytes.Trim(modeWant, " ")
|
||||
// modeGiven = bytes.Trim(modeGiven, " ")
|
||||
sub.ModeWant.Scan(modeWant)
|
||||
sub.ModeGiven.Scan(modeGiven)
|
||||
subs = append(subs, sub)
|
||||
@ -2185,7 +2171,6 @@ func (a *adapter) SubscriptionGet(topic string, user t.Uid, keepDeleted bool) (*
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//sub.SetUid(store.EncodeUid(userId))
|
||||
sub.User = store.EncodeUid(userId).String()
|
||||
sub.ModeWant.Scan(modeWant)
|
||||
sub.ModeGiven.Scan(modeGiven)
|
||||
@ -2220,10 +2205,7 @@ func (a *adapter) SubsForUser(forUser t.Uid) ([]t.Subscription, error) {
|
||||
break
|
||||
}
|
||||
|
||||
// sub.SetUid(store.EncodeUid(userId))
|
||||
sub.User = store.EncodeUid(userId).String()
|
||||
// modeWant = bytes.Trim(modeWant, " ")
|
||||
// modeGiven = bytes.Trim(modeGiven, " ")
|
||||
sub.ModeWant.Scan(modeWant)
|
||||
sub.ModeGiven.Scan(modeGiven)
|
||||
subs = append(subs, sub)
|
||||
@ -2286,10 +2268,7 @@ func (a *adapter) SubsForTopic(topic string, keepDeleted bool, opts *t.QueryOpt)
|
||||
break
|
||||
}
|
||||
|
||||
// sub.SetUid(store.EncodeUid(userId))
|
||||
sub.User = store.EncodeUid(userId).String()
|
||||
// modeWant = bytes.Trim(modeWant, " ")
|
||||
// modeGiven = bytes.Trim(modeGiven, " ")
|
||||
sub.ModeWant.Scan(modeWant)
|
||||
sub.ModeGiven.Scan(modeGiven)
|
||||
subs = append(subs, sub)
|
||||
@ -2382,10 +2361,10 @@ func (a *adapter) SubsDelete(topic string, user t.Uid) error {
|
||||
func subsDelForUser(ctx context.Context, tx pgx.Tx, user t.Uid, hard bool) error {
|
||||
var err error
|
||||
if hard {
|
||||
_, err = tx.Exec(ctx, `DELETE FROM subscriptions WHERE userid=$1;`, store.DecodeUid(user))
|
||||
_, err = tx.Exec(ctx, "DELETE FROM subscriptions WHERE userid=$1;", store.DecodeUid(user))
|
||||
} else {
|
||||
now := t.TimeNow()
|
||||
_, err = tx.Exec(ctx, `UPDATE subscriptions SET updatedat=$1,deletedat=$2 WHERE userid=$3 AND deletedat IS NULL;`,
|
||||
_, err = tx.Exec(ctx, "UPDATE subscriptions SET updatedat=$1,deletedat=$2 WHERE userid=$3 AND deletedat IS NULL;",
|
||||
now, now, store.DecodeUid(user))
|
||||
}
|
||||
return err
|
||||
@ -2481,7 +2460,6 @@ func (a *adapter) FindUsers(uid t.Uid, req [][]string, opt []string) ([]t.Subscr
|
||||
// Skip the callee
|
||||
continue
|
||||
}
|
||||
//sub.SetUid(store.EncodeUid(userId))
|
||||
sub.User = store.EncodeUid(userId).String()
|
||||
sub.SetPublic(public)
|
||||
sub.SetTrusted(trusted)
|
||||
@ -2935,9 +2913,9 @@ func deviceDelete(ctx context.Context, tx pgx.Tx, uid t.Uid, deviceID string) er
|
||||
var err error
|
||||
var res pgconn.CommandTag
|
||||
if deviceID == "" {
|
||||
res, err = tx.Exec(ctx, `DELETE FROM devices WHERE userid=$1;`, store.DecodeUid(uid))
|
||||
res, err = tx.Exec(ctx, "DELETE FROM devices WHERE userid=$1", store.DecodeUid(uid))
|
||||
} else {
|
||||
res, err = tx.Exec(ctx, `DELETE FROM devices WHERE userid=$1 AND hash=$2;`, store.DecodeUid(uid), deviceHasher(deviceID))
|
||||
res, err = tx.Exec(ctx, "DELETE FROM devices WHERE userid=$1 AND hash=$2", store.DecodeUid(uid), deviceHasher(deviceID))
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
@ -3472,6 +3450,75 @@ func (a *adapter) FileLinkAttachments(topic string, userId, pggId t.Uid, fids []
|
||||
return tx.Commit(ctx)
|
||||
}
|
||||
|
||||
// PCacheGet reads a persistet cache entry.
|
||||
func (a *adapter) PCacheGet(key string) (string, error) {
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var value string
|
||||
if err := a.db.QueryRow(ctx, `SELECT "value" FROM kvmeta WHERE "key"=$1 LIMIT 1`, key).Scan(&value); err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return "", t.ErrNotFound
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// PCacheUpsert creates or updates a persistent cache entry.
|
||||
func (a *adapter) PCacheUpsert(key string, value string, failOnDuplicate bool) error {
|
||||
if strings.Contains(key, "%") {
|
||||
// Do not allow % in keys: it interferes with LIKE query.
|
||||
return t.ErrMalformed
|
||||
}
|
||||
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var action string
|
||||
if failOnDuplicate {
|
||||
action = "INSERT"
|
||||
} else {
|
||||
action = "REPLACE"
|
||||
}
|
||||
|
||||
_, err := a.db.Exec(ctx, action+` INTO kvmeta("key",createdat,"value") VALUES($1,$2,$3)`, key, t.TimeNow(), value)
|
||||
if isDupe(err) {
|
||||
return t.ErrDuplicate
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// PCacheDelete deletes one persistent cache entry.
|
||||
func (a *adapter) PCacheDelete(key string) error {
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
_, err := a.db.Exec(ctx, `DELETE FROM kvmeta WHERE "key"=$1`, key)
|
||||
return err
|
||||
}
|
||||
|
||||
// PCacheExpire expires old entries with the given key prefix.
|
||||
func (a *adapter) PCacheExpire(keyPrefix string, olderThan time.Time) error {
|
||||
if keyPrefix == "" {
|
||||
return t.ErrMalformed
|
||||
}
|
||||
|
||||
ctx, cancel := a.getContext()
|
||||
if cancel != nil {
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
_, err := a.db.Exec(ctx, `DELETE FROM kvmeta WHERE "key" LIKE $1 AND createdat<$2`, keyPrefix+"%", olderThan)
|
||||
return err
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
// Check if MySQL error is a Error Code: 1062. Duplicate entry ... for key ...
|
||||
@ -3577,21 +3624,6 @@ func setConnStr(c configType) (string, error) {
|
||||
return connStr, nil
|
||||
}
|
||||
|
||||
func repeatStringValues(str string, src *int, max int) string {
|
||||
if max <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var newString string
|
||||
|
||||
for max > 0 {
|
||||
newString += fmt.Sprintf(str, *src)
|
||||
*src += 1
|
||||
max--
|
||||
}
|
||||
return newString
|
||||
}
|
||||
|
||||
func expandQuery(query string, args ...interface{}) (string, []interface{}) {
|
||||
var expandedArgs []interface{}
|
||||
var expandedQuery string
|
||||
|
@ -223,15 +223,24 @@
|
||||
|
||||
// Configurations of individual adapters.
|
||||
"adapters": {
|
||||
// PostgreSQL configuration. See https://godoc.org/github.com/jackc/pgx#Config
|
||||
// for other possible options.
|
||||
"postgres": {
|
||||
// PostgreSQL connection settings.
|
||||
"User":"postgres",
|
||||
"Passwd": "postgres",
|
||||
"Host": "localhost",
|
||||
"Port": "5432",
|
||||
"DBName": "tinode",
|
||||
|
||||
// PostgreSQL connection pool settings.
|
||||
// Maximum number of open connections to the database. Zero means unlimited.
|
||||
"max_open_conns": 64,
|
||||
// Maximum number of connections in the idle connection pool. Zero means no idle connections are retained.
|
||||
"max_idle_conns": 64,
|
||||
// Maximum amount of time a connection may be reused. Zero means unlimited.
|
||||
"conn_max_lifetime": 60,
|
||||
// Maximum amount of time waiting for a connection from the pool. Zero means no timeout.
|
||||
"sql_timeout": 10
|
||||
},
|
||||
|
||||
|
@ -13,6 +13,9 @@ This utility initializes the `tinode` database (or upgrades an existing DB from
|
||||
- **MongoDB**
|
||||
`go build -tags mongodb` or `go build -i -tags mongodb` to automatically install missing dependencies.
|
||||
|
||||
- **PostgreSQL**
|
||||
`go build -tags postgres` or `go build -i -tags postgres` to automatically install missing dependencies.
|
||||
|
||||
|
||||
## Run
|
||||
|
||||
@ -48,3 +51,4 @@ Avatar photos curtesy of https://www.pexels.com/ under [CC0 license](https://www
|
||||
* [RethinkDB schema](https://github.com/tinode/chat/tree/master/server/db/rethinkdb/schema.md)
|
||||
* [MySQL schema](https://github.com/tinode/chat/tree/master/server/db/mysql/schema.sql)
|
||||
* [MongoDB schema](https://github.com/tinode/chat/tree/master/server/db/mongodb/schema.md)
|
||||
* [PostgreSQL schema](https://github.com/tinode/chat/tree/master/server/db/postgres/schema.sql)
|
||||
|
Reference in New Issue
Block a user