mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 04:55:42 +09:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a39410623e | ||
|
|
a4802c6004 | ||
|
|
812919759a |
No files matched your search
@@ -215,7 +215,7 @@ x-fluxer-app-healthcheck: &fluxer-app-healthcheck
|
||||
x-fluxer-svc-healthcheck: &fluxer-svc-healthcheck
|
||||
<<: *fluxer-app-healthcheck
|
||||
start_period: ${FLUXER_SVC_HEALTHCHECK_START_PERIOD:-60s}
|
||||
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/8090 && printf 'GET /_health HTTP/1.0\\r\\n\\r\\n' >&3 && head -n 1 <&3 | grep -q ' 200 '"]
|
||||
test: ["CMD-SHELL", "bash -c 'exec 3<>/dev/tcp/127.0.0.1/8090 && printf \"GET /_health HTTP/1.0\\r\\n\\r\\n\" >&3 && head -n 1 <&3 | grep -q \" 200 \"'"]
|
||||
|
||||
services:
|
||||
edge:
|
||||
@@ -469,7 +469,7 @@ services:
|
||||
<<: *fluxer-env
|
||||
FLUXER_API_PORT: "8080"
|
||||
FLUXER_SETUP_TOKEN: ${FLUXER_SETUP_TOKEN:-}
|
||||
NODE_OPTIONS: --enable-source-maps${FLUXER_API_NODE_HEAP_MB:+ --max-old-space-size=$FLUXER_API_NODE_HEAP_MB}${FLUXER_API_NODE_OPTIONS:+ $FLUXER_API_NODE_OPTIONS}
|
||||
NODE_OPTIONS: --enable-source-maps${FLUXER_API_NODE_HEAP_MB:+ --max-old-space-size=${FLUXER_API_NODE_HEAP_MB:-}}${FLUXER_API_NODE_OPTIONS:+ ${FLUXER_API_NODE_OPTIONS:-}}
|
||||
NODE_EXTRA_CA_CERTS: ${FLUXER_NODE_EXTRA_CA_CERTS:-/etc/ssl/certs/ca-certificates.crt}
|
||||
FLUXER_POSTGRES_MAX_CONNECTIONS: "${FLUXER_API_POSTGRES_MAX_CONNECTIONS:-25}"
|
||||
FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED: "${FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED:-true}"
|
||||
@@ -503,13 +503,13 @@ services:
|
||||
command: ["node", "dist/WorkerEntrypoint.js"]
|
||||
environment:
|
||||
<<: *fluxer-env
|
||||
NODE_OPTIONS: --enable-source-maps${FLUXER_WORKER_NODE_HEAP_MB:+ --max-old-space-size=$FLUXER_WORKER_NODE_HEAP_MB}${FLUXER_WORKER_NODE_OPTIONS:+ $FLUXER_WORKER_NODE_OPTIONS}
|
||||
NODE_OPTIONS: --enable-source-maps${FLUXER_WORKER_NODE_HEAP_MB:+ --max-old-space-size=${FLUXER_WORKER_NODE_HEAP_MB:-}}${FLUXER_WORKER_NODE_OPTIONS:+ ${FLUXER_WORKER_NODE_OPTIONS:-}}
|
||||
NODE_EXTRA_CA_CERTS: ${FLUXER_NODE_EXTRA_CA_CERTS:-/etc/ssl/certs/ca-certificates.crt}
|
||||
FLUXER_API_WORKER_MODE: all_lanes
|
||||
FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER: "true"
|
||||
FLUXER_POSTGRES_MAX_CONNECTIONS: "${FLUXER_WORKER_POSTGRES_MAX_CONNECTIONS:-25}"
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "const age=Date.now()-require('node:fs').statSync('/tmp/fluxer-worker-heartbeat').mtimeMs;if(age>30000){console.error('worker heartbeat is '+Math.round(age)+'ms old');process.exit(1)}"]
|
||||
test: ["CMD-SHELL", "node -e \"const age=Date.now()-require('node:fs').statSync('/tmp/fluxer-worker-heartbeat').mtimeMs;if(age>30000){console.error('worker heartbeat is '+Math.round(age)+'ms old');process.exit(1)}\""]
|
||||
<<: *fluxer-app-healthcheck
|
||||
retries: ${FLUXER_WORKER_HEALTHCHECK_RETRIES:-3}
|
||||
depends_on:
|
||||
@@ -867,7 +867,7 @@ services:
|
||||
FLUXER_API_ENDPOINT: http://api:8080
|
||||
FLUXER_STATIC_CDN_ENDPOINT: ${FLUXER_STATIC_CDN_ENDPOINT:-${FLUXER_PUBLIC_ORIGIN:-${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}}}
|
||||
healthcheck:
|
||||
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/8080 && printf 'GET /_health HTTP/1.0\\r\\n\\r\\n' >&3 && head -n 1 <&3 | grep -q ' 200 '"]
|
||||
test: ["CMD-SHELL", "bash -c 'exec 3<>/dev/tcp/127.0.0.1/8080 && printf \"GET /_health HTTP/1.0\\r\\n\\r\\n\" >&3 && head -n 1 <&3 | grep -q \" 200 \"'"]
|
||||
<<: *fluxer-app-healthcheck
|
||||
start_period: ${FLUXER_SVC_HEALTHCHECK_START_PERIOD:-60s}
|
||||
depends_on:
|
||||
|
||||
@@ -571,6 +571,12 @@ pub const HTMX_FLASH_SCRIPT: &str = r#"
|
||||
showToast(failed ? 'error' : 'success', isLoginResponse(xhr) ? 'Session expired. Sign in again.' : failed ? 'Action failed.' : 'Done.');
|
||||
markToastHandled(event.detail);
|
||||
}, true);
|
||||
|
||||
document.addEventListener('htmx:sendError', function (event) {
|
||||
if (requestVerb(event.detail) !== 'get') return;
|
||||
var path = event.detail.pathInfo && event.detail.pathInfo.finalRequestPath;
|
||||
if (path) window.location.assign(path);
|
||||
}, true);
|
||||
})();
|
||||
"#;
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ const ACCEPTED_TABLE_FINDINGS = new Map<string, Readonly<Partial<Record<TableRul
|
||||
['http-api/guild-members.mdx', {'table-identifier': 2}],
|
||||
['http-api/guild-moderation.mdx', {'table-cell': 1}],
|
||||
['http-api/guild-stickers.mdx', {'table-cell': 3}],
|
||||
['http-api/guilds.mdx', {'table-fit': 1, 'table-identifier': 4}],
|
||||
['http-api/guilds.mdx', {'table-fit': 1, 'table-identifier': 5}],
|
||||
['http-api/instance.mdx', {'table-identifier': 8}],
|
||||
['http-api/invites.mdx', {'table-cell': 6}],
|
||||
['http-api/messages.mdx', {'table-fit': 2, 'table-cell': 20, 'table-identifier': 2}],
|
||||
|
||||
@@ -246,26 +246,74 @@ A resolved everyone mask that has `SEND_MESSAGES` and no [thread permission bit]
|
||||
| id<sup>1</sup> | decimal string \| integer | Template-local channel ID |
|
||||
| type<sup>2</sup> | integer | Source channel type |
|
||||
| name? | ?string | Channel name, defaulting to the empty string |
|
||||
| topic? | ?string | Channel topic |
|
||||
| topic?<sup>6</sup> | ?string | Channel topic, or forum guidelines on a forum or media channel |
|
||||
| position | integer | The sort position of the channel |
|
||||
| parent_id?<sup>3</sup> | ?decimal string \| integer | Template-local parent category ID |
|
||||
| bitrate?<sup>4</sup> | ?integer | Voice bitrate, defaulting to 64000 for a voice channel and capped at 96000 |
|
||||
| user_limit?<sup>4</sup> | ?integer | Voice occupancy limit, defaulting to 0 for a voice channel |
|
||||
| voice_connection_limit?<sup>4</sup> | ?integer | Per-user voice connection limit, defaulting to 5 for a voice channel |
|
||||
| nsfw? | boolean | Whether the channel is age restricted (default false) |
|
||||
| rate_limit_per_user? | integer | Slowmode interval in seconds, defaulting to 0 |
|
||||
| permission_overwrites?<sup>5</sup> | array[[guild template role permission overwrite](#guild-template-role-permission-overwrite-object) object] | Role permission overwrites |
|
||||
| nsfw? | ?boolean | Whether the channel is age restricted (default false) |
|
||||
| rate_limit_per_user? | ?integer | Slowmode interval in seconds, defaulting to 0 |
|
||||
| permission_overwrites?<sup>5</sup> | ?array[[guild template role permission overwrite](#guild-template-role-permission-overwrite-object) object] | Role permission overwrites |
|
||||
| available_tags?<sup>7</sup> | ?array[[guild template forum tag](#guild-template-forum-tag-object) object] | Tags for a forum or media channel |
|
||||
| default_reaction_emoji?<sup>8</sup> | ?[guild template default reaction](#guild-template-default-reaction-object) object | Default reaction shown on posts in a forum or media channel |
|
||||
| default_sort_order?<sup>9</sup> | ?integer | Default [forum sort order](/http-api/forums/#forum-sort-orders) of a forum or media channel |
|
||||
| default_forum_layout?<sup>9</sup> | ?integer | Default [forum layout](/http-api/forums/#forum-layouts) of a forum channel |
|
||||
| default_tag_setting?<sup>9</sup> | ?string | Default [forum tag setting](/http-api/forums/#forum-tag-settings) of a forum or media channel |
|
||||
| default_thread_rate_limit_per_user?<sup>10</sup> | ?integer | Slowmode interval in seconds copied onto new threads |
|
||||
| default_auto_archive_duration?<sup>9</sup> | ?integer | Default [auto archive duration](/http-api/threads/#auto-archive-durations) in minutes for new threads |
|
||||
| flags?<sup>11</sup> | ?integer | [Channel flags](/http-api/forums/#channel-flags) of a forum or media channel |
|
||||
|
||||
<sup>1</sup> A duplicate identifier in the same template rejects creation with 400 `GUILD_TEMPLATE_INVALID`
|
||||
|
||||
<sup>2</sup> The value 0 creates a text channel, 2 a voice channel, 4 a category, and 5 an announcement channel. The value 13, the stage channel type of another platform, creates a voice channel. Fluxer skips every other value, a forum channel (15) and a media channel (16) included, and creates no channel for it
|
||||
<sup>2</sup> The value 0 creates a text channel, 2 a voice channel, 4 a category, 5 an announcement channel, 15 a forum channel, and 16 a media channel. The value 13, the stage channel type of another platform, creates a voice channel. Fluxer skips every other value and creates no channel for it
|
||||
|
||||
<sup>3</sup> The identifier is applied only when it resolves to a category in the same template, and every other value leaves the channel at the guild root
|
||||
<sup>3</sup> The identifier is applied only when it resolves to a category in the same template and the channel is not itself a category, and every other value leaves the channel at the guild root
|
||||
|
||||
<sup>4</sup> These voice fields are stored only on a voice channel and are null on every other channel type
|
||||
|
||||
<sup>5</sup> An entry is applied only when its resolved `type` is 0 and its `id` resolves to a role in the same template, and the identifier `0` resolves to the everyone role. Fluxer skips every other entry, and a channel whose entries are all skipped is created with no overwrites
|
||||
|
||||
<sup>6</sup> A topic longer than 4096 characters on a forum or media channel, or 1024 characters on any other channel, is shortened to that limit
|
||||
|
||||
<sup>7</sup> Applied only to a forum or media channel. Each tag gets a new ID, its name is trimmed and shortened to 50 characters, a tag with an empty or repeated name is skipped, and Fluxer keeps at most 20 tags
|
||||
|
||||
<sup>8</sup> Applied only to a forum or media channel, and only when `emoji_name` is a single unicode emoji
|
||||
|
||||
<sup>9</sup> An unknown value is ignored and the channel keeps the default. `default_sort_order` and `default_tag_setting` apply only to a forum or media channel, `default_forum_layout` only to a forum channel, and `default_auto_archive_duration` to a text, announcement, forum or media channel
|
||||
|
||||
<sup>10</sup> Applied to a text, announcement, forum or media channel, capped at 21600, and a value of 0 or less is ignored
|
||||
|
||||
<sup>11</sup> Only the flags a forum or media channel can carry are kept, and `REQUIRE_TAG` is dropped when no imported tag can be applied by members without moderator permissions
|
||||
|
||||
### Guild template forum tag object
|
||||
|
||||
#### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| name | string | Tag name |
|
||||
| moderated? | ?boolean | Whether only moderators can apply the tag |
|
||||
| emoji_id?<sup>1</sup> | ?string \| integer | Custom emoji of the tag |
|
||||
| emoji_name?<sup>2</sup> | ?string | Unicode emoji of the tag |
|
||||
|
||||
<sup>1</sup> Ignored on import, because the custom emoji does not exist in the new guild
|
||||
|
||||
<sup>2</sup> Kept only when the value is a single unicode emoji
|
||||
|
||||
### Guild template default reaction object
|
||||
|
||||
#### Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| emoji_id?<sup>1</sup> | ?string \| integer | Custom emoji of the reaction |
|
||||
| emoji_name?<sup>2</sup> | ?string | Unicode emoji of the reaction |
|
||||
|
||||
<sup>1</sup> Ignored on import, because the custom emoji does not exist in the new guild
|
||||
|
||||
<sup>2</sup> Kept only when the value is a single unicode emoji
|
||||
|
||||
### Guild template role permission overwrite object
|
||||
|
||||
#### Structure
|
||||
|
||||
@@ -1454,6 +1454,17 @@ function Get-FluxerFreeKb([string]$Path) {
|
||||
return [long]($drive.AvailableFreeSpace / 1024)
|
||||
}
|
||||
|
||||
function Start-FluxerStopped([string[]]$Ids) {
|
||||
if ($Ids.Count -eq 0) {
|
||||
return 0
|
||||
}
|
||||
$result = Invoke-FluxerCapture (@('start') + $Ids)
|
||||
if ($result.Code -ne 0 -and $result.Error.Length -gt 0) {
|
||||
Write-FluxerProblem $result.Error
|
||||
}
|
||||
return $result.Code
|
||||
}
|
||||
|
||||
# Step 2 continued: the volume copy.
|
||||
#
|
||||
# A live copy can catch a file mid-write, so the stack stops for it. The stack comes back up on
|
||||
@@ -1461,9 +1472,10 @@ function Get-FluxerFreeKb([string]$Path) {
|
||||
# working instance rather than a stopped one.
|
||||
#
|
||||
# By hand:
|
||||
# docker compose ps -q > running
|
||||
# docker compose stop
|
||||
# docker run --rm -v fluxer_seaweedfs-data:/data -v "${PWD}\backups:/backup" alpine tar czf /backup/seaweedfs-data.tgz -C /data .
|
||||
# docker compose up -d
|
||||
# docker start (Get-Content running)
|
||||
function Copy-FluxerVolumes([string]$Record, [string]$Project, [string]$TargetDir) {
|
||||
if (-not (Test-FluxerStackDefinesService 'seaweedfs' $TargetDir)) {
|
||||
Write-FluxerLine 'Skipping the uploads copy. This stack defines no seaweedfs service, so its objects are outside the stack and only the operator of that store can copy them.'
|
||||
@@ -1497,6 +1509,11 @@ function Copy-FluxerVolumes([string]$Record, [string]$Project, [string]$TargetDi
|
||||
$tarExtension = 'tar'
|
||||
}
|
||||
Write-FluxerLine 'Stopping the stack for a consistent copy of the uploads.'
|
||||
$running = Invoke-FluxerCapture @('compose', 'ps', '-q')
|
||||
if ($running.Code -ne 0) {
|
||||
Stop-Fluxer "docker compose ps failed, so the containers to start again after the copy cannot be read. Compose printed:`n$(Get-FluxerIndented $running.Error ' ')" $FluxerExitBackup
|
||||
}
|
||||
$stopped = @($running.Text.Split("`n") | ForEach-Object {$_.Trim()} | Where-Object {$_.Length -gt 0})
|
||||
if ((Invoke-FluxerDocker @('compose', 'stop')) -ne 0) {
|
||||
Stop-Fluxer 'docker compose stop failed.' $FluxerExitBackup
|
||||
}
|
||||
@@ -1505,13 +1522,13 @@ function Copy-FluxerVolumes([string]$Record, [string]$Project, [string]$TargetDi
|
||||
Write-FluxerLine "Copying $full."
|
||||
$code = Invoke-FluxerDocker @('run', '--rm', '-v', "${full}:/data:ro", '-v', "${Record}:/backup", $FluxerHelperImage, 'tar', $tarFlags, "/backup/$volume.$tarExtension", '-C', '/data', '.')
|
||||
if ($code -ne 0) {
|
||||
[void](Invoke-FluxerDocker @('compose', 'up', '-d', '--remove-orphans'))
|
||||
[void](Start-FluxerStopped $stopped)
|
||||
Stop-Fluxer "Copying $full failed. The stack is started again on the images it was running." $FluxerExitBackup
|
||||
}
|
||||
}
|
||||
Write-FluxerLine 'Starting the stack again before the upgrade continues.'
|
||||
if ((Invoke-FluxerDocker @('compose', 'up', '-d', '--remove-orphans')) -ne 0) {
|
||||
Stop-Fluxer 'docker compose up -d failed after the copy. Read docker compose logs.' $FluxerExitBackup
|
||||
if ((Start-FluxerStopped $stopped) -ne 0) {
|
||||
Stop-Fluxer 'docker start failed after the copy. Read docker compose logs.' $FluxerExitBackup
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1686,6 +1686,11 @@ fluxer_free_kb() {
|
||||
df -Pk "$1" 2>/dev/null | awk 'NR==2 {print $4}'
|
||||
}
|
||||
|
||||
fluxer_start_stopped() {
|
||||
[ -s "$fluxer_scratch/stopped" ] || return 0
|
||||
xargs $fluxer_engine start < "$fluxer_scratch/stopped" > /dev/null
|
||||
}
|
||||
|
||||
# Step 2 continued: the volume copy.
|
||||
#
|
||||
# A live copy can catch a file mid-write, so the stack stops for it. The stack
|
||||
@@ -1693,9 +1698,10 @@ fluxer_free_kb() {
|
||||
# happens, so a failure here leaves a working instance rather than a stopped one.
|
||||
#
|
||||
# By hand:
|
||||
# docker compose ps -q > running
|
||||
# docker compose stop
|
||||
# docker run --rm -v fluxer_seaweedfs-data:/data -v "$PWD/backups:/backup" alpine tar czf /backup/seaweedfs-data.tgz -C /data .
|
||||
# docker compose up -d
|
||||
# xargs docker start < running
|
||||
fluxer_copy_volumes() {
|
||||
if ! fluxer_stack_defines_service seaweedfs; then
|
||||
fluxer_say 'Skipping the uploads copy. This stack defines no seaweedfs service, so its objects are outside the stack and only the operator of that store can copy them.'
|
||||
@@ -1738,6 +1744,9 @@ $(fluxer_volume_error ' ')" ;;
|
||||
fluxer_tar_ext='tgz'
|
||||
fi
|
||||
fluxer_say 'Stopping the stack for a consistent copy of the uploads.'
|
||||
if ! $fluxer_engine compose ps -q > "$fluxer_scratch/stopped"; then
|
||||
fluxer_fail 7 "$fluxer_engine compose ps failed in $opt_dir, so the containers to start again after the copy cannot be read."
|
||||
fi
|
||||
if ! $fluxer_engine compose stop; then
|
||||
fluxer_fail 7 "$fluxer_engine compose stop failed in $opt_dir."
|
||||
fi
|
||||
@@ -1746,13 +1755,13 @@ $(fluxer_volume_error ' ')" ;;
|
||||
fluxer_full="${fluxer_project}_${fluxer_volume}"
|
||||
fluxer_say "Copying $fluxer_full."
|
||||
if ! $fluxer_engine run --rm -v "$fluxer_full:/data:ro" -v "$fluxer_record:/backup" "$FLUXER_HELPER_IMAGE" tar "$fluxer_tar_flags" "/backup/$fluxer_volume.$fluxer_tar_ext" -C /data .; then
|
||||
$fluxer_engine compose up -d --remove-orphans || true
|
||||
fluxer_start_stopped || true
|
||||
fluxer_fail 7 "Copying $fluxer_full failed. The stack is started again on the images it was running."
|
||||
fi
|
||||
done < "$fluxer_scratch/copy-volumes"
|
||||
fluxer_say 'Starting the stack again before the upgrade continues.'
|
||||
if ! $fluxer_engine compose up -d --remove-orphans; then
|
||||
fluxer_fail 7 "$fluxer_engine compose up -d failed in $opt_dir after the copy. Read $fluxer_engine compose logs there."
|
||||
if ! fluxer_start_stopped; then
|
||||
fluxer_fail 7 "$fluxer_engine start failed in $opt_dir after the copy. Read $fluxer_engine compose logs there."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user