Compare commits
26 Commits
v1.0.1
...
17145628a0
Author | SHA1 | Date | |
---|---|---|---|
17145628a0 | |||
195c353710 | |||
8c23e9d811 | |||
5a56496538 | |||
9c06401557 | |||
9b1b84e5be | |||
23cc4c3876 | |||
9e62a84843 | |||
dda3be0101 | |||
3fd24c75fc | |||
07bb33006e | |||
aab53f1e54 | |||
0e6ca5859a | |||
7986ad2f88 | |||
7c4c20b3ce | |||
b407497713 | |||
90d20978b1 | |||
1a26b0b3fb | |||
71efbfcc83 | |||
27ef2d4ca3 | |||
1aa1964853 | |||
aae43a0001 | |||
61392e296c | |||
997afcdd9e | |||
5a611dd893 | |||
2adde253c9 |
@ -7,15 +7,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Datadog Static Analyzer
|
name: Datadog Static Analyzer
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Check code for comitted secrets
|
- name: Check code for comitted secrets
|
||||||
id: datadog-static-analysis
|
id: datadog-static-analysis
|
||||||
uses: DataDog/datadog-static-analyzer-github-action@v1
|
uses: DataDog/datadog-static-analyzer-github-action@v1
|
||||||
with:
|
with:
|
||||||
dd_api_key: ${{ secrets.DD_API_KEY }}
|
dd_api_key: ${{ secrets.DD_API_KEY }}
|
||||||
dd_app_key: ${{ secrets.DD_APP_KEY }}
|
dd_app_key: ${{ secrets.DD_APP_KEY }}
|
||||||
dd_site: datadoghq.com
|
dd_site: datadoghq.com
|
||||||
secrets_enabled: true
|
secrets_enabled: true
|
||||||
static_analysis_enabled: false
|
static_analysis_enabled: false
|
||||||
cpu_count: 2
|
cpu_count: 8
|
||||||
|
@ -17,3 +17,25 @@ jobs:
|
|||||||
dd_app_key: ${{ secrets.DD_APP_KEY }}
|
dd_app_key: ${{ secrets.DD_APP_KEY }}
|
||||||
dd_site: datadoghq.com
|
dd_site: datadoghq.com
|
||||||
cpu_count: 2
|
cpu_count: 2
|
||||||
|
- name: Run Semgrep
|
||||||
|
run: |
|
||||||
|
python3 -m pip install --break-system-package semgrep
|
||||||
|
semgrep scan --sarif -o /tmp/semgrep.sarif
|
||||||
|
cat /tmp/semgrep.sarif
|
||||||
|
# Download and install nvm:
|
||||||
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
|
||||||
|
# in lieu of restarting the shell
|
||||||
|
\. "$HOME/.nvm/nvm.sh"
|
||||||
|
# Download and install Node.js:
|
||||||
|
nvm install 22
|
||||||
|
# Verify the Node.js version:
|
||||||
|
node -v # Should print "v22.14.0".
|
||||||
|
nvm current # Should print "v22.14.0".
|
||||||
|
# Verify npm version:
|
||||||
|
npm -v # Should print "10.9.2".
|
||||||
|
npm install -g @datadog/datadog-ci
|
||||||
|
datadog-ci sarif upload /tmp/semgrep.sarif
|
||||||
|
env:
|
||||||
|
DD_API_KEY: ${{ secrets.DD_API_KEY }}
|
||||||
|
DD_APP_KEY: ${{ secrets.DD_APP_KEY }}
|
||||||
|
DD_SITE: datadoghq.com
|
@ -167,6 +167,7 @@ COPY . .
|
|||||||
CMD ["python", "main.py"]
|
CMD ["python", "main.py"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Docker Compose Example
|
## Docker Compose Example
|
||||||
|
|
||||||
Below is an example `docker-compose.yml` file to deploy Foldsite using Docker Compose:
|
Below is an example `docker-compose.yml` file to deploy Foldsite using Docker Compose:
|
||||||
|
16
config.toml
Normal file
16
config.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[paths]
|
||||||
|
content_dir = "/home/dubey/projects/foldsitedocs/content"
|
||||||
|
templates_dir = "/home/dubey/projects/foldsitedocs/templates"
|
||||||
|
styles_dir = "/home/dubey/projects/foldsitedocs/styles"
|
||||||
|
|
||||||
|
[server]
|
||||||
|
listen_address = "0.0.0.0"
|
||||||
|
listen_port = 8080
|
||||||
|
enable_admin_browser = false
|
||||||
|
admin_password = "password"
|
||||||
|
max_threads = 4
|
||||||
|
debug = false
|
||||||
|
access_log = true
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
# Added Tools for the Template
|
|
||||||
|
|
||||||
Foldsite provides additional tools for templates, such as functions to get sibling content files, text document previews, and folder contents.
|
|
@ -1,19 +0,0 @@
|
|||||||
# Configuration
|
|
||||||
|
|
||||||
The configuration file is written in TOML format and contains various settings for the application. Below is an example configuration file (`config.toml`):
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[paths]
|
|
||||||
content_dir = "example/content"
|
|
||||||
templates_dir = "templates"
|
|
||||||
styles_dir = "styles"
|
|
||||||
|
|
||||||
[server]
|
|
||||||
listen_address = "127.0.0.1"
|
|
||||||
listen_port = 8080
|
|
||||||
debug = false
|
|
||||||
access_log = true
|
|
||||||
max_threads = 4
|
|
||||||
admin_browser = false
|
|
||||||
admin_password = "your_admin_password"
|
|
||||||
```
|
|
@ -1,12 +0,0 @@
|
|||||||
# Deployment
|
|
||||||
|
|
||||||
To deploy Foldsite, you can use Docker. Below is an example Dockerfile:
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
FROM python:3.13.2-bookworm
|
|
||||||
WORKDIR /app
|
|
||||||
COPY requirements.txt requirements.txt
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
COPY . .
|
|
||||||
CMD ["python", "main.py"]
|
|
||||||
```
|
|
@ -1,16 +0,0 @@
|
|||||||
# Docker Compose Example
|
|
||||||
|
|
||||||
Below is an example `docker-compose.yml` file to deploy Foldsite using Docker Compose:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3.8'
|
|
||||||
services:
|
|
||||||
foldsite:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
volumes:
|
|
||||||
- .:/app
|
|
||||||
environment:
|
|
||||||
- CONFIG_PATH=config.toml
|
|
||||||
```
|
|
@ -1,39 +0,0 @@
|
|||||||
# Example Usages for Tools and Types
|
|
||||||
|
|
||||||
### Example Usage of `get_sibling_content_files`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ul>
|
|
||||||
{% for file in get_sibling_content_files('path/to/directory') %}
|
|
||||||
<li>{{ file[0] }} - {{ file[1] }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example Usage of `get_text_document_preview`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div>
|
|
||||||
{{ get_text_document_preview('path/to/document.md') }}
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example Usage of `get_sibling_content_folders`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ul>
|
|
||||||
{% for folder in get_sibling_content_folders('path/to/directory') %}
|
|
||||||
<li>{{ folder[0] }} - {{ folder[1] }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example Usage of `get_folder_contents`
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ul>
|
|
||||||
{% for item in get_folder_contents('path/to/directory') %}
|
|
||||||
<li>{{ item.name }} - {{ item.path }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
```
|
|
@ -1,23 +0,0 @@
|
|||||||
# How a Template is Written
|
|
||||||
|
|
||||||
Templates are written in HTML and use Jinja2 syntax for dynamic content. Below is an example template (`base.html`):
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='base.css') }}">
|
|
||||||
{% for style in styles %}
|
|
||||||
<link rel="stylesheet" href="{{ style }}">
|
|
||||||
{% endfor %}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="content">
|
|
||||||
{{ content }}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
@ -1,20 +0,0 @@
|
|||||||
# Foldsite Documentation
|
|
||||||
|
|
||||||
Welcome to the Foldsite documentation. This site will guide you through the setup, configuration, and usage of Foldsite.
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
1. [Introduction](introduction.md)
|
|
||||||
2. [Configuration](configuration.md)
|
|
||||||
3. [Template Setup](template-setup.md)
|
|
||||||
4. [Site Setup](site-setup.md)
|
|
||||||
5. [Style Setup](style-setup.md)
|
|
||||||
6. [Template and Style Search](template-style-search.md)
|
|
||||||
7. [How a Template is Written](how-template-written.md)
|
|
||||||
8. [Jinja Primer](jinja-primer.md)
|
|
||||||
9. [Added Tools for the Template](added-tools.md)
|
|
||||||
10. [Tool Input and Return Types](tool-input-return-types.md)
|
|
||||||
11. [Example Usages for Tools and Types](example-usages.md)
|
|
||||||
12. [Deployment](deployment.md)
|
|
||||||
13. [Docker Compose Example](docker-compose-example.md)
|
|
||||||
14. [Folder Layout](folder-layout.md)
|
|
@ -1,5 +0,0 @@
|
|||||||
# Introduction
|
|
||||||
|
|
||||||
Foldsite is a dynamic site generator built with Python and Flask. It allows you to create and manage a website using Markdown content, HTML templates, and CSS styles.
|
|
||||||
|
|
||||||
This documentation will guide you through the setup, configuration, and usage of Foldsite.
|
|
@ -1,8 +0,0 @@
|
|||||||
# Jinja Primer
|
|
||||||
|
|
||||||
Jinja2 is a templating engine for Python. It allows you to include dynamic content in your HTML templates. Below are some basic Jinja2 syntax examples:
|
|
||||||
|
|
||||||
- Variables: `{{ variable }}`
|
|
||||||
- Loops: `{% for item in list %} ... {% endfor %}`
|
|
||||||
- Conditionals: `{% if condition %} ... {% endif %}`
|
|
||||||
- Includes: `{% include 'template.html' %}`
|
|
@ -1,3 +0,0 @@
|
|||||||
# Site Setup
|
|
||||||
|
|
||||||
The site content is stored in the `content` directory. Each Markdown file represents a page on your site. The directory structure of the `content` directory determines the URL structure of your site.
|
|
@ -1,3 +0,0 @@
|
|||||||
# Style Setup
|
|
||||||
|
|
||||||
Styles are CSS files that define the appearance of your web pages. They are stored in the `styles` directory. You can create specific styles for different types of content and categories.
|
|
@ -1,3 +0,0 @@
|
|||||||
# Template Setup
|
|
||||||
|
|
||||||
Templates are HTML files that define the structure of your web pages. They are stored in the `templates` directory. Each template can include other templates and use Jinja2 syntax for dynamic content.
|
|
@ -1,3 +0,0 @@
|
|||||||
# Template and Style Search
|
|
||||||
|
|
||||||
Templates and styles are searched in a specific order to apply the most specific styles first, followed by more general styles, and finally the base style.
|
|
@ -1,13 +0,0 @@
|
|||||||
# Tool Input and Return Types
|
|
||||||
|
|
||||||
### `get_sibling_content_files(path: str) -> list`
|
|
||||||
Returns a list of sibling content files in the specified directory.
|
|
||||||
|
|
||||||
### `get_text_document_preview(path: str) -> str`
|
|
||||||
Generates a preview of the text document located at the given path.
|
|
||||||
|
|
||||||
### `get_sibling_content_folders(path: str) -> list`
|
|
||||||
Returns a list of sibling content folders within a specified directory.
|
|
||||||
|
|
||||||
### `get_folder_contents(path: str) -> list`
|
|
||||||
Retrieves the contents of a folder and returns a list of `TemplateFile` objects.
|
|
@ -1,24 +0,0 @@
|
|||||||
article {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
article h1, article h2, article h3, article h4, article h5, article h6 {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
article p {
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
article pre {
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 1rem;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
article code {
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
line-height: 1.6;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: #333;
|
|
||||||
color: #fff;
|
|
||||||
padding: 1rem 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li {
|
|
||||||
display: inline;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li a {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul li a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background: #333;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem 0;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
4
docs/templates/__file.md.html
vendored
4
docs/templates/__file.md.html
vendored
@ -1,4 +0,0 @@
|
|||||||
<article>
|
|
||||||
{{ content|safe }}
|
|
||||||
</article>
|
|
||||||
|
|
42
docs/templates/base.html
vendored
42
docs/templates/base.html
vendored
@ -1,42 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='base.css') }}">
|
|
||||||
{% for style in styles %}
|
|
||||||
<link rel="stylesheet" href="{{ style }}">
|
|
||||||
{% endfor %}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>Foldsite Documentation</h1>
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a href="{{ url_for('default_route', path='index.md') }}">Home</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='introduction.md') }}">Introduction</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='configuration.md') }}">Configuration</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='template-setup.md') }}">Template Setup</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='site-setup.md') }}">Site Setup</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='style-setup.md') }}">Style Setup</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='template-style-search.md') }}">Template and Style Search</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='how-template-written.md') }}">How a Template is Written</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='jinja-primer.md') }}">Jinja Primer</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='added-tools.md') }}">Added Tools</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='tool-input-return-types.md') }}">Tool Input and Return Types</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='example-usages.md') }}">Example Usages</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='deployment.md') }}">Deployment</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='docker-compose-example.md') }}">Docker Compose Example</a></li>
|
|
||||||
<li><a href="{{ url_for('default_route', path='folder-layout.md') }}">Folder Layout</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
{{ content|safe }}
|
|
||||||
</main>
|
|
||||||
<footer>
|
|
||||||
<p>© 2023 Foldsite</p>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
5
main.py
5
main.py
@ -6,11 +6,6 @@ from src.rendering.helpers import TemplateHelpers
|
|||||||
from src.server.file_manager import create_filemanager_blueprint
|
from src.server.file_manager import create_filemanager_blueprint
|
||||||
|
|
||||||
|
|
||||||
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
|
||||||
PASSWORD = "YiaysZ4g8QX1R8R"
|
|
||||||
AWS_ACCESS_KEY_ID = "AIDAJQABLZS4A3QDU576"
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = create_parser()
|
parser = create_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
@ -34,9 +34,9 @@ def generate_thumbnail(image_path, resize_percent, min_width, max_width):
|
|||||||
if orientation == 3:
|
if orientation == 3:
|
||||||
img = img.rotate(180, expand=True)
|
img = img.rotate(180, expand=True)
|
||||||
elif orientation == 6:
|
elif orientation == 6:
|
||||||
img = img.rotate(0, expand=True)
|
img = img.rotate(270, expand=True)
|
||||||
elif orientation == 8:
|
elif orientation == 8:
|
||||||
img = img.rotate(180, expand=True)
|
img = img.rotate(90, expand=True)
|
||||||
except (AttributeError, KeyError, IndexError):
|
except (AttributeError, KeyError, IndexError):
|
||||||
# cases: image don't have getexif
|
# cases: image don't have getexif
|
||||||
exif = b""
|
exif = b""
|
||||||
|
@ -69,6 +69,7 @@ def render_error_page(
|
|||||||
error_message: str,
|
error_message: str,
|
||||||
error_description: str,
|
error_description: str,
|
||||||
template_path: Path = Path("./"),
|
template_path: Path = Path("./"),
|
||||||
|
currentPath: str = "",
|
||||||
):
|
):
|
||||||
inp = DEFAULT_ERROR_TEMPLATE
|
inp = DEFAULT_ERROR_TEMPLATE
|
||||||
if (template_path / "__error.html").exists():
|
if (template_path / "__error.html").exists():
|
||||||
@ -84,6 +85,7 @@ def render_error_page(
|
|||||||
(template_path / "base.html").read_text(),
|
(template_path / "base.html").read_text(),
|
||||||
content=content,
|
content=content,
|
||||||
styles=["/base.css", "/__error.css"],
|
styles=["/base.css", "/__error.css"],
|
||||||
|
currentPath=currentPath,
|
||||||
),
|
),
|
||||||
error_code,
|
error_code,
|
||||||
)
|
)
|
||||||
@ -125,6 +127,7 @@ def render_page(
|
|||||||
error_message="Not Found",
|
error_message="Not Found",
|
||||||
error_description="The requested resource was not found on this server.",
|
error_description="The requested resource was not found on this server.",
|
||||||
template_path=template_path,
|
template_path=template_path,
|
||||||
|
currentPath=str(path.relative_to(base_path)),
|
||||||
)
|
)
|
||||||
target_path = path
|
target_path = path
|
||||||
target_file = path
|
target_file = path
|
||||||
@ -200,6 +203,7 @@ def render_page(
|
|||||||
"Not Found",
|
"Not Found",
|
||||||
"The requested resource was not found on this server.",
|
"The requested resource was not found on this server.",
|
||||||
template_path,
|
template_path,
|
||||||
|
currentPath=str(relative_path),
|
||||||
)
|
)
|
||||||
|
|
||||||
content = ""
|
content = ""
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from pathlib import Path
|
|
||||||
from src.config.config import Configuration
|
|
||||||
from src.rendering.renderer import render_page, render_error_page
|
|
||||||
from flask import send_file, request
|
|
||||||
from src.rendering.image import generate_thumbnail
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import request, send_file
|
||||||
|
|
||||||
|
from src.config.config import Configuration
|
||||||
|
from src.rendering.image import generate_thumbnail
|
||||||
|
from src.rendering.renderer import render_error_page, render_page
|
||||||
|
|
||||||
|
|
||||||
class RouteManager:
|
class RouteManager:
|
||||||
@ -32,7 +34,6 @@ class RouteManager:
|
|||||||
|
|
||||||
for part in secure_path_parts:
|
for part in secure_path_parts:
|
||||||
if part == "." or part == "..":
|
if part == "." or part == "..":
|
||||||
print("Illegal path nice try")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Reconstruct the secure path
|
# Reconstruct the secure path
|
||||||
@ -45,13 +46,15 @@ class RouteManager:
|
|||||||
|
|
||||||
for part in secure_path.parts:
|
for part in secure_path.parts:
|
||||||
if part.startswith("___"):
|
if part.startswith("___"):
|
||||||
print("hidden file")
|
|
||||||
raise Exception("Illegal path")
|
raise Exception("Illegal path")
|
||||||
|
|
||||||
return secure_path
|
return secure_path
|
||||||
|
|
||||||
def _ensure_route(self, path: str):
|
def _ensure_route(self, path: str):
|
||||||
file_path: Path = self.config.content_dir / (path if path else "index.md")
|
file_path: Path = self.config.content_dir / path
|
||||||
|
if not path or file_path.is_dir():
|
||||||
|
file_path = file_path / "index.md"
|
||||||
|
|
||||||
if file_path < self.config.content_dir:
|
if file_path < self.config.content_dir:
|
||||||
raise Exception("Illegal path")
|
raise Exception("Illegal path")
|
||||||
|
|
||||||
@ -70,7 +73,9 @@ class RouteManager:
|
|||||||
"The requested resource was not found on this server.",
|
"The requested resource was not found on this server.",
|
||||||
self.config.templates_dir,
|
self.config.templates_dir,
|
||||||
)
|
)
|
||||||
file_path: Path = self.config.content_dir / (path if path else "index.md")
|
file_path: Path = self.config.content_dir / path
|
||||||
|
if not path or file_path.is_dir():
|
||||||
|
file_path = file_path / "index.md"
|
||||||
return render_page(
|
return render_page(
|
||||||
file_path,
|
file_path,
|
||||||
base_path=self.config.content_dir,
|
base_path=self.config.content_dir,
|
||||||
@ -120,8 +125,10 @@ class RouteManager:
|
|||||||
return (
|
return (
|
||||||
thumbnail_bytes,
|
thumbnail_bytes,
|
||||||
200,
|
200,
|
||||||
{"Content-Type": f"image/{img_format.lower()}",
|
{
|
||||||
"cache-control": "public, max-age=31536000"},
|
"Content-Type": f"image/{img_format.lower()}",
|
||||||
|
"cache-control": "public, max-age=31536000",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
return send_file(file_path)
|
return send_file(file_path)
|
||||||
else:
|
else:
|
||||||
|
@ -40,6 +40,7 @@ def create_filemanager_blueprint(base_dir, url_prefix='/files', auth_password=No
|
|||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
else:
|
else:
|
||||||
flash("Incorrect password")
|
flash("Incorrect password")
|
||||||
|
#no-dd-sa
|
||||||
return render_template_string('''
|
return render_template_string('''
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
@ -149,10 +150,10 @@ def create_filemanager_blueprint(base_dir, url_prefix='/files', auth_password=No
|
|||||||
</ul>
|
</ul>
|
||||||
<button onclick="bulkCut()">Bulk Cut Selected</button>
|
<button onclick="bulkCut()">Bulk Cut Selected</button>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>Upload File</h2>
|
<h2>Upload File(s)</h2>
|
||||||
<form action="{{ url_for('filemanager.upload') }}" method="post" enctype="multipart/form-data">
|
<form action="{{ url_for('filemanager.upload') }}" method="post" enctype="multipart/form-data">
|
||||||
<input type="hidden" name="path" value="{{ rel_path }}">
|
<input type="hidden" name="path" value="{{ rel_path }}">
|
||||||
<input type="file" name="file">
|
<input type="file" name="file" multiple>
|
||||||
<input type="submit" value="Upload">
|
<input type="submit" value="Upload">
|
||||||
</form>
|
</form>
|
||||||
<hr>
|
<hr>
|
||||||
@ -276,11 +277,13 @@ def create_filemanager_blueprint(base_dir, url_prefix='/files', auth_password=No
|
|||||||
return "Invalid path", 400
|
return "Invalid path", 400
|
||||||
if not os.path.isdir(abs_path):
|
if not os.path.isdir(abs_path):
|
||||||
return "Not a directory", 400
|
return "Not a directory", 400
|
||||||
file = request.files.get('file')
|
files = request.files.getlist('file')
|
||||||
if file:
|
if files:
|
||||||
filename = secure_filename(file.filename)
|
for file in files:
|
||||||
file.save(os.path.join(abs_path, filename))
|
if file and file.filename:
|
||||||
flash("Uploaded successfully")
|
filename = secure_filename(file.filename)
|
||||||
|
file.save(os.path.join(abs_path, filename))
|
||||||
|
flash("Uploaded files successfully")
|
||||||
return redirect(url_for('filemanager.index', path=rel_path))
|
return redirect(url_for('filemanager.index', path=rel_path))
|
||||||
|
|
||||||
@filemanager.route('/rename', methods=['POST'])
|
@filemanager.route('/rename', methods=['POST'])
|
||||||
|
Reference in New Issue
Block a user