├── .babelrc ├── .dockerignore ├── .gitignore ├── .requirements ├── Dockerfile ├── LICENSE ├── README.md ├── __init__.py ├── api ├── __init__.py └── v1 │ ├── __init__.py │ └── basic.py ├── app.py ├── assets ├── fonts │ ├── Butler_Black.otf │ ├── Butler_Bold.otf │ ├── Butler_ExtraBold.otf │ ├── Butler_Light.otf │ ├── Butler_Medium.otf │ ├── Butler_Regular.otf │ └── Butler_Ultra_Light.otf ├── images │ ├── lines-of-code.gif │ ├── loading.gif │ └── write.gif └── scss │ ├── _fonts.scss │ ├── _functions.scss │ ├── _layout.scss │ └── app.scss ├── database.db ├── database.py ├── docker-compose.yml ├── js ├── app.js └── components │ ├── app.jsx │ ├── intro.jsx │ └── quill.jsx ├── models ├── Component.py └── __init__.py ├── package-lock.json ├── package.json ├── readme-files └── flask-react.gif ├── run.sh ├── static └── .gitkeep ├── templates └── app.html └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets":[ 3 | "@babel/preset-env", "@babel/preset-react" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | webkit-build 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # exclude everything 17 | static/* 18 | 19 | # exception to the rule 20 | !static/.gitkeep 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # virtualenv 26 | venv 27 | 28 | # tox 29 | .eggs 30 | .tox 31 | 32 | # Logs 33 | logs 34 | *.log 35 | npm-debug.log* 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Directory for instrumented libs generated by jscoverage/JSCover 44 | lib-cov 45 | 46 | # node-waf configuration 47 | .lock-wscript 48 | 49 | # Compiled binary addons (https://nodejs.org/api/addons.html) 50 | build/Release 51 | 52 | # Dependency directories 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Typescript v1 declaration files 57 | typings/ 58 | 59 | # Optional npm cache directory 60 | .npm 61 | 62 | # Optional eslint cache 63 | .eslintcache 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # dotenv environment variables file 72 | .env 73 | 74 | # assets manifest 75 | manifest.json 76 | *.json~ 77 | -------------------------------------------------------------------------------- /.requirements: -------------------------------------------------------------------------------- 1 | Flask==2.2.2 2 | Flask-Static-Digest==0.4.0 3 | Flask-SQLAlchemy==2.4.4 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | MAINTAINER Benjamin Knox "bknox.contact@gmail.com" 3 | 4 | ENV LC_ALL C.UTF-8 5 | ENV LANG en_US.UTF-8 6 | ENV LANGUAGE en_US:en 7 | ENV TERM screen 8 | 9 | RUN useradd --user-group --create-home --shell /bin/false app-user 10 | # RUN wget -qO - https://raw.githubusercontent.com/yarnpkg/releases/gh-pages/debian/pubkey.gpg | apt-key add - 11 | RUN apt-get update 12 | RUN apt-get install -y python3 python3-venv python3-pip build-essential autoconf libtool pkg-config nasm 13 | RUN rm -rf /var/lib/apt/lists/* 14 | 15 | ENV HOME=/home/app-user 16 | WORKDIR $HOME/app 17 | 18 | COPY package*.json ./ 19 | 20 | RUN chown -R app-user:app-user $HOME/* 21 | USER app-user 22 | 23 | RUN npm install 24 | 25 | ENV VIRTUAL_ENV=/home/app-user/env 26 | RUN python3 -m venv $VIRTUAL_ENV 27 | ENV PATH="$VIRTUAL_ENV/bin:$PATH" 28 | 29 | COPY .requirements . 30 | RUN pip3 install -r .requirements 31 | 32 | ADD . $HOME/app 33 | 34 | WORKDIR $HOME/app 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Benjamin Knox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Flask React 3 |

4 | 5 | Because getting started developing web apps with flask and react can be easier, this boilerplate for developing them exists. This is a docker-compose script that builds a flask API with a react front end. Built so you can get right to coding an app. 6 | 7 | ![Flask React](./readme-files/flask-react.gif) 8 | 9 | ## Usage 10 | 11 | To use Flask React pull the repository and open the repository root, then run this command: 12 | ```shell 13 | docker-compose up 14 | ``` 15 | 16 | You should be able to go to http://localhost:5001 to see the front end after the initial build finishes. It's that simple. 17 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/__init__.py -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/api/__init__.py -------------------------------------------------------------------------------- /api/v1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/api/v1/__init__.py -------------------------------------------------------------------------------- /api/v1/basic.py: -------------------------------------------------------------------------------- 1 | from models import Component 2 | from database import db_session, init_db 3 | from flask import Blueprint, render_template, jsonify, request 4 | 5 | basic_route = Blueprint('basic_route', __name__) 6 | 7 | def getComponent(componentRow): 8 | try: 9 | component = Component.query.filter(Component.name == componentRow.name).first() 10 | 11 | if(component is None): 12 | component = insertComponent(componentRow) 13 | except: 14 | component = insertComponent(componentRow) 15 | 16 | return component 17 | 18 | def insertComponent(componentRow): 19 | init_db() 20 | component = componentRow 21 | 22 | db_session.add(component) 23 | db_session.commit() 24 | 25 | @basic_route.route('/basic') 26 | def basic(): 27 | component = getComponent(Component( 28 | 'basic', 29 | 'Hello World!!!!!!!', 30 | 'A boilerplate for a Flask and React app.', 31 | 'Now with Materiaul UI' 32 | )) 33 | 34 | return jsonify({ 35 | 'header': component.header, 36 | 'body': component.body, 37 | 'tag': component.tag 38 | }) 39 | 40 | @basic_route.route('/editor-content') 41 | def editorContent(): 42 | 43 | component = getComponent(Component( 44 | 'editor', 45 | 'Comes With Quill', 46 | 'Quill is your powerful rich text editor

Quill.com', 47 | None, 48 | '

Text Editor

Post-ironic roof party anim et dreamcatcher. Sint id kale chips drinking vinegar palo santo proident veniam sriracha brunch photo booth man braid tbh flexitarian in. Semiotics umami mollit whatever knausgaard, single-origin coffee direct trade in tumblr normcore ennui quinoa. Esse fugiat in wolf raclette consequat.


Truffaut etsy synth, do tote bag intelligentsia bicycle rights farm-to-table cardigan raw denim id schlitz quinoa dreamcatcher. Esse raclette hell of air plant vape tempor deserunt. Raw denim eu pork belly, master cleanse.


Lorem Generator Credit: hipsum


' 49 | )) 50 | 51 | return jsonify({ 52 | 'header': component.header, 53 | 'body': component.body, 54 | 'editorHTML': component.editorContent 55 | }) 56 | 57 | @basic_route.route('/save-editor', methods=['POST']) 58 | def saveEditor(): 59 | component = Component.query.filter(Component.name == "editor").first() 60 | component.editorContent = request.get_json()["editorContent"] 61 | db_session.add(component) 62 | db_session.commit() 63 | return "" -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, send_from_directory 2 | from flask_static_digest import FlaskStaticDigest 3 | from database import db_session 4 | 5 | from api.v1.basic import basic_route 6 | 7 | flask_static_digest = FlaskStaticDigest() 8 | 9 | app = Flask(__name__, static_url_path='/static') 10 | apiRoute = '/api/v1' 11 | 12 | app.config.update( 13 | DEBUG=True, 14 | ) 15 | 16 | flask_static_digest.init_app(app) 17 | 18 | @app.route('/dist/') 19 | def send_js(path): 20 | return send_from_directory('webkit-build', path) 21 | 22 | @app.route('/') 23 | @app.route('/
') 24 | def home(section="top"): 25 | return render_template('app.html', section=section) 26 | 27 | # Register REST API routes 28 | app.register_blueprint(basic_route, url_prefix=apiRoute) 29 | 30 | @app.teardown_appcontext 31 | def shutdown_session(exception=None): 32 | db_session.remove() 33 | -------------------------------------------------------------------------------- /assets/fonts/Butler_Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Black.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Bold.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_ExtraBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_ExtraBold.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Light.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Medium.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Regular.otf -------------------------------------------------------------------------------- /assets/fonts/Butler_Ultra_Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/fonts/Butler_Ultra_Light.otf -------------------------------------------------------------------------------- /assets/images/lines-of-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/images/lines-of-code.gif -------------------------------------------------------------------------------- /assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/images/loading.gif -------------------------------------------------------------------------------- /assets/images/write.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/assets/images/write.gif -------------------------------------------------------------------------------- /assets/scss/_fonts.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Butler'; 3 | font-weight: normal; 4 | src: asset-url('../fonts/Butler_Regular.otf'); 5 | } 6 | 7 | @font-face { 8 | font-family: 'Butler'; 9 | font-weight: bold; 10 | src: asset-url('../fonts/Butler_Bold.otf'); 11 | } 12 | 13 | @font-face { 14 | font-family: 'Butler'; 15 | font-weight: lighter; 16 | src: asset-url('../fonts/Butler_Light.otf'); 17 | } 18 | @font-face { 19 | font-family: 'Butler'; 20 | font-weight: 100; 21 | src: asset-url('../fonts/Butler_Ultra_Light.otf'); 22 | } 23 | 24 | @font-face { 25 | font-family: 'Butler'; 26 | font-weight: 200; 27 | src: asset-url('../fonts/Butler_Light.otf'); 28 | } 29 | 30 | @font-face { 31 | font-family: 'Butler'; 32 | font-weight: 300; 33 | src: asset-url('../fonts/Butler_Light.otf'); 34 | } 35 | 36 | @font-face { 37 | font-family: 'Butler'; 38 | font-weight: 400; 39 | src: asset-url('../fonts/Butler_Regular.otf'); 40 | } 41 | 42 | @font-face { 43 | font-family: 'Butler'; 44 | font-weight: 500; 45 | src: asset-url('../fonts/Butler_Medium.otf'); 46 | } 47 | 48 | @font-face { 49 | font-family: 'Butler'; 50 | font-weight: 600; 51 | src: asset-url('../fonts/Butler_Medium.otf'); 52 | } 53 | 54 | @font-face { 55 | font-family: 'Butler'; 56 | font-weight: 700; 57 | src: asset-url('../fonts/Butler_Bold.otf'); 58 | } 59 | 60 | @font-face { 61 | font-family: 'Butler'; 62 | font-weight: 800; 63 | src: asset-url('../fonts/Butler_ExtraBold.otf'); 64 | } 65 | 66 | @font-face { 67 | font-family: 'Butler'; 68 | font-weight: 900; 69 | src: asset-url('../fonts/Butler_Black.otf'); 70 | } 71 | -------------------------------------------------------------------------------- /assets/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | @function asset-url($path) { 2 | @return url('~!file-loader!' + $path); 3 | } 4 | -------------------------------------------------------------------------------- /assets/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | background: #EEE; 3 | font-family: "Butler"; 4 | p { 5 | font-size: 20px; 6 | } 7 | } 8 | 9 | h1 { 10 | font-family: "Butler"; 11 | font-weight: 600; 12 | } 13 | 14 | .gif { 15 | text-align: center; 16 | 17 | &.loading { 18 | background: asset-url('../images/loading.gif'); 19 | width: 709px; 20 | height: 606px; 21 | } 22 | 23 | &.lines-of-code { 24 | background: asset-url('../images/lines-of-code.gif'); 25 | width: 400px; 26 | height: 300px; 27 | } 28 | 29 | &.write { 30 | background: asset-url('../images/write.gif'); 31 | width: calc(100% - 10px); 32 | height: 180px; 33 | background-size: 100% 100%; 34 | opacity: 50%; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /assets/scss/app.scss: -------------------------------------------------------------------------------- 1 | @import "functions"; 2 | @import "fonts"; 3 | @import "layout"; 4 | -------------------------------------------------------------------------------- /database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/database.db -------------------------------------------------------------------------------- /database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import scoped_session, sessionmaker 3 | from sqlalchemy.ext.declarative import declarative_base 4 | 5 | engine = create_engine('sqlite:///database.db') 6 | db_session = scoped_session(sessionmaker(autocommit=False, 7 | autoflush=False, 8 | bind=engine)) 9 | Base = declarative_base() 10 | Base.query = db_session.query_property() 11 | 12 | def init_db(): 13 | import models 14 | Base.metadata.create_all(bind=engine) 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | flask-react: 4 | build: . 5 | command: bash -c "/home/app-user/app/run.sh" 6 | volumes: 7 | - .:/home/app-user/app 8 | - /home/app-user/app/node_modules 9 | ports: 10 | - "5001:5000" 11 | environment: 12 | - FLASK_APP=app.py 13 | - FLASK_DEBUG=1 14 | user: root 15 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/app.jsx'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /js/components/app.jsx: -------------------------------------------------------------------------------- 1 | import Quill from './quill.jsx'; 2 | import Intro from './intro.jsx'; 3 | import React, { useState } from 'react'; 4 | import { styled } from '@mui/material/styles'; 5 | 6 | const Center = styled('h2')(({ theme }) =>` 7 | margin-left: auto; 8 | margin-right: auto; 9 | text-align: center; 10 | 11 | & > * { 12 | margin: ${theme.spacing(1)}; 13 | } 14 | `); 15 | 16 | const Name = styled('h1')` 17 | text-align: center; 18 | font-size: 64px; 19 | font-weight: lighter; 20 | `; 21 | 22 | const ArticleItem = styled('div')` 23 | text-align: center; 24 | margin-bottom: 30px; 25 | `; 26 | 27 | export default function App() { 28 | return ( 29 |
30 | Flask React 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | Code By Benjamin Knox 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /js/components/intro.jsx: -------------------------------------------------------------------------------- 1 | import ReactQuill from 'react-quill'; 2 | import React, { useState } from 'react'; 3 | import Grid from '@mui/material/Grid'; 4 | import Paper from '@mui/material/Paper'; 5 | import Avatar from '@mui/material/Avatar'; 6 | import Divider from '@mui/material/Divider'; 7 | import { styled } from '@mui/material/styles'; 8 | import LocalCafeIcon from '@mui/icons-material/LocalCafe'; 9 | import NewReleasesIcon from '@mui/icons-material/NewReleases'; 10 | import DeveloperModeIcon from '@mui/icons-material/DeveloperMode'; 11 | 12 | const RoundedAvatar = styled(Avatar)` 13 | color: #fff; 14 | background-color: #1BB3A6; 15 | `; 16 | 17 | const Center = styled('div')(({ theme }) =>` 18 | margin-left: auto; 19 | margin-right: auto; 20 | justify-content: center; 21 | display: flex; 22 | 23 | & > *: { 24 | margin: ${theme.spacing(1)}; 25 | } 26 | `); 27 | 28 | export default function Intro() { 29 | const [page, setPage] = useState([]); 30 | const [gif, setGif] = useState("loading"); 31 | 32 | if(page.length == 0) { 33 | fetch(`/api/v1/basic`) 34 | .then(result => { 35 | return result.json(); 36 | }) 37 | .then(response => { 38 | setPage(response); 39 | setGif("lines-of-code"); 40 | }); 41 | } 42 | 43 | return ( 44 |
45 | 46 | 47 | 48 |

{ page.header }

49 |

{ page.body }

50 | 51 |

{ page.tag }

52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 |
64 | 65 |
66 |
67 |
68 |
69 |
70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /js/components/quill.jsx: -------------------------------------------------------------------------------- 1 | import ReactQuill from 'react-quill'; 2 | import React, { useState } from 'react'; 3 | import 'react-quill/dist/quill.snow.css'; 4 | import Grid from '@mui/material/Grid'; 5 | import Paper from '@mui/material/Paper'; 6 | import { styled } from '@mui/material/styles'; 7 | 8 | const QuillWrapper = styled(Grid)` 9 | padding: 10px; 10 | box-sizing: border-box; 11 | `; 12 | 13 | const Editor = styled('div')` 14 | height: '300px'; 15 | 16 | & p { 17 | font-size: '12pt'; 18 | margin-top: '6px'; 19 | } 20 | `; 21 | 22 | let editor = React.createRef(); 23 | let changeTimeout; 24 | 25 | let handleChange = () => { 26 | if(changeTimeout) { 27 | clearTimeout(changeTimeout); 28 | } 29 | 30 | changeTimeout = setTimeout(function() { 31 | fetch(`/api/v1/save-editor`, { 32 | method: 'POST', 33 | body: JSON.stringify({editorContent: editor.current.getEditorContents()}), 34 | headers: { 'Content-Type': 'application/json'} 35 | }); 36 | clearTimeout(changeTimeout); 37 | }, 300); 38 | } 39 | 40 | export default function Quill() { 41 | const [content, setContent] = useState([]); 42 | const [page, setPage] = useState([]); 43 | 44 | if(content.length == 0) { 45 | fetch(`/api/v1/editor-content`) 46 | .then(result => { 47 | return result.json(); 48 | }) 49 | .then(response => { 50 | setContent(response.editorHTML); 51 | setPage(response); 52 | }); 53 | } 54 | 55 | const toolbarOptions = { 56 | toolbar: [ 57 | ['bold', 'italic', 'underline', 'strike'], 58 | ['blockquote', 'code-block'], 59 | 60 | [{ 'header': 1 }, { 'header': 2 }], 61 | [{ 'list': 'ordered'}, { 'list': 'bullet' }], 62 | [{ 'script': 'sub'}, { 'script': 'super' }], 63 | [{ 'indent': '-1'}, { 'indent': '+1' }], 64 | [{ 'direction': 'rtl' }], 65 | 66 | [{ 'size': ['small', false, 'large', 'huge'] }], 67 | [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 68 | 69 | [{ 'color': [] }, { 'background': [] }], 70 | [{ 'font': [] }], 71 | [{ 'align': [] }], 72 | 73 | ['clean'] 74 | ] 75 | }; 76 | 77 | return ( 78 |
79 | 80 | 81 | 82 |

{ page.header }

83 |

84 |
85 |
86 | 87 | 92 | 93 | 94 | 95 |
96 |
97 |
98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /models/Component.py: -------------------------------------------------------------------------------- 1 | from database import Base 2 | from sqlalchemy import Column, Integer, String, Text 3 | 4 | class Component(Base): 5 | __tablename__ = 'component' 6 | id = Column(Integer, primary_key=True) 7 | name=Column(String(120)) 8 | header = Column(String(255)) 9 | body = Column(Text) 10 | tag = Column(Text) 11 | editorContent = Column(Text) 12 | 13 | def __init__(self, name=None, header=None, body=None, tag=None, editorContent=None): 14 | self.name = name 15 | self.header = header 16 | self.body = body 17 | self.tag = tag 18 | self.editorContent = editorContent 19 | 20 | def __repr__(self): 21 | return '' % (self.name) 22 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | from models.Component import Component -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flask-react-assets", 3 | "version": "0.1.0", 4 | "main": "webpack.config.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack --mode development --watch" 9 | }, 10 | "devDependencies": { 11 | "@babel/core": "^7.7.7", 12 | "@babel/preset-env": "^7.7.7", 13 | "@babel/preset-react": "^7.7.4", 14 | "babel-loader": "^8.0.6", 15 | "braces": ">=2.3.1", 16 | "css-loader": "^3.4.0", 17 | "deep-extend": ">=0.5.1", 18 | "extend": ">=3.0.2", 19 | "file-loader": "^5.0.2", 20 | "image-webpack-loader": "^6.0.0", 21 | "js-yaml": ">=3.13.1", 22 | "lodash": ">=4.17.21", 23 | "lodash.template": ">=4.5.0", 24 | "mini-css-extract-plugin": "^2.7.6", 25 | "randomatic": ">=3.0.0", 26 | "sass-loader": "^13.3.2", 27 | "tunnel-agent": ">=0.6.0", 28 | "webpack": "^5.0.0", 29 | "webpack-cli": "^5.1.4", 30 | "webpack-yam-plugin": "^1.0.1" 31 | }, 32 | "dependencies": { 33 | "@emotion/react": "^11.11.1", 34 | "@emotion/styled": "^11.11.0", 35 | "@mui/icons-material": "^5.14.9", 36 | "@mui/material": "^5.14.9", 37 | "react": "^18.2.0", 38 | "react-dom": "^18.2.0", 39 | "react-quill": "^2.0.0", 40 | "sass": "^1.68.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /readme-files/flask-react.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/readme-files/flask-react.gif -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf static/* 4 | 5 | npm install && npm run build && python3 -m flask digest compile & \ 6 | while [ ! -f static/cache_manifest.json ] ; \ 7 | do sleep 1 ; echo 'waiting for chache_manifest.json from flask digest' ; done && \ 8 | python3 -m flask run --host=0.0.0.0 9 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benjaminknox/flask-react/82337cd3c0f9e219e391b01e5b4f8492cea32195/static/.gitkeep -------------------------------------------------------------------------------- /templates/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Flask React 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | 4 | let node_modules = path.join(__dirname, "node_modules"); 5 | (rootAssetPath = "./assets"), 6 | (extractSASS = new MiniCssExtractPlugin({ 7 | filename: "[name].css", 8 | })), 9 | (buildPath = path.join(__dirname, "static")), 10 | (manifestPath = path.join(buildPath, "manifest.json")); 11 | 12 | module.exports = { 13 | entry: { 14 | app_js: ["./js/app.js"], 15 | app_css: [rootAssetPath + "/scss/app.scss"], 16 | }, 17 | output: { 18 | path: buildPath, 19 | publicPath: "/static/", 20 | filename: "[name].js", 21 | chunkFilename: "[id].chunk", 22 | }, 23 | resolve: { 24 | extensions: [".js", ".scss", ".css"], 25 | modules: [node_modules], 26 | }, 27 | resolveLoader: { 28 | modules: [node_modules], 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.(js|jsx)$/i, 34 | exclude: /node_modules/, 35 | use: [ 36 | { 37 | loader: "babel-loader", 38 | options: { presets: ["@babel/preset-react"] }, 39 | }, 40 | ], 41 | }, 42 | { 43 | test: /(\.scss|\.css)$/i, 44 | use: [ 45 | MiniCssExtractPlugin.loader, 46 | { 47 | loader: "css-loader", 48 | }, 49 | { 50 | loader: "sass-loader", 51 | }, 52 | ], 53 | }, 54 | { 55 | test: /\.(jpe?g|png|gif|eot|ttf|woff|woff2|otf|svg([\?]?.*))$/i, 56 | use: [ 57 | { 58 | loader: 59 | "file-loader?context=" + 60 | rootAssetPath + 61 | "&name=[path][name].[ext]", 62 | }, 63 | { 64 | loader: 65 | "image-webpack-loader?bypassOnDebug&optimizationLevel=7&interlaced=false", 66 | }, 67 | ], 68 | }, 69 | ], 70 | }, 71 | watchOptions: { 72 | aggregateTimeout: 300, 73 | poll: 500, 74 | ignored: '**/node_modules', 75 | }, 76 | plugins: [ 77 | extractSASS, 78 | ], 79 | }; 80 | --------------------------------------------------------------------------------