Compare commits
No commits in common. "a8b830d3d1055566e7b713e901458dd11213d0d5" and "c26758f4681c8a280d85509530b7b55a8c1e2687" have entirely different histories.
a8b830d3d1
...
c26758f468
15
README.md
15
README.md
|
@ -1,15 +0,0 @@
|
||||||
# UnderCover
|
|
||||||
|
|
||||||
https://undercover.sagev.space/ is a site designed to act as a simple interface for
|
|
||||||
generating templated cover letters.
|
|
||||||
|
|
||||||
The site is built on Flask, with a very simple VanillaJS frontend, and
|
|
||||||
leverages LaTeX for document generation. User data, including logins and stored
|
|
||||||
templates, is stored in postgres, with bcrypt-hashed credentials.
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
While not implemented as part of this repo, the site itself uses a pseudo
|
|
||||||
blue/green deployment system, with Testing and Production both running on one
|
|
||||||
server. A simple script toggles the proxy between pointing visitors to the blue
|
|
||||||
or the green instance.
|
|
|
@ -167,7 +167,7 @@ def add_letter() -> Response:
|
||||||
|
|
||||||
existing_letter_count = len(db.get_user_letters(user.id))
|
existing_letter_count = len(db.get_user_letters(user.id))
|
||||||
if user.in_free_tier() and existing_letter_count >= FREE_TIER_TEMPLATES:
|
if user.in_free_tier() and existing_letter_count >= FREE_TIER_TEMPLATES:
|
||||||
return redirect('/?error=template_limit')
|
return render_index(error=f'A maximum of {FREE_TIER_TEMPLATES} templates are available to each user.')
|
||||||
new_letter_name = f'Letter{existing_letter_count + 1}'
|
new_letter_name = f'Letter{existing_letter_count + 1}'
|
||||||
default_form_json = jsonify(CLForm().to_cl_data()).get_data(True)
|
default_form_json = jsonify(CLForm().to_cl_data()).get_data(True)
|
||||||
db.add_letter(user.id, new_letter_name, default_form_json)
|
db.add_letter(user.id, new_letter_name, default_form_json)
|
||||||
|
@ -194,9 +194,6 @@ def index_get() -> Response:
|
||||||
|
|
||||||
form.letterName.data = 1
|
form.letterName.data = 1
|
||||||
selected_letter = request.args.get('letter_name')
|
selected_letter = request.args.get('letter_name')
|
||||||
error = None
|
|
||||||
if request.args.get('error') == 'template_limit':
|
|
||||||
error = f'A maximum of {FREE_TIER_TEMPLATES} templates are available to each user.'
|
|
||||||
|
|
||||||
if selected_letter:
|
if selected_letter:
|
||||||
for i, letter in enumerate(letters):
|
for i, letter in enumerate(letters):
|
||||||
|
@ -219,7 +216,7 @@ def index_get() -> Response:
|
||||||
form.skillTypes.data = data['skillTypes']
|
form.skillTypes.data = data['skillTypes']
|
||||||
form.username.data = data['username']
|
form.username.data = data['username']
|
||||||
|
|
||||||
return render_index(form=form, error=error)
|
return render_index(form=form)
|
||||||
|
|
||||||
|
|
||||||
@writing_blueprint.route('/reset', methods=['POST', 'GET'])
|
@writing_blueprint.route('/reset', methods=['POST', 'GET'])
|
||||||
|
@ -287,7 +284,7 @@ def status_page() -> Response:
|
||||||
status_message = f"Currently running on '{os.environ.get('UNDERCOVER_SERVER_NAME')}' server"
|
status_message = f"Currently running on '{os.environ.get('UNDERCOVER_SERVER_NAME')}' server"
|
||||||
git_commands = 'git log -1 --pretty=%B; git rev-parse --short HEAD'
|
git_commands = 'git log -1 --pretty=%B; git rev-parse --short HEAD'
|
||||||
current_git_hash = subprocess.check_output([git_commands], shell=True, universal_newlines=True)
|
current_git_hash = subprocess.check_output([git_commands], shell=True, universal_newlines=True)
|
||||||
return make_response(render_template('error.jinja2', status=200, error_text=status_message, extra_text='Latest commit: ' + current_git_hash), 200)
|
return make_response(render_template('error.jinja2', status=200, error_text=status_message, extra_text=current_git_hash), 200)
|
||||||
|
|
||||||
|
|
||||||
@writing_blueprint.route('/', methods=['POST'])
|
@writing_blueprint.route('/', methods=['POST'])
|
||||||
|
|
|
@ -2,48 +2,6 @@ html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
line-height: 180%;
|
|
||||||
font-size: 110%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: 'Barlow', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transition, .transition * {
|
|
||||||
transition-duration: 0.5s;
|
|
||||||
transition-property: color, border-color, background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-toggler {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark-mode {
|
|
||||||
color: #e7e7e7;
|
|
||||||
background: linear-gradient(to bottom right, #2e2e62, #1a1226);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2 {
|
|
||||||
font-family: 'BarlowMedium', sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
body.dark-mode h1 {
|
|
||||||
color: #e7e7e7;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark-mode h2 {
|
|
||||||
color: #e7e7e7;
|
|
||||||
}
|
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
@ -79,10 +37,26 @@ h1:hover span.logo.right {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
line-height: 180%;
|
||||||
|
font-size: 110%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: 'Barlow', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
font-family: 'BarlowMedium', sans-serif;
|
font-family: 'BarlowMedium', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
font-family: 'BarlowMedium', sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
@ -110,10 +84,6 @@ label {
|
||||||
transition-timing-function: ease-out;
|
transition-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode label {
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
label:hover {
|
label:hover {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: #222;
|
background-color: #222;
|
||||||
|
@ -123,38 +93,12 @@ input, textarea {
|
||||||
font-family: 'Barlow', sans-serif;
|
font-family: 'Barlow', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode input[type="text"] {
|
|
||||||
color: #eee;
|
|
||||||
background-color: #000;
|
|
||||||
border: none;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark-mode textarea {
|
|
||||||
color: #eee;
|
|
||||||
background-color: #000;
|
|
||||||
border: none;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
background-color: white;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
padding: 10px;
|
border-color: #ccc;
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
select, body.dark-mode select:hover {
|
|
||||||
background-color: white;
|
|
||||||
border-color: #8f8f9d;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark-mode select, select:hover {
|
|
||||||
background-color: black;
|
|
||||||
border-color: black;
|
|
||||||
color: #eee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.letter-form {
|
.letter-form {
|
||||||
|
@ -186,49 +130,44 @@ div.user form input, label {
|
||||||
|
|
||||||
div.user form input {
|
div.user form input {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #ddd;
|
||||||
|
padding: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.white-black-button {
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
}
|
color: black;
|
||||||
|
background-color: white;
|
||||||
input.btn {
|
|
||||||
padding: 0.8em 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-fade {
|
|
||||||
transition-property: color, background-color;
|
transition-property: color, background-color;
|
||||||
transition-duration: 135ms;
|
transition-duration: 135ms;
|
||||||
transition-timing-function: ease-out;
|
transition-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary {
|
.white-black-button:hover {
|
||||||
color: white;
|
color: white;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary:hover {
|
.black-white-button {
|
||||||
color: black;
|
font-weight: bold;
|
||||||
background-color: white;
|
border-radius: 0;
|
||||||
}
|
border-style: none;
|
||||||
|
|
||||||
.secondary, body.dark-mode .secondary:hover {
|
|
||||||
color: black;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #8f8f9d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary a {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary:hover, body.dark-mode .secondary {
|
|
||||||
color: white;
|
color: white;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
border: 1px solid black;
|
transition-property: color, background-color;
|
||||||
|
transition-duration: 135ms;
|
||||||
|
transition-timing-function: ease-out;
|
||||||
|
width: min-content;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.black-white-button:hover {
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logged-in {
|
.logged-in {
|
||||||
|
@ -332,10 +271,6 @@ textarea {
|
||||||
transition-timing-function: ease-out;
|
transition-timing-function: ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode div.modal-content {
|
|
||||||
background-color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-background {
|
.modal-background {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
@ -353,13 +288,8 @@ body.dark-mode div.modal-content {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-right {
|
.modal-close-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-button {
|
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
|
@ -367,16 +297,14 @@ body.dark-mode div.modal-content {
|
||||||
color: #444;
|
color: #444;
|
||||||
margin: 0.25em;
|
margin: 0.25em;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-button:hover {
|
.modal-close-button:hover {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode .text-button:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Small screens */
|
/* Small screens */
|
||||||
@media only screen and (max-width: 720px) {
|
@media only screen and (max-width: 720px) {
|
||||||
body {
|
body {
|
||||||
|
@ -413,6 +341,9 @@ body.dark-mode .text-button:hover {
|
||||||
.bigtext {
|
.bigtext {
|
||||||
min-height: 40vh;
|
min-height: 40vh;
|
||||||
}
|
}
|
||||||
|
.login-button {
|
||||||
|
padding: 0.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Big screens */
|
/* Big screens */
|
||||||
|
@ -427,7 +358,6 @@ body.dark-mode .text-button:hover {
|
||||||
input, textarea {
|
input, textarea {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
font-size: 105%;
|
font-size: 105%;
|
||||||
padding: 4px;
|
|
||||||
}
|
}
|
||||||
dd {
|
dd {
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
|
@ -442,8 +372,13 @@ body.dark-mode .text-button:hover {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
div.user form input {
|
div.user form input {
|
||||||
|
font-size: 110%;
|
||||||
|
padding: 0.5em;
|
||||||
height: min-content;
|
height: min-content;
|
||||||
}
|
}
|
||||||
|
.login-button {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.user {
|
.user {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.5em;
|
top: 0.5em;
|
||||||
|
@ -480,4 +415,4 @@ body.dark-mode .text-button:hover {
|
||||||
|
|
||||||
.scroll-lock {
|
.scroll-lock {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
|
@ -38,7 +38,7 @@
|
||||||
{% macro modal() %}
|
{% macro modal() %}
|
||||||
<div id='modal' class='modal modal-background transparent' onclick="event.target.id === 'modal' && closeModal()">
|
<div id='modal' class='modal modal-background transparent' onclick="event.target.id === 'modal' && closeModal()">
|
||||||
<div class='modal modal-content'>
|
<div class='modal modal-content'>
|
||||||
<button class='top-right text-button' onclick="closeModal()">×</button>
|
<button class='modal-close-button' onclick="closeModal()">×</button>
|
||||||
<h2 id='modal-title'>Login Now</h2>
|
<h2 id='modal-title'>Login Now</h2>
|
||||||
<form action="/login" method="post" id="create-account-form">
|
<form action="/login" method="post" id="create-account-form">
|
||||||
<div>
|
<div>
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
<input id="password" maxlength="32" minlength="4" name="password" type="password">
|
<input id="password" maxlength="32" minlength="4" name="password" type="password">
|
||||||
</div>
|
</div>
|
||||||
<div id="confirm-password-div">
|
<div id="confirm-password-div">
|
||||||
<div style="margin-bottom: 1.5em;">Password must be between 8 and 64 characters</div>
|
<div style="font-size: 80%; margin-bottom: 1.5em;">Password must be between 8 and 64 characters</div>
|
||||||
<label id='confirm-password-label' for="confirm-password">Confirm Password: </label>
|
<label id='confirm-password-label' for="confirm-password">Confirm Password: </label>
|
||||||
<input id="confirm-password" maxlength="32" minlength="4" name="confirm-password" type="password">
|
<input id="confirm-password" maxlength="32" minlength="4" name="confirm-password" type="password">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,75 +3,48 @@
|
||||||
{% from "_formhelpers.jinja2" import modal %}
|
{% from "_formhelpers.jinja2" import modal %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>{% block title %}UnderCover{% endblock title %}</title>
|
<title>{% block title %}UnderCover{% endblock title %}</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
<link rel="icon" href="{{ url_for('static', filename='favicon.png') }}" />
|
<link rel="icon" href="{{ url_for('static', filename='favicon.png') }}" />
|
||||||
<meta name="description" content="UnderCover is an easy way to generate unique cover letters, making it simple to send personalized applications to many companies quickly.">
|
<meta name="description" content="UnderCover is an easy way to generate unique cover letters, making it simple to send personalized applications to many companies quickly.">
|
||||||
<meta name="viewport" content="user-scalable=no; width=device-width">
|
<meta name="viewport" content="user-scalable=no; width=device-width">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
function closeModal() {
|
||||||
const darkSystem = () => window.matchMedia('(prefers-color-scheme: dark)').matches
|
document.getElementById('modal').classList.add('transparent')
|
||||||
const lightStored = () => localStorage.getItem('dark-mode') === 'false'
|
document.body.classList.remove('scroll-lock')
|
||||||
const darkStored = () => localStorage.getItem('dark-mode') === 'true'
|
|
||||||
if (!lightStored() && (darkSystem() || darkStored())) {
|
|
||||||
document.body.classList.add('dark-mode')
|
|
||||||
}
|
}
|
||||||
const togglers = document.getElementsByClassName('theme-toggler');
|
function showModal(login, text) {
|
||||||
for (let i = 0; i < togglers.length; i++) {
|
document.getElementById('modal').classList.remove('transparent')
|
||||||
togglers[i].addEventListener('click', () => {
|
document.getElementById('modal-title').innerText = text
|
||||||
document.body.classList.toggle('dark-mode')
|
document.body.classList.add('scroll-lock')
|
||||||
const darkMode = document.body.classList.contains('dark-mode')
|
const createAccountElements = [
|
||||||
localStorage.setItem("dark-mode", darkMode)
|
'confirm-password',
|
||||||
})
|
'confirm-password-label',
|
||||||
|
'create-account-form-button'
|
||||||
|
]
|
||||||
|
const loginElements = [
|
||||||
|
'log-in-form-button'
|
||||||
|
]
|
||||||
|
const visibleElements = login ? loginElements : createAccountElements
|
||||||
|
visibleElements.forEach(element => document.getElementById(element).classList.remove('hidden'))
|
||||||
|
|
||||||
|
const hiddenElements = login ? createAccountElements : loginElements
|
||||||
|
hiddenElements.forEach(element => document.getElementById(element).classList.add('hidden'))
|
||||||
}
|
}
|
||||||
setTimeout(() => document.body.classList.add('transition'), 600)
|
</script>
|
||||||
})
|
<style>
|
||||||
function toggleTheme() {
|
|
||||||
document.body.classList.toggle('dark-mode')
|
|
||||||
}
|
|
||||||
function closeModal() {
|
|
||||||
document.getElementById('modal').classList.add('transparent')
|
|
||||||
document.body.classList.remove('scroll-lock')
|
|
||||||
}
|
|
||||||
function showModal(login, text) {
|
|
||||||
document.getElementById('modal').classList.remove('transparent')
|
|
||||||
document.getElementById('modal-title').innerText = text
|
|
||||||
document.body.classList.add('scroll-lock')
|
|
||||||
|
|
||||||
const createAccountElements = [
|
|
||||||
'confirm-password',
|
|
||||||
'confirm-password-label',
|
|
||||||
'create-account-form-button'
|
|
||||||
]
|
|
||||||
const loginElements = [
|
|
||||||
'log-in-form-button'
|
|
||||||
]
|
|
||||||
const [visibleElements, hiddenElements] = login
|
|
||||||
? [loginElements, createAccountElements]
|
|
||||||
: [createAccountElements, loginElements]
|
|
||||||
|
|
||||||
visibleElements.forEach(element => document.getElementById(element).classList.remove('hidden'))
|
|
||||||
hiddenElements.forEach(element => document.getElementById(element).classList.add('hidden'))
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Barlow';
|
font-family: 'Barlow';
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url('/static/fonts/Barlow-Regular.ttf');
|
src: url('/static/fonts/Barlow-Regular.ttf');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'BarlowMedium';
|
font-family: 'BarlowMedium';
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url('/static/fonts/Barlow-Medium.ttf');
|
src: url('/static/fonts/Barlow-Medium.ttf');
|
||||||
}
|
}
|
||||||
@font-face {
|
</style>
|
||||||
font-family: 'BarlowBold';
|
{% block head %}{{ head }}{% endblock head %}
|
||||||
font-display: swap;
|
|
||||||
src: url('/static/fonts/Barlow-Bold.ttf');
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% block head %}{{ head }}{% endblock head %}
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -80,22 +53,20 @@
|
||||||
<div class="user logged-in">
|
<div class="user logged-in">
|
||||||
<p style="margin: 0 1em 0 0;">{{ username }}</p>
|
<p style="margin: 0 1em 0 0;">{{ username }}</p>
|
||||||
<form action="/logout">
|
<form action="/logout">
|
||||||
<input class="btn primary color-fade" style="font-family: 'BarlowMedium'; margin-right: 1em; margin: 0;" type="submit" value="Log Out"></input>
|
<input class="black-white-button" style="margin: 0; padding: 0.4em;" type="submit" value="Logout">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="user logged-out">
|
<div class="user logged-out">
|
||||||
<form action="/create_account">
|
<form action="/create_account">
|
||||||
<div class="btn primary color-fade" style="font-family: 'BarlowMedium'; margin-right: 1em; padding: 0.4em 1em;" onclick="showModal(false, 'Create your account')">
|
<div class="black-white-button" style="font-family: 'BarlowMedium'; margin-right: 1em; padding: 0.4em 1em;" onclick="showModal(false, 'Create your account')">Create account</div>
|
||||||
Create account
|
<div class="white-black-button" style="margin: 0; padding: 0.4em 1em;" onclick="showModal(true, 'Login now')">Log in</div>
|
||||||
</div>
|
|
||||||
<div class="btn secondary color-fade" style="margin: 0; padding: 0.4em 1em;" onclick="showModal(true, 'Login now')">Log in</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!--<a href="/" style="text-decoration: none;">-->
|
<a href="/" style="text-decoration: none;">
|
||||||
<h1 title="Click to toggle dark mode" class="theme-toggler"><span class="logo left">Under</span><span class="logo right">Cover</span></h1>
|
<h1><span class="logo left">Under</span><span class="logo right">Cover</span></h1>
|
||||||
<!--</a>-->
|
</a>
|
||||||
<h2>The secret cover letter generator</h2>
|
<h2>The secret cover letter generator</h2>
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<div class="errors">
|
<div class="errors">
|
||||||
|
|
|
@ -49,12 +49,11 @@
|
||||||
onchange="window.location = '/?letter_name=' + this.options[this.value - 1].label"
|
onchange="window.location = '/?letter_name=' + this.options[this.value - 1].label"
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
<div class="btn secondary color-fade" style="display: flex; justify-content: center; align-items: center; padding-left: 0.75em; padding-right: 0.75em; margin-left: 0.5em;">
|
<a
|
||||||
<a
|
href="/add_letter"
|
||||||
href="/add_letter"
|
class="white-black-button"
|
||||||
style="text-decoration: none; margin: 0; padding: 0;"
|
style="padding-left: 0.75em; padding-right: 0.75em; text-decoration: none; margin-left: 0.5em; text-align: center;"
|
||||||
> + </a>
|
> + </a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
10
start
10
start
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
|
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
|
||||||
|
|
||||||
source $SCRIPT_DIR/env
|
|
||||||
|
|
||||||
if ! test -f "$SCRIPT_DIR/.undercover_init_successful"; then
|
if ! test -f "$SCRIPT_DIR/.undercover_init_successful"; then
|
||||||
if ! "$SCRIPT_DIR/init"; then
|
if ! "$SCRIPT_DIR/init"; then
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -15,15 +13,9 @@ if ! pip3 -V | grep -E "$SCRIPT_DIR/\.?venv"; then
|
||||||
source "$SCRIPT_DIR/.venv/bin/activate"
|
source "$SCRIPT_DIR/.venv/bin/activate"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PROD_PORT=8080
|
|
||||||
|
|
||||||
if [[ "$UNDERCOVER_PROD_PORT" != "" ]]; then
|
|
||||||
PROD_PORT="$UNDERCOVER_PROD_PORT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$1" == "prod" ]]; then
|
if [[ "$1" == "prod" ]]; then
|
||||||
echo "Starting gunicorn production server..."
|
echo "Starting gunicorn production server..."
|
||||||
gunicorn -b "localhost:$PROD_PORT" "app:create_app()"
|
gunicorn -b localhost:8080 "app:create_app()"
|
||||||
else
|
else
|
||||||
echo "Starting local dev server..."
|
echo "Starting local dev server..."
|
||||||
export FLASK_APP=app
|
export FLASK_APP=app
|
||||||
|
|
Loading…
Reference in New Issue