Move all instance files to instance directory

Still to move: thumbnails
This commit is contained in:
augustin64 2024-01-16 18:50:19 +01:00
parent f43b1e1090
commit 6f5031623c
13 changed files with 162 additions and 73 deletions

View File

@ -19,3 +19,8 @@ BASE_URL="http://localhost:5000"
# Session expiration, in days
MAX_AGE=31
# Instance path ie. where are all the files + the database stored
# Keep in mind that this config option can only be loaded from default_config.py,
# as the custom config is stored in $INSTANCE_PATH/
INSTANCE_PATH="instance"

26
make.sh
View File

@ -1,25 +1,27 @@
#!/bin/bash
INSTANCE_PATH="instance"
init () {
mkdir -p "instance"
mkdir -p "partitioncloud/partitions"
mkdir -p "partitioncloud/attachments"
mkdir -p "partitioncloud/search-partitions"
mkdir -p "partitioncloud/static/thumbnails"
mkdir -p "partitioncloud/static/search-thumbnails"
mkdir -p "$INSTANCE_PATH"
mkdir -p "$INSTANCE_PATH/partitions"
mkdir -p "$INSTANCE_PATH/attachments"
mkdir -p "$INSTANCE_PATH/search-partitions"
mkdir -p "$INSTANCE_PATH/static/thumbnails"
mkdir -p "$INSTANCE_PATH/static/search-thumbnails"
if ! test -f "instance/config.py"; then
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" > instance/config.py
if ! test -f "$INSTANCE_PATH/config.py"; then
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" > "$INSTANCE_PATH/config.py"
fi
if test -f "instance/partitioncloud.sqlite"; then
if test -f "$INSTANCE_PATH/partitioncloud.sqlite"; then
printf "Souhaitez vous supprimer la base de données existante ? [y/n] "
read -r CONFIRMATION
[[ $CONFIRMATION == y ]] || exit 1
fi
sqlite3 "instance/partitioncloud.sqlite" '.read partitioncloud/schema.sql'
sqlite3 "$INSTANCE_PATH/partitioncloud.sqlite" '.read partitioncloud/schema.sql'
echo "Base de données créé"
sqlite3 "instance/partitioncloud.sqlite" '.read partitioncloud/init.sql'
sqlite3 "$INSTANCE_PATH/partitioncloud.sqlite" '.read partitioncloud/init.sql'
echo "Utilisateur root:root ajouté"
}
@ -43,7 +45,7 @@ usage () {
if [[ $1 && $(type "$1") = *"is a"*"function"* || $(type "$1") == *"est une fonction"* ]]; then
# Import config
source "default_config.py"
[[ ! -x instance/config.py ]] && source "instance/config.py"
[[ ! -x" $INSTANCE_PATH/config.py" ]] && source "$INSTANCE_PATH/config.py"
$1 ${*:2} # Call the function
else
usage

View File

@ -3,8 +3,10 @@
Main file
"""
import os
import sys
import datetime
import subprocess
import importlib.util
from flask import Flask, g, redirect, render_template, request, send_file, flash, session, abort
from werkzeug.security import generate_password_hash
@ -16,14 +18,48 @@ from .modules.db import get_db
app = Flask(__name__)
app.config.from_mapping(
DATABASE=os.path.join(app.instance_path, f"{__name__}.sqlite"),
)
app.config.from_object('default_config')
if os.path.exists("instance/config.py"):
app.config.from_object('instance.config')
else:
print("[WARNING] Using default config")
def load_config():
app.config.from_object('default_config')
app.instance_path = os.path.abspath(app.config["INSTANCE_PATH"])
if not os.path.exists(app.instance_path):
print("[ERROR] Instance path does not exist. Make sure to use an existing directory.")
sys.exit(1)
if os.path.exists(f"{app.instance_path}/config.py"):
# Load module from instance_path/config.py in user_config object
spec = importlib.util.spec_from_file_location(
".",
os.path.join(app.instance_path, "config.py")
)
user_config = importlib.util.module_from_spec(spec)
spec.loader.exec_module(user_config)
app.config.from_object(user_config)
if os.path.abspath(app.config["INSTANCE_PATH"]) != app.instance_path:
print("[ERROR] Using two different instance path. \
\nPlease modify INSTANCE_PATH only in default_config.py and remove it from $INSTANCE_PATH/config.py")
sys.exit(1)
else:
print("[WARNING] Using default config")
app.config.from_mapping(
DATABASE=os.path.join(app.instance_path, f"{__name__}.sqlite"),
)
def get_version():
try:
result = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, check=True)
return result.stdout.decode('utf8')
except (FileNotFoundError, subprocess.CalledProcessError):
# In case git not found or any platform specific weird error
return "unknown"
load_config()
app.register_blueprint(auth.bp)
app.register_blueprint(admin.bp)
@ -31,13 +67,7 @@ app.register_blueprint(groupe.bp)
app.register_blueprint(albums.bp)
app.register_blueprint(partition.bp)
try:
result = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, check=True)
__version__ = result.stdout.decode('utf8')
except (FileNotFoundError, subprocess.CalledProcessError):
# In case git not found or any platform specific weird error
__version__ = "unknown"
__version__ = get_version()
@app.route("/")
@ -99,7 +129,7 @@ def search_thumbnail(uuid):
f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \
remove -crop 178x178+0+0 \
partitioncloud/search-partitions/{uuid}.pdf[0] \
{app.instance_path}/search-partitions/{uuid}.pdf[0] \
partitioncloud/static/search-thumbnails/{uuid}.jpg'
)

View File

@ -42,7 +42,7 @@ def search_page():
query = request.form["query"]
nb_queries = abs(int(request.form["nb-queries"]))
search.flush_cache()
search.flush_cache(current_app.instance_path)
partitions_local = search.local_search(query, utils.get_all_partitions())
user = User(user_id=session.get("user_id"))
@ -52,7 +52,7 @@ def search_page():
nb_queries = min(current_app.config["MAX_ONLINE_QUERIES"], nb_queries)
else:
nb_queries = min(10, nb_queries) # Query limit is 10 for an admin
google_results = search.online_search(query, nb_queries)
google_results = search.online_search(query, nb_queries, current_app.instance_path)
else:
google_results = []
@ -207,7 +207,7 @@ def delete_album(uuid):
flash(error)
return redirect(request.referrer)
album.delete()
album.delete(current_app.instance_path)
flash("Album supprimé.")
return redirect("/albums")
@ -278,20 +278,32 @@ def add_partition(album_uuid):
)
db.commit()
partition_path = os.path.join(
current_app.instance_path,
"partitions",
f"{partition_uuid}.pdf"
)
if partition_type == "file":
file = request.files["file"]
file.save(f"partitioncloud/partitions/{partition_uuid}.pdf")
file.save(partition_path)
else:
search_partition_path = os.path.join(
current_app.instance_path,
"search-partitions",
f"{search_uuid}.pdf"
)
shutil.copyfile(
f"partitioncloud/search-partitions/{search_uuid}.pdf",
f"partitioncloud/partitions/{partition_uuid}.pdf"
search_partition_path,
partition_path
)
os.system(
f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \
remove -crop 178x178+0+0 \
partitioncloud/partitions/{partition_uuid}.pdf[0] \
{partition_path}[0] \
partitioncloud/static/thumbnails/{partition_uuid}.jpg'
)
db.commit()

View File

@ -82,7 +82,7 @@ class Album():
).fetchall()
def delete(self):
def delete(self, instance_path):
"""
Supprimer l'album
"""
@ -130,9 +130,9 @@ class Album():
attachments = [Attachment(data=i) for i in data]
for attachment in attachments:
attachment.delete()
attachment.delete(instance_path)
os.remove(f"partitioncloud/partitions/{partition['uuid']}.pdf")
os.remove(f"{instance_path}/partitions/{partition['uuid']}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg")

View File

@ -29,7 +29,7 @@ class Attachment():
self.filetype = data["filetype"]
self.partition_uuid = data["partition_uuid"]
def delete(self):
def delete(self, instance_path):
db = get_db()
db.execute(
"""
@ -40,7 +40,7 @@ class Attachment():
)
db.commit()
os.remove(f"partitioncloud/attachments/{self.uuid}.{self.filetype}")
os.remove(f"{instance_path}/attachments/{self.uuid}.{self.filetype}")
def __repr__(self):
return f"{self.name}.{self.filetype}"

View File

@ -21,7 +21,7 @@ class Groupe():
self.albums = None
self.admins = None
def delete(self):
def delete(self, instance_path):
"""
Supprime le groupe, et les albums laissés orphelins (sans utilisateur)
"""
@ -63,7 +63,7 @@ class Groupe():
for i in data:
album = Album(id=i["id"])
album.delete()
album.delete(instance_path)
def get_users(self):

View File

@ -29,7 +29,7 @@ class Partition():
else:
raise LookupError
def delete(self):
def delete(self, instance_path):
self.load_attachments()
db = get_db()
@ -42,7 +42,7 @@ class Partition():
)
db.commit()
os.remove(f"partitioncloud/partitions/{self.uuid}.pdf")
os.remove(f"{instance_path}/partitions/{self.uuid}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{self.uuid}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{self.uuid}.jpg")
@ -56,7 +56,7 @@ class Partition():
db.commit()
for attachment in self.attachments:
attachment.delete()
attachment.delete(instance_path)
def update(self, name=None, author="", body=""):
if name is None:

View File

@ -3,7 +3,7 @@
Groupe module
"""
from flask import (Blueprint, abort, flash, redirect, render_template,
request, session)
request, session, current_app)
from .auth import login_required
from .db import get_db
@ -155,7 +155,7 @@ def delete_groupe(uuid):
flash(error)
return redirect(request.referrer)
groupe.delete()
groupe.delete(current_app.instance_path)
flash("Groupe supprimé.")
return redirect("/albums")

View File

@ -4,7 +4,8 @@ Partition module
"""
import os
from uuid import uuid4
from flask import Blueprint, abort, send_file, render_template, request, redirect, flash, session
from flask import (Blueprint, abort, send_file, render_template,
request, redirect, flash, session, current_app)
from .db import get_db
from .auth import login_required, admin_required
@ -21,9 +22,11 @@ def get_partition(uuid):
abort(404)
return send_file(
os.path.join("partitions", f"{uuid}.pdf"),
download_name = f"{partition.name}.pdf"
return send_file(os.path.join(
current_app.instance_path,
"partitions",
f"{uuid}.pdf"
), download_name = f"{partition.name}.pdf"
)
@bp.route("/<uuid>/attachments")
@ -51,7 +54,7 @@ def add_attachment(uuid):
user = User(user_id=session.get("user_id"))
if user.id != partition.user_id and user.access_level != 1:
flash("Cette partition ne vous appartient pas")
flash("Cette partition ne vous current_appartient pas")
return redirect(request.referrer)
error = None # À mettre au propre
@ -90,7 +93,11 @@ def add_attachment(uuid):
db.commit()
file = request.files["file"]
file.save(f"partitioncloud/attachments/{attachment_uuid}.{ext}")
file.save(os.path.join(
current_app.instance_path,
"attachments",
f"{attachment_uuid}.{ext}"
))
break
except db.IntegrityError:
@ -114,9 +121,11 @@ def get_attachment(uuid, filetype):
assert filetype == attachment.filetype
return send_file(
os.path.join("attachments", f"{uuid}.{attachment.filetype}"),
download_name = f"{attachment.name}.{attachment.filetype}"
return send_file(os.path.join(
current_app.instance_path,
"attachments",
f"{uuid}.{attachment.filetype}"
), download_name = f"{attachment.name}.{attachment.filetype}"
)
@ -223,7 +232,7 @@ def delete(uuid):
if request.method == "GET":
return render_template("partition/delete.html", partition=partition, user=user)
partition.delete()
partition.delete(current_app.instance_path)
flash("Partition supprimée.")
return redirect("/albums")
@ -245,7 +254,13 @@ def partition_search(uuid):
abort(404)
if request.args.get("redirect") == "true" and partition["url"] is not None:
return redirect(partition["url"])
return send_file(os.path.join("search-partitions", f"{uuid}.pdf"))
return send_file(os.path.join(
current_app.instance_path,
"search-partitions",
f"{uuid}.pdf"
)
)
@bp.route("/")

View File

@ -49,19 +49,19 @@ def local_search(query, partitions):
return selection
def download_search_result(element):
def download_search_result(element, instance_path):
uuid = element["uuid"]
url = element["url"]
try:
urllib.request.urlretrieve(url, f"partitioncloud/search-partitions/{uuid}.pdf")
urllib.request.urlretrieve(url, f"{instance_path}/search-partitions/{uuid}.pdf")
except (urllib.error.HTTPError, urllib.error.URLError):
with open(f"partitioncloud/search-partitions/{uuid}.pdf", 'a', encoding="utf8") as _:
with open(f"{instance_path}/search-partitions/{uuid}.pdf", 'a', encoding="utf8") as _:
pass # Create empty file
def online_search(query, num_queries):
def online_search(query, num_queries, instance_path):
"""
Renvoie les 3 résultats les plus pertinents depuis google
"""
@ -103,7 +103,12 @@ def online_search(query, num_queries):
except urllib.error.URLError: # Unable to access network
return []
threads = [threading.Thread(target=download_search_result, args=(elem,)) for elem in partitions]
threads = [
threading.Thread(
target=download_search_result,
args=(elem, instance_path)
) for elem in partitions
]
for thread in threads:
thread.start()
@ -114,7 +119,7 @@ def online_search(query, num_queries):
for element in partitions.copy():
uuid = element["uuid"]
url = element["url"]
if os.stat(f"partitioncloud/search-partitions/{uuid}.pdf").st_size == 0:
if os.stat(f"{instance_path}/search-partitions/{uuid}.pdf").st_size == 0:
print("An error occured", url)
db.execute(
"""
@ -125,14 +130,14 @@ def online_search(query, num_queries):
)
db.commit()
os.remove(f"partitioncloud/search-partitions/{uuid}.pdf")
os.remove(f"{instance_path}/search-partitions/{uuid}.pdf")
partitions.remove(element)
return partitions
def flush_cache():
def flush_cache(instance_path):
"""
Supprimer les résultats de recherche datant de plus de 15 minutes
"""
@ -146,7 +151,7 @@ def flush_cache():
for element in expired_cache:
uuid = element["uuid"]
try:
os.remove(f"partitioncloud/search-partitions/{uuid}.pdf")
os.remove(f"{instance_path}/search-partitions/{uuid}.pdf")
except FileNotFoundError:
pass
try:

View File

@ -1,4 +1,5 @@
import os
import shutil
import sqlite3
from hooks import utils
from colorama import Fore, Style
@ -142,3 +143,21 @@ def base_url_parameter_added():
def install_qrcode():
os.system("pip install qrcode -qq")
"""
v1.5.*
"""
def move_instance():
paths = [
"attachments",
"partitions",
"search-partitions"
]
for path in paths:
shutil.move(
os.path.join("partitioncloud", path),
os.path.join("instance", path)
)

View File

@ -33,6 +33,7 @@ hooks = [
],
),
("v1.4.1", [("Install qrcode", v1_hooks.install_qrcode)]),
("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)])
]
@ -86,10 +87,10 @@ def backup_instance(version, verbose=True):
os.path.join(dest, "search-partitions"),
),
]
for src, dst in paths:
for src, dst in paths: # Only the first one exists after v1.5.0
if os.path.exists(src):
print_verbose(f"\tBacking up {src}")
shutil.copy_tree(src, dst)
shutil.copytree(src, dst)
def print_hooks(hooks_list):
@ -108,7 +109,7 @@ def apply_hooks(hooks_list):
def migrate(current, target, skip_backup=False, prog_name="scripts/migration.py"):
""""""
"""Migrate from one version to another"""
print(f"Trying to migrate from {current} to {target}")
assert is_newer(target, current)
@ -132,7 +133,7 @@ def migrate(current, target, skip_backup=False, prog_name="scripts/migration.py"
)
print(
f"If something goes wrong, recover with {Style.BRIGHT}{Fore.BLUE}{prog_name}\
--restore {current}{Style.RESET_ALL}"
--restore {current}{Style.RESET_ALL}"
)
else:
print("Skipping automatic backup")
@ -168,13 +169,13 @@ def restore(version):
os.path.join(dest, "search-partitions"),
),
]
for src, dst in paths:
for src, dst in paths: # Only the first one exists after v1.5.0
if os.path.exists(src):
shutil.rmtree(src)
if os.path.exists(dst):
print(f"\tRestoring {src}")
shutil.copy_tree(dst, src)
shutil.copytree(dst, src)
else:
print(
f"\t{Fore.RED}No available backup for {src}, \