Fixed login required redirect, implemented getSequelize func to avoid
sequelize being used on spa
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
import { sequelize } from '$lib/server/database';
|
||||
import { getSequelize } from '$lib/server/database';
|
||||
import { unsign } from 'cookie-signature';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import sodium from 'libsodium-wrappers-sumo';
|
||||
@@ -17,9 +17,10 @@ export async function handle({ event, resolve }) {
|
||||
return await resolve(event);
|
||||
}
|
||||
|
||||
const sequelize = getSequelize();
|
||||
if (!sequelizeAuthenticated) {
|
||||
await sequelize.sync();
|
||||
await sequelize.authenticate();
|
||||
await sequelize.sequelize.sync();
|
||||
await sequelize.sequelize.authenticate();
|
||||
sequelizeAuthenticated = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { verifySolution } from 'altcha-lib';
|
||||
import { CaptchaTable } from './database';
|
||||
import { getSequelize } from './database';
|
||||
|
||||
export async function verifyCaptcha(payload: string, key: string, maxUses: number = -1) {
|
||||
const passedCaptcha = await verifySolution(payload, key, true);
|
||||
@@ -22,11 +22,14 @@ export async function verifyCaptcha(payload: string, key: string, maxUses: numbe
|
||||
}
|
||||
|
||||
if (maxUses !== -1) {
|
||||
if ((await CaptchaTable.count({ where: { signature: captchaSignature } })) >= maxUses) {
|
||||
if (
|
||||
(await getSequelize().CaptchaTable.count({ where: { signature: captchaSignature } })) >=
|
||||
maxUses
|
||||
) {
|
||||
throw error(400, 'Unsupported payload');
|
||||
}
|
||||
|
||||
await CaptchaTable.create({
|
||||
await getSequelize().CaptchaTable.create({
|
||||
signature: captchaSignature,
|
||||
created: new Date()
|
||||
});
|
||||
|
||||
@@ -1,70 +1,132 @@
|
||||
import { Sequelize, DataTypes, type Model } from 'sequelize';
|
||||
import { Sequelize, DataTypes, Model, type ModelCtor } from 'sequelize';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
export const sequelize = new Sequelize(
|
||||
// Use sqlite::memory: if not provided.
|
||||
env.DATABASE_CONNECTION_URI ? env.DATABASE_CONNECTION_URI : 'sqlite::memory:'
|
||||
);
|
||||
let UserTable: ModelCtor<Model<any, any>>;
|
||||
let ChannelSubscriptionTable: ModelCtor<Model<any, any>>;
|
||||
let CaptchaTable: ModelCtor<Model<any, any>>;
|
||||
|
||||
export interface UserTableModel extends Model {
|
||||
id: string;
|
||||
username: string;
|
||||
passwordHash: string;
|
||||
passwordSalt: string;
|
||||
created: Date;
|
||||
decryptionKeySalt: string;
|
||||
masterKeyCipher: string;
|
||||
masterKeyNonce: string;
|
||||
}
|
||||
let sequelizeInstance: Sequelize | null = null;
|
||||
|
||||
export const UserTable = sequelize.define(
|
||||
'User',
|
||||
{
|
||||
// Don't want to make our Sequelize instance always
|
||||
export function getSequelize(): {
|
||||
sequelize: Sequelize;
|
||||
UserTable: ModelCtor<Model<any, any>>;
|
||||
ChannelSubscriptionTable: ModelCtor<Model<any, any>>;
|
||||
CaptchaTable: ModelCtor<Model<any, any>>;
|
||||
} {
|
||||
if (sequelizeInstance) {
|
||||
return {
|
||||
sequelize: sequelizeInstance,
|
||||
UserTable,
|
||||
ChannelSubscriptionTable,
|
||||
CaptchaTable
|
||||
};
|
||||
}
|
||||
|
||||
sequelizeInstance = new Sequelize(
|
||||
env.DATABASE_CONNECTION_URI ? env.DATABASE_CONNECTION_URI : 'sqlite::memory:'
|
||||
);
|
||||
|
||||
console.log('sequelizeInstance');
|
||||
|
||||
UserTable = sequelizeInstance.define(
|
||||
'User',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.UUIDV4,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
unique: true
|
||||
},
|
||||
username: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
passwordHash: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
passwordSalt: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
created: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
decryptionKeySalt: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
masterKeyCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
masterKeyNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['id', 'username']
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
ChannelSubscriptionTable = sequelizeInstance.define('Subscriptions', {
|
||||
id: {
|
||||
type: DataTypes.UUIDV4,
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
unique: true
|
||||
},
|
||||
username: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
primaryKey: true
|
||||
},
|
||||
passwordHash: {
|
||||
channelIdCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
passwordSalt: {
|
||||
channelIdNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
channelNameCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
channelNameNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastRSSFetch: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
|
||||
CaptchaTable = sequelizeInstance.define('Captchas', {
|
||||
signature: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
},
|
||||
created: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
decryptionKeySalt: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
masterKeyCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
masterKeyNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
},
|
||||
{
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['id', 'username']
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
UserTable.hasMany(ChannelSubscriptionTable);
|
||||
|
||||
return {
|
||||
sequelize: sequelizeInstance,
|
||||
UserTable,
|
||||
ChannelSubscriptionTable,
|
||||
CaptchaTable
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChannelSubscriptionModel {
|
||||
id: string; // Hashed authId with subscription key on client.
|
||||
@@ -76,44 +138,13 @@ export interface ChannelSubscriptionModel {
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export const ChannelSubscriptionTable = sequelize.define('Subscriptions', {
|
||||
id: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
},
|
||||
channelIdCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
channelIdNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
channelNameCipher: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
channelNameNonce: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastRSSFetch: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
|
||||
UserTable.hasMany(ChannelSubscriptionTable);
|
||||
|
||||
export const CaptchaTable = sequelize.define('Captchas', {
|
||||
signature: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
},
|
||||
created: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
}
|
||||
});
|
||||
export interface UserTableModel extends Model {
|
||||
id: string;
|
||||
username: string;
|
||||
passwordHash: string;
|
||||
passwordSalt: string;
|
||||
created: Date;
|
||||
decryptionKeySalt: string;
|
||||
masterKeyCipher: string;
|
||||
masterKeyNonce: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
ChannelSubscriptionTable,
|
||||
UserTable,
|
||||
type ChannelSubscriptionModel,
|
||||
type UserTableModel
|
||||
} from './database';
|
||||
import { getSequelize, type ChannelSubscriptionModel, type UserTableModel } from './database';
|
||||
import { Op } from 'sequelize';
|
||||
import crypto from 'crypto';
|
||||
import { error } from '@sveltejs/kit';
|
||||
@@ -35,11 +30,11 @@ export class User {
|
||||
}
|
||||
|
||||
async delete() {
|
||||
await UserTable.destroy(this.userWhere);
|
||||
await getSequelize().UserTable.destroy(this.userWhere);
|
||||
}
|
||||
|
||||
async subscriptionRssUpdated(id: string) {
|
||||
await ChannelSubscriptionTable.update(
|
||||
await getSequelize().ChannelSubscriptionTable.update(
|
||||
{ lastRSSFetch: new Date() },
|
||||
{
|
||||
where: {
|
||||
@@ -51,14 +46,14 @@ export class User {
|
||||
}
|
||||
|
||||
async addSubscription(subscription: Omit<ChannelSubscriptionModel, 'userId'>) {
|
||||
await ChannelSubscriptionTable.create({
|
||||
await getSequelize().ChannelSubscriptionTable.create({
|
||||
...subscription,
|
||||
UserId: this.id
|
||||
});
|
||||
}
|
||||
|
||||
async removeSubscription(id: string) {
|
||||
await ChannelSubscriptionTable.destroy({
|
||||
await getSequelize().ChannelSubscriptionTable.destroy({
|
||||
where: {
|
||||
id
|
||||
}
|
||||
@@ -67,7 +62,7 @@ export class User {
|
||||
|
||||
async amSubscribed(id: string): Promise<boolean> {
|
||||
return (
|
||||
(await ChannelSubscriptionTable.count({
|
||||
(await getSequelize().ChannelSubscriptionTable.count({
|
||||
where: {
|
||||
id
|
||||
}
|
||||
@@ -76,7 +71,7 @@ export class User {
|
||||
}
|
||||
|
||||
async subscriptions(): Promise<ChannelSubscriptionModel[]> {
|
||||
const subscriptions = await ChannelSubscriptionTable.findAll({
|
||||
const subscriptions = await getSequelize().ChannelSubscriptionTable.findAll({
|
||||
where: {
|
||||
userId: this.data.id
|
||||
}
|
||||
@@ -117,7 +112,7 @@ export async function createUser(user: CreateUser): Promise<User> {
|
||||
|
||||
let userCreated = false;
|
||||
try {
|
||||
await UserTable.create(createdUser);
|
||||
await getSequelize().UserTable.create(createdUser);
|
||||
userCreated = true;
|
||||
} catch {
|
||||
userCreated = false;
|
||||
@@ -131,7 +126,7 @@ export async function createUser(user: CreateUser): Promise<User> {
|
||||
}
|
||||
|
||||
export async function getUser(identifier: string): Promise<User> {
|
||||
const user = await UserTable.findOne({
|
||||
const user = await getSequelize().UserTable.findOne({
|
||||
where: {
|
||||
[Op.or]: [{ id: identifier }, { username: identifier }]
|
||||
}
|
||||
@@ -145,7 +140,7 @@ export async function getUser(identifier: string): Promise<User> {
|
||||
}
|
||||
|
||||
export async function authenticateUser(username: string, passwordHash: string): Promise<User> {
|
||||
const user = await UserTable.findOne({
|
||||
const user = await getSequelize().UserTable.findOne({
|
||||
where: {
|
||||
username
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { isYTBackend } from '$lib/misc.js';
|
||||
import { isOwnBackend } from '$lib/shared/index.js';
|
||||
|
||||
export const ssr = false;
|
||||
export const prerender = false;
|
||||
|
||||
export async function load({ url }) {
|
||||
if (browser) {
|
||||
@@ -46,18 +47,6 @@ export async function load({ url }) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!get(instanceStore) && !isYTBackend() && !url.pathname.endsWith('/setup')) {
|
||||
goto(resolve('/setup', {}), { replaceState: true });
|
||||
}
|
||||
|
||||
if (
|
||||
isOwnBackend()?.requireAuth &&
|
||||
!get(rawMasterKeyStore) &&
|
||||
!url.pathname.endsWith('/internal/login')
|
||||
) {
|
||||
goto(resolve('/internal/login', {}), { replaceState: true });
|
||||
}
|
||||
|
||||
const resolvedRoot = resolve('/', {});
|
||||
if (url.pathname.startsWith(resolvedRoot + '@')) {
|
||||
const username = url.pathname.substring(resolvedRoot.length).split('/')[0];
|
||||
@@ -87,4 +76,14 @@ export async function load({ url }) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const isLoginPage = url.pathname.endsWith('/internal/login');
|
||||
|
||||
if (!isLoginPage) {
|
||||
if (isOwnBackend()?.requireAuth && !get(rawMasterKeyStore)) {
|
||||
goto(resolve('/internal/login', {}), { replaceState: true });
|
||||
} else if (!get(instanceStore) && !isYTBackend() && !url.pathname.endsWith('/setup')) {
|
||||
goto(resolve('/setup', {}), { replaceState: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user