feat: Add nextcloud and immich api integration
This commit is contained in:
33
server.py
33
server.py
@@ -18,6 +18,8 @@ from datetime import datetime
|
|||||||
import dotenv
|
import dotenv
|
||||||
from authlib.integrations.flask_client import OAuth
|
from authlib.integrations.flask_client import OAuth
|
||||||
from flask_caching import Cache
|
from flask_caching import Cache
|
||||||
|
from tools.cloud import getUserQuota
|
||||||
|
from tools.immich import get_immich_stats
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
@@ -201,6 +203,37 @@ def api_data():
|
|||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/v1/cloud_quota", methods=["GET"])
|
||||||
|
def api_cloud_quota():
|
||||||
|
"""
|
||||||
|
API endpoint to get the user's cloud quota information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = session.get("user")
|
||||||
|
if not user:
|
||||||
|
return jsonify({"error": "Unauthorized"}), 401
|
||||||
|
|
||||||
|
quota_info = getUserQuota(user["preferred_username"])
|
||||||
|
if "error" in quota_info:
|
||||||
|
return jsonify(quota_info), 500
|
||||||
|
return jsonify(quota_info)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/v1/immich", methods=["GET"])
|
||||||
|
def api_immich_stats():
|
||||||
|
"""
|
||||||
|
API endpoint to get the user's Immich stats.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = session.get("user")
|
||||||
|
if not user:
|
||||||
|
return jsonify({"error": "Unauthorized"}), 401
|
||||||
|
stats = get_immich_stats(user["sub"])
|
||||||
|
if "error" in stats:
|
||||||
|
return jsonify(stats), 500
|
||||||
|
return jsonify(stats)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
"id": "podcast",
|
"id": "podcast",
|
||||||
"name": "WVAC Podcast",
|
"name": "WVAC Podcast",
|
||||||
"url": "https://podcast.woodburn.au",
|
"url": "https://podcast.woodburn.au",
|
||||||
"description": "WVAC Podcast"
|
"description": "WVAC Podcast",
|
||||||
|
"icon": "https://ya.c.woodburn.au/assets/img/favicon.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "transfer_sh",
|
"id": "transfer_sh",
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ a:hover {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-card:hover {
|
.service-card:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px);
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-icon {
|
.service-icon {
|
||||||
@@ -66,6 +68,13 @@ a:hover {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service-note {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
|||||||
45
templates/assets/js/index.js
Normal file
45
templates/assets/js/index.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
// If the user is logged in, fetch their cloud quota and display it
|
||||||
|
const cloudLink = document.getElementById("cloud");
|
||||||
|
if (cloudLink) {
|
||||||
|
fetch("/api/v1/cloud_quota")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.error) {
|
||||||
|
// Create a new span element to display the quota
|
||||||
|
const quotaLabel = document.createElement("p");
|
||||||
|
quotaLabel.classList.add("service-note");
|
||||||
|
quotaLabel.textContent = `${data.used} GB used / ${data.total} GB total`;
|
||||||
|
// Append the quota span to the cloud link
|
||||||
|
cloudLink.appendChild(quotaLabel);
|
||||||
|
} else {
|
||||||
|
console.error("Error fetching cloud quota:", data.error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error fetching cloud quota:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch Immich stats and display them
|
||||||
|
const immichLink = document.getElementById("immich");
|
||||||
|
if (immichLink) {
|
||||||
|
fetch("/api/v1/immich")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.error) {
|
||||||
|
// Create a new span element to display the stats
|
||||||
|
const statsLabel = document.createElement("p");
|
||||||
|
statsLabel.classList.add("service-note");
|
||||||
|
statsLabel.textContent = `Images: ${data.images}, Videos: ${data.videos}`;
|
||||||
|
// Append the stats span to the Immich link
|
||||||
|
immichLink.appendChild(statsLabel);
|
||||||
|
} else {
|
||||||
|
console.error("Error fetching Immich stats:", data.error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error fetching Immich stats:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<title>Woodburn/</title>
|
<title>Woodburn/</title>
|
||||||
<link rel="icon" href="/assets/img/favicon.png" type="image/png">
|
<link rel="icon" href="/assets/img/favicon.png" type="image/png">
|
||||||
<link rel="stylesheet" href="/assets/css/index.css">
|
<link rel="stylesheet" href="/assets/css/index.css">
|
||||||
|
<script src="/assets/js/index.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 class="section-title">External Services</h3>
|
<h3 class="section-title">Services</h3>
|
||||||
<div class="services-grid">
|
<div class="services-grid">
|
||||||
{% for service in services.external %}
|
{% for service in services.external %}
|
||||||
<a href="{{ service.url }}" class="service-card" target="_blank">
|
<a href="{{ service.url }}" class="service-card" target="_blank">
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
<h3 class="section-title">Internal Services</h3>
|
<h3 class="section-title">Internal Services</h3>
|
||||||
<div class="services-grid">
|
<div class="services-grid">
|
||||||
{% for service in services.internal %}
|
{% for service in services.internal %}
|
||||||
<a href="{{ service.url }}" class="service-card" target="_blank">
|
<a href="{{ service.url }}" class="service-card" target="_blank" id="{{ service.id }}">
|
||||||
<img src="/services/internal/{{ service.id }}.png" alt="{{ service.name }}" class="service-icon">
|
<img src="/services/internal/{{ service.id }}.png" alt="{{ service.name }}" class="service-icon">
|
||||||
<h4 class="service-name">{{ service.name }}</h4>
|
<h4 class="service-name">{{ service.name }}</h4>
|
||||||
<p class="service-desc">{{ service.description }}</p>
|
<p class="service-desc">{{ service.description }}</p>
|
||||||
|
|||||||
35
tools/adventure.py
Normal file
35
tools/adventure.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
IMMICH_API_KEY = os.getenv("IMMICH_API_KEY")
|
||||||
|
|
||||||
|
|
||||||
|
def get_immich_stats(user_sub: str) -> dict[str, int | str]:
|
||||||
|
"""
|
||||||
|
Get the user's Immich stats from the API.
|
||||||
|
"""
|
||||||
|
if not IMMICH_API_KEY:
|
||||||
|
return {"error": "IMMICH_API_KEY environment variable not set"}
|
||||||
|
headers = {"x-api-key": IMMICH_API_KEY, "Accept": "application/json"}
|
||||||
|
response = requests.get(
|
||||||
|
"https://immich.woodburn.au/api/admin/users", headers=headers
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {"error": f"Failed to fetch Immich stats: {response.status_code}"}
|
||||||
|
data = response.json()
|
||||||
|
user_id = None
|
||||||
|
for user in data:
|
||||||
|
if user.get("oauthId") == user_sub:
|
||||||
|
user_id = user.get("id")
|
||||||
|
break
|
||||||
|
if not user_id:
|
||||||
|
return {"error": "User not found in Immich"}
|
||||||
|
# Get user stats
|
||||||
|
response = requests.get(
|
||||||
|
f"https://immich.woodburn.au/api/admin/users/{user_id}/statistics",
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {"error": f"Failed to fetch Immich user stats: {response.status_code}"}
|
||||||
|
stats = response.json()
|
||||||
|
return stats
|
||||||
49
tools/cloud.py
Normal file
49
tools/cloud.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
login = os.getenv("WOODBURN_USER")
|
||||||
|
|
||||||
|
|
||||||
|
def getUserQuota(user: str) -> dict[str, int | str]:
|
||||||
|
"""
|
||||||
|
Get the user's quota information from the environment variable.
|
||||||
|
Returns a dictionary with 'used' and 'total' as integers.
|
||||||
|
"""
|
||||||
|
headers = {"OCS-APIRequest": "true", "Accept": "application/json"}
|
||||||
|
if not login:
|
||||||
|
return {
|
||||||
|
"used": 0,
|
||||||
|
"total": 5,
|
||||||
|
"percentage": "0.0",
|
||||||
|
"error": "WOODBURN_USER environment variable not set",
|
||||||
|
}
|
||||||
|
# curl -u user https://cloud.woodburn.au/ocs/v1.php/cloud/users/nathan OCS-APIRequest:true
|
||||||
|
response = requests.get(
|
||||||
|
f"https://cloud.woodburn.au/ocs/v1.php/cloud/users/{user}",
|
||||||
|
headers=headers,
|
||||||
|
auth=(login.split(":")[0], login.split(":")[1]),
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {
|
||||||
|
"used": 0,
|
||||||
|
"total": 5,
|
||||||
|
"percentage": "0.0",
|
||||||
|
"error": f"Failed to fetch quota: {response.status_code}",
|
||||||
|
}
|
||||||
|
data = response.json()
|
||||||
|
# If the request failed
|
||||||
|
if data.get("ocs", {}).get("meta", {}).get("status") != "ok":
|
||||||
|
return {
|
||||||
|
"used": 0,
|
||||||
|
"total": 5,
|
||||||
|
"percentage": "0.0",
|
||||||
|
"error": data.get("ocs", {})
|
||||||
|
.get("meta", {})
|
||||||
|
.get("message", "Unknown error"),
|
||||||
|
}
|
||||||
|
|
||||||
|
quota = data.get("ocs", {}).get("data", {}).get("quota", {})
|
||||||
|
# Convert to GB
|
||||||
|
used = int(quota.get("used", 0)) // (1024 * 1024 * 1024)
|
||||||
|
total = int(quota.get("total", 0)) // (1024 * 1024 * 1024)
|
||||||
|
return {"used": used, "total": total, "percentage": quota.get("relative", "0.0")}
|
||||||
35
tools/immich.py
Normal file
35
tools/immich.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
IMMICH_API_KEY = os.getenv("IMMICH_API_KEY")
|
||||||
|
|
||||||
|
|
||||||
|
def get_immich_stats(user_sub: str) -> dict[str, int | str]:
|
||||||
|
"""
|
||||||
|
Get the user's Immich stats from the API.
|
||||||
|
"""
|
||||||
|
if not IMMICH_API_KEY:
|
||||||
|
return {"error": "IMMICH_API_KEY environment variable not set"}
|
||||||
|
headers = {"x-api-key": IMMICH_API_KEY, "Accept": "application/json"}
|
||||||
|
response = requests.get(
|
||||||
|
"https://immich.woodburn.au/api/admin/users", headers=headers
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {"error": f"Failed to fetch Immich stats: {response.status_code}"}
|
||||||
|
data = response.json()
|
||||||
|
user_id = None
|
||||||
|
for user in data:
|
||||||
|
if user.get("oauthId") == user_sub:
|
||||||
|
user_id = user.get("id")
|
||||||
|
break
|
||||||
|
if not user_id:
|
||||||
|
return {"error": "User not found in Immich"}
|
||||||
|
# Get user stats
|
||||||
|
response = requests.get(
|
||||||
|
f"https://immich.woodburn.au/api/admin/users/{user_id}/statistics",
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return {"error": f"Failed to fetch Immich user stats: {response.status_code}"}
|
||||||
|
stats = response.json()
|
||||||
|
return stats
|
||||||
Reference in New Issue
Block a user