sequelize, storage for userdata

This commit is contained in:
Felix Kaspar 2024-05-29 23:19:07 +02:00
parent 9d540d6aa2
commit 09a3d83dc5
12 changed files with 1493 additions and 51 deletions

2
.gitignore vendored
View File

@ -6,5 +6,5 @@ android/
ios/ ios/
releases/ releases/
.vscode/ .vscode/
.env .env.local
/server-node/jobs /server-node/jobs

1254
package-lock.json generated

File diff suppressed because it is too large Load Diff

2
server-node/.env Normal file
View File

@ -0,0 +1,2 @@
JOBS_DIR="./jobs"
SESSION_SECRET="default-secret"

View File

@ -41,6 +41,8 @@
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"rollup-plugin-copy": "^3.5.0", "rollup-plugin-copy": "^3.5.0",
"rollup-plugin-dynamic-import-variables": "^1.1.0", "rollup-plugin-dynamic-import-variables": "^1.1.0",
"sequelize": "^6.37.3",
"sqlite3": "^5.1.7",
"toml": "^3.0.0", "toml": "^3.0.0",
"tsconfig-paths": "^4.2.0", "tsconfig-paths": "^4.2.0",
"vite-plugin-compile-time": "^0.2.1", "vite-plugin-compile-time": "^0.2.1",

View File

@ -6,14 +6,14 @@ import { HeaderAPIKeyStrategy as HeaderAPIKeyStrategy } from "passport-headerapi
export function initialize(passport: typeof import("passport")) { export function initialize(passport: typeof import("passport")) {
passport.use("local", new LocalStrategy( passport.use("local", new LocalStrategy(
function(username, password, done) { function(username, password, done) {
User.findOne({ username: username }, function (err, user) { User.findOne({ username: username }, async function (err, user) {
if (err) { if (err) {
return done(err); return done(err, false);
} }
if (!user) { if (!user) {
return done(null, false); return done(null, false);
} }
if (!User.verifyPassword(user, password)) { if (!await User.verifyPassword(user, password)) {
return done(null, false); return done(null, false);
} }
return done(null, user); return done(null, user);
@ -35,7 +35,7 @@ export function initialize(passport: typeof import("passport")) {
return done(null, user); return done(null, user);
}); });
} }
)); ));
passport.serializeUser((user, done) => { passport.serializeUser((user, done) => {
done(null, user.id) //TODO: Extend Express.User to include id wich is set by passport done(null, user.id) //TODO: Extend Express.User to include id wich is set by passport

View File

@ -1,16 +1,55 @@
import { User } from "./user-model"; import { Error as SequelizeError, Op } from "sequelize";
import { Password, User } from "./user-model";
export function findOne(params: {id?: number, username?: string, apikey?: string}, cb: (err: Error | null, user: User) => void): undefined { type PickOne<T, F extends keyof T> = Pick<T, F> & { [K in keyof Omit<T, F>]?: never };
//TODO: replace with db connection.
cb(null, { export function findOne(params: {id?: number, username?: string, apikey?: string}, cb: (err: Error | null, user: User | null) => void): undefined {
id: 1, const query: any = params;
username: "test",
mail: "test@test.com", for (let key in query) {
accessControlList: [] if (query[key] === undefined) {
}); delete query[key];
}
}
if(Object.keys(query).length == 0) {
cb(new Error("You need to provide at least one argument."), null)
}
User.findOne({
where: query
}).then(user => {
if(user)
cb(null, user);
else
cb(new Error("The requested user was not found."), null);
}).catch(e =>
cb(e, null)
);
} }
export function verifyPassword(user: User, password: string) { export async function verifyPassword(user: User, password: string): Promise<boolean> {
//TODO: replace with db connection. const passwordRecord = await user.getPassword();
return password == "test"; if(!passwordRecord) {
throw new Error("This user does not have a password set!");
}
return passwordRecord.password == password;
}
export function createUser(params: { username: string, password: string }, cb: (err: SequelizeError | null, user: User | null) => void ) {
User.create({ username: params.username, authenticationMethod: "password" }).then(async user => {
user.setPassword(await Password.create({
password: params.password,
})).then(password => {
cb(null, user as any as User)
}).catch(e =>
cb(e, null)
);
}).catch(e =>
cb(e, null)
);
}
export function createAPIKey(user: User, apikey?: string) {
} }

View File

@ -1,6 +1,80 @@
export interface User { import {
id: number, Association, DataTypes, Model, ModelDefined, Optional,
username: string, Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey,
mail: string,
accessControlList: string[], HasManyAddAssociationMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin,
HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin,
HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin,
HasOneGetAssociationMixin, HasOneSetAssociationMixin, HasOneCreateAssociationMixin,
} from 'sequelize';
export class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
declare id: CreationOptional<number>;
declare username: string;
declare mail?: string;
declare authenticationMethod: string;
declare getPassword: HasOneGetAssociationMixin<Password | undefined>; // Note the null assertions!
declare setPassword: HasOneSetAssociationMixin<Password | undefined, number>;
declare createPassword: HasOneCreateAssociationMixin<Password>;
declare getAccessRules: HasManyGetAssociationsMixin<AccessRule | undefined>; // Note the null assertions!
declare addAccessRule: HasManyAddAssociationMixin<AccessRule | undefined, number>;
declare addAccessRules: HasManyAddAssociationsMixin<AccessRule | undefined, number>;
declare setAccessRules: HasManySetAssociationsMixin<AccessRule | undefined, number>;
declare removeAccessRule: HasManyRemoveAssociationMixin<AccessRule | undefined, number>;
declare removeAccessRules: HasManyRemoveAssociationsMixin<AccessRule | undefined, number>;
declare hasAccessRule: HasManyHasAssociationMixin<AccessRule | undefined, number>;
declare hasAccessRules: HasManyHasAssociationsMixin<AccessRule | undefined, number>;
declare countAccessRules: HasManyCountAssociationsMixin;
declare createAccessRule: HasManyCreateAssociationMixin<AccessRule, 'userId'>;
declare getAPIKeys: HasManyGetAssociationsMixin<APIKey | undefined>; // Note the null assertions!
declare addAPIKey: HasManyAddAssociationMixin<APIKey | undefined, number>;
declare addAPIKeys: HasManyAddAssociationsMixin<APIKey | undefined, number>;
declare setAPIKeys: HasManySetAssociationsMixin<APIKey | undefined, number>;
declare removeAPIKey: HasManyRemoveAssociationMixin<APIKey | undefined, number>;
declare removeAPIKeys: HasManyRemoveAssociationsMixin<APIKey | undefined, number>;
declare hasAPIKey: HasManyHasAssociationMixin<APIKey | undefined, number>;
declare hasAPIKeys: HasManyHasAssociationsMixin<APIKey | undefined, number>;
declare countAPIKeys: HasManyCountAssociationsMixin;
declare createAPIKey: HasManyCreateAssociationMixin<APIKey, 'userId'>;
declare createdAt: CreationOptional<Date>;
declare updatedAt: CreationOptional<Date>;
}
export class Password extends Model<InferAttributes<Password>, InferCreationAttributes<Password>> {
declare id: CreationOptional<number>;
declare password: string;
declare ownerId: ForeignKey<User['id']>;
declare owner?: NonAttribute<User>;
declare createdAt: CreationOptional<Date>;
declare updatedAt: CreationOptional<Date>;
}
export class AccessRule extends Model<InferAttributes<AccessRule>, InferCreationAttributes<AccessRule>> {
declare id: CreationOptional<number>;
declare grants: string;
declare userId: ForeignKey<User['id']>;
declare user?: NonAttribute<User>;
declare createdAt: CreationOptional<Date>;
declare updatedAt: CreationOptional<Date>;
}
export class APIKey extends Model<InferAttributes<APIKey>, InferCreationAttributes<APIKey>> {
declare id: CreationOptional<number>;
declare apikey: string;
declare userId: ForeignKey<User['id']>;
declare user?: NonAttribute<User>;
declare createdAt: CreationOptional<Date>;
declare updatedAt: CreationOptional<Date>;
} }

View File

@ -0,0 +1,87 @@
import { Sequelize, DataTypes } from "sequelize";
//TODO: Make this configurable
const sequelize = new Sequelize("sqlite::memory:");
import { User, AccessRule, APIKey, Password } from "../auth/user/user-model";
User.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
mail: {
type: DataTypes.STRING,
unique: true,
},
authenticationMethod: {
type: DataTypes.STRING,
allowNull: false,
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{ sequelize },
);
Password.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
password: DataTypes.STRING,
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{ sequelize },
);
Password.hasOne(User);
User.hasOne(Password);
AccessRule.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
grants: DataTypes.STRING,
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{ sequelize },
);
AccessRule.hasOne(User);
User.hasMany(AccessRule);
APIKey.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
apikey: DataTypes.STRING,
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{ sequelize },
);
APIKey.hasOne(User);
User.hasMany(APIKey);
export default sequelize;
(async () => {
await sequelize.sync({ force: true });
console.log("Database synced!")
})();

View File

@ -27,6 +27,12 @@ console.log("Available Modules: ", listOperatorNames());
import "./jobs/jobs-controller"; import "./jobs/jobs-controller";
/**
* database
*/
import "./data/sequelize-relations";
/* /*
* EXPRESS * EXPRESS
*/ */

View File

@ -3,8 +3,8 @@ const router = express.Router();
router.post('/logout', function(req, res, next) { router.post('/logout', function(req, res, next) {
req.logout(function(err) { req.logout(function(err) {
if (err) { return next(err); } if (err) { return next(err); }
res.redirect('/'); res.redirect('/');
}); });
}); });

View File

@ -1,8 +1,35 @@
import { error } from "pdf-lib";
import * as User from "../../auth/user/user-controller";
import express, { Request, Response } from "express"; import express, { Request, Response } from "express";
const router = express.Router(); const router = express.Router();
router.post('/register', async function(req: Request, res: Response) { router.post('/register', async function(req: Request, res: Response) {
//TODO: Register new user //TODO: Register new user
});
router.post('/register/password', async function(req: Request, res: Response) {
if(req.query) {
if(!req.query.username) {
res.status(400).json({error: "no username was provided"});
return;
}
if(!req.query.password) {
res.status(400).json({error: "no password was provided"});
return;
}
User.createUser({username: req.query.username as string, password: req.query.password as string}, async (err, user) => {
if(err) {
res.status(500).json(err);
return;
}
res.json(user);
});
}
else {
res.status(400).json({error: "no params were provided"})
}
}); });
export default router; export default router;

View File

@ -10,6 +10,11 @@ export default defineConfig({
// vite server configs, for details see [vite doc](https://vitejs.dev/config/#server-host) // vite server configs, for details see [vite doc](https://vitejs.dev/config/#server-host)
port: 8000 port: 8000
}, },
optimizeDeps: {
exclude: [
"pg-hstore" // sequelize
]
},
plugins: [ plugins: [
...VitePluginNode({ ...VitePluginNode({
// Nodejs native Request adapter // Nodejs native Request adapter