132 lines
3.6 KiB
JavaScript
132 lines
3.6 KiB
JavaScript
const crypto = require("crypto")
|
|
const {parse: parseCookie} = require("cookie")
|
|
const constants = require("./constants")
|
|
const db = require("./db")
|
|
|
|
function getToken(req, responseHeaders) {
|
|
if (!req.headers.cookie) req.headers.cookie = ""
|
|
const cookie = parseCookie(req.headers.cookie)
|
|
let token = cookie.token
|
|
if (!token) {
|
|
if (responseHeaders) { // we should create a token
|
|
token = setToken(responseHeaders).token
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
db.prepare(
|
|
"INSERT INTO SeenTokens (token, seen) VALUES (?, ?)"
|
|
+ " ON CONFLICT (token) DO UPDATE SET seen = excluded.seen"
|
|
).run([token, Date.now()])
|
|
return token
|
|
}
|
|
|
|
function setToken(responseHeaders, token) {
|
|
const setCookie = responseHeaders["set-cookie"] || []
|
|
if (!token) token = crypto.randomBytes(18).toString("base64").replace(/\W/g, "_")
|
|
setCookie.push(`token=${token}; Path=/; Max-Age=2147483648; HttpOnly; SameSite=Lax`)
|
|
responseHeaders["set-cookie"] = setCookie
|
|
return {token, responseHeaders}
|
|
}
|
|
|
|
class User {
|
|
constructor(token) {
|
|
this.token = token
|
|
}
|
|
|
|
/** @return {{instance?: string, save_history?: boolean, local?: boolean, quality?: number}} */
|
|
getSettings() {
|
|
if (this.token) {
|
|
return db.prepare("SELECT * FROM Settings WHERE token = ?").get(this.token) || {}
|
|
} else {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
/** @return {{instance?: string, save_history?: boolean, local?: boolean, quality?: number}} */
|
|
getSettingsOrDefaults() {
|
|
const settings = this.getSettings()
|
|
for (const key of Object.keys(constants.user_settings)) {
|
|
if (settings[key] == null) settings[key] = constants.user_settings[key].default
|
|
}
|
|
return settings
|
|
}
|
|
|
|
getSubscriptions() {
|
|
if (this.token) {
|
|
return db.prepare("SELECT ucid FROM Subscriptions WHERE token = ?").pluck().all(this.token)
|
|
} else {
|
|
return []
|
|
}
|
|
}
|
|
|
|
isSubscribed(ucid) {
|
|
if (this.token) {
|
|
return !!db.prepare("SELECT * FROM Subscriptions WHERE token = ? AND ucid = ?").get([this.token, ucid])
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
getWatchedVideos() {
|
|
const settings = this.getSettingsOrDefaults()
|
|
if (this.token && settings.save_history) {
|
|
return db.prepare("SELECT videoID FROM WatchedVideos WHERE token = ?").pluck().all(this.token)
|
|
} else {
|
|
return []
|
|
}
|
|
}
|
|
|
|
addWatchedVideoMaybe(videoID) {
|
|
const settings = this.getSettingsOrDefaults()
|
|
if (videoID && this.token && settings.save_history) {
|
|
db.prepare("INSERT OR IGNORE INTO WatchedVideos (token, videoID) VALUES (?, ?)").run([this.token, videoID])
|
|
}
|
|
}
|
|
|
|
getFilters() {
|
|
if (this.token) {
|
|
return db.prepare("SELECT * FROM Filters WHERE token = ? ORDER BY data ASC").all(this.token)
|
|
} else {
|
|
return []
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {any} [responseHeaders] supply this to create a token
|
|
*/
|
|
function getUser(req, responseHeaders) {
|
|
const token = getToken(req, responseHeaders)
|
|
return new User(token)
|
|
}
|
|
|
|
function generateCSRF() {
|
|
const token = crypto.randomBytes(16).toString("hex")
|
|
const expires = Date.now() + constants.caching.csrf_time
|
|
db.prepare("INSERT INTO CSRFTokens (token, expires) VALUES (?, ?)").run(token, expires)
|
|
return token
|
|
}
|
|
|
|
function checkCSRF(token) {
|
|
const row = db.prepare("SELECT * FROM CSRFTokens WHERE token = ? AND expires > ?").get(token, Date.now())
|
|
if (row) {
|
|
db.prepare("DELETE FROM CSRFTokens WHERE token = ?").run(token)
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function cleanCSRF() {
|
|
db.prepare("DELETE FROM CSRFTokens WHERE expires <= ?").run(Date.now())
|
|
}
|
|
cleanCSRF()
|
|
setInterval(cleanCSRF, constants.caching.csrf_time).unref()
|
|
|
|
module.exports.getToken = getToken
|
|
module.exports.setToken = setToken
|
|
module.exports.generateCSRF = generateCSRF
|
|
module.exports.checkCSRF = checkCSRF
|
|
module.exports.getUser = getUser
|