0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 18:32:41 +00:00

Merge branch 'master' into writeinBrewTheme

This commit is contained in:
David Bolack
2025-01-07 20:36:53 -06:00
9 changed files with 219 additions and 136 deletions

View File

@@ -1,4 +1,4 @@
FROM node:20-alpine
FROM node:22-alpine
RUN apk --no-cache add git
ENV NODE_ENV=docker
@@ -9,7 +9,10 @@ WORKDIR /usr/src/app
# Copy package.json into the image, then run yarn install
# This improves caching so we don't have to download the dependencies every time the code changes
COPY package.json ./
COPY config/docker.json usr/src/app/config
# --ignore-scripts tells yarn not to run postbuild. We run it explicitly later
RUN node --version
RUN npm --version
RUN npm install --ignore-scripts
# Bundle app source and build application

View File

@@ -1,12 +1,119 @@
# Running Homebrewery via Docker
# Offline Install Instructions: Docker
The repo includes a Dockerfile and a docker-compose.yml file.
These instructions are for setting up a persistent instance of the Homebrewery application locally using Docker.
To run the application via docker-compose.yml:
`docker-compose up -d`
If you intend to develop with Homebrewery, following the Homebrewery application section of this guide is not recommended. Using docker to deploy MongoDB locally for development is not a bad idea at all, however.
To stop the application:
`docker-compose down`
# Install Docker
## Docker Desktop (MacOS/Windows)
Windows and Mac installs use Docker Desktop. Current install instructions are below.
* [Mac](https://docs.docker.com/desktop/mac/install/)
* [Windows](https://docs.docker.com/desktop/windows/install/)
You can set up the docker engine to start on boot via the Docker desktop UI.
## Docker Engine
Linux installs use Docker Engine. Docker provides installers and instructions for several of the most common distrubutions. If you do not see yours listed, it is very likely supported indirectly by your distribution.
* [Arch](https://docs.docker.com/desktop/setup/install/linux/archlinux/)
* [CentOS](https://docs.docker.com/engine/install/centos/)
* [Debian](https://docs.docker.com/engine/install/debian/)
* [Fedora](https://docs.docker.com/engine/install/fedora/)
* [RHEL](https://docs.docker.com/engine/install/rhel/)
* [Ubuntu](https://docs.docker.com/engine/install/ubuntu/)
### Post installation steps
[Manage Docker as a non-root user (highly recommended)](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user)
[Enable Docker to start on boot (highly recommended)](https://docs.docker.com/engine/install/linux-postinstall/#configure-docker-to-start-on-boot)
# Build Homebrewery Image
Next we build the homebrewery docker image. Start by cloning the repository.
```shell
git clone https://github.com/naturalcrit/homebrewery.git
cd homebrewery
```
Make an changes you need to `config/docker.json` then build the image. If it does not exist,the below as a template.
```
{
"host" : "localhost:8000",
"naturalcrit_url" : "local.naturalcrit.com:8010",
"secret" : "secret",
"web_port" : 8000,
"enable_v3" : true,
"mongodb_uri": "mongodb://172.17.0.2/homebrewery",
"enable_themes" : true,
}
```
```shell
docker-compose build homebrewery
```
# Add Mongo container
Once docker is installed and running, it is time to set up the containers. First up, Mongo.
```shell
docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 mongo:latest
```
Older CPUs may run into an issue with AVX support.
```
WARNING: MongoDB 5.0+ requires a CPU with AVX support, and your current system does not appear to have that!
see https://jira.mongodb.org/browse/SERVER-54407
see also https://www.mongodb.com/community/forums/t/mongodb-5-0-cpu-intel-g4650-compatibility/116610/2
see also https://github.com/docker-library/mongo/issues/485#issuecomment-891991814
```
If you see a message similar to this, try using the bitnami mongo instead.
```shell
docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 bitnami/mongo:latest
```
If your distribution is running on an arm device such as a Raspberry Pi, you will need to run the arm-built MongoDB v4.4.
```shell
docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 arm64v8/mongo:4.4
```
## Run the Homebrewery Image
```shell
# Make sure you run this in the homebrewery directory
docker run --name homebrewery-app -d --restart unless-stopped -e NODE_ENV=docker -v $(pwd)/config/docker.json:/usr/src/app/config/docker.json -p 8000:8000 docker.io/library/homebrewery:latest
```
## Updating the Image
When Homebrewery code updates, your docker container will not automatically follow the changes. To do so you will need to rebuild your homebrewery image.
First, return to your homebrewery clone (from Build Homebrewery Image above) or recreate the clone if you deleted your copy of the code.
First, delete the existing image.
```shell
docker rm -f homebrewery-app
```
Next, update the clone's code to the latest version.
```shell
cd homebrewery
git checkout master
git pull upstream master
```
Finally, rebuild and restart the homebrewery image.
```shell
docker-compose build homebrewery
docker run --name homebrewery-app -d --restart unless-stopped -e NODE_ENV=docker -v $(pwd)/config/docker.json:/usr/src/app/config/docker.json -p 8000:8000 docker.io/library/homebrewery:latest
```
To stop the application and remove all data:
`docker-compose down -v`

View File

@@ -6,10 +6,8 @@ function Dialog({ dismisskeys = [], closeText = 'Close', blocking = false, ...re
const dialogRef = useRef(null);
useEffect(()=>{
if(dismisskeys.length !== 0) {
blocking ? dialogRef.current?.showModal() : dialogRef.current?.show();
}
}, [dialogRef.current, dismisskeys]);
blocking ? dialogRef.current?.showModal() : dialogRef.current?.show();
}, []);
const dismiss = ()=>{
dismisskeys.forEach((key)=>{

View File

@@ -1,75 +1,53 @@
require('./errorBar.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const ErrorBar = createClass({
displayName : 'ErrorBar',
getDefaultProps : function() {
return {
errors : []
};
},
import Dialog from '../../../components/dialog.jsx';
hasOpenError : false,
hasCloseError : false,
hasMatchError : false,
const DISMISS_BUTTON = <i className='fas fa-times dismiss' />;
renderErrors : function(){
this.hasOpenError = false;
this.hasCloseError = false;
this.hasMatchError = false;
const ErrorBar = (props)=>{
if(!props.errors.length) return null;
let hasOpenError = false, hasCloseError = false, hasMatchError = false;
props.errors.map((err)=>{
if(err.id === 'OPEN') hasOpenError = true;
if(err.id === 'CLOSE') hasCloseError = true;
if(err.id === 'MISMATCH') hasMatchError = true;
});
const errors = _.map(this.props.errors, (err, idx)=>{
if(err.id == 'OPEN') this.hasOpenError = true;
if(err.id == 'CLOSE') this.hasCloseError = true;
if(err.id == 'MISMATCH') this.hasMatchError = true;
return <li key={idx}>
Line {err.line} : {err.text}, '{err.type}' tag
</li>;
});
const renderErrors = ()=>(
<ul>
{props.errors.map((err, idx)=>{
return <li key={idx}>
Line {err.line} : {err.text}, '{err.type}' tag
</li>;
})}
</ul>
);
return <ul>{errors}</ul>;
},
renderProtip : function(){
const msg = [];
if(this.hasOpenError){
msg.push(<div>
An unmatched opening tag means there's an opened tag that isn't closed. You need to close your tags, like this {'</div>'}. Make sure to match types!
</div>);
}
if(this.hasCloseError){
msg.push(<div>
An unmatched closing tag means you closed a tag without opening it. Either remove it, or check to where you think you opened it.
</div>);
}
if(this.hasMatchError){
msg.push(<div>
A type mismatch means you closed a tag, but the last open tag was a different type.
</div>);
}
return <div className='protips'>
const renderProtip = ()=>(
<div className='protips'>
<h4>Protips!</h4>
{msg}
</div>;
},
{hasOpenError && <div>Unmatched opening tag. Close your tags, like this {'</div>'}. Match types!</div>}
{hasCloseError && <div>Unmatched closing tag. Either remove it or check where it was opened.</div>}
{hasMatchError && <div>Type mismatch. Closed a tag with a different type.</div>}
</div>
);
render : function(){
if(!this.props.errors.length) return null;
return <div className='errorBar'>
<i className='fas fa-exclamation-triangle' />
<h3> There are HTML errors in your markup</h3>
<small>If these aren't fixed your brew will not render properly when you print it to PDF or share it</small>
{this.renderErrors()}
return (
<Dialog className='errorBar' closeText={DISMISS_BUTTON} >
<div>
<i className='fas fa-exclamation-triangle' />
<h2> There are HTML errors in your markup</h2>
<small>
If these aren't fixed your brew will not render properly when you print it to PDF or share it
</small>
{renderErrors()}
</div>
<hr />
{this.renderProtip()}
</div>;
}
});
{renderProtip()}
</Dialog>
);
};
module.exports = ErrorBar;

View File

@@ -1,60 +1,58 @@
.errorBar{
.errorBar {
position : absolute;
z-index : 10000;
box-sizing : border-box;
top : 32px;
z-index : 1;
width : 100%;
margin-right : 13px;
padding : 20px;
padding-bottom : 10px;
padding-left : 100px;
background-color : @red;
color : white;
i{
position : absolute;
left : 30px;
opacity : 0.8;
font-size : 3em;
}
h3{
font-size : 1.1em;
font-weight : 800;
}
ul{
margin-top : 15px;
font-size : 0.8em;
list-style-position : inside;
list-style-type : disc;
li{
line-height : 1.6em;
background-color : @red;
border : unset;
div {
> i {
float : left;
margin-right : 10px;
margin-bottom : 20px;
font-size : 3em;
opacity : 0.8;
}
h2 { font-weight : 800; }
ul {
margin-top : 15px;
font-size : 0.8em;
list-style-position : inside;
list-style-type : disc;
li { line-height : 1.6em; }
}
}
hr{
box-sizing : border-box;
hr {
height : 2px;
width : 150%;
margin-top : 25px;
margin-bottom : 15px;
margin-left : -100px;
background-color : darken(@red, 8%);
border : none;
}
small{
font-size: 0.6em;
opacity: 0.7;
small {
font-size : 0.6em;
opacity : 0.7;
}
.protips{
margin-left : -80px;
font-size : 0.6em;
&>div{
margin-bottom : 10px;
line-height : 1.2em;
}
h4{
opacity : 0.8;
.protips {
font-size : 0.6em;
line-height : 1.2em;
h4 {
font-weight : 800;
line-height : 1.5em;
text-transform : uppercase;
}
}
button.dismiss {
position : absolute;
top : 20px;
right : 30px;
padding : unset;
font-size : 40px;
background-color : transparent;
opacity : 0.6;
&:hover { opacity : 1; }
}
}

View File

@@ -49,6 +49,7 @@ const NotificationPopup = ()=>{
));
};
if(!notifications.length) return;
return <Dialog className='notificationPopup' dismisskeys={dissmissKeyList} closeText={DISMISS_BUTTON} >
<div className='header'>
<i className='fas fa-info-circle info'></i>

View File

@@ -1,4 +1,3 @@
version: '2'
services:
mongodb:
image: mongo:latest

31
package-lock.json generated
View File

@@ -36,12 +36,12 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.3",
"marked-extended-tables": "^1.0.10",
"marked-extended-tables": "^1.1.0",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
"mongoose": "^8.9.2",
"mongoose": "^8.9.3",
"nanoid": "5.0.9",
"nconf": "^0.12.1",
"react": "^18.3.1",
@@ -54,7 +54,7 @@
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1",
"babel-plugin-transform-import-meta": "^2.2.1",
"babel-plugin-transform-import-meta": "^2.3.2",
"eslint": "^9.17.0",
"eslint-plugin-jest": "^28.10.0",
"eslint-plugin-react": "^7.37.3",
@@ -3659,14 +3659,13 @@
}
},
"node_modules/babel-plugin-transform-import-meta": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz",
"integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.3.2.tgz",
"integrity": "sha512-902o4GiQqI1GqAXfD5rEoz0PJamUfJ3VllpdWaNsFTwdaNjFSFHawvBO+cp5K2j+g2h3bZ4lnM1Xb6yFYGihtA==",
"dev": true,
"license": "BSD",
"dependencies": {
"@babel/template": "^7.4.4",
"tslib": "^2.4.0"
"@babel/template": "^7.25.9",
"tslib": "^2.8.1"
},
"peerDependencies": {
"@babel/core": "^7.10.0"
@@ -9791,11 +9790,11 @@
}
},
"node_modules/marked-extended-tables": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz",
"integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.1.0.tgz",
"integrity": "sha512-xPQlnmr/mpIJEjwg9/guSKzxoKu7hyCD/sM59mcZc+nMIh2JuVM2se+kCa1Jo1UH+BEHcNlZ221ziJ/cOxAgCA==",
"peerDependencies": {
"marked": ">=3 <15"
"marked": ">=3 <16"
}
},
"node_modules/marked-gfm-heading-id": {
@@ -10170,9 +10169,9 @@
}
},
"node_modules/mongoose": {
"version": "8.9.2",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.2.tgz",
"integrity": "sha512-mLWynmZS1v8HTeMxyLhskQncS1SkrjW1eLNuFDYGQMQ/5QrFrxTLNwWXeCRZeKT2lXyaxW8bnJC9AKPT9jYMkw==",
"version": "8.9.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.3.tgz",
"integrity": "sha512-G50GNPdMqhoiRAJ/24GYAzg13yxXDD3FOOFeYiFwtHmHpAJem3hxbYIxAhLJGWbYEiUZL0qFMu2LXYkgGAmo+Q==",
"dependencies": {
"bson": "^6.10.1",
"kareem": "2.6.3",

View File

@@ -110,12 +110,12 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.3",
"marked-extended-tables": "^1.0.10",
"marked-extended-tables": "^1.1.0",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
"mongoose": "^8.9.2",
"mongoose": "^8.9.3",
"nanoid": "5.0.9",
"nconf": "^0.12.1",
"react": "^18.3.1",
@@ -128,7 +128,7 @@
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1",
"babel-plugin-transform-import-meta": "^2.2.1",
"babel-plugin-transform-import-meta": "^2.3.2",
"eslint": "^9.17.0",
"eslint-plugin-jest": "^28.10.0",
"eslint-plugin-react": "^7.37.3",