mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-08-24 10:51:42 +02:00
Compare commits
17 Commits
1e822729c3
...
152ebf86f9
Author | SHA1 | Date | |
---|---|---|---|
|
152ebf86f9 | ||
|
68069d1043 | ||
|
9ff926ae4d | ||
|
b28cbdad31 | ||
|
3c1bae463e | ||
|
91fbdb9ec1 | ||
|
c1abc56ebc | ||
|
f1582165bc | ||
|
0f155369a1 | ||
|
f81c1d1b31 | ||
|
0360184c2d | ||
|
099a39a2a2 | ||
|
c1cd7ca64f | ||
|
4cc108ab7f | ||
|
0028310aa5 | ||
|
18e9453fed | ||
|
db74ddfdf5 |
@ -5,7 +5,7 @@ manage online service access.
|
|||||||
# This will make sure the app is always imported when
|
# This will make sure the app is always imported when
|
||||||
# Django starts so that shared_task will use this app.
|
# Django starts so that shared_task will use this app.
|
||||||
|
|
||||||
__version__ = '4.8.0'
|
__version__ = '4.9.0'
|
||||||
__title__ = 'Alliance Auth'
|
__title__ = 'Alliance Auth'
|
||||||
__title_useragent__ = 'AllianceAuth'
|
__title_useragent__ = 'AllianceAuth'
|
||||||
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
ul#nav-right:has(li) + ul#nav-right-character-control > li:first-child {
|
ul#nav-right:has(li) + ul#nav-right-character-control > li:first-child {
|
||||||
display: list-item !important;
|
display: list-item !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.is-submitting button[type="submit"] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-width: 991px) {
|
@media all and (max-width: 991px) {
|
||||||
|
@ -0,0 +1,300 @@
|
|||||||
|
/**
|
||||||
|
* Functions and utilities for the Alliance Auth framework.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint -W097 */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given item is an array.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```javascript
|
||||||
|
* if (isArray(someVariable)) {
|
||||||
|
* console.log('This is an array');
|
||||||
|
* } else {
|
||||||
|
* console.log('This is not an array');
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {*} item - The item to check.
|
||||||
|
* @returns {boolean} True if the item is an array, false otherwise.
|
||||||
|
*/
|
||||||
|
const isArray = (item) => {
|
||||||
|
return Array.isArray(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given item is a plain object, excluding arrays and dates.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```javascript
|
||||||
|
* if (isObject(someVariable)) {
|
||||||
|
* console.log('This is a plain object');
|
||||||
|
* } else {
|
||||||
|
* console.log('This is not a plain object');
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {*} item - The item to check.
|
||||||
|
* @returns {boolean} True if the item is a plain object, false otherwise.
|
||||||
|
*/
|
||||||
|
const isObject = (item) => {
|
||||||
|
return (
|
||||||
|
item && typeof item === 'object' && !isArray(item) && !(item instanceof Date)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data from an ajax URL
|
||||||
|
*
|
||||||
|
* Do not call this function directly, use `fetchGet` or `fetchPost` instead.
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to fetch data from
|
||||||
|
* @param {string} method The HTTP method to use for the request (default: 'get')
|
||||||
|
* @param {string|null} csrfToken The CSRF token to include in the request headers (default: null)
|
||||||
|
* @param {string|null} payload The payload (JSON|Object) to send with the request (default: null)
|
||||||
|
* @param {boolean} responseIsJson Whether the response is expected to be JSON or not (default: true)
|
||||||
|
* @returns {Promise<string>} The fetched data
|
||||||
|
* @throws {Error} Throws an error when:
|
||||||
|
* - The method is not valid (only `get` and `post` are allowed).
|
||||||
|
* - The CSRF token is required but not provided for POST requests.
|
||||||
|
* - The payload is not an object when using POST method.
|
||||||
|
* - The response status is not OK (HTTP 200-299).
|
||||||
|
* - There is a network error or if the response cannot be parsed as JSON.
|
||||||
|
*/
|
||||||
|
const _fetchAjaxData = async ({
|
||||||
|
url,
|
||||||
|
method = 'get',
|
||||||
|
csrfToken = null,
|
||||||
|
payload = null,
|
||||||
|
responseIsJson = true
|
||||||
|
}) => {
|
||||||
|
const normalizedMethod = method.toLowerCase();
|
||||||
|
|
||||||
|
// Validate the method
|
||||||
|
const validMethods = ['get', 'post'];
|
||||||
|
|
||||||
|
if (!validMethods.includes(normalizedMethod)) {
|
||||||
|
throw new Error(`Invalid method: ${method}. Valid methods are: get, post`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {};
|
||||||
|
|
||||||
|
// Set headers based on response type
|
||||||
|
if (responseIsJson) {
|
||||||
|
headers['Accept'] = 'application/json'; // jshint ignore:line
|
||||||
|
headers['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
|
|
||||||
|
let requestUrl = url;
|
||||||
|
let body = null;
|
||||||
|
|
||||||
|
if (normalizedMethod === 'post') {
|
||||||
|
if (!csrfToken) {
|
||||||
|
throw new Error('CSRF token is required for POST requests');
|
||||||
|
}
|
||||||
|
|
||||||
|
headers['X-CSRFToken'] = csrfToken;
|
||||||
|
|
||||||
|
if (payload !== null && !isObject(payload)) {
|
||||||
|
throw new Error('Payload must be an object when using POST method');
|
||||||
|
}
|
||||||
|
|
||||||
|
body = payload ? JSON.stringify(payload) : null;
|
||||||
|
} else if (normalizedMethod === 'get' && payload) {
|
||||||
|
const queryParams = new URLSearchParams(payload).toString(); // jshint ignore:line
|
||||||
|
|
||||||
|
requestUrl += (url.includes('?') ? '&' : '?') + queryParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error with a formatted message.
|
||||||
|
*
|
||||||
|
* @param {Response} response The error object containing the message to throw.
|
||||||
|
*/
|
||||||
|
const throwHTTPStatusError = (response) => {
|
||||||
|
throw new Error(`Error: ${response.status} - ${response.statusText}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(requestUrl, {
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
headers: headers,
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error if the response status is not OK (HTTP 200-299).
|
||||||
|
* This is used to handle HTTP errors gracefully.
|
||||||
|
*/
|
||||||
|
if (!response.ok) {
|
||||||
|
throwHTTPStatusError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseIsJson ? await response.json() : await response.text();
|
||||||
|
} catch (error) {
|
||||||
|
// Log the error message to the console
|
||||||
|
console.log(`Error: ${error.message}`);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data from an ajax URL using the GET method.
|
||||||
|
* This function is a wrapper around _fetchAjaxData to simplify GET requests.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```javascript
|
||||||
|
* fetchGet({
|
||||||
|
* url: url,
|
||||||
|
* responseIsJson: false
|
||||||
|
* }).then((data) => {
|
||||||
|
* // Process the fetched data
|
||||||
|
* }).catch((error) => {
|
||||||
|
* console.error(`Error: ${error.message}`);
|
||||||
|
*
|
||||||
|
* // Handle the error appropriately
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to fetch data from
|
||||||
|
* @param {string|null} payload The payload (JSON) to send with the request (default: null)
|
||||||
|
* @param {boolean} responseIsJson Whether the response is expected to be JSON or not (default: true)
|
||||||
|
* @return {Promise<string>} The fetched data
|
||||||
|
*/
|
||||||
|
const fetchGet = async ({
|
||||||
|
url,
|
||||||
|
payload = null,
|
||||||
|
responseIsJson = true
|
||||||
|
}) => {
|
||||||
|
return await _fetchAjaxData({
|
||||||
|
url: url,
|
||||||
|
method: 'get',
|
||||||
|
payload: payload,
|
||||||
|
responseIsJson: responseIsJson
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data from an ajax URL using the POST method.
|
||||||
|
* This function is a wrapper around _fetchAjaxData to simplify POST requests.
|
||||||
|
* It requires a CSRF token for security purposes.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```javascript
|
||||||
|
* fetchPost({
|
||||||
|
* url: url,
|
||||||
|
* csrfToken: csrfToken,
|
||||||
|
* payload: {
|
||||||
|
* key: 'value',
|
||||||
|
* anotherKey: 'anotherValue'
|
||||||
|
* },
|
||||||
|
* responseIsJson: true
|
||||||
|
* }).then((data) => {
|
||||||
|
* // Process the fetched data
|
||||||
|
* }).catch((error) => {
|
||||||
|
* console.error(`Error: ${error.message}`);
|
||||||
|
*
|
||||||
|
* // Handle the error appropriately
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to fetch data from
|
||||||
|
* @param {string|null} csrfToken The CSRF token to include in the request headers (default: null)
|
||||||
|
* @param {string|null} payload The payload (JSON) to send with the request (default: null)
|
||||||
|
* @param {boolean} responseIsJson Whether the response is expected to be JSON or not (default: true)
|
||||||
|
* @return {Promise<string>} The fetched data
|
||||||
|
*/
|
||||||
|
const fetchPost = async ({
|
||||||
|
url,
|
||||||
|
csrfToken,
|
||||||
|
payload = null,
|
||||||
|
responseIsJson = true
|
||||||
|
}) => {
|
||||||
|
return await _fetchAjaxData({
|
||||||
|
url: url,
|
||||||
|
method: 'post',
|
||||||
|
csrfToken: csrfToken,
|
||||||
|
payload: payload,
|
||||||
|
responseIsJson: responseIsJson
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively merges properties from source objects into a target object. If a property at the current level is an object,
|
||||||
|
* and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property.
|
||||||
|
* This function does not modify the source objects and prevents prototype pollution by not allowing __proto__, constructor,
|
||||||
|
* and prototype property names.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```javascript
|
||||||
|
* const target = {a: 1, b: {c: 2}};
|
||||||
|
* const source1 = {b: {d: 3}, e: 4 };
|
||||||
|
* const source2 = {a: 5, b: {c: 6}};
|
||||||
|
*
|
||||||
|
* const merged = objectDeepMerge(target, source1, source2);
|
||||||
|
*
|
||||||
|
* console.log(merged); // {a: 5, b: {c: 6, d: 3}, e: 4}
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {Object} target The target object to merge properties into.
|
||||||
|
* @param {...Object} sources One or more source objects from which to merge properties.
|
||||||
|
* @returns {Object} The target object after merging properties from sources.
|
||||||
|
*/
|
||||||
|
function objectDeepMerge (target, ...sources) {
|
||||||
|
if (!sources.length) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through each source object without modifying the `sources` array.
|
||||||
|
sources.forEach(source => {
|
||||||
|
if (isObject(target) && isObject(source)) {
|
||||||
|
for (const key in source) {
|
||||||
|
if (isObject(source[key])) {
|
||||||
|
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||||
|
continue; // Skip potentially dangerous keys to prevent prototype pollution.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target[key] || !isObject(target[key])) {
|
||||||
|
target[key] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
objectDeepMerge(target[key], source[key]);
|
||||||
|
} else {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the document is ready …
|
||||||
|
*/
|
||||||
|
$(document).ready(() => {
|
||||||
|
/**
|
||||||
|
* Prevent double form submits by adding a class to the form
|
||||||
|
* when it is submitted.
|
||||||
|
*
|
||||||
|
* This class can be used to show a visual indicator that the form is being
|
||||||
|
* submitted, such as a spinner.
|
||||||
|
*
|
||||||
|
* This is useful to prevent users from double-clicking the submit button
|
||||||
|
* and submitting the form multiple times.
|
||||||
|
*/
|
||||||
|
document.querySelectorAll('form').forEach((form) => {
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
// Prevent if already submitting
|
||||||
|
if (form.classList.contains('is-submitting')) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add class to hook our visual indicator on
|
||||||
|
form.classList.add('is-submitting');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
23
allianceauth/framework/templates/framework/svg/sprite.svg
Normal file
23
allianceauth/framework/templates/framework/svg/sprite.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<svg id="alliance-auth-svg-sprite" width="0" height="0" display="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<!-- Alliance Auth Logo -->
|
||||||
|
<symbol id="aa-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
|
||||||
|
<g transform="translate(41.953499,36.607802)">
|
||||||
|
<path style="display:inline;fill:#e14852;stroke-width:0.32" d="M 131.07236,159.67687 C 109.26615,147.02458 91.302022,136.55002 91.152067,136.40007 l -0.272649,-0.27265 23.786292,-13.82371 c 13.08247,-7.60304 23.9186,-13.82025 24.08029,-13.81602 l 0.294,0.008 15.93273,36.83413 c 8.763,20.25877 15.891,36.95054 15.84,37.09283 l -0.0927,0.25869 z" />
|
||||||
|
<path style="display:inline;fill:#436195;stroke-width:0.32" d="m 1.28,182.46369 c 0,-0.16969 17.354495,-40.46543 38.565546,-89.546103 L 78.411088,3.68 C 79.919052,1.4903841 82.294641,0.02199886 86.08,0.01224344 89.865359,0.00248802 92.288,1.4677954 93.674477,3.5158445 l 21.668143,50.1206965 21.66814,50.120699 -0.26538,0.23285 C 136.59942,104.11816 106.528,121.61441 69.92,142.87065 33.312,164.12688 2.892,181.80046 2.32,182.14527 l -1.04,0.62693 z" />
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
<!-- Loading Spinner -->
|
||||||
|
<symbol id="aa-loading-spinner" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="12" r="10" fill="none" stroke-width="1" stroke-linecap="round">
|
||||||
|
<animate attributeName="stroke-dasharray" dur="1.5s" calcMode="spline" values="0 150;42 150;42 150;42 150" keyTimes="0;0.475;0.95;1" keySplines="0.42,0,0.58,1;0.42,0,0.58,1;0.42,0,0.58,1" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="stroke-dashoffset" dur="1.5s" calcMode="spline" values="0;-16;-59;-59" keyTimes="0;0.475;0.95;1" keySplines="0.42,0,0.58,1;0.42,0,0.58,1;0.42,0,0.58,1" repeatCount="indefinite" />
|
||||||
|
</circle>
|
||||||
|
|
||||||
|
<animateTransform attributeName="transform" type="rotate" dur="2s" values="0 12 12;360 12 12" repeatCount="indefinite" />
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -70,8 +70,8 @@
|
|||||||
{% translate "Leave" %}
|
{% translate "Leave" %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="btn btn-primary" disabled>
|
<button type="button" class="btn btn-secondary cursor-help me-1" data-bs-tooltip="aa-tooltip" title="{% translate 'Request pending' %}">
|
||||||
{% translate "Pending" %}
|
<i class="fa-regular fa-hourglass-half"></i>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif not g.request %}
|
{% elif not g.request %}
|
||||||
@ -85,9 +85,13 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="btn btn-primary" disabled>
|
<button type="button" class="btn btn-secondary cursor-help me-1" data-bs-tooltip="aa-tooltip" title="{% translate 'Request pending' %}">
|
||||||
{% translate "Pending" %}
|
<i class="fa-regular fa-hourglass-half"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<a href="{% url 'groupmanagement:request_retract' g.group.id %}" class="btn btn-danger">
|
||||||
|
{% translate "Retract" %}
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -10,6 +10,11 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"group/request/leave/<int:group_id>/", views.group_request_leave, name="request_leave"
|
"group/request/leave/<int:group_id>/", views.group_request_leave, name="request_leave"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"group/request/retract/<int:group_id>/",
|
||||||
|
views.group_request_retract,
|
||||||
|
name="request_retract"
|
||||||
|
),
|
||||||
# group management
|
# group management
|
||||||
path("groupmanagement/requests/", views.group_management, name="management"),
|
path("groupmanagement/requests/", views.group_management, name="management"),
|
||||||
path("groupmanagement/membership/", views.group_membership, name="membership"),
|
path("groupmanagement/membership/", views.group_membership, name="membership"),
|
||||||
|
@ -420,3 +420,42 @@ def group_request_leave(request, group_id):
|
|||||||
grouprequest.notify_leaders()
|
grouprequest.notify_leaders()
|
||||||
messages.success(request, _('Applied to leave group %(group)s.') % {"group": group})
|
messages.success(request, _('Applied to leave group %(group)s.') % {"group": group})
|
||||||
return redirect("groupmanagement:groups")
|
return redirect("groupmanagement:groups")
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def group_request_retract(request, group_id):
|
||||||
|
logger.debug(
|
||||||
|
f"group_request_retract called by user {request.user} for group id {group_id}"
|
||||||
|
)
|
||||||
|
group = get_object_or_404(Group, id=group_id)
|
||||||
|
|
||||||
|
if not GroupManager.check_internal_group(group):
|
||||||
|
logger.warning(
|
||||||
|
f"User {request.user} attempted to retract group request for "
|
||||||
|
f"group id {group_id} but it is not a joinable group"
|
||||||
|
)
|
||||||
|
messages.warning(
|
||||||
|
request,
|
||||||
|
_("You cannot retract that request"),
|
||||||
|
)
|
||||||
|
return redirect('groupmanagement:groups')
|
||||||
|
|
||||||
|
try:
|
||||||
|
group_request = GroupRequest.objects.get(
|
||||||
|
user=request.user, group=group, leave_request=False
|
||||||
|
)
|
||||||
|
group_request.delete()
|
||||||
|
|
||||||
|
logger.info(f"Deleted group request for user {request.user} to group {group}")
|
||||||
|
messages.success(
|
||||||
|
request, _('Retracted application to group %(group)s.') % {"group": group}
|
||||||
|
)
|
||||||
|
except GroupRequest.DoesNotExist:
|
||||||
|
logger.info(
|
||||||
|
f"{request.user} attempted to retract group request for "
|
||||||
|
f"group id {group_id} but has no open request"
|
||||||
|
)
|
||||||
|
messages.warning(
|
||||||
|
request, _("You have no open request for that group.")
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect("groupmanagement:groups")
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
@ -1,56 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
version="1.1"
|
|
||||||
id="svg122"
|
|
||||||
width="256"
|
|
||||||
height="256"
|
|
||||||
viewBox="0 0 255.99999 256"
|
|
||||||
sodipodi:docname="allianceauth (2).svg"
|
|
||||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs126" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview124"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#eeeeee"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="3.8506576"
|
|
||||||
inkscape:cx="30.514269"
|
|
||||||
inkscape:cy="102.57988"
|
|
||||||
inkscape:window-width="2560"
|
|
||||||
inkscape:window-height="1369"
|
|
||||||
inkscape:window-x="1912"
|
|
||||||
inkscape:window-y="-8"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="g128" />
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
inkscape:label="Image"
|
|
||||||
id="g128">
|
|
||||||
<g
|
|
||||||
id="g2338"
|
|
||||||
transform="translate(41.953499,36.607802)">
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#e14852;stroke-width:0.32"
|
|
||||||
d="M 131.07236,159.67687 C 109.26615,147.02458 91.302022,136.55002 91.152067,136.40007 l -0.272649,-0.27265 23.786292,-13.82371 c 13.08247,-7.60304 23.9186,-13.82025 24.08029,-13.81602 l 0.294,0.008 15.93273,36.83413 c 8.763,20.25877 15.891,36.95054 15.84,37.09283 l -0.0927,0.25869 z"
|
|
||||||
id="path2342"
|
|
||||||
sodipodi:nodetypes="cscsccsscc" />
|
|
||||||
<path
|
|
||||||
style="display:inline;fill:#436195;stroke-width:0.32"
|
|
||||||
d="m 1.28,182.46369 c 0,-0.16969 17.354495,-40.46543 38.565546,-89.546103 L 78.411088,3.68 C 79.919052,1.4903841 82.294641,0.02199886 86.08,0.01224344 89.865359,0.00248802 92.288,1.4677954 93.674477,3.5158445 l 21.668143,50.1206965 21.66814,50.120699 -0.26538,0.23285 C 136.59942,104.11816 106.528,121.61441 69.92,142.87065 33.312,164.12688 2.892,181.80046 2.32,182.14527 l -1.04,0.62693 z"
|
|
||||||
id="path2340"
|
|
||||||
sodipodi:nodetypes="ssczcccssscs" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.2 KiB |
@ -1,4 +1,4 @@
|
|||||||
/* global notificationUpdateSettings */
|
/* global notificationUpdateSettings, fetchGet */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This script refreshed the notification icon in the top menu
|
* This script refreshed the notification icon in the top menu
|
||||||
@ -19,22 +19,9 @@ $(() => {
|
|||||||
* Update the notification icon in the top menu
|
* Update the notification icon in the top menu
|
||||||
*/
|
*/
|
||||||
const updateNotificationIcon = () => {
|
const updateNotificationIcon = () => {
|
||||||
fetch(userNotificationCountViewUrl)
|
fetchGet({url: userNotificationCountViewUrl})
|
||||||
.then((response) => {
|
.then((data) => {
|
||||||
if (response.ok) {
|
elementNotificationIcon.toggleClass('text-danger', data.unread_count > 0);
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Something went wrong');
|
|
||||||
})
|
|
||||||
.then((responseJson) => {
|
|
||||||
const unreadCount = responseJson.unread_count;
|
|
||||||
|
|
||||||
if (unreadCount > 0) {
|
|
||||||
elementNotificationIcon.addClass('text-danger');
|
|
||||||
} else {
|
|
||||||
elementNotificationIcon.removeClass('text-danger');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(`Failed to load HTMl to render notifications item. Error: ${error.message}`);
|
console.log(`Failed to load HTMl to render notifications item. Error: ${error.message}`);
|
||||||
|
@ -8,30 +8,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const elemCard = document.getElementById("esi-alert");
|
const elemCard = document.getElementById('esi-alert');
|
||||||
const elemMessage = document.getElementById("esi-data");
|
const elemMessage = document.getElementById('esi-data');
|
||||||
const elemCode = document.getElementById("esi-code");
|
const elemCode = document.getElementById('esi-code');
|
||||||
|
|
||||||
fetch('{% url "authentication:esi_check" %}')
|
fetchGet({url: '{% url "authentication:esi_check" %}'})
|
||||||
.then((response) => {
|
.then((data) => {
|
||||||
if (response.ok) {
|
console.log('ESI Check: ', JSON.stringify(data, null, 2));
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
throw new Error("Something went wrong");
|
|
||||||
})
|
|
||||||
.then((responseJson) => {
|
|
||||||
console.log("ESI Check: ", JSON.stringify(responseJson, null, 2));
|
|
||||||
|
|
||||||
const status = responseJson.status;
|
if (data.status !== 200) {
|
||||||
if (status !== 200) {
|
elemCode.textContent = data.status;
|
||||||
elemCode.textContent = status
|
elemMessage.textContent = data.data.error;
|
||||||
elemMessage.textContent = responseJson.data.error;
|
|
||||||
new bootstrap.Collapse(elemCard, {
|
new bootstrap.Collapse(elemCard, {toggle: true});
|
||||||
toggle: true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.error('Error fetching ESI check:', error);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,23 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% if debug %}
|
||||||
|
<div id="aa-dashboard-panel-debug" class="col-12 mb-3">
|
||||||
|
<div class="card text-bg-warning">
|
||||||
|
<div class="card-body">
|
||||||
|
{% translate "Debug mode" as widget_title %}
|
||||||
|
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p class="text-center">
|
||||||
|
{% translate "Debug mode is currently turned on!<br>Make sure to turn it off as soon as you are finished testing." %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if notifications %}
|
{% if notifications %}
|
||||||
<div id="aa-dashboard-panel-admin-notifications" class="col-12 mb-3">
|
<div id="aa-dashboard-panel-admin-notifications" class="col-12 mb-3">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -118,49 +135,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if debug %}
|
|
||||||
<div id="aa-dashboard-panel-debug" class="col-12 mb-3">
|
|
||||||
<div class="card text-bg-warning">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">{% translate "Debug mode" %}</h5>
|
|
||||||
<p class="card-text">
|
|
||||||
{% translate "Debug mode is currently turned on. Make sure to turn it off as soon as you are finished testing" %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const elemRunning = document.getElementById("task-counts");
|
const elemRunning = document.getElementById('task-counts');
|
||||||
const elemQueued = document.getElementById("queued-tasks-count");
|
const elemQueued = document.getElementById('queued-tasks-count');
|
||||||
|
|
||||||
fetch('{% url "authentication:task_counts" %}')
|
fetchGet({url: '{% url "authentication:task_counts" %}'})
|
||||||
.then((response) => {
|
.then((data) => {
|
||||||
if (response.ok) {
|
const running = data.tasks_running;
|
||||||
return response.json();
|
const queued = data.tasks_queued;
|
||||||
}
|
|
||||||
throw new Error("Something went wrong");
|
|
||||||
})
|
|
||||||
.then((responseJson) => {
|
|
||||||
const running = responseJson.tasks_running;
|
|
||||||
if (running == null) {
|
|
||||||
elemRunning.textContent = "N/A";
|
|
||||||
} else {
|
|
||||||
elemRunning.textContent = running.toLocaleString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const queued = responseJson.tasks_queued;
|
const updateTaskCount = (element, value) => {
|
||||||
if (queued == null) {
|
element.textContent = value == null ? 'N/A' : value.toLocaleString();
|
||||||
elemQueued.textContent = "N/A";
|
};
|
||||||
} else {
|
|
||||||
elemQueued.textContent = queued.toLocaleString();
|
updateTaskCount(elemRunning, running);
|
||||||
}
|
updateTaskCount(elemQueued, queued);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.error('Error fetching task queue:', error);
|
||||||
elemRunning.textContent = "ERROR";
|
|
||||||
elemQueued.textContent = "ERROR";
|
[elemRunning, elemQueued].forEach(elem => elem.textContent = 'ERROR');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
{% include 'bundles/fontawesome.html' %}
|
{% include 'bundles/fontawesome.html' %}
|
||||||
{% include 'bundles/auth-framework-css.html' %}
|
{% include 'bundles/auth-framework-css.html' %}
|
||||||
|
|
||||||
|
{% include 'bundles/jquery-js.html' %}
|
||||||
|
{% include 'bundles/auth-framework-js.html' %}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@media all {
|
@media all {
|
||||||
.nav-padding {
|
.nav-padding {
|
||||||
@ -137,8 +140,6 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% include 'bundles/jquery-js.html' %}
|
|
||||||
|
|
||||||
{% theme_js %}
|
{% theme_js %}
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
@ -159,5 +160,7 @@
|
|||||||
{% block extra_script %}
|
{% block extra_script %}
|
||||||
{% endblock extra_script %}
|
{% endblock extra_script %}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{% include "framework/svg/sprite.svg" %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
3
allianceauth/templates/bundles/auth-framework-js.html
Normal file
3
allianceauth/templates/bundles/auth-framework-js.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% load sri %}
|
||||||
|
|
||||||
|
{% sri_static 'allianceauth/framework/js/auth-framework.js' %}
|
@ -1,3 +1,3 @@
|
|||||||
{% load static %}
|
<svg class="svg-alliance-auth-logo" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_height }}{% else %}{{ logo_width|default:"128px" }}{% endif %}">
|
||||||
|
<use href="#aa-logo"></use>
|
||||||
<img src="{% static 'allianceauth/images/auth-logo.svg' %}" width="{{ logo_width|default:"128px" }}" height="{% if logo_height %}{{ logo_height }}{% else %}{{ logo_width|default:"128px" }}{% endif %}" alt="{{ SITE_NAME }}">
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 217 B |
@ -1,7 +1,7 @@
|
|||||||
PROTOCOL=https://
|
PROTOCOL=https://
|
||||||
AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN%
|
AUTH_SUBDOMAIN=%AUTH_SUBDOMAIN%
|
||||||
DOMAIN=%DOMAIN%
|
DOMAIN=%DOMAIN%
|
||||||
AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v4.8.0
|
AA_DOCKER_TAG=registry.gitlab.com/allianceauth/allianceauth/auth:v4.9.0
|
||||||
|
|
||||||
# Nginx Proxy Manager
|
# Nginx Proxy Manager
|
||||||
PROXY_HTTP_PORT=80
|
PROXY_HTTP_PORT=80
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
ARG AUTH_VERSION=v4.8.0
|
ARG AUTH_VERSION=v4.9.0
|
||||||
ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION}
|
ARG AUTH_PACKAGE=allianceauth==${AUTH_VERSION}
|
||||||
ENV AUTH_USER=allianceauth
|
ENV AUTH_USER=allianceauth
|
||||||
ENV AUTH_GROUP=allianceauth
|
ENV AUTH_GROUP=allianceauth
|
||||||
|
@ -44,8 +44,8 @@ x-allianceauth-health-check: &allianceauth-health-checks
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
auth_mysql:
|
auth_mysql:
|
||||||
image: mariadb:10.11
|
image: mariadb:11.8
|
||||||
command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --default-authentication-plugin=mysql_native_password]
|
command: [mariadbd, --default-authentication-plugin=mysql_native_password]
|
||||||
volumes:
|
volumes:
|
||||||
- ./mysql-data:/var/lib/mysql
|
- ./mysql-data:/var/lib/mysql
|
||||||
- ./setup.sql:/docker-entrypoint-initdb.d/setup.sql
|
- ./setup.sql:/docker-entrypoint-initdb.d/setup.sql
|
||||||
@ -77,7 +77,7 @@ services:
|
|||||||
max-file: "5"
|
max-file: "5"
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7
|
image: redis:8
|
||||||
command: redis-server
|
command: redis-server
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
@ -132,7 +132,7 @@ services:
|
|||||||
replicas: 2
|
replicas: 2
|
||||||
|
|
||||||
grafana:
|
grafana:
|
||||||
image: grafana/grafana-oss:9.5.2
|
image: grafana/grafana-oss:latest
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- auth_mysql
|
- auth_mysql
|
||||||
|
@ -13,5 +13,7 @@ The Alliance Auth framework is split into several submodules, each of which is d
|
|||||||
|
|
||||||
framework/api
|
framework/api
|
||||||
framework/css
|
framework/css
|
||||||
|
framework/js
|
||||||
framework/templates
|
framework/templates
|
||||||
|
framework/svg-sprite
|
||||||
:::
|
:::
|
||||||
|
124
docs/development/custom/framework/js.md
Normal file
124
docs/development/custom/framework/js.md
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
# JavaScript Framework
|
||||||
|
|
||||||
|
This contains some simple JavaScript functions that are used throughout Alliance Auth,
|
||||||
|
so they can be used by community app developers as well.
|
||||||
|
|
||||||
|
The JS file is already loaded in the base template, so it is globally available.
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
The following functions are available in the JavaScript framework:
|
||||||
|
|
||||||
|
### isArray()
|
||||||
|
|
||||||
|
Checks if the given value is an array.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* global isArray */
|
||||||
|
|
||||||
|
if (isArray(someVariable)) {
|
||||||
|
console.log('This is an array');
|
||||||
|
} else {
|
||||||
|
console.log('This is not an array');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### isObject()
|
||||||
|
|
||||||
|
Checks if the given value is an object.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* global isObject */
|
||||||
|
|
||||||
|
if (isObject(someVariable)) {
|
||||||
|
console.log('This is a plain object');
|
||||||
|
} else {
|
||||||
|
console.log('This is not a plain object');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### fetchGet()
|
||||||
|
|
||||||
|
Performs a GET request to the given URL and returns a Promise that resolves with the response data.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* global fetchGet */
|
||||||
|
|
||||||
|
fetchGet({
|
||||||
|
url: url,
|
||||||
|
responseIsJson: false
|
||||||
|
}).then((data) => {
|
||||||
|
// Process the fetched data
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(`Error: ${error.message}`);
|
||||||
|
|
||||||
|
// Handle the error appropriately
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### fetchGet() Parameters
|
||||||
|
|
||||||
|
- `url`: The URL to fetch data from.
|
||||||
|
- `payload`: Optional data to send with the request. Can be an object or a string.
|
||||||
|
- `responseIsJson`: Optional boolean indicating if the response should be parsed as JSON (default is `true`).
|
||||||
|
|
||||||
|
### fetchPost()
|
||||||
|
|
||||||
|
Performs a POST request to the given URL with the provided data and returns a Promise that resolves with the response data.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* global fetchPost */
|
||||||
|
|
||||||
|
fetchPost({
|
||||||
|
url: url,
|
||||||
|
csrfToken: csrfToken,
|
||||||
|
payload: {
|
||||||
|
key: 'value',
|
||||||
|
anotherKey: 'anotherValue'
|
||||||
|
},
|
||||||
|
responseIsJson: true
|
||||||
|
}).then((data) => {
|
||||||
|
// Process the fetched data
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(`Error: ${error.message}`);
|
||||||
|
|
||||||
|
// Handle the error appropriately
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### fetchPost() Parameters
|
||||||
|
|
||||||
|
- `url`: The URL to send the POST request to.
|
||||||
|
- `csrfToken`: The CSRF token to include in the request headers.
|
||||||
|
- `payload`: The data as JS object to send with the request.
|
||||||
|
- `responseIsJson`: Optional boolean indicating if the response should be parsed as JSON (default is `true`).
|
||||||
|
|
||||||
|
### objectDeepMerge()
|
||||||
|
|
||||||
|
Recursively merges properties from source objects into a target object. If a property at the current level is an object,
|
||||||
|
and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property.
|
||||||
|
|
||||||
|
This function does not modify the source objects and prevents prototype pollution by not allowing `__proto__`, `constructor`,
|
||||||
|
and `prototype` property names.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/* global objectDeepMerge */
|
||||||
|
|
||||||
|
const target = {a: 1, b: {c: 2}};
|
||||||
|
const source1 = {b: {d: 3}, e: 4 };
|
||||||
|
const source2 = {a: 5, b: {c: 6}};
|
||||||
|
|
||||||
|
const merged = objectDeepMerge(target, source1, source2);
|
||||||
|
|
||||||
|
console.log(merged); // {a: 5, b: {c: 6, d: 3}, e: 4}
|
||||||
|
```
|
50
docs/development/custom/framework/svg-sprite.md
Normal file
50
docs/development/custom/framework/svg-sprite.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# SVG Sprite
|
||||||
|
|
||||||
|
An SVG sprite is a collection of SVG images combined into a single SVG file. This allows for efficient loading and management of icons in web applications.
|
||||||
|
|
||||||
|
The Alliance Auth framework provides a built-in SVG sprite that contains a selection of icons used in Alliance Auth. This sprite is automatically included in the base template, so you don't need to do anything special to use it.
|
||||||
|
|
||||||
|
## Using the SVG Sprite
|
||||||
|
|
||||||
|
To use an icon from the SVG sprite, you can use the following HTML syntax:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg>
|
||||||
|
<use href="#[icon-name]"></use>
|
||||||
|
</svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `[icon-name]` with the name of the icon you want to use. For example, to use the Alliance Auth logo, you would write:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg>
|
||||||
|
<use href="#aa-logo"></use>
|
||||||
|
</svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Icons
|
||||||
|
|
||||||
|
The following icons are available in the Alliance Auth SVG sprite:
|
||||||
|
|
||||||
|
- `aa-logo`: The Alliance Auth logo
|
||||||
|
- `aa-loading-spinner`: A loading spinner icon
|
||||||
|
|
||||||
|
### Alliance Auth Logo
|
||||||
|
|
||||||
|
The Alliance Auth logo can be used with the following code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg>
|
||||||
|
<use href="#aa-logo"></use>
|
||||||
|
</svg>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loading Spinner
|
||||||
|
|
||||||
|
The loading spinner can be used with the following code:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<svg>
|
||||||
|
<use href="#aa-loading-spinner"></use>
|
||||||
|
</svg>
|
||||||
|
```
|
@ -14,9 +14,8 @@ Alliance Auth can be installed on any in-support *nix operating system.
|
|||||||
|
|
||||||
Our install documentation targets the following operating systems.
|
Our install documentation targets the following operating systems.
|
||||||
|
|
||||||
- Ubuntu 20.04 - Not Recommended for new installs
|
|
||||||
- Ubuntu 22.04
|
- Ubuntu 22.04
|
||||||
- Centos 7
|
- Ubuntu 24.04
|
||||||
- CentOS Stream 8
|
- CentOS Stream 8
|
||||||
- CentOS Stream 9
|
- CentOS Stream 9
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ To install on your favorite flavour of Linux, identify and install equivalent pa
|
|||||||
It is recommended to ensure your OS is fully up-to-date before proceeding. We may also add Package Repositories here, used later in the documentation.
|
It is recommended to ensure your OS is fully up-to-date before proceeding. We may also add Package Repositories here, used later in the documentation.
|
||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@ -35,14 +34,6 @@ sudo apt-get upgrade
|
|||||||
sudo do-dist-upgrade
|
sudo do-dist-upgrade
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
|
|
||||||
```shell
|
|
||||||
yum install epel-release
|
|
||||||
sudo yum upgrade
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
|
|
||||||
@ -70,7 +61,7 @@ Install Python 3.11 and related tools on your system.
|
|||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
|
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo add-apt-repository ppa:deadsnakes/ppa
|
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||||
@ -78,20 +69,6 @@ sudo apt-get update
|
|||||||
sudo apt-get install python3.11 python3.11-dev python3.11-venv
|
sudo apt-get install python3.11 python3.11-dev python3.11-venv
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
We need to build Python from source
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~
|
|
||||||
sudo yum install gcc openssl-devel bzip2-devel libffi-devel wget
|
|
||||||
wget https://www.python.org/ftp/python/3.11.7/Python-3.11.7.tgz
|
|
||||||
tar xvf Python-3.11.7.tgz
|
|
||||||
cd Python-3.11.7/
|
|
||||||
./configure --enable-optimizations --enable-shared
|
|
||||||
sudo make altinstall
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
We need to build Python from source
|
We need to build Python from source
|
||||||
@ -125,27 +102,19 @@ sudo make altinstall
|
|||||||
|
|
||||||
### Database
|
### Database
|
||||||
|
|
||||||
It's recommended to use a database service instead of SQLite. Many options are available, but this guide will use MariaDB 10.11
|
It's recommended to use a database service instead of SQLite. Many options are available, but this guide will use MariaDB 11.8
|
||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=20.04+%22focal%22&v=10.11&r_m=osuosl> to add the MariaDB repository to your host.
|
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=24.04+"noble"&v=11.8> to add the MariaDB repository to your host.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get install mariadb-server mariadb-client libmysqlclient-dev
|
sudo apt-get install mariadb-server mariadb-client libmysqlclient-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=CentOS+7&v=10.11&r_m=osuosl> to add the MariaDB repository to your host.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo yum install MariaDB-server MariaDB-client MariaDB-devel MariaDB-shared
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=CentOS+Stream&v=10.11&r_m=osuosl> to add the MariaDB repository to your host.
|
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=CentOS+Stream&v=11.8> to add the MariaDB repository to your host.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo dnf install mariadb mariadb-server mariadb-devel
|
sudo dnf install mariadb mariadb-server mariadb-devel
|
||||||
@ -153,7 +122,7 @@ sudo dnf install mariadb mariadb-server mariadb-devel
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 9
|
:::{group-tab} CentOS Stream 9
|
||||||
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=CentOS+Stream&v=10.11&r_m=osuosl> to add the MariaDB repository to your host.
|
Follow the instructions at <https://mariadb.org/download/?t=repo-config&d=CentOS+Stream&v=11.8> to add the MariaDB repository to your host.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo dnf install mariadb mariadb-server mariadb-devel
|
sudo dnf install mariadb mariadb-server mariadb-devel
|
||||||
@ -164,12 +133,9 @@ sudo dnf install mariadb mariadb-server mariadb-devel
|
|||||||
|
|
||||||
:::::{important}
|
:::::{important}
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
If you don't plan on running the database on the same server as auth you still need to install the `libmysqlclient-dev` package
|
If you don't plan on running the database on the same server as auth you still need to install the `libmysqlclient-dev` package
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
If you don't plan on running the database on the same server as auth you still need to install the `mariadb-devel` package
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
If you don't plan on running the database on the same server as auth you still need to install the `mariadb-devel` package
|
If you don't plan on running the database on the same server as auth you still need to install the `mariadb-devel` package
|
||||||
:::
|
:::
|
||||||
@ -185,7 +151,7 @@ A few extra utilities are also required for the installation of packages.
|
|||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
|
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
|
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
|
||||||
@ -195,18 +161,6 @@ sudo apt-get update
|
|||||||
sudo apt-get install unzip git redis-server curl libssl-dev libbz2-dev libffi-dev build-essential pkg-config
|
sudo apt-get install unzip git redis-server curl libssl-dev libbz2-dev libffi-dev build-essential pkg-config
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo yum install gcc gcc-c++ unzip git redis curl bzip2-devel openssl-devel libffi-devel wget pkg-config
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo systemctl enable redis.service
|
|
||||||
sudo systemctl start redis.service
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
|
|
||||||
@ -282,19 +236,12 @@ mysql_secure_installation
|
|||||||
For security and permissions, it's highly recommended you create a separate user to install auth under. Do not log in as this account.
|
For security and permissions, it's highly recommended you create a separate user to install auth under. Do not log in as this account.
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
|
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo adduser --disabled-login allianceserver --shell /bin/bash
|
sudo adduser --disabled-login allianceserver --shell /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo passwd -l allianceserver
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
|
|
||||||
@ -497,27 +444,12 @@ exit
|
|||||||
|
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
|
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo apt-get install supervisor
|
sudo apt-get install supervisor
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo dnf install supervisor
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo systemctl enable supervisord.service
|
|
||||||
```
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo systemctl start supervisord.service
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
|
|
||||||
@ -554,19 +486,12 @@ sudo systemctl start supervisord.service
|
|||||||
Once installed, it needs a configuration file to know which processes to watch. Your Alliance Auth project comes with a ready-to-use template which will ensure the Celery workers, Celery task scheduler and Gunicorn are all running.
|
Once installed, it needs a configuration file to know which processes to watch. Your Alliance Auth project comes with a ready-to-use template which will ensure the Celery workers, Celery task scheduler and Gunicorn are all running.
|
||||||
::::{tabs}
|
::::{tabs}
|
||||||
|
|
||||||
:::{group-tab} Ubuntu 2004, 2204, 2404
|
:::{group-tab} Ubuntu 2204, 2404
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
ln -s /home/allianceserver/myauth/supervisor.conf /etc/supervisor/conf.d/myauth.conf
|
ln -s /home/allianceserver/myauth/supervisor.conf /etc/supervisor/conf.d/myauth.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
|
||||||
:::{group-tab} CentOS 7
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo ln -s /home/allianceserver/myauth/supervisor.conf /etc/supervisord.d/myauth.ini
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::{group-tab} CentOS Stream 8
|
:::{group-tab} CentOS Stream 8
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user