2024-02-19 15:03:39 -05:00
|
|
|
import Database, { SqliteError } from 'better-sqlite3'
|
2024-01-23 01:21:41 -05:00
|
|
|
import { generateUUID } from '$lib/utils'
|
2024-02-01 18:10:15 -05:00
|
|
|
import { isValidURL } from '$lib/utils'
|
2024-01-23 01:21:41 -05:00
|
|
|
|
|
|
|
|
const db = new Database('./src/lib/server/users.db', { verbose: console.info })
|
|
|
|
|
db.pragma('foreign_keys = ON')
|
2024-02-19 15:03:39 -05:00
|
|
|
const initUsersTable = `CREATE TABLE IF NOT EXISTS Users(
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
username VARCHAR(30) UNIQUE NOT NULL,
|
|
|
|
|
password VARCHAR(72) NOT NULL
|
|
|
|
|
)`
|
2024-02-18 00:01:54 -05:00
|
|
|
const initConnectionsTable = `CREATE TABLE IF NOT EXISTS Connections(
|
|
|
|
|
id VARCHAR(36) PRIMARY KEY,
|
|
|
|
|
userId VARCHAR(36) NOT NULL,
|
|
|
|
|
service TEXT NOT NULL,
|
|
|
|
|
accessToken TEXT NOT NULL,
|
|
|
|
|
refreshToken TEXT,
|
|
|
|
|
expiry NUMBER,
|
|
|
|
|
FOREIGN KEY(userId) REFERENCES Users(id)
|
|
|
|
|
)`
|
2024-02-01 18:10:15 -05:00
|
|
|
db.exec(initUsersTable), db.exec(initConnectionsTable)
|
2024-01-23 01:21:41 -05:00
|
|
|
|
2024-02-01 18:10:15 -05:00
|
|
|
interface ConnectionsTableSchema {
|
2024-01-24 14:16:24 -05:00
|
|
|
id: string
|
|
|
|
|
userId: string
|
2024-02-01 18:10:15 -05:00
|
|
|
service: string
|
2024-02-18 00:01:54 -05:00
|
|
|
accessToken: string
|
|
|
|
|
refreshToken?: string
|
|
|
|
|
expiry?: number
|
2024-01-24 14:16:24 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-23 01:21:41 -05:00
|
|
|
export class Users {
|
2024-02-19 15:03:39 -05:00
|
|
|
static getUser = (id: string): User | null => {
|
|
|
|
|
const user = db.prepare('SELECT * FROM Users WHERE id = ?').get(id) as User | null
|
2024-01-25 03:05:13 -05:00
|
|
|
return user
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 15:03:39 -05:00
|
|
|
static getUsername = (username: string): User | null => {
|
|
|
|
|
const user = db.prepare('SELECT * FROM Users WHERE lower(username) = ?').get(username.toLowerCase()) as User | null
|
2024-01-25 03:05:13 -05:00
|
|
|
return user
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 15:03:39 -05:00
|
|
|
static addUser = (username: string, hashedPassword: string): User | null => {
|
|
|
|
|
if (this.getUsername(username)) return null
|
2024-01-24 14:16:24 -05:00
|
|
|
|
2024-01-23 01:21:41 -05:00
|
|
|
const userId = generateUUID()
|
|
|
|
|
db.prepare('INSERT INTO Users(id, username, password) VALUES(?, ?, ?)').run(userId, username, hashedPassword)
|
2024-01-25 03:05:13 -05:00
|
|
|
return this.getUser(userId)!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static deleteUser = (id: string): void => {
|
|
|
|
|
const commandInfo = db.prepare('DELETE FROM Users WHERE id = ?').run(id)
|
|
|
|
|
if (commandInfo.changes === 0) throw new Error(`User with id ${id} does not exist`)
|
2024-01-23 01:21:41 -05:00
|
|
|
}
|
2024-01-24 14:16:24 -05:00
|
|
|
}
|
2024-01-23 01:21:41 -05:00
|
|
|
|
2024-01-23 12:05:33 -05:00
|
|
|
export class Connections {
|
2024-02-01 18:10:15 -05:00
|
|
|
static getConnection = (id: string): Connection => {
|
2024-02-18 00:01:54 -05:00
|
|
|
const { userId, service, accessToken, refreshToken, expiry } = db.prepare('SELECT * FROM Connections WHERE id = ?').get(id) as ConnectionsTableSchema
|
|
|
|
|
const connection: Connection = { id, userId, service: JSON.parse(service), accessToken, refreshToken, expiry }
|
2024-01-24 14:16:24 -05:00
|
|
|
return connection
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 18:10:15 -05:00
|
|
|
static getUserConnections = (userId: string): Connection[] => {
|
|
|
|
|
const connectionRows = db.prepare('SELECT * FROM Connections WHERE userId = ?').all(userId) as ConnectionsTableSchema[]
|
|
|
|
|
const connections: Connection[] = []
|
2024-02-17 00:31:11 -05:00
|
|
|
for (const row of connectionRows) {
|
2024-02-18 00:01:54 -05:00
|
|
|
const { id, service, accessToken, refreshToken, expiry } = row
|
|
|
|
|
connections.push({ id, userId, service: JSON.parse(service), accessToken, refreshToken, expiry })
|
2024-02-17 00:31:11 -05:00
|
|
|
}
|
2024-01-25 03:05:13 -05:00
|
|
|
return connections
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-18 00:01:54 -05:00
|
|
|
static addConnection = (userId: string, service: Service, accessToken: string, refreshToken?: string, expiry?: number): Connection => {
|
2024-01-24 14:16:24 -05:00
|
|
|
const connectionId = generateUUID()
|
2024-02-01 18:10:15 -05:00
|
|
|
if (!isValidURL(service.urlOrigin)) throw new Error('Service does not have valid url')
|
2024-02-18 00:01:54 -05:00
|
|
|
db.prepare('INSERT INTO Connections(id, userId, service, accessToken, refreshToken, expiry) VALUES(?, ?, ?, ?, ?, ?)').run(connectionId, userId, JSON.stringify(service), accessToken, refreshToken, expiry)
|
2024-01-24 14:16:24 -05:00
|
|
|
return this.getConnection(connectionId)
|
|
|
|
|
}
|
2024-01-25 03:05:13 -05:00
|
|
|
|
|
|
|
|
static deleteConnection = (id: string): void => {
|
|
|
|
|
const commandInfo = db.prepare('DELETE FROM Connections WHERE id = ?').run(id)
|
|
|
|
|
if (commandInfo.changes === 0) throw new Error(`Connection with id: ${id} does not exist`)
|
|
|
|
|
}
|
2024-02-17 00:31:11 -05:00
|
|
|
|
2024-02-18 00:01:54 -05:00
|
|
|
static updateTokens = (id: string, accessToken: string, refreshToken?: string, expiry?: number): void => {
|
|
|
|
|
const commandInfo = db.prepare('UPDATE Connections SET accessToken = ?, refreshToken = ?, expiry = ? WHERE id = ?').run(accessToken, refreshToken, expiry, id)
|
2024-02-17 00:31:11 -05:00
|
|
|
if (commandInfo.changes === 0) throw new Error('Failed to update tokens')
|
|
|
|
|
}
|
2024-02-18 00:01:54 -05:00
|
|
|
|
|
|
|
|
static getExpiredConnections = (userId: string): Connection[] => {
|
|
|
|
|
const expiredRows = db.prepare('SELECT * FROM Connections WHERE userId = ? AND expiry < ?').all(userId, Date.now()) as ConnectionsTableSchema[]
|
|
|
|
|
const connections: Connection[] = []
|
|
|
|
|
for (const row of expiredRows) {
|
|
|
|
|
const { id, userId, service, accessToken, refreshToken, expiry } = row
|
|
|
|
|
connections.push({ id, userId, service: JSON.parse(service), accessToken, refreshToken, expiry })
|
|
|
|
|
}
|
|
|
|
|
return connections
|
|
|
|
|
}
|
2024-01-23 12:05:33 -05:00
|
|
|
}
|