0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 16:22:44 +00:00

Merge branch 'master' into experimentalGoogleServiceAccountChange

This commit is contained in:
G.Ambatte
2025-11-10 19:00:06 +13:00
committed by GitHub
86 changed files with 5162 additions and 2917 deletions

View File

@@ -35,6 +35,7 @@ import contentNegotiation from './middleware/content-negotiation.js';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import forceSSL from './forcessl.mw.js';
import dbCheck from './middleware/dbCheck.js';
const sanitizeBrew = (brew, accessType)=>{
@@ -274,7 +275,7 @@ app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{
app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
//User Page
app.get('/user/:username', async (req, res, next)=>{
app.get('/user/:username', dbCheck, async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
req.ogMeta = { ...defaultMetaTags,
@@ -349,7 +350,7 @@ app.get('/user/:username', async (req, res, next)=>{
});
//Change author name on brews
app.put('/api/user/rename', async (req, res)=>{
app.put('/api/user/rename', dbCheck, async (req, res)=>{
const { username, newUsername } = req.body;
const ownAccount = req.account && (req.account.username == newUsername);
@@ -435,7 +436,7 @@ app.get('/new', asyncHandler(async(req, res, next)=>{
}));
//Share Page
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
const { brew } = req;
req.ogMeta = { ...defaultMetaTags,
title : `${req.brew.title || 'Untitled Brew'} - ${req.brew.authors[0] || 'No author.'}`,
@@ -462,7 +463,7 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
}));
//Account Page
app.get('/account', asyncHandler(async (req, res, next)=>{
app.get('/account', dbCheck, asyncHandler(async (req, res, next)=>{
const data = {};
data.title = 'Account Information Page';
@@ -565,8 +566,6 @@ const renderPage = async (req, res)=>{
brews : req.brews,
googleBrews : req.googleBrews,
account : req.account,
enable_v3 : config.get('enable_v3'),
enable_themes : config.get('enable_themes'),
config : configuration,
ogMeta : req.ogMeta,
userThemes : req.userThemes

View File

@@ -22,16 +22,29 @@ const handleConnectionError = (error)=>{
}
};
const addListeners = (conn)=>{
conn.connection.on('disconnecting', ()=>{console.log('Mongo disconnecting...');});
conn.connection.on('disconnected', ()=>{console.log('Mongo disconnected!');});
conn.connection.on('connecting', ()=>{console.log('Mongo connecting...');});
conn.connection.on('connected', ()=>{console.log('Mongo connected!');});
return conn;
};
const disconnect = async ()=>{
return await Mongoose.disconnect();
};
const connect = async (config)=>{
return await Mongoose.connect(getMongoDBURL(config), { retryWrites: false })
.catch((error)=>handleConnectionError(error));
return await Mongoose.connect(getMongoDBURL(config), {
retryWrites : false,
autoIndex : (config.get('local_environments').includes(config.get('node_env')))
})
.then(addListeners(Mongoose))
.catch((error)=>handleConnectionError(error));
};
export default {
connect,
disconnect
};

View File

@@ -4,15 +4,16 @@ import { model as HomebrewModel } from './homebrew.model.js';
import express from 'express';
import zlib from 'zlib';
import GoogleActions from './googleActions.js';
import Markdown from '../shared/naturalcrit/markdown.js';
import Markdown from '../shared/markdown.js';
import yaml from 'js-yaml';
import asyncHandler from 'express-async-handler';
import { nanoid } from 'nanoid';
import {makePatches, applyPatches, stringifyPatches, parsePatch} from '@sanity/diff-match-patch';
import { makePatches, applyPatches, stringifyPatches, parsePatch } from '@sanity/diff-match-patch';
import { md5 } from 'hash-wasm';
import { splitTextStyleAndMetadata,
import { splitTextStyleAndMetadata,
brewSnippetsToJSON, debugTextMismatch } from '../shared/helpers.js';
import checkClientVersion from './middleware/check-client-version.js';
import dbCheck from './middleware/dbCheck.js';
const router = express.Router();
@@ -377,14 +378,14 @@ const api = {
// Patch to a throwaway variable while parallelizing - we're more concerned with error/no error.
const patchedResult = decodeURI(applyPatches(patches, encodeURI(brewFromServer.text))[0]);
if(patchedResult != brewFromClient.text)
throw("Patches did not apply cleanly, text mismatch detected");
throw ('Patches did not apply cleanly, text mismatch detected');
// brew.text = applyPatches(patches, brewFromServer.text)[0];
} catch (err) {
//debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`);
console.error('Failed to apply patches:', {
//patches : brewFromClient.patches,
brewId : brewFromClient.editId || 'unknown',
error : err
brewId : brewFromClient.editId || 'unknown',
error : err
});
// While running in parallel, don't throw the error upstream.
// throw err; // rethrow to preserve the 500 behavior
@@ -480,6 +481,7 @@ const api = {
await HomebrewModel.deleteOne({ editId: id });
return next();
}
throw(err);
}
let brew = req.brew;
@@ -530,6 +532,8 @@ const api = {
}
};
router.use(dbCheck);
router.post('/api', checkClientVersion, asyncHandler(api.newBrew));
router.put('/api/:id', checkClientVersion, asyncHandler(api.getBrew('edit', false)), asyncHandler(api.updateBrew));
router.put('/api/update/:id', checkClientVersion, asyncHandler(api.getBrew('edit', false)), asyncHandler(api.updateBrew));

View File

@@ -7,29 +7,29 @@ import zlib from 'zlib';
const HomebrewSchema = mongoose.Schema({
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
googleId : { type: String },
title : { type: String, default: '' },
googleId : { type: String, index: true },
title : { type: String, default: '', index: true },
text : { type: String, default: '' },
textBin : { type: Buffer },
pageCount : { type: Number, default: 1 },
pageCount : { type: Number, default: 1, index: true },
description : { type: String, default: '' },
tags : [String],
tags : { type: [String], index: true },
systems : [String],
lang : { type: String, default: 'en' },
renderer : { type: String, default: '' },
authors : [String],
lang : { type: String, default: 'en', index: true },
renderer : { type: String, default: '', index: true },
authors : { type: [String], index: true },
invitedAuthors : [String],
published : { type: Boolean, default: false },
thumbnail : { type: String, default: '' },
published : { type: Boolean, default: false, index: true },
thumbnail : { type: String, default: '', index: true },
createdAt : { type: Date, default: Date.now },
updatedAt : { type: Date, default: Date.now },
lastViewed : { type: Date, default: Date.now },
createdAt : { type: Date, default: Date.now, index: true },
updatedAt : { type: Date, default: Date.now, index: true },
lastViewed : { type: Date, default: Date.now, index: true },
views : { type: Number, default: 0 },
version : { type: Number, default: 1 },
version : { type: Number, default: 1, index: true },
lock : { type: Object }
lock : { type: Object, index: true }
}, { versionKey: false });
HomebrewSchema.statics.increaseView = async function(query) {
@@ -43,6 +43,8 @@ HomebrewSchema.statics.increaseView = async function(query) {
return brew;
};
// STATIC FUNCTIONS
HomebrewSchema.statics.get = async function(query, fields=null){
const brew = await Homebrew.findOne(query, fields).orFail()
.catch((error)=>{throw 'Can not find brew';});
@@ -63,6 +65,15 @@ HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, f
return brews;
};
// INDEXES
HomebrewSchema.index({ updatedAt: -1, lastViewed: -1 });
HomebrewSchema.index({ published: 1, title: 'text' });
HomebrewSchema.index({ lock: 1, sparse: true });
HomebrewSchema.path('lock.reviewRequested').index({ sparse: true });
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
export {

View File

@@ -0,0 +1,15 @@
import mongoose from 'mongoose';
import config from '../config.js';
export default (req, res, next)=>{
// Bypass DB checks during testing
if(config.get('node_env') == 'test') return next();
if(mongoose.connection.readyState == 1) return next();
throw {
HBErrorCode : '13',
name : 'Database Connection Error',
message : 'Unable to connect to database',
status : mongoose.connection.readyState
};
};

View File

@@ -0,0 +1,28 @@
import mongoose from 'mongoose';
import dbCheck from './dbCheck.js';
import config from '../config.js';
describe('dbCheck middleware', ()=>{
const next = jest.fn();
afterEach(()=>jest.clearAllMocks());
it('should skip check in test mode', ()=>{
config.get = jest.fn(()=>'test');
expect(()=>dbCheck({}, {}, next)).not.toThrow();
expect(next).toHaveBeenCalled();
});
it('should call next if readyState == 1', ()=>{
config.get = jest.fn(()=>'production');
mongoose.connection.readyState = 1;
dbCheck({}, {}, next);
expect(next).toHaveBeenCalled();
});
it('should throw if readyState != 1', ()=>{
config.get = jest.fn(()=>'production');
mongoose.connection.readyState = 99;
expect(()=>dbCheck({}, {}, next)).toThrow(/Unable to connect/);
});
});