database.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. const {defaultSettings} = require('./util/default.json');
  2. const {Client} = require('pg');
  3. const db = new Client();
  4. db.on( 'error', dberror => {
  5. console.log( '- Error while connecting to the database: ' + dberror );
  6. } );
  7. const schema = [`
  8. BEGIN TRANSACTION;
  9. CREATE TABLE patreons (
  10. patreon TEXT PRIMARY KEY
  11. UNIQUE
  12. NOT NULL,
  13. count INTEGER NOT NULL
  14. );
  15. CREATE INDEX idx_patreons_patreon ON patreons (
  16. patreon
  17. );
  18. CREATE TABLE discord (
  19. main TEXT UNIQUE
  20. CHECK (main = guild),
  21. guild TEXT NOT NULL
  22. REFERENCES discord (main) ON DELETE CASCADE,
  23. channel TEXT,
  24. wiki TEXT NOT NULL
  25. DEFAULT '${defaultSettings.wiki}',
  26. lang TEXT NOT NULL
  27. DEFAULT '${defaultSettings.lang}',
  28. role TEXT,
  29. inline INTEGER,
  30. prefix TEXT NOT NULL
  31. DEFAULT '${process.env.prefix}',
  32. patreon TEXT REFERENCES patreons (patreon) ON DELETE SET NULL,
  33. voice INTEGER,
  34. UNIQUE (
  35. guild,
  36. channel
  37. )
  38. );
  39. CREATE INDEX idx_discord_channel ON discord (
  40. guild,
  41. channel DESC
  42. NULLS LAST
  43. );
  44. CREATE INDEX idx_discord_patreon ON discord (
  45. patreon
  46. )
  47. WHERE patreon IS NOT NULL;
  48. CREATE INDEX idx_discord_voice ON discord (
  49. voice
  50. )
  51. WHERE voice IS NOT NULL;
  52. CREATE TABLE verification (
  53. guild TEXT NOT NULL
  54. REFERENCES discord (main) ON DELETE CASCADE,
  55. configid INTEGER NOT NULL,
  56. channel TEXT NOT NULL,
  57. role TEXT NOT NULL,
  58. editcount INTEGER NOT NULL
  59. DEFAULT 0,
  60. postcount INTEGER DEFAULT 0,
  61. usergroup TEXT NOT NULL
  62. DEFAULT 'user',
  63. accountage INTEGER NOT NULL
  64. DEFAULT 0,
  65. rename INTEGER NOT NULL
  66. DEFAULT 0,
  67. UNIQUE (
  68. guild,
  69. configid
  70. )
  71. );
  72. CREATE INDEX idx_verification_config ON verification (
  73. guild,
  74. configid ASC,
  75. channel
  76. );
  77. CREATE TABLE rcgcdw (
  78. guild TEXT NOT NULL
  79. REFERENCES discord (main) ON DELETE CASCADE,
  80. configid INTEGER NOT NULL,
  81. webhook TEXT NOT NULL
  82. UNIQUE,
  83. wiki TEXT NOT NULL,
  84. lang TEXT NOT NULL
  85. DEFAULT '${defaultSettings.lang}',
  86. display INTEGER NOT NULL
  87. DEFAULT 1,
  88. rcid INTEGER,
  89. postid TEXT DEFAULT '-1',
  90. UNIQUE (
  91. guild,
  92. configid
  93. )
  94. );
  95. CREATE INDEX idx_rcgcdw_wiki ON rcgcdw (
  96. wiki
  97. );
  98. CREATE INDEX idx_rcgcdw_webhook ON rcgcdw (
  99. webhook
  100. );
  101. CREATE INDEX idx_rcgcdw_config ON rcgcdw (
  102. guild,
  103. configid ASC
  104. );
  105. CREATE TABLE blocklist (
  106. wiki TEXT UNIQUE
  107. NOT NULL,
  108. reason TEXT
  109. );
  110. CREATE INDEX idx_blocklist_wiki ON blocklist (
  111. wiki
  112. );
  113. COMMIT TRANSACTION;
  114. ALTER DATABASE "${process.env.PGDATABASE}" SET my.version TO 1;
  115. `];
  116. module.exports = db.connect().then( () => {
  117. return db.query( 'SELECT CURRENT_SETTING($1) AS version', ['my.version'] ).then( ({rows:[row]}) => {
  118. row.version = parseInt(row.version, 10);
  119. if ( isNaN(row.version) || row.version > schema.length ) {
  120. console.log( '- Invalid database version: v' + row.version );
  121. return Promise.reject();
  122. }
  123. if ( row.version === schema.length ) {
  124. console.log( '- The database is up to date: v' + row.version );
  125. return;
  126. }
  127. console.log( '- The database is outdated: v' + row.version );
  128. if ( process.env.READONLY ) return Promise.reject();
  129. return db.query( schema.filter( (sql, version) => {
  130. if ( row.version === 0 ) return ( version === 0 );
  131. return ( row.version <= version );
  132. } ).join('\n') ).then( () => {
  133. console.log( '- The database has been updated to: v' + schema.length );
  134. }, dberror => {
  135. console.log( '- Error while updating the database: ' + dberror );
  136. return Promise.reject();
  137. } );
  138. }, dberror => {
  139. if ( dberror.message === 'unrecognized configuration parameter "my.version"' ) {
  140. return db.query( schema[0] ).then( () => {
  141. console.log( '- The database has been updated to: v' + schema.length );
  142. }, dberror => {
  143. console.log( '- Error while updating the database: ' + dberror );
  144. return Promise.reject();
  145. } );
  146. }
  147. console.log( '- Error while getting the database version: ' + dberror );
  148. return Promise.reject();
  149. } );
  150. }, dberror => {
  151. console.log( '- Error while connecting to the database: ' + dberror );
  152. return Promise.reject();
  153. } ).then( () => {
  154. db.end().then( () => {
  155. console.log( '- Closed the database connection.' );
  156. }, dberror => {
  157. console.log( '- Error while closing the database connection: ' + dberror );
  158. } );
  159. }, () => {
  160. return db.end().then( () => {
  161. console.log( '- Closed the database connection.' );
  162. }, dberror => {
  163. console.log( '- Error while closing the database connection: ' + dberror );
  164. } ).then( () => {
  165. return Promise.reject();
  166. } );
  167. } );