auth.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { Database } from "bun:sqlite";
  2. const authDB = new Database("./data/auth.sqlite");
  3. authDB.run(`create table if not exists verifications (
  4. email text not null primary key,
  5. token text not null,
  6. code integer not null,
  7. timestamp datetime default current_timestamp
  8. );`)
  9. authDB.run(`create table if not exists users (
  10. email text not null primary key,
  11. name text,
  12. timestamp datetime default current_timestamp
  13. );`)
  14. authDB.run(`create table if not exists sessions (
  15. id text not null primary key,
  16. secretHash text not null,
  17. email text not null,
  18. timestamp datetime default current_timestamp
  19. );`)
  20. import nodemailer from "nodemailer"
  21. import generateSecureRandomString from "./generateSecureRandomString";
  22. import generateSecureCode from "./generateSecureCode";
  23. import hashSecret from "./hashSecret";
  24. import Meteostanice from "./meteostanice";
  25. const transporter = nodemailer.createTransport({
  26. host: process.env.AUTH_EMAIL_SMTP_HOSTNAME,
  27. port: process.env.AUTH_EMAIL_SMTP_PORT,
  28. secure: true, // Use true for port 465, false for port 587
  29. auth: {
  30. user: process.env.AUTH_EMAIL_SMTP_USERNAME,
  31. pass: process.env.AUTH_EMAIL_SMTP_PASSWORD,
  32. },
  33. });
  34. export default class Auth {
  35. static addVerification(email) {
  36. const token = generateSecureRandomString()
  37. const code = generateSecureCode()
  38. authDB.prepare(`
  39. INSERT INTO verifications (email, token, code)
  40. VALUES (?, ?, ?)
  41. ON CONFLICT(email) DO UPDATE SET
  42. token = excluded.token,
  43. code = excluded.code;
  44. `).run(email, token, code)
  45. return { email, token, code }
  46. }
  47. static async sendVerification(email, subject, text, html) {
  48. return await transporter.sendMail({
  49. from: process.env.AUTH_EMAIL_SMTP_FROM,
  50. to: email,
  51. subject,
  52. text, // Plain-text version of the message
  53. html, // HTML version of the message
  54. });
  55. }
  56. static getVerification(token) {
  57. const statement = authDB.prepare(`
  58. SELECT *,
  59. ( CASE
  60. WHEN (strftime('%s', 'now') - strftime('%s', timestamp)) > $seconds THEN 0
  61. ELSE 1
  62. END
  63. ) AS valid
  64. FROM verifications
  65. WHERE token = $token;
  66. `)
  67. const result = statement.get({
  68. $seconds: process.env.EMAIL_VERIFICATION_TIMEOUT,
  69. $token: token
  70. });
  71. return result;
  72. }
  73. static removeVerification(token) {
  74. authDB.prepare(`
  75. DELETE
  76. FROM verifications
  77. WHERE token = ?;
  78. `).run(token)
  79. }
  80. static removeUserVerifications(email) {
  81. authDB.prepare(`
  82. DELETE
  83. FROM verifications
  84. WHERE email = ?;
  85. `).run(email)
  86. }
  87. static addUser(email) {
  88. const statement = authDB.prepare("INSERT INTO users (email) VALUES (?);")
  89. statement.run(
  90. email
  91. );
  92. return { email };
  93. }
  94. static getUser(email) {
  95. const statement = authDB.prepare("select * from users where email = ?;")
  96. const result = statement.get(
  97. email
  98. );
  99. return result;
  100. }
  101. static editUser(email, newName, newEmail) {
  102. const statement = authDB.prepare("select * from users where email = ?;")
  103. const user = statement.get(
  104. email
  105. );
  106. if (!user) return null
  107. const result = authDB.prepare(`
  108. update users
  109. set name = ?,
  110. email = ?
  111. where email = ?;
  112. `).run(newName, newEmail, email)
  113. Meteostanice.editOwnerOnOwned(email, newEmail)
  114. return result
  115. }
  116. static deleteUser(email) {
  117. Meteostanice.deleteOwned(email)
  118. this.removeUserSessions(email)
  119. this.removeUserVerifications(email)
  120. authDB.prepare(`
  121. DELETE
  122. FROM users
  123. WHERE email = ?;
  124. `).run(email)
  125. }
  126. static async createSession(email) {
  127. const id = generateSecureRandomString();
  128. const secret = generateSecureRandomString();
  129. const secretHash = await hashSecret(secret);
  130. const token = id + "." + secret;
  131. const statement = authDB.prepare("INSERT INTO sessions (id, secretHash, email) VALUES (?, ?, ?);")
  132. statement.run(
  133. id,
  134. secretHash,
  135. email
  136. );
  137. return { id, secretHash, token, email };
  138. }
  139. static async getSession(token) {
  140. if (!token) return null
  141. const [id, secret] = token.split(".")
  142. const statement = authDB.prepare(`
  143. SELECT *,
  144. ( CASE
  145. WHEN (strftime('%s', 'now') - strftime('%s', timestamp)) > $seconds THEN 0
  146. ELSE 1
  147. END
  148. ) AS valid
  149. FROM sessions
  150. WHERE id = $id AND secretHash = $secretHash;
  151. `)
  152. const result = statement.get({
  153. $seconds: process.env.SESSION_TIMEOUT,
  154. $id: id,
  155. $secretHash: await hashSecret(secret)
  156. });
  157. return result
  158. }
  159. static removeSession(id) {
  160. authDB.prepare(`
  161. DELETE
  162. FROM sessions
  163. WHERE id = ?;
  164. `).run(id)
  165. }
  166. static removeUserSessions(email) {
  167. authDB.prepare(`
  168. DELETE
  169. FROM sessions
  170. WHERE email = ?;
  171. `).run(email)
  172. }
  173. }