Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMPR: Embeds and a crappy file viewer. #249

Open
wants to merge 32 commits into
base: dev/0.15.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a8f1d55
mongoo
XWasHere Nov 6, 2023
3cfef2b
fix style stuff
XWasHere Nov 6, 2023
ac377ba
oops
XWasHere Nov 6, 2023
537ba6c
Merge branch 'dev/0.15.0-mongodb' into dev/0.15.0
XWasHere Nov 6, 2023
25da5b3
Merge branch 'dev/0.15.0' of https://github.com/XWasHere/ass into dev…
XWasHere Nov 6, 2023
73301a5
Merge remote-tracking branch 'upstream/dev/0.15.0' into dev/0.15.0
XWasHere Nov 6, 2023
46453b8
image viewer
XWasHere Nov 9, 2023
81cb4aa
embed
XWasHere Nov 9, 2023
7af42c7
embed templates
XWasHere Nov 9, 2023
c30f45b
oops
XWasHere Nov 9, 2023
6499353
uhh
XWasHere Nov 9, 2023
cb0d3eb
uhh
XWasHere Nov 9, 2023
83bfd21
more embed operations
XWasHere Nov 10, 2023
a1a0975
oops
XWasHere Nov 10, 2023
db32bb1
sitename
XWasHere Nov 10, 2023
a0571d7
Merge branch 'dev/0.15.0' into dev/0.15.0
XWasHere Nov 27, 2023
3660b6c
meow
XWasHere Nov 27, 2023
8a5684c
template inclusion
XWasHere Nov 27, 2023
ab94343
fuck
XWasHere Nov 27, 2023
e1a4982
fuck
XWasHere Nov 27, 2023
841556e
restrict include to admin defined templates
XWasHere Dec 1, 2023
275cb73
Merge remote-tracking branch 'upstream/dev/0.15.0' into dev/0.15.0
XWasHere Dec 1, 2023
839c7a7
fix this
XWasHere Dec 1, 2023
b8b4b5e
Merge branch 'dev/0.15.0' of https://github.com/XWasHere/ass into dev…
XWasHere Dec 1, 2023
704b622
Merge branch 'dev/0.15.0' into dev/0.15.0-embeds
XWasHere Dec 1, 2023
22569a5
fix a lot of broken stuff:
XWasHere Dec 1, 2023
fcdb049
Merge branch 'dev/0.15.0' into dev/0.15.0
XWasHere Dec 4, 2023
710930f
Merge branch 'dev/0.15.0' of https://github.com/XWasHere/ass into dev…
XWasHere Dec 5, 2023
4f69afd
OCTOPUS MERGE UHIHUJIKABIO:
XWasHere Dec 5, 2023
c6a5007
Merge branch 'dev/0.15.0-embeds' into dev/0.15.0
XWasHere Dec 5, 2023
cb051d1
Merge remote-tracking branch 'upstream/dev/0.15.0' into dev/0.15.0
XWasHere Dec 5, 2023
8cbe4b7
update postgres db for new db interface
XWasHere Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions backend/UserConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { UserConfiguration, UserConfigTypeChecker, PostgresConfiguration } from 'ass';
import { UserConfiguration, UserConfigTypeChecker, PostgresConfiguration, MongoDBConfiguration } from 'ass';

import fs from 'fs-extra';
import { path } from '@tycrek/joint';

import { log } from './log.js';
import { prepareTemplate } from './templates/parser.js';
import { TemplateError } from './templates/error.js';
import { DEFAULT_EMBED, validateEmbed } from './embed.js';

const FILEPATH = path.join('.ass-data/userconfig.json');

Expand Down Expand Up @@ -58,9 +62,6 @@ const Checkers: UserConfigTypeChecker = {
password: basicStringChecker,
database: basicStringChecker,
port: (val) => numChecker(val) && val >= 1 && val <= 65535
},
postgres: {
port: (val) => numChecker(val) && val >= 1 && val <= 65535
}
},

Expand Down Expand Up @@ -109,18 +110,13 @@ export class UserConfig {
// * Optional database config(s)
if (config.database != null) {
// these both have the same schema so we can just check both
if (config.database.kind == 'mysql' || config.database.kind == 'postgres') {
if (config.database.kind == 'mysql' || config.database.kind == 'postgres' || config.database.kind == 'mongodb') {
if (config.database.options != undefined) {
if (!Checkers.sql.mySql.host(config.database.options.host)) throw new Error('Invalid database host');
if (!Checkers.sql.mySql.user(config.database.options.user)) throw new Error('Invalid databse user');
if (!Checkers.sql.mySql.password(config.database.options.password)) throw new Error('Invalid database password');
if (!Checkers.sql.mySql.database(config.database.options.database)) throw new Error('Invalid database');
if (!Checkers.sql.mySql.port(config.database.options.port)) throw new Error('Invalid database port');
if (config.database.kind == 'postgres') {
if (!Checkers.sql.postgres.port((config.database.options as PostgresConfiguration).port)) {
throw new Error("Invalid database port");
}
}
} else throw new Error('Database options missing');
}
}
Expand All @@ -132,6 +128,30 @@ export class UserConfig {
if (!Checkers.rateLimit.endpoint(config.rateLimit.api)) throw new Error('Invalid API rate limit configuration');
}

// * the embed
if (config.embed != null) {
try {
for (let part of ['title', 'description', 'sitename'] as ('title' | 'description' | 'sitename')[]) {
if (config.embed[part] != null) {
if (typeof config.embed[part] == 'string') {
config.embed[part] = prepareTemplate(config.embed[part] as string, {
allowIncludeFile: true
});
} else throw new Error(`Template string for embed ${part} is not a string`);
} else config.embed[part] = DEFAULT_EMBED[part];
}

validateEmbed(config.embed);
} catch (err) {
if (err instanceof TemplateError) {
// tlog messes up the formatting
console.error(err.format());

throw new Error('Template error');
} else throw err;
}
} else config.embed = DEFAULT_EMBED;

// All is fine, carry on!
return config;
}
Expand Down
4 changes: 4 additions & 0 deletions backend/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DBManager } from './sql/database.js';
import { JSONDatabase } from './sql/json.js';
import { MySQLDatabase } from './sql/mysql.js';
import { PostgreSQLDatabase } from './sql/postgres.js';
import { MongoDBDatabase } from './sql/mongodb.js';
import { buildFrontendRouter } from './routers/_frontend.js';

/**
Expand Down Expand Up @@ -128,6 +129,9 @@ async function main() {
case 'postgres':
await DBManager.use(new PostgreSQLDatabase());
break;
case 'mongodb':
await DBManager.use(new MongoDBDatabase());
break;
}
} catch (err) { throw new Error(`Failed to configure SQL`); }
} else { // default to json database
Expand Down
1 change: 1 addition & 0 deletions backend/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type DataSector = 'files' | 'users';
const DBNAMES = {
'mysql': 'MySQL',
'postgres': 'PostgreSQL',
'mongodb': 'MongoDB',
'json': 'JSON'
};

Expand Down
32 changes: 32 additions & 0 deletions backend/embed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { AssFile, AssUser, EmbedTemplate, PreparedEmbed } from 'ass'

import { TemplateExecutor } from './templates/executor.js';

let executor = TemplateExecutor.createExecutor();

export const DEFAULT_EMBED: EmbedTemplate = {
sitename: 'ass',
title: '',
description: ''
};

// ensures a template is valid
export const validateEmbed = (template: EmbedTemplate) => {
// lets hope this works
let context = executor.createContext(null!, null!);

executor.validateTemplate(template.title, context);
executor.validateTemplate(template.description, context);
executor.validateTemplate(template.sitename, context);
}

// cooks up the embed
export const prepareEmbed = (template: EmbedTemplate, user: AssUser, file: AssFile): PreparedEmbed => {
let context = executor.createContext(user, file);

return {
title: executor.executeTemplate(template.title, context),
description: executor.executeTemplate(template.description, context),
sitename: executor.executeTemplate(template.sitename, context)
};
};
13 changes: 9 additions & 4 deletions backend/routers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DBManager } from '../sql/database.js';
import { JSONDatabase } from '../sql/json.js';
import { MySQLDatabase } from '../sql/mysql.js';
import { PostgreSQLDatabase } from '../sql/postgres.js';
import { MongoDBDatabase } from '../sql/mongodb.js';

const router = Router({ caseSensitive: true });

Expand Down Expand Up @@ -41,13 +42,16 @@ router.post('/setup', BodyParserJson(), async (req, res) => {
case 'postgres':
await DBManager.use(new PostgreSQLDatabase());
break;
case 'mongodb':
await DBManager.use(new MongoDBDatabase());
break;
}
}

// set rate limits
if (UserConfig.config.rateLimit?.api) setRateLimiter('api', UserConfig.config.rateLimit.api);
if (UserConfig.config.rateLimit?.login) setRateLimiter('login', UserConfig.config.rateLimit.login);
if (UserConfig.config.rateLimit?.upload) setRateLimiter('upload', UserConfig.config.rateLimit.upload);;
if (UserConfig.config.rateLimit?.api) setRateLimiter('api', UserConfig.config.rateLimit.api);
if (UserConfig.config.rateLimit?.login) setRateLimiter('login', UserConfig.config.rateLimit.login);
if (UserConfig.config.rateLimit?.upload) setRateLimiter('upload', UserConfig.config.rateLimit.upload);

log.success('Setup', 'completed');

Expand All @@ -62,10 +66,11 @@ router.post('/setup', BodyParserJson(), async (req, res) => {
router.post('/login', rateLimiterMiddleware('login', UserConfig.config?.rateLimit?.login), BodyParserJson(), (req, res) => {
const { username, password } = req.body;

// something tells me we shouldnt be using getall here
data.getAll('users')
.then((users) => {
if (!users) throw new Error('Missing users data');
else return Object.entries(users as AssUser[])
else return Object.entries(users as AssUser[])
.filter(([_uid, user]: [string, AssUser]) => user.username === username)[0][1]; // [0] is the first item in the filter results, [1] is AssUser
})
.then((user) => Promise.all([bcrypt.compare(password, user.password), user]))
Expand Down
48 changes: 45 additions & 3 deletions backend/routers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BusBoyFile, AssFile } from 'ass';
import { BusBoyFile, AssFile, AssUser } from 'ass';

import fs from 'fs-extra';
import bb from 'express-busboy';
Expand All @@ -13,6 +13,8 @@ import { random } from '../generators.js';
import { UserConfig } from '../UserConfig.js';
import { getFileS3, uploadFileS3 } from '../s3.js';
import { rateLimiterMiddleware } from '../ratelimit.js';
import { DBManager } from '../sql/database.js';
import { DEFAULT_EMBED, prepareEmbed } from '../embed.js';

const router = Router({ caseSensitive: true });

Expand All @@ -30,7 +32,7 @@ bb.extend(router, {
router.get('/', (req, res) => UserConfig.ready ? res.render('index', { version: App.pkgVersion }) : res.redirect('/setup'));

// Upload flow
router.post('/', rateLimiterMiddleware("upload", UserConfig.config?.rateLimit?.upload), async (req, res) => {
router.post('/', rateLimiterMiddleware('upload', UserConfig.config?.rateLimit?.upload), async (req, res) => {

// Check user config
if (!UserConfig.ready) return res.status(500).type('text').send('Configuration missing!');
Expand Down Expand Up @@ -96,7 +98,47 @@ router.post('/', rateLimiterMiddleware("upload", UserConfig.config?.rateLimit?.u
}
});

router.get('/:fakeId', (req, res) => res.redirect(`/direct/${req.params.fakeId}`));
router.get('/:fakeId', async (req, res) => {
if (!UserConfig.ready) res.redirect('/setup');

// Get the ID
const fakeId = req.params.fakeId;

// Get the file metadata
let _data;
try { _data = await DBManager.get('assfiles', fakeId); }
catch (err) {
log.error('Failed to get', fakeId);
console.error(err);
return res.status(500).send();
}

if (!_data) return res.status(404).send();
else {
let meta = _data as AssFile;
let user = await DBManager.get('assusers', meta.uploader) as AssUser | undefined;

res.render("viewer", {
url: `/direct/${fakeId}`,
uploader: user?.username ?? 'unknown',
size: meta.size,
time: meta.timestamp,
embed: prepareEmbed({
title: UserConfig.config.embed?.title ?? DEFAULT_EMBED.title,
description: UserConfig.config.embed?.description ?? DEFAULT_EMBED.description,
sitename: UserConfig.config.embed?.sitename ?? DEFAULT_EMBED.sitename
}, user ?? {
admin: false,
files: [],
id: "",
meta: {},
password: "",
tokens: [],
username: "unknown"
}, meta)
});
}
});

router.get('/direct/:fakeId', async (req, res) => {
if (!UserConfig.ready) res.redirect('/setup');
Expand Down
8 changes: 4 additions & 4 deletions backend/sql/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class DBManager {
public static configure(): Promise<void> {
if (this._db && this._dbReady) {
return this._db.configure();
} else throw new Error("No database active");
} else throw new Error('No database active');
}

/**
Expand All @@ -44,7 +44,7 @@ export class DBManager {
public static put(table: DatabaseTable, key: NID, data: DatabaseValue): Promise<void> {
if (this._db && this._dbReady) {
return this._db.put(table, key, data);
} else throw new Error("No database active");
} else throw new Error('No database active');
}

/**
Expand All @@ -53,7 +53,7 @@ export class DBManager {
public static get(table: DatabaseTable, key: NID): Promise<DatabaseValue> {
if (this._db && this._dbReady) {
return this._db.get(table, key);
} else throw new Error("No database active");
} else throw new Error('No database active');
}

/**
Expand All @@ -62,6 +62,6 @@ export class DBManager {
public static getAll(table: DatabaseTable): Promise<DatabaseValue[]> {
if (this._db && this._dbReady) {
return this._db.getAll(table);
} else throw new Error("No database active");
} else throw new Error('No database active');
}
}
Loading