mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-10-30 14:07:03 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1523b625bc | ||
|
|
fb91eeb692 | ||
|
|
601d2e02cb | ||
|
|
0a662d34eb | ||
|
|
5cd4693e9d | ||
|
|
f3f0f860e3 | ||
|
|
93a5cf8a79 | ||
|
|
7cf15cbc21 | ||
|
|
30bc6d20b2 | ||
|
|
b39f99fde4 | ||
|
|
0e8aa9efa4 | ||
|
|
e1fcde36e3 | ||
|
|
7aafe077d3 | ||
|
|
5b8cab5e76 | ||
|
|
4ab56af40a | ||
|
|
e8cea86a76 | ||
|
|
d0a6e0b358 | ||
|
|
8831b3e970 | ||
|
|
f6db6f0914 | ||
|
|
ab8baeedd1 | ||
|
|
eccee5e72e | ||
|
|
4d93055bda | ||
|
|
c60c16e56a | ||
|
|
99b1de5647 | ||
|
|
7efe11a421 | ||
|
|
954108856a | ||
|
|
cbca745ec4 | ||
|
|
e15e7c8f8d | ||
|
|
65e8a520e5 | ||
|
|
3926af5a6d | ||
|
|
556fb33223 | ||
|
|
82295adeab | ||
|
|
efabf060c7 | ||
|
|
96e434ebf5 | ||
|
|
d81e2567cc | ||
|
|
9d7d4fad2e |
64
.github/workflows/build.yml
vendored
64
.github/workflows/build.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- "releases/*"
|
||||
|
||||
env:
|
||||
MIX_ENV: prod
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -53,6 +53,7 @@ jobs:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ssh-key: "${{ secrets.COMMIT_KEY }}"
|
||||
fetch-depth: 0
|
||||
- name: 😅 Cache deps
|
||||
id: cache-deps
|
||||
@@ -97,7 +98,7 @@ jobs:
|
||||
mix git_ops.release --force-patch --yes
|
||||
git push --follow-tags
|
||||
echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Set commit hash for develop
|
||||
id: set-commit-develop
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
@@ -106,11 +107,9 @@ jobs:
|
||||
|
||||
docker:
|
||||
name: 🛠 Build Docker Images
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
needs: build
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
release-tag: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
release-notes: ${{ steps.get-content.outputs.string }}
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
@@ -137,19 +136,6 @@ jobs:
|
||||
ref: ${{ needs.build.outputs.commit_hash }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare Changelog
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
yes | cp -rf CHANGELOG.md priv/changelog/CHANGELOG.md
|
||||
sed -i '1i%{title: "Change Log"}\n\n---\n' priv/changelog/CHANGELOG.md
|
||||
|
||||
- name: Get Release Tag
|
||||
id: get-latest-tag
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: 1.0.0
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -198,26 +184,6 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- uses: markpatterson27/markdown-to-output@v1
|
||||
id: extract-changelog
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
filepath: CHANGELOG.md
|
||||
|
||||
- name: Get content
|
||||
uses: 2428392/gh-truncate-string-action@v1.3.0
|
||||
id: get-content
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
stringToTruncate: |
|
||||
📣 Wanderer new release available 🎉
|
||||
|
||||
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
|
||||
${{ steps.extract-changelog.outputs.body }}
|
||||
maxLength: 500
|
||||
truncationSymbol: "…"
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
@@ -248,9 +214,6 @@ jobs:
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}},enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=semver,pattern={{major}}.{{minor}},enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=semver,pattern={{version}},value=${{ needs.docker.outputs.release-tag }},enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=raw,value=develop,enable=${{ github.ref == 'refs/heads/develop' }}
|
||||
type=raw,value=develop-{{sha}},enable=${{ github.ref == 'refs/heads/develop' }}
|
||||
|
||||
@@ -267,19 +230,25 @@ jobs:
|
||||
create-release:
|
||||
name: 🏷 Create Release
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [docker, merge]
|
||||
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
|
||||
needs: build
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get Release Tag
|
||||
id: get-latest-tag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: 1.0.0
|
||||
|
||||
- name: 🏷 Create Draft Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ needs.docker.outputs.release-tag }}
|
||||
name: Release ${{ needs.docker.outputs.release-tag }}
|
||||
tag_name: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
name: Release ${{ steps.get-latest-tag.outputs.tag }}
|
||||
body: |
|
||||
## Info
|
||||
Commit ${{ github.sha }} was deployed to `staging`. [See code diff](${{ github.event.compare }}).
|
||||
@@ -289,10 +258,3 @@ jobs:
|
||||
## How to Promote?
|
||||
In order to promote this to prod, edit the draft and press **"Publish release"**.
|
||||
draft: true
|
||||
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ needs.docker.outputs.release-notes }}
|
||||
|
||||
184
.github/workflows/docker-arm.yml
vendored
Normal file
184
.github/workflows/docker-arm.yml
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
name: Build Docker ARM Image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
MIX_ENV: prod
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REGISTRY_IMAGE: wandererltd/community-edition-arm
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: 🛠 Build Docker Images
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
release-tag: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
release-notes: ${{ steps.get-content.outputs.string }}
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
repository-projects: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/arm64
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Get Release Tag
|
||||
id: get-latest-tag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: 1.0.0
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare Changelog
|
||||
run: |
|
||||
yes | cp -rf CHANGELOG.md priv/changelog/CHANGELOG.md
|
||||
sed -i '1i%{title: "Change Log"}\n\n---\n' priv/changelog/CHANGELOG.md
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.WANDERER_DOCKER_USER }}
|
||||
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,"name=${{ env.REGISTRY_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
MIX_ENV=prod
|
||||
BUILD_METADATA=${{ steps.meta.outputs.json }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- uses: markpatterson27/markdown-to-output@v1
|
||||
id: extract-changelog
|
||||
with:
|
||||
filepath: CHANGELOG.md
|
||||
|
||||
- name: Get content
|
||||
uses: 2428392/gh-truncate-string-action@v1.3.0
|
||||
id: get-content
|
||||
with:
|
||||
stringToTruncate: |
|
||||
📣 Wanderer **ARM** release available 🎉
|
||||
|
||||
[wandererltd/community-edition-arm:${{ steps.get-latest-tag.outputs.tag }}](https://hub.docker.com/r/wandererltd/community-edition-arm/tags)
|
||||
|
||||
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
|
||||
${{ steps.extract-changelog.outputs.body }}
|
||||
maxLength: 500
|
||||
truncationSymbol: "…"
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- docker
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.WANDERER_DOCKER_USER }}
|
||||
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY_IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}},value=${{ needs.docker.outputs.release-tag }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
notify:
|
||||
name: 🏷 Notify about release
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [docker, merge]
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ needs.docker.outputs.release-notes }}
|
||||
184
.github/workflows/docker.yml
vendored
Normal file
184
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
MIX_ENV: prod
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REGISTRY_IMAGE: wandererltd/community-edition
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
name: 🛠 Build Docker Images
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
release-tag: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
release-notes: ${{ steps.get-content.outputs.string }}
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
repository-projects: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Get Release Tag
|
||||
id: get-latest-tag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@v1"
|
||||
with:
|
||||
fallback: 1.0.0
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Prepare Changelog
|
||||
run: |
|
||||
yes | cp -rf CHANGELOG.md priv/changelog/CHANGELOG.md
|
||||
sed -i '1i%{title: "Change Log"}\n\n---\n' priv/changelog/CHANGELOG.md
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY_IMAGE }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.WANDERER_DOCKER_USER }}
|
||||
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,"name=${{ env.REGISTRY_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
MIX_ENV=prod
|
||||
BUILD_METADATA=${{ steps.meta.outputs.json }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- uses: markpatterson27/markdown-to-output@v1
|
||||
id: extract-changelog
|
||||
with:
|
||||
filepath: CHANGELOG.md
|
||||
|
||||
- name: Get content
|
||||
uses: 2428392/gh-truncate-string-action@v1.3.0
|
||||
id: get-content
|
||||
with:
|
||||
stringToTruncate: |
|
||||
📣 Wanderer new release available 🎉
|
||||
|
||||
[wandererltd/community-edition:${{ steps.get-latest-tag.outputs.tag }}](https://hub.docker.com/r/wandererltd/community-edition/tags)
|
||||
|
||||
**Version**: ${{ steps.get-latest-tag.outputs.tag }}
|
||||
|
||||
${{ steps.extract-changelog.outputs.body }}
|
||||
maxLength: 500
|
||||
truncationSymbol: "…"
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- docker
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.WANDERER_DOCKER_USER }}
|
||||
password: ${{ secrets.WANDERER_DOCKER_PASSWORD }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY_IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}},value=${{ needs.docker.outputs.release-tag }}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
notify:
|
||||
name: 🏷 Notify about release
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [docker, merge]
|
||||
steps:
|
||||
- name: Discord Webhook Action
|
||||
uses: tsickert/discord-webhook@v5.3.0
|
||||
with:
|
||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
content: ${{ needs.docker.outputs.release-notes }}
|
||||
77
CHANGELOG.md
77
CHANGELOG.md
@@ -2,6 +2,83 @@
|
||||
|
||||
<!-- changelog -->
|
||||
|
||||
## [v1.75.11](https://github.com/wanderer-industries/wanderer/compare/v1.75.10...v1.75.11) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.10](https://github.com/wanderer-industries/wanderer/compare/v1.75.9...v1.75.10) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.9](https://github.com/wanderer-industries/wanderer/compare/v1.75.8...v1.75.9) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.8](https://github.com/wanderer-industries/wanderer/compare/v1.75.7...v1.75.8) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.7](https://github.com/wanderer-industries/wanderer/compare/v1.75.6...v1.75.7) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.6](https://github.com/wanderer-industries/wanderer/compare/v1.75.5...v1.75.6) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.5](https://github.com/wanderer-industries/wanderer/compare/v1.75.4...v1.75.5) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
## [v1.75.4](https://github.com/wanderer-industries/wanderer/compare/v1.75.3...v1.75.4) (2025-08-11)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* restore security audit
|
||||
|
||||
## [v1.75.3](https://github.com/wanderer-industries/wanderer/compare/v1.75.2...v1.75.3) (2025-08-10)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* core: Fixed character tracking issues
|
||||
|
||||
## [v1.75.2](https://github.com/wanderer-industries/wanderer/compare/v1.75.1...v1.75.2) (2025-08-10)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Map: Fix indents for ally logos in list "On the map"
|
||||
|
||||
* Map: Fix cancelling ping from system context menu
|
||||
|
||||
* Map: Hide admin settings tab
|
||||
|
||||
* Map: Remote map setting refactoring
|
||||
|
||||
## [v1.75.1](https://github.com/wanderer-industries/wanderer/compare/v1.75.0...v1.75.1) (2025-07-30)
|
||||
|
||||
|
||||
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* unable to cancel ping from right click context menu
|
||||
|
||||
## [v1.75.0](https://github.com/wanderer-industries/wanderer/compare/v1.74.13...v1.75.0) (2025-07-29)
|
||||
|
||||
|
||||
|
||||
@@ -21,21 +21,17 @@ RUN mkdir config
|
||||
# to ensure any relevant config change will trigger the dependencies
|
||||
# to be re-compiled.
|
||||
COPY config/config.exs config/${MIX_ENV}.exs config/
|
||||
|
||||
COPY priv priv
|
||||
|
||||
COPY lib lib
|
||||
|
||||
COPY assets assets
|
||||
|
||||
RUN mix compile
|
||||
|
||||
RUN mix assets.deploy
|
||||
RUN mix compile
|
||||
|
||||
# Changes to config/runtime.exs don't require recompiling the code
|
||||
COPY config/runtime.exs config/
|
||||
|
||||
COPY rel rel
|
||||
|
||||
RUN mix release
|
||||
|
||||
# start a new build stage so that the final image will only contain
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.vertical-tabs-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
min-height: 400px;
|
||||
|
||||
.p-tabview {
|
||||
width: 100%;
|
||||
@@ -68,6 +68,28 @@
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.color-warn {
|
||||
@apply bg-yellow-600/5 border-r-yellow-600/20;
|
||||
|
||||
&:hover {
|
||||
@apply bg-yellow-600/10 border-r-yellow-600/40;
|
||||
}
|
||||
|
||||
|
||||
&.p-tabview-selected {
|
||||
@apply bg-yellow-600/10 border-r-yellow-600;
|
||||
|
||||
.p-tabview-nav-link {
|
||||
@apply text-yellow-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply bg-yellow-600/10 border-r-yellow-600;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface ContextMenuSystemProps {
|
||||
onSystemStatus(val: number): void;
|
||||
onSystemLabels(val: string): void;
|
||||
onCustomLabelDialog(): void;
|
||||
onTogglePing(type: PingType, solar_system_id: string, hasPing: boolean): void;
|
||||
onTogglePing(type: PingType, solar_system_id: string, ping_id: string | undefined, hasPing: boolean): void;
|
||||
onWaypointSet: WaypointSetContextHandler;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ export const useContextMenuSystemItems = ({
|
||||
|
||||
{ separator: true },
|
||||
{
|
||||
command: () => onTogglePing(PingType.Rally, systemId, hasPing),
|
||||
command: () => onTogglePing(PingType.Rally, systemId, ping?.id, hasPing),
|
||||
disabled: !isShowPingBtn,
|
||||
template: () => {
|
||||
const iconClasses = clsx({
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { Node } from 'reactflow';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { ContextMenu } from 'primereact/contextmenu';
|
||||
import { SolarSystemRawType } from '@/hooks/Mapper/types';
|
||||
import { ctxManager } from '@/hooks/Mapper/utils/contextManager.ts';
|
||||
import { NodeSelectionMouseHandler } from '@/hooks/Mapper/components/contexts/types.ts';
|
||||
import { useDeleteSystems } from '@/hooks/Mapper/components/contexts/hooks';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
|
||||
export const useContextMenuSystemMultipleHandlers = () => {
|
||||
const {
|
||||
data: { pings },
|
||||
} = useMapRootState();
|
||||
|
||||
const contextMenuRef = useRef<ContextMenu | null>(null);
|
||||
const [systems, setSystems] = useState<Node<SolarSystemRawType>[]>();
|
||||
|
||||
const { deleteSystems } = useDeleteSystems();
|
||||
|
||||
const ping = useMemo(() => (pings.length === 1 ? pings[0] : undefined), [pings]);
|
||||
|
||||
const handleSystemMultipleContext: NodeSelectionMouseHandler = (ev, systems_) => {
|
||||
setSystems(systems_);
|
||||
ev.preventDefault();
|
||||
@@ -24,13 +31,17 @@ export const useContextMenuSystemMultipleHandlers = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const sysToDel = systems.filter(x => !x.data.locked).map(x => x.id);
|
||||
const sysToDel = systems
|
||||
.filter(x => !x.data.locked)
|
||||
.filter(x => x.id !== ping?.solar_system_id)
|
||||
.map(x => x.id);
|
||||
|
||||
if (sysToDel.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteSystems(sysToDel);
|
||||
}, [deleteSystems, systems]);
|
||||
}, [deleteSystems, systems, ping]);
|
||||
|
||||
return {
|
||||
handleSystemMultipleContext,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MapUserSettings, SettingsWithVersion } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
|
||||
const REQUIRED_KEYS = [
|
||||
export const REQUIRED_KEYS = [
|
||||
'widgets',
|
||||
'interface',
|
||||
'onTheMap',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './useSystemInfo';
|
||||
export * from './useGetOwnOnlineCharacters';
|
||||
export * from './useElementWidth';
|
||||
export * from './useDetectSettingsChanged';
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const useDetectSettingsChanged = () => {
|
||||
const {
|
||||
storedSettings: {
|
||||
interfaceSettings,
|
||||
settingsRoutes,
|
||||
settingsLocal,
|
||||
settingsSignatures,
|
||||
settingsOnTheMap,
|
||||
settingsKills,
|
||||
},
|
||||
} = useMapRootState();
|
||||
const [counter, setCounter] = useState(0);
|
||||
|
||||
useEffect(
|
||||
() => setCounter(x => x + 1),
|
||||
[interfaceSettings, settingsRoutes, settingsLocal, settingsSignatures, settingsOnTheMap, settingsKills],
|
||||
);
|
||||
|
||||
return counter;
|
||||
};
|
||||
@@ -21,7 +21,9 @@ import { KillsCounter } from '@/hooks/Mapper/components/map/components/KillsCoun
|
||||
export const SolarSystemNodeTheme = memo((props: NodeProps<MapSolarSystemType>) => {
|
||||
const nodeVars = useSolarSystemNode(props);
|
||||
const { localCounterCharacters } = useLocalCounter(nodeVars);
|
||||
const { killsCount: localKillsCount, killsActivityType: localKillsActivityType } = useNodeKillsCount(nodeVars.solarSystemId);
|
||||
const { killsCount: localKillsCount, killsActivityType: localKillsActivityType } = useNodeKillsCount(
|
||||
nodeVars.solarSystemId,
|
||||
);
|
||||
|
||||
// console.log('JOipP', `render ${nodeVars.id}`, render++);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
|
||||
const TOOLTIP_PROPS = { content: 'Remove comment', position: TooltipPosition.top };
|
||||
|
||||
@@ -28,8 +29,7 @@ export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownComm
|
||||
const char = useGetCacheCharacter(characterEveId);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const cpRemoveBtnRef = useRef<HTMLElement>();
|
||||
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
|
||||
const { outCommand } = useMapRootState();
|
||||
const ref = useRef({ outCommand, id });
|
||||
@@ -45,9 +45,6 @@ export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownComm
|
||||
const handleMouseEnter = useCallback(() => setHovered(true), []);
|
||||
const handleMouseLeave = useCallback(() => setHovered(false), []);
|
||||
|
||||
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
|
||||
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InfoDrawer
|
||||
@@ -68,11 +65,11 @@ export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownComm
|
||||
{!hovered && <TimeAgo timestamp={time} />}
|
||||
{hovered && (
|
||||
// @ts-ignore
|
||||
<div ref={cpRemoveBtnRef}>
|
||||
<div ref={cfRef}>
|
||||
<WdImgButton
|
||||
className={clsx(PrimeIcons.TRASH, 'hover:text-red-400')}
|
||||
tooltip={TOOLTIP_PROPS}
|
||||
onClick={handleShowCP}
|
||||
onClick={cfShow}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -85,9 +82,9 @@ export const MarkdownComment = ({ text, time, characterEveId, id }: MarkdownComm
|
||||
</InfoDrawer>
|
||||
|
||||
<ConfirmPopup
|
||||
target={cpRemoveBtnRef.current}
|
||||
visible={cpRemoveVisible}
|
||||
onHide={handleHideCP}
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="Are you sure you want to delete?"
|
||||
icon="pi pi-exclamation-triangle"
|
||||
accept={handleDelete}
|
||||
|
||||
@@ -16,8 +16,9 @@ import { PrimeIcons } from 'primereact/api';
|
||||
import { Button } from 'primereact/button';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import useRefState from 'react-usestateref';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
|
||||
const PING_PLACEMENT_MAP = {
|
||||
[PingsPlacement.rightTop]: 'top-right',
|
||||
@@ -78,9 +79,7 @@ export interface PingsInterfaceProps {
|
||||
export const PingsInterface = ({ hasLeftOffset }: PingsInterfaceProps) => {
|
||||
const toast = useRef<Toast>(null);
|
||||
const [isShow, setIsShow, isShowRef] = useRefState(false);
|
||||
|
||||
const cpRemoveBtnRef = useRef<HTMLElement>();
|
||||
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
|
||||
const {
|
||||
storedSettings: { interfaceSettings },
|
||||
@@ -98,9 +97,6 @@ export const PingsInterface = ({ hasLeftOffset }: PingsInterfaceProps) => {
|
||||
|
||||
const ping = useMemo(() => (pings.length === 1 ? pings[0] : null), [pings]);
|
||||
|
||||
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
|
||||
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
|
||||
|
||||
const navigateTo = useCallback(() => {
|
||||
if (!ping) {
|
||||
return;
|
||||
@@ -242,11 +238,11 @@ export const PingsInterface = ({ hasLeftOffset }: PingsInterfaceProps) => {
|
||||
/>
|
||||
|
||||
{/*@ts-ignore*/}
|
||||
<div ref={cpRemoveBtnRef}>
|
||||
<div ref={cfRef}>
|
||||
<WdImgButton
|
||||
className={clsx('pi-trash', 'text-red-400 hover:text-red-300')}
|
||||
tooltip={DELETE_TOOLTIP_PROPS}
|
||||
onClick={handleShowCP}
|
||||
onClick={cfShow}
|
||||
/>
|
||||
</div>
|
||||
{/* TODO ADD solar system menu*/}
|
||||
@@ -272,9 +268,9 @@ export const PingsInterface = ({ hasLeftOffset }: PingsInterfaceProps) => {
|
||||
/>
|
||||
|
||||
<ConfirmPopup
|
||||
target={cpRemoveBtnRef.current}
|
||||
visible={cpRemoveVisible}
|
||||
onHide={handleHideCP}
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="Are you sure you want to delete ping?"
|
||||
icon="pi pi-exclamation-triangle text-orange-400"
|
||||
accept={removePing}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Dialog } from 'primereact/dialog';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { OutCommand, UserPermission } from '@/hooks/Mapper/types';
|
||||
import { CONNECTIONS_CHECKBOXES_PROPS, SIGNATURES_CHECKBOXES_PROPS, SYSTEMS_CHECKBOXES_PROPS } from './constants.ts';
|
||||
import {
|
||||
MapSettingsProvider,
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
import { WidgetsSettings } from './components/WidgetsSettings';
|
||||
import { CommonSettings } from './components/CommonSettings';
|
||||
import { SettingsListItem } from './types.ts';
|
||||
import { ImportExport } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/components/ImportExport.tsx';
|
||||
import { ImportExport } from './components/ImportExport.tsx';
|
||||
import { ServerSettings } from './components/ServerSettings.tsx';
|
||||
import { AdminSettings } from './components/AdminSettings.tsx';
|
||||
import { useMapCheckPermissions } from '@/hooks/Mapper/mapRootProvider/hooks/api';
|
||||
|
||||
export interface MapSettingsProps {
|
||||
visible: boolean;
|
||||
@@ -24,6 +27,7 @@ export const MapSettingsComp = ({ visible, onHide }: MapSettingsProps) => {
|
||||
const { outCommand } = useMapRootState();
|
||||
|
||||
const { renderSettingItem, setUserRemoteSettings } = useMapSettings();
|
||||
const isAdmin = useMapCheckPermissions([UserPermission.ADMIN_MAP]);
|
||||
|
||||
const refVars = useRef({ outCommand, onHide, visible });
|
||||
refVars.current = { outCommand, onHide, visible };
|
||||
@@ -58,7 +62,7 @@ export const MapSettingsComp = ({ visible, onHide }: MapSettingsProps) => {
|
||||
header="Map user settings"
|
||||
visible
|
||||
draggable={false}
|
||||
style={{ width: '550px' }}
|
||||
style={{ width: '600px' }}
|
||||
onShow={handleShow}
|
||||
onHide={handleHide}
|
||||
>
|
||||
@@ -92,6 +96,16 @@ export const MapSettingsComp = ({ visible, onHide }: MapSettingsProps) => {
|
||||
<TabPanel header="Import/Export" className="h-full" headerClassName={styles.verticalTabHeader}>
|
||||
<ImportExport />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header="Server Settings" className="h-full" headerClassName="color-warn">
|
||||
<ServerSettings />
|
||||
</TabPanel>
|
||||
|
||||
{isAdmin && (
|
||||
<TabPanel header="Admin Settings" className="h-full" headerClassName="color-warn">
|
||||
<AdminSettings />
|
||||
</TabPanel>
|
||||
)}
|
||||
</TabView>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { Button } from 'primereact/button';
|
||||
import { callToastError, callToastSuccess, callToastWarn } from '@/hooks/Mapper/helpers';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
import { MapUserSettings, RemoteAdminSettingsResponse } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
||||
import fastDeepEqual from 'fast-deep-equal';
|
||||
import { useDetectSettingsChanged } from '@/hooks/Mapper/components/hooks';
|
||||
|
||||
export const AdminSettings = () => {
|
||||
const {
|
||||
storedSettings: { getSettingsForExport },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const settingsChanged = useDetectSettingsChanged();
|
||||
|
||||
const [currentRemoteSettings, setCurrentRemoteSettings] = useState<MapUserSettings | null>(null);
|
||||
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
const toast = useRef<Toast | null>(null);
|
||||
|
||||
const hasSettingsForExport = useMemo(() => !!getSettingsForExport(), [getSettingsForExport]);
|
||||
|
||||
const refVars = useRef({ currentRemoteSettings, getSettingsForExport });
|
||||
refVars.current = { currentRemoteSettings, getSettingsForExport };
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
let res: RemoteAdminSettingsResponse | undefined;
|
||||
try {
|
||||
res = await outCommand({ type: OutCommand.getDefaultSettings, data: null });
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (!res || res.default_settings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentRemoteSettings(parseMapUserSettings(res.default_settings));
|
||||
};
|
||||
|
||||
load();
|
||||
}, [outCommand]);
|
||||
|
||||
const isDirty = useMemo(() => {
|
||||
const { currentRemoteSettings, getSettingsForExport } = refVars.current;
|
||||
const localCurrent = parseMapUserSettings(getSettingsForExport());
|
||||
|
||||
return !fastDeepEqual(currentRemoteSettings, localCurrent);
|
||||
// eslint-disable-next-line
|
||||
}, [settingsChanged, currentRemoteSettings]);
|
||||
|
||||
const handleSync = useCallback(async () => {
|
||||
const settings = getSettingsForExport();
|
||||
|
||||
if (!settings) {
|
||||
callToastWarn(toast.current, 'No settings to save');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let response: { success: boolean } | undefined;
|
||||
|
||||
try {
|
||||
response = await outCommand({
|
||||
type: OutCommand.saveDefaultSettings,
|
||||
data: { settings },
|
||||
});
|
||||
} catch (err) {
|
||||
callToastError(toast.current, 'Something went wrong while saving settings');
|
||||
console.error('ERROR: ', err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || !response.success) {
|
||||
callToastError(toast.current, 'Settings not saved - dont not why it');
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentRemoteSettings(parseMapUserSettings(settings));
|
||||
|
||||
callToastSuccess(toast.current, 'Settings saved successfully');
|
||||
}, [getSettingsForExport, outCommand]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div>
|
||||
<Button
|
||||
// @ts-ignore
|
||||
ref={cfRef}
|
||||
onClick={cfShow}
|
||||
icon="pi pi-save"
|
||||
size="small"
|
||||
severity="danger"
|
||||
label="Save as Map Default"
|
||||
className="py-[4px]"
|
||||
disabled={!hasSettingsForExport || !isDirty}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!isDirty && <span className="text-red-500/70 text-[12px]">*Local and remote are identical.</span>}
|
||||
|
||||
<span className="text-stone-500 text-[12px]">
|
||||
*Will save your current settings as the default for all new users of this map. This action will overwrite any
|
||||
existing default settings.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Toast ref={toast} />
|
||||
|
||||
<ConfirmPopup
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="Your settings will overwrite default. Sure?."
|
||||
icon="pi pi-exclamation-triangle"
|
||||
accept={handleSync}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -7,9 +7,14 @@ import {
|
||||
import { useMapSettings } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/MapSettingsProvider.tsx';
|
||||
import { SettingsListItem } from '@/hooks/Mapper/components/mapRootContent/components/MapSettings/types.ts';
|
||||
import { useCallback } from 'react';
|
||||
import { Button } from 'primereact/button';
|
||||
import { TooltipPosition, WdTooltipWrapper } from '@/hooks/Mapper/components/ui-kit';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
|
||||
export const CommonSettings = () => {
|
||||
const { renderSettingItem } = useMapSettings();
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
|
||||
const renderSettingsList = useCallback(
|
||||
(list: SettingsListItem[]) => {
|
||||
@@ -18,6 +23,8 @@ export const CommonSettings = () => {
|
||||
[renderSettingItem],
|
||||
);
|
||||
|
||||
const handleResetSettings = () => {};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full gap-1">
|
||||
<div>
|
||||
@@ -29,6 +36,33 @@ export const CommonSettings = () => {
|
||||
<div className="grid grid-cols-[1fr_auto]">{renderSettingItem(MINI_MAP_PLACEMENT)}</div>
|
||||
<div className="grid grid-cols-[1fr_auto]">{renderSettingItem(PINGS_PLACEMENT)}</div>
|
||||
<div className="grid grid-cols-[1fr_auto]">{renderSettingItem(THEME_SETTING)}</div>
|
||||
|
||||
<div className="border-b-2 border-dotted border-stone-700/50 h-px my-3" />
|
||||
|
||||
<div className="grid grid-cols-[1fr_auto]">
|
||||
<div />
|
||||
<WdTooltipWrapper content="This dangerous action. And can not be undone" position={TooltipPosition.top}>
|
||||
<Button
|
||||
// @ts-ignore
|
||||
ref={cfRef}
|
||||
className="py-[4px]"
|
||||
onClick={cfShow}
|
||||
outlined
|
||||
size="small"
|
||||
severity="danger"
|
||||
label="Reset Settings"
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
</div>
|
||||
|
||||
<ConfirmPopup
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="All settings for this map will be reset to default."
|
||||
icon="pi pi-exclamation-triangle"
|
||||
accept={handleResetSettings}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { Divider } from 'primereact/divider';
|
||||
import { callToastError, callToastSuccess, callToastWarn } from '@/hooks/Mapper/helpers';
|
||||
|
||||
type SaveDefaultSettingsReturn = { success: boolean; error: string };
|
||||
|
||||
export const DefaultSettings = () => {
|
||||
const {
|
||||
outCommand,
|
||||
storedSettings: { getSettingsForExport },
|
||||
data: { userPermissions },
|
||||
} = useMapRootState();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const toast = useRef<Toast | null>(null);
|
||||
|
||||
const refVars = useRef({ getSettingsForExport, outCommand });
|
||||
refVars.current = { getSettingsForExport, outCommand };
|
||||
|
||||
const handleSaveAsDefault = useCallback(async () => {
|
||||
const settings = refVars.current.getSettingsForExport();
|
||||
if (!settings) {
|
||||
callToastWarn(toast.current, 'No settings to save');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
let response: SaveDefaultSettingsReturn;
|
||||
try {
|
||||
response = await refVars.current.outCommand({
|
||||
type: OutCommand.saveDefaultSettings,
|
||||
data: { settings },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Save default settings error:', error);
|
||||
callToastError(toast.current, 'Failed to save default settings');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.success) {
|
||||
callToastSuccess(toast.current, 'Default settings saved successfully');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
callToastError(toast.current, response.error || 'Failed to save default settings');
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
if (!userPermissions?.admin_map) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<div className="w-full h-full flex flex-col gap-5">
|
||||
<h3 className="text-lg font-semibold">Default Settings (Admin Only)</h3>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleSaveAsDefault}
|
||||
icon="pi pi-save"
|
||||
size="small"
|
||||
severity="danger"
|
||||
label="Save as Map Default"
|
||||
className="py-[4px]"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span className="text-stone-500 text-[12px]">
|
||||
*Will save your current settings as the default for all new users of this map. This action will overwrite
|
||||
any existing default settings.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Toast ref={toast} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
||||
import { Button } from 'primereact/button';
|
||||
import { OutCommand } from '@/hooks/Mapper/types';
|
||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||
import { callToastSuccess } from '@/hooks/Mapper/helpers';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
import { RemoteAdminSettingsResponse } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
|
||||
export const ServerSettings = () => {
|
||||
const {
|
||||
storedSettings: { applySettings },
|
||||
outCommand,
|
||||
} = useMapRootState();
|
||||
|
||||
const [hasSettings, setHasSettings] = useState(false);
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
const toast = useRef<Toast | null>(null);
|
||||
|
||||
const handleSync = useCallback(async () => {
|
||||
let res: RemoteAdminSettingsResponse | undefined;
|
||||
try {
|
||||
res = await outCommand({ type: OutCommand.getDefaultSettings, data: null });
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (res?.default_settings == null) {
|
||||
applySettings(createDefaultWidgetSettings());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
applySettings(parseMapUserSettings(res.default_settings));
|
||||
callToastSuccess(toast.current, 'Settings synchronized successfully');
|
||||
} catch (error) {
|
||||
applySettings(createDefaultWidgetSettings());
|
||||
}
|
||||
}, [applySettings, outCommand]);
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
let res: RemoteAdminSettingsResponse | undefined;
|
||||
try {
|
||||
res = await outCommand({ type: OutCommand.getDefaultSettings, data: null });
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (res?.default_settings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setHasSettings(true);
|
||||
};
|
||||
|
||||
load();
|
||||
}, [outCommand]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div>
|
||||
<Button
|
||||
// @ts-ignore
|
||||
ref={cfRef}
|
||||
onClick={cfShow}
|
||||
icon="pi pi-file-import"
|
||||
size="small"
|
||||
severity="warning"
|
||||
label="Sync with Default Settings"
|
||||
className="py-[4px]"
|
||||
disabled={!hasSettings}
|
||||
/>
|
||||
</div>
|
||||
{!hasSettings && (
|
||||
<span className="text-red-500/70 text-[12px]">*Default settings was not set by map administrator.</span>
|
||||
)}
|
||||
<span className="text-stone-500 text-[12px]">*Will apply admin settings which set as Default for map.</span>
|
||||
</div>
|
||||
|
||||
<Toast ref={toast} />
|
||||
|
||||
<ConfirmPopup
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="You lost your current settings. Sure?."
|
||||
icon="pi pi-exclamation-triangle"
|
||||
accept={handleSync}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,9 @@ export const WidgetsSettings = ({}: WidgetsSettingsProps) => {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="border-b-2 border-dotted border-stone-700/50 h-px my-3" />
|
||||
|
||||
<div className="grid grid-cols-[1fr_auto]">
|
||||
<div />
|
||||
<Button className="py-[4px]" onClick={resetWidgets} outlined size="small" label="Reset Widgets"></Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { Button } from 'primereact/button';
|
||||
import { ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { MapUserSettings } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import {
|
||||
DEFAULT_KILLS_WIDGET_SETTINGS,
|
||||
@@ -15,6 +15,7 @@ import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures.
|
||||
import { Toast } from 'primereact/toast';
|
||||
import { useMapRootState } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { saveTextFile } from '@/hooks/Mapper/utils';
|
||||
import { useConfirmPopup } from '@/hooks/Mapper/hooks';
|
||||
|
||||
const createSettings = function <T>(lsSettings: string | null, defaultValues: T) {
|
||||
return {
|
||||
@@ -24,10 +25,7 @@ const createSettings = function <T>(lsSettings: string | null, defaultValues: T)
|
||||
};
|
||||
|
||||
export const OldSettingsDialog = () => {
|
||||
const cpRemoveBtnRef = useRef<HTMLElement>();
|
||||
const [cpRemoveVisible, setCpRemoveVisible] = useState(false);
|
||||
const handleShowCP = useCallback(() => setCpRemoveVisible(true), []);
|
||||
const handleHideCP = useCallback(() => setCpRemoveVisible(false), []);
|
||||
const { cfShow, cfHide, cfVisible, cfRef } = useConfirmPopup();
|
||||
const toast = useRef<Toast | null>(null);
|
||||
|
||||
const {
|
||||
@@ -143,8 +141,8 @@ export const OldSettingsDialog = () => {
|
||||
<div className="flex items-center justify-end">
|
||||
<Button
|
||||
// @ts-ignore
|
||||
ref={cpRemoveBtnRef}
|
||||
onClick={handleShowCP}
|
||||
ref={cfRef}
|
||||
onClick={cfShow}
|
||||
icon="pi pi-exclamation-triangle"
|
||||
size="small"
|
||||
severity="warning"
|
||||
@@ -192,9 +190,9 @@ export const OldSettingsDialog = () => {
|
||||
</Dialog>
|
||||
|
||||
<ConfirmPopup
|
||||
target={cpRemoveBtnRef.current}
|
||||
visible={cpRemoveVisible}
|
||||
onHide={handleHideCP}
|
||||
target={cfRef.current}
|
||||
visible={cfVisible}
|
||||
onHide={cfHide}
|
||||
message="After click dialog will disappear. Ready?"
|
||||
icon="pi pi-exclamation-triangle"
|
||||
accept={handleProceed}
|
||||
|
||||
@@ -13,6 +13,8 @@ import { InputText } from 'primereact/inputtext';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
|
||||
const itemTemplate = (item: CharacterTypeRaw & WithIsOwnCharacter, options: VirtualScrollerTemplateOptions) => {
|
||||
const showAllyLogoPlaceholder = options.props.items?.some(x => x.alliance_id != null);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(classes.CharacterRow, 'w-full box-border px-2 py-1', {
|
||||
@@ -22,7 +24,15 @@ const itemTemplate = (item: CharacterTypeRaw & WithIsOwnCharacter, options: Virt
|
||||
})}
|
||||
style={{ height: options.props.itemSize + 'px' }}
|
||||
>
|
||||
<CharacterCard showCorporationLogo showAllyLogo showSystem showTicker showShip {...item} />
|
||||
<CharacterCard
|
||||
showCorporationLogo
|
||||
showAllyLogo
|
||||
showAllyLogoPlaceholder={showAllyLogoPlaceholder}
|
||||
showSystem
|
||||
showTicker
|
||||
showShip
|
||||
{...item}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -181,17 +181,20 @@ export const MapWrapper = () => {
|
||||
ref.current.systemContextProps.systemId && setOpenSettings(ref.current.systemContextProps.systemId);
|
||||
}, []);
|
||||
|
||||
const handleTogglePing = useCallback(async (type: PingType, solar_system_id: string, hasPing: boolean) => {
|
||||
if (hasPing) {
|
||||
await outCommand({
|
||||
type: OutCommand.cancelPing,
|
||||
data: { type, solar_system_id: solar_system_id },
|
||||
});
|
||||
return;
|
||||
}
|
||||
const handleTogglePing = useCallback(
|
||||
async (type: PingType, solar_system_id: string, ping_id: string | undefined, hasPing: boolean) => {
|
||||
if (hasPing) {
|
||||
await outCommand({
|
||||
type: OutCommand.cancelPing,
|
||||
data: { type, id: ping_id },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setOpenPing({ type, solar_system_id });
|
||||
}, []);
|
||||
setOpenPing({ type, solar_system_id });
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleCustomLabelDialog = useCallback(() => {
|
||||
const { systemContextProps } = ref.current;
|
||||
|
||||
@@ -24,6 +24,7 @@ export type CharacterCardProps = {
|
||||
useSystemsCache?: boolean;
|
||||
showCorporationLogo?: boolean;
|
||||
showAllyLogo?: boolean;
|
||||
showAllyLogoPlaceholder?: boolean;
|
||||
simpleMode?: boolean;
|
||||
} & WithIsOwnCharacter &
|
||||
WithClassName;
|
||||
@@ -47,6 +48,7 @@ export const CharacterCard = ({
|
||||
showShipName,
|
||||
showCorporationLogo,
|
||||
showAllyLogo,
|
||||
showAllyLogoPlaceholder,
|
||||
showTicker,
|
||||
useSystemsCache,
|
||||
className,
|
||||
@@ -217,6 +219,18 @@ export const CharacterCard = ({
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
|
||||
{showAllyLogo && showAllyLogoPlaceholder && !char.alliance_id && (
|
||||
<WdTooltipWrapper position={TooltipPosition.top} content="No alliance">
|
||||
<span
|
||||
className={clsx(
|
||||
'min-w-[33px] min-h-[33px] w-[33px] h-[33px]',
|
||||
'flex transition-[border-color,opacity] duration-250 rounded-none',
|
||||
'wd-bg-default',
|
||||
)}
|
||||
/>
|
||||
</WdTooltipWrapper>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col flex-grow overflow-hidden w-[50px]">
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from './sortWHClasses';
|
||||
export * from './parseSignatures';
|
||||
export * from './getSystemById';
|
||||
export * from './getEveImageUrl';
|
||||
export * from './toastHelpers';
|
||||
|
||||
28
assets/js/hooks/Mapper/helpers/toastHelpers.ts
Normal file
28
assets/js/hooks/Mapper/helpers/toastHelpers.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Toast } from 'primereact/toast';
|
||||
|
||||
export const callToastWarn = (toast: Toast | null, msg: string, life = 3000) => {
|
||||
toast?.show({
|
||||
severity: 'warn',
|
||||
summary: 'Warning',
|
||||
detail: msg,
|
||||
life,
|
||||
});
|
||||
};
|
||||
|
||||
export const callToastError = (toast: Toast | null, msg: string, life = 3000) => {
|
||||
toast?.show({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: msg,
|
||||
life,
|
||||
});
|
||||
};
|
||||
|
||||
export const callToastSuccess = (toast: Toast | null, msg: string, life = 3000) => {
|
||||
toast?.show({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
detail: msg,
|
||||
life,
|
||||
});
|
||||
};
|
||||
@@ -3,3 +3,4 @@ export * from './useHotkey';
|
||||
export * from './usePageVisibility';
|
||||
export * from './useSkipContextMenu';
|
||||
export * from './useThrottle';
|
||||
export * from './useConfirmPopup';
|
||||
|
||||
10
assets/js/hooks/Mapper/hooks/useConfirmPopup.ts
Normal file
10
assets/js/hooks/Mapper/hooks/useConfirmPopup.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
export const useConfirmPopup = () => {
|
||||
const cfRef = useRef<HTMLElement>();
|
||||
const [cfVisible, setCfVisible] = useState(false);
|
||||
const cfShow = useCallback(() => setCfVisible(true), []);
|
||||
const cfHide = useCallback(() => setCfVisible(false), []);
|
||||
|
||||
return { cfRef, cfVisible, cfShow, cfHide };
|
||||
};
|
||||
@@ -131,6 +131,7 @@ export interface MapRootContextProps {
|
||||
hasOldSettings: boolean;
|
||||
getSettingsForExport(): string | undefined;
|
||||
applySettings(settings: MapUserSettings): boolean;
|
||||
resetSettings(settings: MapUserSettings): void;
|
||||
checkOldSettings(): void;
|
||||
};
|
||||
}
|
||||
@@ -175,6 +176,7 @@ const MapRootContext = createContext<MapRootContextProps>({
|
||||
hasOldSettings: false,
|
||||
getSettingsForExport: () => '',
|
||||
applySettings: () => false,
|
||||
resetSettings: () => null,
|
||||
checkOldSettings: () => null,
|
||||
},
|
||||
});
|
||||
@@ -196,7 +198,7 @@ const MapRootHandlers = forwardRef(({ children }: WithChildren, fwdRef: Forwarde
|
||||
export const MapRootProvider = ({ children, fwdRef, outCommand }: MapRootProviderProps) => {
|
||||
const { update, ref } = useContextStore<MapRootData>({ ...INITIAL_DATA });
|
||||
|
||||
const storedSettings = useMapUserSettings(ref);
|
||||
const storedSettings = useMapUserSettings(ref, outCommand);
|
||||
|
||||
const { windowsSettings, toggleWidgetVisibility, updateWidgetSettings, resetWidgets } =
|
||||
useStoreWidgets(storedSettings);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { MapUserSettings } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import {
|
||||
DEFAULT_KILLS_WIDGET_SETTINGS,
|
||||
DEFAULT_ON_THE_MAP_SETTINGS,
|
||||
DEFAULT_ROUTES_SETTINGS,
|
||||
DEFAULT_WIDGET_LOCAL_SETTINGS,
|
||||
getDefaultWidgetProps,
|
||||
STORED_INTERFACE_DEFAULT_VALUES,
|
||||
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
|
||||
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures.ts';
|
||||
|
||||
// TODO - we need provide and compare version
|
||||
const createWidgetSettingsWithVersion = <T>(settings: T) => {
|
||||
return {
|
||||
version: 0,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
||||
export const createDefaultWidgetSettings = (): MapUserSettings => {
|
||||
return {
|
||||
killsWidget: createWidgetSettingsWithVersion(DEFAULT_KILLS_WIDGET_SETTINGS),
|
||||
localWidget: createWidgetSettingsWithVersion(DEFAULT_WIDGET_LOCAL_SETTINGS),
|
||||
widgets: createWidgetSettingsWithVersion(getDefaultWidgetProps()),
|
||||
routes: createWidgetSettingsWithVersion(DEFAULT_ROUTES_SETTINGS),
|
||||
onTheMap: createWidgetSettingsWithVersion(DEFAULT_ON_THE_MAP_SETTINGS),
|
||||
signaturesWidget: createWidgetSettingsWithVersion(DEFAULT_SIGNATURE_SETTINGS),
|
||||
interface: createWidgetSettingsWithVersion(STORED_INTERFACE_DEFAULT_VALUES),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
import { OutCommand, OutCommandHandler } from '@/hooks/Mapper/types';
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
MapUserSettings,
|
||||
MapUserSettingsStructure,
|
||||
RemoteAdminSettingsResponse,
|
||||
} from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||
import { parseMapUserSettings } from '@/hooks/Mapper/components/helpers';
|
||||
|
||||
interface UseActualizeRemoteMapSettingsProps {
|
||||
outCommand: OutCommandHandler;
|
||||
mapUserSettings: MapUserSettingsStructure;
|
||||
applySettings: (val: MapUserSettings) => void;
|
||||
setMapUserSettings: Dispatch<SetStateAction<MapUserSettingsStructure>>;
|
||||
map_slug: string | null;
|
||||
}
|
||||
|
||||
export const useActualizeRemoteMapSettings = ({
|
||||
outCommand,
|
||||
mapUserSettings,
|
||||
setMapUserSettings,
|
||||
applySettings,
|
||||
map_slug,
|
||||
}: UseActualizeRemoteMapSettingsProps) => {
|
||||
const refVars = useRef({ applySettings, mapUserSettings, setMapUserSettings, map_slug });
|
||||
refVars.current = { applySettings, mapUserSettings, setMapUserSettings, map_slug };
|
||||
|
||||
const actualizeRemoteMapSettings = useCallback(async () => {
|
||||
const { applySettings } = refVars.current;
|
||||
|
||||
let res: RemoteAdminSettingsResponse | undefined;
|
||||
try {
|
||||
res = await outCommand({ type: OutCommand.getDefaultSettings, data: null });
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if (res?.default_settings == null) {
|
||||
applySettings(createDefaultWidgetSettings());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
applySettings(parseMapUserSettings(res.default_settings));
|
||||
} catch (error) {
|
||||
applySettings(createDefaultWidgetSettings());
|
||||
}
|
||||
}, [outCommand]);
|
||||
|
||||
useEffect(() => {
|
||||
const { mapUserSettings } = refVars.current;
|
||||
|
||||
// INFO: Do nothing if slug is not set
|
||||
if (map_slug == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// INFO: Do nothing if user have already data
|
||||
if (map_slug in mapUserSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
actualizeRemoteMapSettings();
|
||||
}, [actualizeRemoteMapSettings, map_slug]);
|
||||
};
|
||||
@@ -1,44 +1,16 @@
|
||||
import useLocalStorageState from 'use-local-storage-state';
|
||||
import { MapUserSettings, MapUserSettingsStructure } from '@/hooks/Mapper/mapRootProvider/types.ts';
|
||||
import {
|
||||
DEFAULT_KILLS_WIDGET_SETTINGS,
|
||||
DEFAULT_ON_THE_MAP_SETTINGS,
|
||||
DEFAULT_ROUTES_SETTINGS,
|
||||
DEFAULT_WIDGET_LOCAL_SETTINGS,
|
||||
getDefaultWidgetProps,
|
||||
STORED_INTERFACE_DEFAULT_VALUES,
|
||||
} from '@/hooks/Mapper/mapRootProvider/constants.ts';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { DEFAULT_SIGNATURE_SETTINGS } from '@/hooks/Mapper/constants/signatures';
|
||||
import { MapRootData } from '@/hooks/Mapper/mapRootProvider';
|
||||
import { useSettingsValueAndSetter } from '@/hooks/Mapper/mapRootProvider/hooks/useSettingsValueAndSetter.ts';
|
||||
import fastDeepEqual from 'fast-deep-equal';
|
||||
|
||||
// import { actualizeSettings } from '@/hooks/Mapper/mapRootProvider/helpers';
|
||||
|
||||
// TODO - we need provide and compare version
|
||||
const createWidgetSettingsWithVersion = <T>(settings: T) => {
|
||||
return {
|
||||
version: 0,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
||||
const createDefaultWidgetSettings = (): MapUserSettings => {
|
||||
return {
|
||||
killsWidget: createWidgetSettingsWithVersion(DEFAULT_KILLS_WIDGET_SETTINGS),
|
||||
localWidget: createWidgetSettingsWithVersion(DEFAULT_WIDGET_LOCAL_SETTINGS),
|
||||
widgets: createWidgetSettingsWithVersion(getDefaultWidgetProps()),
|
||||
routes: createWidgetSettingsWithVersion(DEFAULT_ROUTES_SETTINGS),
|
||||
onTheMap: createWidgetSettingsWithVersion(DEFAULT_ON_THE_MAP_SETTINGS),
|
||||
signaturesWidget: createWidgetSettingsWithVersion(DEFAULT_SIGNATURE_SETTINGS),
|
||||
interface: createWidgetSettingsWithVersion(STORED_INTERFACE_DEFAULT_VALUES),
|
||||
};
|
||||
};
|
||||
import { OutCommandHandler } from '@/hooks/Mapper/types';
|
||||
import { useActualizeRemoteMapSettings } from '@/hooks/Mapper/mapRootProvider/hooks/useActualizeRemoteMapSettings.ts';
|
||||
import { createDefaultWidgetSettings } from '@/hooks/Mapper/mapRootProvider/helpers/createDefaultWidgetSettings.ts';
|
||||
|
||||
const EMPTY_OBJ = {};
|
||||
|
||||
export const useMapUserSettings = ({ map_slug }: MapRootData) => {
|
||||
export const useMapUserSettings = ({ map_slug }: MapRootData, outCommand: OutCommandHandler) => {
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const [hasOldSettings, setHasOldSettings] = useState(false);
|
||||
|
||||
@@ -49,19 +21,25 @@ export const useMapUserSettings = ({ map_slug }: MapRootData) => {
|
||||
const ref = useRef({ mapUserSettings, setMapUserSettings, map_slug });
|
||||
ref.current = { mapUserSettings, setMapUserSettings, map_slug };
|
||||
|
||||
useEffect(() => {
|
||||
const { mapUserSettings, setMapUserSettings } = ref.current;
|
||||
if (map_slug === null) {
|
||||
return;
|
||||
const applySettings = useCallback((settings: MapUserSettings) => {
|
||||
const { map_slug, mapUserSettings, setMapUserSettings } = ref.current;
|
||||
|
||||
if (map_slug == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(map_slug in mapUserSettings)) {
|
||||
setMapUserSettings({
|
||||
...mapUserSettings,
|
||||
[map_slug]: createDefaultWidgetSettings(),
|
||||
});
|
||||
if (fastDeepEqual(settings, mapUserSettings[map_slug])) {
|
||||
return false;
|
||||
}
|
||||
}, [map_slug]);
|
||||
|
||||
setMapUserSettings(old => ({
|
||||
...old,
|
||||
[map_slug]: settings,
|
||||
}));
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
useActualizeRemoteMapSettings({ outCommand, applySettings, mapUserSettings, setMapUserSettings, map_slug });
|
||||
|
||||
const [interfaceSettings, setInterfaceSettings] = useSettingsValueAndSetter(
|
||||
mapUserSettings,
|
||||
@@ -178,23 +156,9 @@ export const useMapUserSettings = ({ map_slug }: MapRootData) => {
|
||||
return JSON.stringify(ref.current.mapUserSettings[map_slug]);
|
||||
}, []);
|
||||
|
||||
const applySettings = useCallback((settings: MapUserSettings) => {
|
||||
const { map_slug, mapUserSettings, setMapUserSettings } = ref.current;
|
||||
|
||||
if (map_slug == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fastDeepEqual(settings, mapUserSettings[map_slug])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setMapUserSettings(old => ({
|
||||
...old,
|
||||
[map_slug]: settings,
|
||||
}));
|
||||
return true;
|
||||
}, []);
|
||||
const resetSettings = useCallback(() => {
|
||||
applySettings(createDefaultWidgetSettings());
|
||||
}, [applySettings]);
|
||||
|
||||
return {
|
||||
isReady,
|
||||
@@ -217,6 +181,7 @@ export const useMapUserSettings = ({ map_slug }: MapRootData) => {
|
||||
|
||||
getSettingsForExport,
|
||||
applySettings,
|
||||
resetSettings,
|
||||
checkOldSettings,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -85,3 +85,7 @@ export type MapUserSettings = {
|
||||
export type MapUserSettingsStructure = {
|
||||
[mapId: string]: MapUserSettings;
|
||||
};
|
||||
|
||||
export type WdResponse<T> = T;
|
||||
|
||||
export type RemoteAdminSettingsResponse = { default_settings?: string };
|
||||
|
||||
@@ -269,6 +269,8 @@ export enum OutCommand {
|
||||
showTracking = 'show_tracking',
|
||||
getUserSettings = 'get_user_settings',
|
||||
updateUserSettings = 'update_user_settings',
|
||||
saveDefaultSettings = 'save_default_settings',
|
||||
getDefaultSettings = 'get_default_settings',
|
||||
unlinkSignature = 'unlink_signature',
|
||||
searchSystems = 'search_systems',
|
||||
undoDeleteSignatures = 'undo_delete_signatures',
|
||||
|
||||
BIN
assets/static/images/news/2025/07-27-settings/admin_settings.png
Normal file
BIN
assets/static/images/news/2025/07-27-settings/admin_settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
@@ -102,6 +102,23 @@ config :error_tracker,
|
||||
repo: WandererApp.Repo,
|
||||
otp_app: :wanderer_app
|
||||
|
||||
# Security Audit Configuration
|
||||
config :wanderer_app, WandererApp.SecurityAudit,
|
||||
enabled: true,
|
||||
# Set to true in production for better performance
|
||||
async: false,
|
||||
batch_size: 100,
|
||||
flush_interval: 5000,
|
||||
log_level: :info,
|
||||
threat_detection: %{
|
||||
enabled: true,
|
||||
max_failed_attempts: 5,
|
||||
max_permission_denials: 10,
|
||||
window_seconds: 300,
|
||||
bulk_operation_threshold: 10000
|
||||
},
|
||||
retention_days: 90
|
||||
|
||||
config :git_ops,
|
||||
mix_project: Mix.Project.get!(),
|
||||
changelog_file: "CHANGELOG.md",
|
||||
|
||||
@@ -27,5 +27,8 @@ config :swoosh, local: false
|
||||
config :logger,
|
||||
level: :info
|
||||
|
||||
# Enable async security audit processing in production
|
||||
config :wanderer_app, WandererApp.SecurityAudit, async: true
|
||||
|
||||
# Runtime production configuration, including reading
|
||||
# of environment variables, is done on config/runtime.exs.
|
||||
|
||||
@@ -28,6 +28,7 @@ defmodule WandererApp.Api do
|
||||
resource WandererApp.Api.MapSubscription
|
||||
resource WandererApp.Api.MapTransaction
|
||||
resource WandererApp.Api.MapUserSettings
|
||||
resource WandererApp.Api.MapDefaultSettings
|
||||
resource WandererApp.Api.User
|
||||
resource WandererApp.Api.ShipTypeInfo
|
||||
resource WandererApp.Api.UserActivity
|
||||
|
||||
145
lib/wanderer_app/api/map_default_settings.ex
Normal file
145
lib/wanderer_app/api/map_default_settings.ex
Normal file
@@ -0,0 +1,145 @@
|
||||
defmodule WandererApp.Api.MapDefaultSettings do
|
||||
@moduledoc """
|
||||
Resource for storing default map settings that admins can configure.
|
||||
These settings will be applied to new users when they first access the map.
|
||||
"""
|
||||
|
||||
use Ash.Resource,
|
||||
domain: WandererApp.Api,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshJsonApi.Resource]
|
||||
|
||||
postgres do
|
||||
repo(WandererApp.Repo)
|
||||
table("map_default_settings")
|
||||
end
|
||||
|
||||
json_api do
|
||||
type "map_default_settings"
|
||||
|
||||
includes([
|
||||
:map,
|
||||
:created_by,
|
||||
:updated_by
|
||||
])
|
||||
|
||||
routes do
|
||||
base("/map_default_settings")
|
||||
|
||||
get(:read)
|
||||
index(:read)
|
||||
post(:create)
|
||||
patch(:update)
|
||||
delete(:destroy)
|
||||
end
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define(:create, action: :create)
|
||||
define(:update, action: :update)
|
||||
define(:destroy, action: :destroy)
|
||||
define(:get_by_map_id, action: :get_by_map_id)
|
||||
end
|
||||
|
||||
actions do
|
||||
default_accept [
|
||||
:map_id,
|
||||
:settings
|
||||
]
|
||||
|
||||
defaults [:read, :destroy]
|
||||
|
||||
create :create do
|
||||
primary?(true)
|
||||
accept [:map_id, :settings]
|
||||
|
||||
change relate_actor(:created_by)
|
||||
change relate_actor(:updated_by)
|
||||
|
||||
change fn changeset, _context ->
|
||||
changeset
|
||||
|> validate_json_settings()
|
||||
end
|
||||
end
|
||||
|
||||
update :update do
|
||||
primary?(true)
|
||||
accept [:settings]
|
||||
|
||||
# Required for managing relationships
|
||||
require_atomic? false
|
||||
|
||||
change relate_actor(:updated_by)
|
||||
|
||||
change fn changeset, _context ->
|
||||
changeset
|
||||
|> validate_json_settings()
|
||||
end
|
||||
end
|
||||
|
||||
read :get_by_map_id do
|
||||
argument :map_id, :uuid, allow_nil?: false
|
||||
|
||||
filter expr(map_id == ^arg(:map_id))
|
||||
|
||||
prepare fn query, _context ->
|
||||
Ash.Query.limit(query, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
attribute :settings, :string do
|
||||
allow_nil? false
|
||||
constraints min_length: 2
|
||||
description "JSON string containing the default map settings"
|
||||
end
|
||||
|
||||
create_timestamp(:inserted_at)
|
||||
update_timestamp(:updated_at)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :map, WandererApp.Api.Map do
|
||||
primary_key? false
|
||||
allow_nil? false
|
||||
public? true
|
||||
end
|
||||
|
||||
belongs_to :created_by, WandererApp.Api.Character do
|
||||
allow_nil? true
|
||||
public? true
|
||||
end
|
||||
|
||||
belongs_to :updated_by, WandererApp.Api.Character do
|
||||
allow_nil? true
|
||||
public? true
|
||||
end
|
||||
end
|
||||
|
||||
identities do
|
||||
identity :unique_map_settings, [:map_id]
|
||||
end
|
||||
|
||||
defp validate_json_settings(changeset) do
|
||||
case Ash.Changeset.get_attribute(changeset, :settings) do
|
||||
nil ->
|
||||
changeset
|
||||
|
||||
settings ->
|
||||
case Jason.decode(settings) do
|
||||
{:ok, _} ->
|
||||
changeset
|
||||
|
||||
{:error, _} ->
|
||||
Ash.Changeset.add_error(
|
||||
changeset,
|
||||
field: :settings,
|
||||
message: "must be valid JSON"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -31,6 +31,8 @@ defmodule WandererApp.Api.MapSubscription do
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define(:create, action: :create)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
action: :read
|
||||
@@ -39,6 +41,15 @@ defmodule WandererApp.Api.MapSubscription do
|
||||
define(:all_active, action: :all_active)
|
||||
define(:all_by_map, action: :all_by_map)
|
||||
define(:active_by_map, action: :active_by_map)
|
||||
define(:destroy, action: :destroy)
|
||||
define(:cancel, action: :cancel)
|
||||
define(:expire, action: :expire)
|
||||
|
||||
define(:update_plan, action: :update_plan)
|
||||
define(:update_characters_limit, action: :update_characters_limit)
|
||||
define(:update_hubs_limit, action: :update_hubs_limit)
|
||||
define(:update_active_till, action: :update_active_till)
|
||||
define(:update_auto_renew, action: :update_auto_renew)
|
||||
end
|
||||
|
||||
actions do
|
||||
@@ -51,7 +62,7 @@ defmodule WandererApp.Api.MapSubscription do
|
||||
:auto_renew?
|
||||
]
|
||||
|
||||
defaults [:read]
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
|
||||
read :all_active do
|
||||
prepare build(sort: [updated_at: :asc])
|
||||
|
||||
@@ -31,6 +31,9 @@ defmodule WandererApp.Api.MapSystemComment do
|
||||
end
|
||||
|
||||
code_interface do
|
||||
define(:create, action: :create)
|
||||
define(:destroy, action: :destroy)
|
||||
|
||||
define(:by_id,
|
||||
get_by: [:id],
|
||||
action: :read
|
||||
@@ -46,7 +49,7 @@ defmodule WandererApp.Api.MapSystemComment do
|
||||
:text
|
||||
]
|
||||
|
||||
defaults [:read]
|
||||
defaults [:read, :destroy]
|
||||
|
||||
create :create do
|
||||
primary? true
|
||||
|
||||
@@ -29,19 +29,7 @@ defmodule WandererApp.Api.MapTransaction do
|
||||
:amount
|
||||
]
|
||||
|
||||
defaults [:create]
|
||||
|
||||
read :read do
|
||||
primary?(true)
|
||||
|
||||
pagination offset?: true,
|
||||
default_limit: 25,
|
||||
max_page_size: 100,
|
||||
countable: true,
|
||||
required?: false
|
||||
|
||||
prepare build(sort: [inserted_at: :desc])
|
||||
end
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
|
||||
read :by_map do
|
||||
argument(:map_id, :string, allow_nil?: false)
|
||||
|
||||
@@ -40,6 +40,7 @@ defmodule WandererApp.Api.MapUserSettings do
|
||||
action: :read
|
||||
)
|
||||
|
||||
define(:update_hubs, action: :update_hubs)
|
||||
define(:update_settings, action: :update_settings)
|
||||
define(:update_following_character, action: :update_following_character)
|
||||
define(:update_main_character, action: :update_main_character)
|
||||
@@ -52,7 +53,7 @@ defmodule WandererApp.Api.MapUserSettings do
|
||||
:settings
|
||||
]
|
||||
|
||||
defaults [:create, :read]
|
||||
defaults [:create, :read, :update, :destroy]
|
||||
|
||||
update :update_settings do
|
||||
accept [:settings]
|
||||
|
||||
@@ -145,7 +145,12 @@ defmodule WandererApp.Api.UserActivity do
|
||||
:admin_action,
|
||||
:config_change,
|
||||
:bulk_operation,
|
||||
:security_alert
|
||||
:security_alert,
|
||||
# Subscription events
|
||||
:subscription_created,
|
||||
:subscription_updated,
|
||||
:subscription_deleted,
|
||||
:subscription_unknown
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ defmodule WandererApp.Application do
|
||||
Supervisor.child_spec({Cachex, name: :tracked_characters},
|
||||
id: :tracked_characters_cache_worker
|
||||
),
|
||||
Supervisor.child_spec({Cachex, name: :wanderer_app_cache},
|
||||
id: :wanderer_app_cache_worker
|
||||
),
|
||||
{Registry, keys: :unique, name: WandererApp.MapRegistry},
|
||||
{Registry, keys: :unique, name: WandererApp.Character.TrackerRegistry},
|
||||
{PartitionSupervisor,
|
||||
@@ -60,6 +63,14 @@ defmodule WandererApp.Application do
|
||||
if Application.get_env(:wanderer_app, :environment) == :test do
|
||||
[]
|
||||
else
|
||||
security_audit_children =
|
||||
if Application.get_env(:wanderer_app, WandererApp.SecurityAudit, [])
|
||||
|> Keyword.get(:async, false) do
|
||||
[WandererApp.SecurityAudit.AsyncProcessor]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
[
|
||||
WandererApp.Esi.InitClientsTask,
|
||||
WandererApp.Scheduler,
|
||||
@@ -68,7 +79,7 @@ defmodule WandererApp.Application do
|
||||
{WandererApp.Character.TrackerPoolSupervisor, []},
|
||||
WandererApp.Character.TrackerManager,
|
||||
WandererApp.Map.Manager
|
||||
]
|
||||
] ++ security_audit_children
|
||||
end
|
||||
|
||||
children =
|
||||
|
||||
150
lib/wanderer_app/audit/request_context.ex
Normal file
150
lib/wanderer_app/audit/request_context.ex
Normal file
@@ -0,0 +1,150 @@
|
||||
defmodule WandererApp.Audit.RequestContext do
|
||||
@moduledoc """
|
||||
Provides utilities for extracting request context information
|
||||
for audit logging purposes.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
|
||||
@doc """
|
||||
Extract the client's IP address from the connection.
|
||||
|
||||
Simply returns the remote_ip from the connection.
|
||||
"""
|
||||
def get_ip_address(conn) do
|
||||
conn.remote_ip
|
||||
|> :inet.ntoa()
|
||||
|> to_string()
|
||||
rescue
|
||||
error ->
|
||||
Logger.warning("Failed to get IP address: #{inspect(error)}",
|
||||
error: error,
|
||||
stacktrace: __STACKTRACE__
|
||||
)
|
||||
|
||||
"unknown"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract the user agent from the request headers.
|
||||
"""
|
||||
def get_user_agent(conn) do
|
||||
get_header(conn, "user-agent") || "unknown"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract or generate a session ID for the request.
|
||||
"""
|
||||
def get_session_id(conn) do
|
||||
# Try to get from session
|
||||
session_id = get_session(conn, :session_id)
|
||||
|
||||
# Fall back to request ID
|
||||
session_id || get_request_id(conn)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract or generate a request ID for correlation.
|
||||
"""
|
||||
def get_request_id(conn) do
|
||||
# Try standard request ID headers
|
||||
get_header(conn, "x-request-id") ||
|
||||
get_header(conn, "x-correlation-id") ||
|
||||
Logger.metadata()[:request_id] ||
|
||||
generate_request_id()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Build a complete request metadata map for audit logging.
|
||||
"""
|
||||
def build_request_metadata(conn) do
|
||||
%{
|
||||
ip_address: get_ip_address(conn),
|
||||
user_agent: get_user_agent(conn),
|
||||
session_id: get_session_id(conn),
|
||||
request_id: get_request_id(conn),
|
||||
request_path: conn.request_path,
|
||||
method: conn.method |> to_string() |> String.upcase(),
|
||||
host: conn.host,
|
||||
port: conn.port,
|
||||
scheme: conn.scheme |> to_string()
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract user information from the connection.
|
||||
|
||||
Returns a map with user_id and any additional user context.
|
||||
"""
|
||||
def get_user_info(conn) do
|
||||
case conn.assigns[:current_user] do
|
||||
%{id: user_id} = user ->
|
||||
%{
|
||||
user_id: user_id,
|
||||
username: Map.get(user, :username),
|
||||
email: Map.get(user, :email)
|
||||
}
|
||||
|
||||
nil ->
|
||||
%{user_id: nil}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Build a minimal request details map for audit events.
|
||||
|
||||
This is used by existing audit calls that expect specific fields.
|
||||
"""
|
||||
def build_request_details(conn) do
|
||||
metadata = build_request_metadata(conn)
|
||||
|
||||
%{
|
||||
ip_address: metadata.ip_address,
|
||||
user_agent: metadata.user_agent,
|
||||
session_id: metadata.session_id,
|
||||
request_path: metadata.request_path,
|
||||
method: metadata.method
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Set request context in the process dictionary for async logging.
|
||||
"""
|
||||
def set_request_context(conn) do
|
||||
context = %{
|
||||
metadata: build_request_metadata(conn),
|
||||
user_info: get_user_info(conn),
|
||||
timestamp: DateTime.utc_now()
|
||||
}
|
||||
|
||||
Process.put(:audit_request_context, context)
|
||||
conn
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get request context from the process dictionary.
|
||||
"""
|
||||
def get_request_context do
|
||||
Process.get(:audit_request_context)
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp get_header(conn, header) do
|
||||
case Plug.Conn.get_req_header(conn, header) do
|
||||
[value | _] -> value
|
||||
[] -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp get_session(conn, key) do
|
||||
conn
|
||||
|> Plug.Conn.get_session(key)
|
||||
rescue
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
defp generate_request_id do
|
||||
"req_#{:crypto.strong_rand_bytes(16) |> Base.url_encode64(padding: false)}"
|
||||
end
|
||||
end
|
||||
@@ -28,7 +28,7 @@ defmodule WandererApp.Character do
|
||||
Cachex.put(:character_cache, character_id, character)
|
||||
{:ok, character}
|
||||
|
||||
_ ->
|
||||
error ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
|
||||
@@ -283,39 +283,44 @@ defmodule WandererApp.Character do
|
||||
|> case do
|
||||
{:ok, settings} when not is_nil(settings) ->
|
||||
character
|
||||
|> Map.put(:online, false)
|
||||
|> Map.merge(settings)
|
||||
|> Map.merge(%{
|
||||
solar_system_id: settings.solar_system_id,
|
||||
structure_id: settings.structure_id,
|
||||
station_id: settings.station_id,
|
||||
ship: settings.ship,
|
||||
ship_name: settings.ship_name,
|
||||
ship_item_id: settings.ship_item_id
|
||||
})
|
||||
|
||||
_ ->
|
||||
character
|
||||
|> Map.put(:online, false)
|
||||
|> Map.merge(@default_character_tracking_data)
|
||||
end
|
||||
|> Map.merge(%{tracking_paused: tracking_paused})
|
||||
|> Map.merge(%{online: false, tracking_paused: tracking_paused})
|
||||
end
|
||||
|
||||
defp prepare_search_results(result) do
|
||||
{:ok, characters} =
|
||||
_load_eve_info(Map.get(result, "character"), :get_character_info, &_map_character_info/1)
|
||||
load_eve_info(Map.get(result, "character"), :get_character_info, &map_character_info/1)
|
||||
|
||||
{:ok, corporations} =
|
||||
_load_eve_info(
|
||||
load_eve_info(
|
||||
Map.get(result, "corporation"),
|
||||
:get_corporation_info,
|
||||
&_map_corporation_info/1
|
||||
&map_corporation_info/1
|
||||
)
|
||||
|
||||
{:ok, alliances} =
|
||||
_load_eve_info(Map.get(result, "alliance"), :get_alliance_info, &_map_alliance_info/1)
|
||||
load_eve_info(Map.get(result, "alliance"), :get_alliance_info, &map_alliance_info/1)
|
||||
|
||||
[[characters | corporations] | alliances] |> List.flatten()
|
||||
end
|
||||
|
||||
defp _load_eve_info(nil, _, _), do: {:ok, []}
|
||||
defp load_eve_info(nil, _, _), do: {:ok, []}
|
||||
|
||||
defp _load_eve_info([], _, _), do: {:ok, []}
|
||||
defp load_eve_info([], _, _), do: {:ok, []}
|
||||
|
||||
defp _load_eve_info(eve_ids, method, map_function),
|
||||
defp load_eve_info(eve_ids, method, map_function),
|
||||
do:
|
||||
{:ok,
|
||||
Enum.map(eve_ids, fn eve_id ->
|
||||
@@ -331,7 +336,7 @@ defmodule WandererApp.Character do
|
||||
end)
|
||||
|> Enum.filter(fn result -> not is_nil(result) end)}
|
||||
|
||||
defp _map_alliance_info(info) do
|
||||
defp map_alliance_info(info) do
|
||||
%{
|
||||
label: info["name"],
|
||||
value: info["eve_id"] |> to_string(),
|
||||
@@ -339,7 +344,7 @@ defmodule WandererApp.Character do
|
||||
}
|
||||
end
|
||||
|
||||
defp _map_character_info(info) do
|
||||
defp map_character_info(info) do
|
||||
%{
|
||||
label: info["name"],
|
||||
value: info["eve_id"] |> to_string(),
|
||||
@@ -347,7 +352,7 @@ defmodule WandererApp.Character do
|
||||
}
|
||||
end
|
||||
|
||||
defp _map_corporation_info(info) do
|
||||
defp map_corporation_info(info) do
|
||||
%{
|
||||
label: info["name"],
|
||||
value: info["eve_id"] |> to_string(),
|
||||
|
||||
@@ -34,7 +34,10 @@ defmodule WandererApp.ExternalEvents.EventFilter do
|
||||
# ACL events
|
||||
:acl_member_added,
|
||||
:acl_member_removed,
|
||||
:acl_member_updated
|
||||
:acl_member_updated,
|
||||
# Rally point events
|
||||
:rally_point_added,
|
||||
:rally_point_removed
|
||||
]
|
||||
|
||||
@type event_type :: atom()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
defmodule WandererApp.Map.Audit do
|
||||
@moduledoc """
|
||||
Manager map subscription plans
|
||||
|
||||
This module now delegates to SecurityAudit for consistency.
|
||||
It maintains backward compatibility while using the centralized audit system.
|
||||
"""
|
||||
|
||||
require Ash.Query
|
||||
@@ -13,19 +16,15 @@ defmodule WandererApp.Map.Audit do
|
||||
@audit_expired_seconds @month_seconds * 3
|
||||
|
||||
def track_map_subscription_event(event_type, metadata) do
|
||||
case event_type do
|
||||
"subscription.created" ->
|
||||
track_map_event(event_type, metadata)
|
||||
mapped_type =
|
||||
case event_type do
|
||||
"subscription.created" -> :subscription_created
|
||||
"subscription.updated" -> :subscription_updated
|
||||
"subscription.deleted" -> :subscription_deleted
|
||||
_ -> :subscription_unknown
|
||||
end
|
||||
|
||||
"subscription.updated" ->
|
||||
track_map_event(event_type, metadata)
|
||||
|
||||
"subscription.deleted" ->
|
||||
track_map_event(event_type, metadata)
|
||||
|
||||
_ ->
|
||||
{:ok, nil}
|
||||
end
|
||||
track_map_event(mapped_type, metadata)
|
||||
end
|
||||
|
||||
def archive() do
|
||||
@@ -40,191 +39,16 @@ defmodule WandererApp.Map.Audit do
|
||||
end
|
||||
|
||||
def get_activity_query(map_id, period, activity) do
|
||||
{from, to} = period |> get_period()
|
||||
|
||||
query =
|
||||
WandererApp.Api.UserActivity
|
||||
|> Ash.Query.filter(
|
||||
and: [
|
||||
[entity_id: map_id],
|
||||
[inserted_at: [greater_than_or_equal: from]],
|
||||
[inserted_at: [less_than_or_equal: to]]
|
||||
]
|
||||
)
|
||||
|
||||
query =
|
||||
activity
|
||||
|> case do
|
||||
"all" ->
|
||||
query
|
||||
|
||||
activity ->
|
||||
query
|
||||
|> Ash.Query.filter(event_type: activity)
|
||||
end
|
||||
|
||||
query
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
SecurityAudit.get_map_activity_query(map_id, period, activity)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get combined activity including security events for a map.
|
||||
"""
|
||||
def get_combined_activity_query(map_id, period, activity) do
|
||||
{from, to} = period |> get_period()
|
||||
|
||||
# Get regular map activity
|
||||
map_query = get_activity_query(map_id, period, activity)
|
||||
|
||||
# Get security events related to this map
|
||||
security_query =
|
||||
WandererApp.Api.UserActivity
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.filter(inserted_at: [greater_than_or_equal: from])
|
||||
|> Ash.Query.filter(inserted_at: [less_than_or_equal: to])
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|
||||
# Execute both queries and combine results
|
||||
case {Ash.read(map_query), Ash.read(security_query)} do
|
||||
{{:ok, map_activities}, {:ok, security_activities}} ->
|
||||
# Combine and sort by timestamp
|
||||
combined =
|
||||
(map_activities ++ security_activities)
|
||||
|> Enum.sort_by(& &1.inserted_at, {:desc, DateTime})
|
||||
|
||||
{:ok, combined}
|
||||
|
||||
{{:error, _} = error, _} ->
|
||||
error
|
||||
|
||||
{_, {:error, _} = error} ->
|
||||
error
|
||||
end
|
||||
def track_acl_event(event_type, metadata) do
|
||||
SecurityAudit.track_acl_event(event_type, metadata)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get security events for a specific map.
|
||||
"""
|
||||
def get_security_events_for_map(map_id, period \\ "1D") do
|
||||
{from, to} = period |> get_period()
|
||||
|
||||
# Get security events that might be related to this map
|
||||
# This could include data access events, permission denied events, etc.
|
||||
SecurityAudit.get_events_in_range(from, to)
|
||||
|> Enum.filter(fn event ->
|
||||
case Jason.decode(event.event_data || "{}") do
|
||||
{:ok, data} ->
|
||||
# Check if the event data contains references to this map
|
||||
data["resource_id"] == map_id ||
|
||||
data["entity_id"] == map_id ||
|
||||
data["map_id"] == map_id
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end)
|
||||
def track_map_event(event_type, metadata) do
|
||||
SecurityAudit.track_map_event(event_type, metadata)
|
||||
end
|
||||
|
||||
def track_acl_event(
|
||||
event_type,
|
||||
%{user_id: user_id, acl_id: acl_id} = metadata
|
||||
)
|
||||
when not is_nil(user_id) and not is_nil(acl_id),
|
||||
do:
|
||||
WandererApp.Api.UserActivity.new(%{
|
||||
user_id: user_id,
|
||||
entity_type: :access_list,
|
||||
entity_id: acl_id,
|
||||
event_type: event_type,
|
||||
event_data: metadata |> Map.drop([:user_id, :acl_id]) |> Jason.encode!()
|
||||
})
|
||||
|
||||
def track_acl_event(_event_type, _metadata), do: {:ok, nil}
|
||||
|
||||
def track_map_event(
|
||||
event_type,
|
||||
%{character_id: character_id, user_id: user_id, map_id: map_id} = metadata
|
||||
)
|
||||
when not is_nil(character_id) and not is_nil(user_id) and not is_nil(map_id) do
|
||||
# Log regular map activity
|
||||
result =
|
||||
WandererApp.Api.UserActivity.new(%{
|
||||
character_id: character_id,
|
||||
user_id: user_id,
|
||||
entity_type: :map,
|
||||
entity_id: map_id,
|
||||
event_type: event_type,
|
||||
event_data: metadata |> Map.drop([:character_id, :user_id, :map_id]) |> Jason.encode!()
|
||||
})
|
||||
|
||||
# Also log security-relevant map events
|
||||
if security_relevant_event?(event_type) do
|
||||
SecurityAudit.log_data_access(
|
||||
"map",
|
||||
map_id,
|
||||
user_id,
|
||||
event_type,
|
||||
metadata
|
||||
)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def track_map_event(_event_type, _metadata), do: {:ok, nil}
|
||||
|
||||
defp get_period("1H") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-1 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1D") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1W") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 7, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("2M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31 * 2, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("3M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = now |> DateTime.add(-24 * 3600 * 31 * 3, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period(_), do: get_period("1H")
|
||||
|
||||
defp get_expired_at(), do: DateTime.utc_now() |> DateTime.add(-@audit_expired_seconds, :second)
|
||||
|
||||
defp security_relevant_event?(event_type) do
|
||||
# Define which map events should also be logged as security events
|
||||
event_type in [
|
||||
:map_acl_added,
|
||||
:map_acl_removed,
|
||||
:map_acl_updated,
|
||||
:map_acl_member_added,
|
||||
:map_acl_member_removed,
|
||||
:map_acl_member_updated,
|
||||
:map_removed,
|
||||
:character_added,
|
||||
:character_removed
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
898
lib/wanderer_app/security_audit.ex
Normal file
898
lib/wanderer_app/security_audit.ex
Normal file
@@ -0,0 +1,898 @@
|
||||
defmodule WandererApp.SecurityAudit do
|
||||
@moduledoc """
|
||||
Comprehensive security audit logging system.
|
||||
|
||||
This module provides centralized logging for security-related events including:
|
||||
- Authentication events (login, logout, failures)
|
||||
- Authorization events (permission denied, privilege escalation)
|
||||
- Data access events (sensitive queries, bulk exports)
|
||||
- Configuration changes and admin actions
|
||||
"""
|
||||
|
||||
require Logger
|
||||
require Ash.Query
|
||||
|
||||
alias WandererApp.Api.UserActivity
|
||||
|
||||
@doc """
|
||||
Log a security event with structured data.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> WandererApp.SecurityAudit.log_event(:auth_success, user_id, %{
|
||||
...> ip_address: "192.168.1.100",
|
||||
...> user_agent: "Mozilla/5.0...",
|
||||
...> auth_method: "session"
|
||||
...> })
|
||||
:ok
|
||||
"""
|
||||
def log_event(event_type, user_id, details \\ %{}) do
|
||||
audit_entry = %{
|
||||
event_type: event_type,
|
||||
user_id: user_id,
|
||||
timestamp: DateTime.utc_now(),
|
||||
details: details,
|
||||
severity: determine_severity(event_type),
|
||||
session_id: details[:session_id],
|
||||
ip_address: details[:ip_address],
|
||||
user_agent: details[:user_agent]
|
||||
}
|
||||
|
||||
# Store in database
|
||||
store_audit_entry(audit_entry)
|
||||
|
||||
# Send to telemetry for monitoring
|
||||
emit_telemetry_event(audit_entry)
|
||||
|
||||
# Log to application logs
|
||||
log_to_application_log(audit_entry)
|
||||
|
||||
# Check for security alerts
|
||||
check_security_alerts(audit_entry)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log authentication events.
|
||||
"""
|
||||
def log_auth_event(event_type, user_id, request_details) do
|
||||
# Start with the basic required fields
|
||||
details = %{
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
auth_method: request_details[:auth_method],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
# Merge any additional fields from request_details
|
||||
details = Map.merge(details, request_details)
|
||||
|
||||
log_event(event_type, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log data access events.
|
||||
"""
|
||||
def log_data_access(resource_type, resource_id, user_id, action, request_details \\ %{}) do
|
||||
details = %{
|
||||
resource_type: resource_type,
|
||||
resource_id: resource_id,
|
||||
action: action,
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
log_event(:data_access, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log permission denied events.
|
||||
"""
|
||||
def log_permission_denied(
|
||||
resource_type,
|
||||
resource_id,
|
||||
user_id,
|
||||
attempted_action,
|
||||
request_details \\ %{}
|
||||
) do
|
||||
details = %{
|
||||
resource_type: resource_type,
|
||||
resource_id: resource_id,
|
||||
attempted_action: attempted_action,
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
log_event(:permission_denied, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log admin actions.
|
||||
"""
|
||||
def log_admin_action(action, user_id, target_resource, request_details \\ %{}) do
|
||||
details = %{
|
||||
action: action,
|
||||
target_resource: target_resource,
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
log_event(:admin_action, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log configuration changes.
|
||||
"""
|
||||
def log_config_change(config_key, old_value, new_value, user_id, request_details \\ %{}) do
|
||||
details = %{
|
||||
config_key: config_key,
|
||||
old_value: sanitize_sensitive_data(old_value),
|
||||
new_value: sanitize_sensitive_data(new_value),
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
log_event(:config_change, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log bulk data operations.
|
||||
"""
|
||||
def log_bulk_operation(operation_type, record_count, user_id, request_details \\ %{}) do
|
||||
details = %{
|
||||
operation_type: operation_type,
|
||||
record_count: record_count,
|
||||
ip_address: request_details[:ip_address],
|
||||
user_agent: request_details[:user_agent],
|
||||
session_id: request_details[:session_id]
|
||||
}
|
||||
|
||||
log_event(:bulk_operation, user_id, details)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get audit events for a specific user.
|
||||
"""
|
||||
def get_user_audit_events(user_id, limit \\ 100) do
|
||||
UserActivity
|
||||
|> Ash.Query.filter(user_id: user_id)
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|> Ash.Query.limit(limit)
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get recent security events.
|
||||
"""
|
||||
def get_recent_events(limit \\ 50) do
|
||||
UserActivity
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|> Ash.Query.limit(limit)
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get security events by type.
|
||||
"""
|
||||
def get_events_by_type(event_type, limit \\ 50) do
|
||||
UserActivity
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.filter(event_type: event_type)
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|> Ash.Query.limit(limit)
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get security events within a time range.
|
||||
"""
|
||||
def get_events_in_range(from_datetime, to_datetime, limit \\ 100) do
|
||||
UserActivity
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.filter(inserted_at: [greater_than_or_equal: from_datetime])
|
||||
|> Ash.Query.filter(inserted_at: [less_than_or_equal: to_datetime])
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|> Ash.Query.limit(limit)
|
||||
|> Ash.read!()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Track map-related events (compatibility with Map.Audit).
|
||||
"""
|
||||
def track_map_event(
|
||||
event_type,
|
||||
%{character_id: character_id, user_id: user_id, map_id: map_id} = metadata
|
||||
)
|
||||
when not is_nil(character_id) and not is_nil(user_id) and not is_nil(map_id) do
|
||||
# Sanitize and prepare metadata
|
||||
sanitized_metadata =
|
||||
metadata
|
||||
|> Map.drop([:character_id, :user_id, :map_id])
|
||||
|> sanitize_metadata()
|
||||
|
||||
attrs = %{
|
||||
character_id: character_id,
|
||||
user_id: user_id,
|
||||
entity_type: :map,
|
||||
entity_id: map_id,
|
||||
event_type: normalize_event_type(event_type),
|
||||
event_data: Jason.encode!(sanitized_metadata)
|
||||
}
|
||||
|
||||
case UserActivity.new(attrs) do
|
||||
{:ok, activity} ->
|
||||
{:ok, activity}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to track map event",
|
||||
error: inspect(error),
|
||||
event_type: event_type,
|
||||
map_id: map_id
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def track_map_event(_event_type, _metadata), do: {:ok, nil}
|
||||
|
||||
@doc """
|
||||
Track ACL-related events (compatibility with Map.Audit).
|
||||
"""
|
||||
def track_acl_event(
|
||||
event_type,
|
||||
%{user_id: user_id, acl_id: acl_id} = metadata
|
||||
)
|
||||
when not is_nil(user_id) and not is_nil(acl_id) do
|
||||
# Sanitize and prepare metadata
|
||||
sanitized_metadata =
|
||||
metadata
|
||||
|> Map.drop([:user_id, :acl_id])
|
||||
|> sanitize_metadata()
|
||||
|
||||
attrs = %{
|
||||
user_id: user_id,
|
||||
entity_type: :access_list,
|
||||
entity_id: acl_id,
|
||||
event_type: normalize_event_type(event_type),
|
||||
event_data: Jason.encode!(sanitized_metadata)
|
||||
}
|
||||
|
||||
case UserActivity.new(attrs) do
|
||||
{:ok, activity} ->
|
||||
{:ok, activity}
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to track ACL event",
|
||||
error: inspect(error),
|
||||
event_type: event_type,
|
||||
acl_id: acl_id
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def track_acl_event(_event_type, _metadata), do: {:ok, nil}
|
||||
|
||||
@doc """
|
||||
Get activity query for maps (compatibility with Map.Audit).
|
||||
"""
|
||||
def get_map_activity_query(map_id, period, activity \\ "all") do
|
||||
{from, to} = get_period(period)
|
||||
|
||||
query =
|
||||
UserActivity
|
||||
|> Ash.Query.filter(
|
||||
and: [
|
||||
[entity_id: map_id],
|
||||
[inserted_at: [greater_than_or_equal: from]],
|
||||
[inserted_at: [less_than_or_equal: to]]
|
||||
]
|
||||
)
|
||||
|
||||
query =
|
||||
case activity do
|
||||
"all" ->
|
||||
query
|
||||
|
||||
activity ->
|
||||
query
|
||||
|> Ash.Query.filter(event_type: normalize_event_type(activity))
|
||||
end
|
||||
|
||||
query
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
end
|
||||
|
||||
defp get_period("1H") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -1 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1D") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -24 * 3600, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1W") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -24 * 3600 * 7, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("1M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -24 * 3600 * 31, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("2M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -24 * 3600 * 31 * 2, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period("3M") do
|
||||
now = DateTime.utc_now()
|
||||
start_date = DateTime.add(now, -24 * 3600 * 31 * 3, :second)
|
||||
{start_date, now}
|
||||
end
|
||||
|
||||
defp get_period(_), do: get_period("1H")
|
||||
|
||||
@doc """
|
||||
Check for suspicious patterns in user activity.
|
||||
"""
|
||||
def analyze_user_behavior(user_id, time_window \\ 3600) do
|
||||
now = DateTime.utc_now()
|
||||
from_time = DateTime.add(now, -time_window, :second)
|
||||
|
||||
# Get recent activities
|
||||
activities =
|
||||
UserActivity
|
||||
|> Ash.Query.filter(user_id: user_id)
|
||||
|> Ash.Query.filter(entity_type: :security_event)
|
||||
|> Ash.Query.filter(inserted_at: [greater_than_or_equal: from_time])
|
||||
|> Ash.Query.sort(inserted_at: :desc)
|
||||
|> Ash.read!()
|
||||
|
||||
# Analyze patterns
|
||||
patterns = analyze_patterns(activities)
|
||||
risk_score = calculate_risk_score(patterns)
|
||||
recommendations = generate_recommendations(patterns, risk_score)
|
||||
|
||||
%{
|
||||
risk_score: risk_score,
|
||||
suspicious_patterns: patterns,
|
||||
recommendations: recommendations,
|
||||
activities_analyzed: length(activities),
|
||||
time_window_seconds: time_window
|
||||
}
|
||||
end
|
||||
|
||||
defp analyze_patterns(activities) do
|
||||
patterns = []
|
||||
|
||||
# Count by event type
|
||||
event_counts = Enum.frequencies_by(activities, & &1.event_type)
|
||||
|
||||
# Check for multiple auth failures
|
||||
auth_failures = Map.get(event_counts, :auth_failure, 0)
|
||||
|
||||
patterns =
|
||||
if auth_failures >= 3 do
|
||||
[{:multiple_auth_failures, auth_failures} | patterns]
|
||||
else
|
||||
patterns
|
||||
end
|
||||
|
||||
# Check for permission denied spikes
|
||||
permission_denied = Map.get(event_counts, :permission_denied, 0)
|
||||
|
||||
patterns =
|
||||
if permission_denied >= 5 do
|
||||
[{:excessive_permission_denials, permission_denied} | patterns]
|
||||
else
|
||||
patterns
|
||||
end
|
||||
|
||||
# Check for rapid activity (more than 100 events in time window)
|
||||
patterns =
|
||||
if length(activities) > 100 do
|
||||
[{:high_activity_volume, length(activities)} | patterns]
|
||||
else
|
||||
patterns
|
||||
end
|
||||
|
||||
# Check for geographic anomalies by analyzing unique IPs
|
||||
unique_ips =
|
||||
activities
|
||||
|> Enum.map(fn activity ->
|
||||
case Jason.decode(activity.event_data || "{}") do
|
||||
{:ok, data} -> data["ip_address"]
|
||||
_ -> nil
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.uniq()
|
||||
|> length()
|
||||
|
||||
patterns =
|
||||
if unique_ips > 5 do
|
||||
[{:multiple_ip_addresses, unique_ips} | patterns]
|
||||
else
|
||||
patterns
|
||||
end
|
||||
|
||||
patterns
|
||||
end
|
||||
|
||||
defp calculate_risk_score(patterns) do
|
||||
score =
|
||||
Enum.reduce(patterns, 0, fn
|
||||
{:multiple_auth_failures, count}, acc -> acc + count * 2
|
||||
{:excessive_permission_denials, count}, acc -> acc + count * 1.5
|
||||
{:high_activity_volume, _}, acc -> acc + 5
|
||||
{:multiple_ip_addresses, count}, acc -> acc + count * 3
|
||||
_, acc -> acc
|
||||
end)
|
||||
|
||||
cond do
|
||||
score >= 20 -> :critical
|
||||
score >= 10 -> :high
|
||||
score >= 5 -> :medium
|
||||
true -> :low
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_recommendations(patterns, risk_score) do
|
||||
base_recommendations =
|
||||
case risk_score do
|
||||
:critical -> ["Immediate review required", "Consider blocking user temporarily"]
|
||||
:high -> ["Monitor user activity closely", "Review recent actions"]
|
||||
:medium -> ["Keep user under observation"]
|
||||
:low -> []
|
||||
end
|
||||
|
||||
pattern_recommendations =
|
||||
Enum.flat_map(patterns, fn
|
||||
{:multiple_auth_failures, _} ->
|
||||
["Reset user password", "Enable MFA"]
|
||||
|
||||
{:excessive_permission_denials, _} ->
|
||||
["Review user permissions", "Check for compromised account"]
|
||||
|
||||
{:high_activity_volume, _} ->
|
||||
["Check for automated activity", "Review API usage"]
|
||||
|
||||
{:multiple_ip_addresses, _} ->
|
||||
["Verify user location changes", "Check for account sharing"]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end)
|
||||
|
||||
Enum.uniq(base_recommendations ++ pattern_recommendations)
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp store_audit_entry(audit_entry) do
|
||||
# Handle async processing if enabled
|
||||
if async_enabled?() do
|
||||
WandererApp.SecurityAudit.AsyncProcessor.log_event(audit_entry)
|
||||
else
|
||||
do_store_audit_entry(audit_entry)
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def do_store_audit_entry(audit_entry) do
|
||||
# Ensure event_type is properly formatted
|
||||
event_type = normalize_event_type(audit_entry.event_type)
|
||||
|
||||
attrs = %{
|
||||
user_id: audit_entry.user_id,
|
||||
character_id: nil,
|
||||
entity_id: hash_identifier(audit_entry.session_id),
|
||||
entity_type: :security_event,
|
||||
event_type: event_type,
|
||||
event_data: encode_event_data(audit_entry)
|
||||
}
|
||||
|
||||
case UserActivity.new(attrs) do
|
||||
{:ok, _activity} ->
|
||||
:ok
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to store security audit entry",
|
||||
error: inspect(error),
|
||||
event_type: event_type,
|
||||
user_id: audit_entry.user_id
|
||||
)
|
||||
|
||||
# Emit telemetry for monitoring
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :security_audit, :storage_error],
|
||||
%{count: 1},
|
||||
%{event_type: event_type, error: error}
|
||||
)
|
||||
|
||||
# Don't block the request, but track the failure
|
||||
{:error, :storage_failed}
|
||||
end
|
||||
end
|
||||
|
||||
defp hash_identifier(identifier) when is_binary(identifier) do
|
||||
secret_salt =
|
||||
Application.get_env(:wanderer_app, :secret_key_base) ||
|
||||
raise "SECRET_KEY_BASE not configured"
|
||||
|
||||
:crypto.hash(:sha256, secret_salt <> identifier)
|
||||
|> Base.encode16(case: :lower)
|
||||
end
|
||||
|
||||
defp hash_identifier(nil), do: generate_entity_id()
|
||||
|
||||
defp normalize_event_type(event_type) when is_atom(event_type), do: event_type
|
||||
|
||||
defp normalize_event_type(event_type) when is_binary(event_type) do
|
||||
try do
|
||||
String.to_existing_atom(event_type)
|
||||
rescue
|
||||
ArgumentError -> :security_alert
|
||||
end
|
||||
end
|
||||
|
||||
defp normalize_event_type(_), do: :security_alert
|
||||
|
||||
defp encode_event_data(audit_entry) do
|
||||
sanitized_details = sanitize_for_json(audit_entry.details)
|
||||
|
||||
data =
|
||||
Map.merge(sanitized_details, %{
|
||||
timestamp: convert_datetime(audit_entry.timestamp),
|
||||
severity: to_string(audit_entry.severity),
|
||||
ip_address: audit_entry.ip_address,
|
||||
user_agent: audit_entry.user_agent
|
||||
})
|
||||
|
||||
case Jason.encode(data) do
|
||||
{:ok, json} -> json
|
||||
{:error, _} -> Jason.encode!(%{error: "Failed to encode audit data"})
|
||||
end
|
||||
end
|
||||
|
||||
defp sanitize_for_json(data) when is_map(data) do
|
||||
data
|
||||
|> Enum.reduce(%{}, fn {key, value}, acc ->
|
||||
sanitized_key = to_string(key)
|
||||
|
||||
# Skip sensitive fields
|
||||
if sanitized_key in ~w(password secret token private_key api_key) do
|
||||
acc
|
||||
else
|
||||
Map.put(acc, sanitized_key, sanitize_value(value))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp sanitize_for_json(data), do: sanitize_value(data)
|
||||
|
||||
defp sanitize_metadata(metadata) do
|
||||
# List of sensitive keys to remove from metadata
|
||||
sensitive_keys = [:password, :token, :secret, :api_key, :private_key, :auth_token]
|
||||
|
||||
metadata
|
||||
|> Map.drop(sensitive_keys)
|
||||
|> Enum.map(fn {k, v} ->
|
||||
# Ensure keys are strings or atoms
|
||||
key = if is_binary(k), do: k, else: to_string(k)
|
||||
{key, sanitize_value(v)}
|
||||
end)
|
||||
|> Enum.into(%{})
|
||||
end
|
||||
|
||||
defp sanitize_value(%DateTime{} = dt), do: DateTime.to_iso8601(dt)
|
||||
defp sanitize_value(%NaiveDateTime{} = dt), do: NaiveDateTime.to_iso8601(dt)
|
||||
defp sanitize_value(%Date{} = date), do: Date.to_iso8601(date)
|
||||
defp sanitize_value(%Time{} = time), do: Time.to_iso8601(time)
|
||||
|
||||
defp sanitize_value(atom) when is_atom(atom) and not is_nil(atom) and not is_boolean(atom),
|
||||
do: to_string(atom)
|
||||
|
||||
defp sanitize_value(list) when is_list(list), do: Enum.map(list, &sanitize_value/1)
|
||||
defp sanitize_value(map) when is_map(map), do: sanitize_for_json(map)
|
||||
defp sanitize_value(value), do: value
|
||||
|
||||
defp convert_datetime(%DateTime{} = dt), do: DateTime.to_iso8601(dt)
|
||||
defp convert_datetime(%NaiveDateTime{} = dt), do: NaiveDateTime.to_iso8601(dt)
|
||||
defp convert_datetime(value), do: value
|
||||
|
||||
defp generate_entity_id do
|
||||
"audit_#{DateTime.utc_now() |> DateTime.to_unix(:microsecond)}_#{System.unique_integer([:positive])}"
|
||||
end
|
||||
|
||||
defp async_enabled? do
|
||||
Application.get_env(:wanderer_app, __MODULE__, [])
|
||||
|> Keyword.get(:async, false)
|
||||
end
|
||||
|
||||
defp emit_telemetry_event(audit_entry) do
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :security_audit],
|
||||
%{count: 1},
|
||||
%{
|
||||
event_type: audit_entry.event_type,
|
||||
severity: audit_entry.severity,
|
||||
user_id: audit_entry.user_id
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp log_to_application_log(audit_entry) do
|
||||
log_level =
|
||||
case audit_entry.severity do
|
||||
:critical -> :error
|
||||
:high -> :warning
|
||||
:medium -> :info
|
||||
:low -> :debug
|
||||
end
|
||||
|
||||
Logger.log(log_level, "Security audit: #{audit_entry.event_type}",
|
||||
user_id: audit_entry.user_id,
|
||||
timestamp: audit_entry.timestamp,
|
||||
details: audit_entry.details
|
||||
)
|
||||
end
|
||||
|
||||
defp check_security_alerts(audit_entry) do
|
||||
case audit_entry.event_type do
|
||||
:auth_failure ->
|
||||
check_failed_login_attempts(audit_entry)
|
||||
|
||||
:permission_denied ->
|
||||
check_privilege_escalation_attempts(audit_entry)
|
||||
|
||||
:bulk_operation ->
|
||||
check_bulk_data_access(audit_entry)
|
||||
|
||||
:security_alert ->
|
||||
# Already a security alert, don't double-check
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp check_failed_login_attempts(audit_entry) do
|
||||
config = threat_detection_config()
|
||||
|
||||
if config[:enabled] do
|
||||
ip_address = audit_entry.ip_address || "unknown"
|
||||
cache_key = "auth_failures:#{ip_address}"
|
||||
window = config[:window_seconds] || 300
|
||||
max_attempts = config[:max_failed_attempts] || 5
|
||||
|
||||
# Increment counter in Cachex with TTL
|
||||
count =
|
||||
case Cachex.incr(:wanderer_app_cache, cache_key) do
|
||||
{:ok, count} ->
|
||||
# Set TTL on first increment
|
||||
if count == 1 do
|
||||
Cachex.expire(:wanderer_app_cache, cache_key, :timer.seconds(window))
|
||||
end
|
||||
|
||||
count
|
||||
|
||||
{:error, :no_key} ->
|
||||
# Key doesn't exist, initialize it with TTL
|
||||
case Cachex.put(:wanderer_app_cache, cache_key, 1, ttl: :timer.seconds(window)) do
|
||||
{:ok, _} ->
|
||||
1
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to initialize auth failure counter",
|
||||
error: inspect(error),
|
||||
cache_key: cache_key
|
||||
)
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
# Other errors - log and return safe default
|
||||
Logger.error("Failed to increment auth failure counter",
|
||||
error: inspect(error),
|
||||
cache_key: cache_key
|
||||
)
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
if count >= max_attempts do
|
||||
Logger.warning("Potential brute force attack detected",
|
||||
ip_address: ip_address,
|
||||
attempts: count,
|
||||
user_id: audit_entry.user_id
|
||||
)
|
||||
|
||||
# Emit security alert
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :security_audit, :threat_detected],
|
||||
%{count: 1},
|
||||
%{threat_type: :brute_force, ip_address: ip_address}
|
||||
)
|
||||
|
||||
# Log a security alert event
|
||||
log_event(:security_alert, audit_entry.user_id, %{
|
||||
threat_type: "brute_force",
|
||||
ip_address: ip_address,
|
||||
failed_attempts: count,
|
||||
window_seconds: window
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp check_privilege_escalation_attempts(audit_entry) do
|
||||
config = threat_detection_config()
|
||||
|
||||
if config[:enabled] && audit_entry.user_id do
|
||||
cache_key = "privilege_escalation:#{audit_entry.user_id}"
|
||||
window = config[:window_seconds] || 300
|
||||
max_denials = config[:max_permission_denials] || 10
|
||||
|
||||
count =
|
||||
case Cachex.incr(:wanderer_app_cache, cache_key) do
|
||||
{:ok, count} ->
|
||||
if count == 1 do
|
||||
Cachex.expire(:wanderer_app_cache, cache_key, :timer.seconds(window))
|
||||
end
|
||||
|
||||
count
|
||||
|
||||
{:error, :no_key} ->
|
||||
# Key doesn't exist, initialize it with TTL
|
||||
case Cachex.put(:wanderer_app_cache, cache_key, 1, ttl: :timer.seconds(window)) do
|
||||
{:ok, _} ->
|
||||
1
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Failed to initialize privilege escalation counter",
|
||||
error: inspect(error),
|
||||
cache_key: cache_key
|
||||
)
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
# Other errors - log and return safe default
|
||||
Logger.error("Failed to increment privilege escalation counter",
|
||||
error: inspect(error),
|
||||
cache_key: cache_key
|
||||
)
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
if count >= max_denials do
|
||||
Logger.warning("Potential privilege escalation attempt detected",
|
||||
user_id: audit_entry.user_id,
|
||||
denials: count,
|
||||
resource_type: audit_entry.details[:resource_type]
|
||||
)
|
||||
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :security_audit, :threat_detected],
|
||||
%{count: 1},
|
||||
%{threat_type: :privilege_escalation, user_id: audit_entry.user_id}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp check_bulk_data_access(audit_entry) do
|
||||
config = threat_detection_config()
|
||||
|
||||
if config[:enabled] && audit_entry.user_id do
|
||||
record_count = audit_entry.details[:record_count] || 0
|
||||
threshold = config[:bulk_operation_threshold] || 10000
|
||||
|
||||
if record_count > threshold do
|
||||
Logger.warning("Large bulk operation detected",
|
||||
user_id: audit_entry.user_id,
|
||||
operation_type: audit_entry.details[:operation_type],
|
||||
record_count: record_count
|
||||
)
|
||||
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :security_audit, :bulk_operation],
|
||||
%{record_count: record_count},
|
||||
%{user_id: audit_entry.user_id, operation_type: audit_entry.details[:operation_type]}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp threat_detection_config do
|
||||
Application.get_env(:wanderer_app, __MODULE__, [])
|
||||
|> Keyword.get(:threat_detection, %{})
|
||||
end
|
||||
|
||||
defp determine_severity(event_type) do
|
||||
case event_type do
|
||||
:auth_failure -> :medium
|
||||
:permission_denied -> :high
|
||||
:privilege_escalation -> :critical
|
||||
:config_change -> :high
|
||||
:admin_action -> :medium
|
||||
:bulk_operation -> :medium
|
||||
:data_access -> :low
|
||||
:auth_success -> :low
|
||||
_ -> :medium
|
||||
end
|
||||
end
|
||||
|
||||
defp sanitize_sensitive_data(value) when is_binary(value) do
|
||||
# Patterns to detect sensitive data
|
||||
sensitive_patterns = [
|
||||
~r/password/i,
|
||||
~r/token/i,
|
||||
~r/secret/i,
|
||||
~r/api[_-]?key/i,
|
||||
~r/private[_-]?key/i,
|
||||
~r/access[_-]?key/i,
|
||||
~r/auth/i,
|
||||
~r/bearer\s+[a-zA-Z0-9\-_]+/i,
|
||||
# Long hex strings (potential tokens)
|
||||
~r/[a-f0-9]{32,}/i
|
||||
]
|
||||
|
||||
# Check if value contains sensitive patterns
|
||||
is_sensitive = Enum.any?(sensitive_patterns, &Regex.match?(&1, value))
|
||||
|
||||
cond do
|
||||
is_sensitive -> "[REDACTED]"
|
||||
String.length(value) > 200 -> String.slice(value, 0, 200) <> "..."
|
||||
true -> value
|
||||
end
|
||||
end
|
||||
|
||||
defp sanitize_sensitive_data(value) when is_map(value) do
|
||||
# Recursively sanitize map values
|
||||
Map.new(value, fn {k, v} ->
|
||||
key_str = to_string(k)
|
||||
|
||||
if Regex.match?(~r/password|token|secret|key|auth/i, key_str) do
|
||||
{k, "[REDACTED]"}
|
||||
else
|
||||
{k, sanitize_sensitive_data(v)}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp sanitize_sensitive_data(value) when is_list(value) do
|
||||
Enum.map(value, &sanitize_sensitive_data/1)
|
||||
end
|
||||
|
||||
defp sanitize_sensitive_data(value), do: value
|
||||
end
|
||||
246
lib/wanderer_app/security_audit/async_processor.ex
Normal file
246
lib/wanderer_app/security_audit/async_processor.ex
Normal file
@@ -0,0 +1,246 @@
|
||||
defmodule WandererApp.SecurityAudit.AsyncProcessor do
|
||||
@moduledoc """
|
||||
GenServer for asynchronous batch processing of security audit events.
|
||||
|
||||
This server buffers audit events in memory and periodically flushes them
|
||||
to the database in batches for improved performance.
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
alias WandererApp.SecurityAudit
|
||||
|
||||
@default_batch_size 100
|
||||
# 5 seconds
|
||||
@default_flush_interval 5_000
|
||||
@max_buffer_size 1_000
|
||||
|
||||
defstruct [
|
||||
:batch_size,
|
||||
:flush_interval,
|
||||
:buffer,
|
||||
:timer_ref,
|
||||
:stats
|
||||
]
|
||||
|
||||
# Client API
|
||||
|
||||
@doc """
|
||||
Start the async processor.
|
||||
"""
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Log an event asynchronously.
|
||||
"""
|
||||
def log_event(audit_entry) do
|
||||
GenServer.cast(__MODULE__, {:log_event, audit_entry})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Force a flush of the buffer.
|
||||
"""
|
||||
def flush do
|
||||
GenServer.call(__MODULE__, :flush)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get current processor statistics.
|
||||
"""
|
||||
def get_stats do
|
||||
GenServer.call(__MODULE__, :get_stats)
|
||||
end
|
||||
|
||||
# Server callbacks
|
||||
|
||||
@impl true
|
||||
def init(opts) do
|
||||
config = Application.get_env(:wanderer_app, WandererApp.SecurityAudit, [])
|
||||
|
||||
batch_size = Keyword.get(opts, :batch_size, config[:batch_size] || @default_batch_size)
|
||||
|
||||
flush_interval =
|
||||
Keyword.get(opts, :flush_interval, config[:flush_interval] || @default_flush_interval)
|
||||
|
||||
state = %__MODULE__{
|
||||
batch_size: batch_size,
|
||||
flush_interval: flush_interval,
|
||||
buffer: [],
|
||||
timer_ref: nil,
|
||||
stats: %{
|
||||
events_processed: 0,
|
||||
batches_flushed: 0,
|
||||
errors: 0,
|
||||
last_flush: nil
|
||||
}
|
||||
}
|
||||
|
||||
# Schedule first flush
|
||||
state = schedule_flush(state)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast({:log_event, audit_entry}, state) do
|
||||
# Add to buffer
|
||||
buffer = [audit_entry | state.buffer]
|
||||
|
||||
# Update stats
|
||||
stats = Map.update!(state.stats, :events_processed, &(&1 + 1))
|
||||
|
||||
# Check if we need to flush
|
||||
cond do
|
||||
length(buffer) >= state.batch_size ->
|
||||
# Flush immediately if batch size reached
|
||||
{:noreply, do_flush(%{state | buffer: buffer, stats: stats})}
|
||||
|
||||
length(buffer) >= @max_buffer_size ->
|
||||
# Force flush if max buffer size reached
|
||||
Logger.warning("Security audit buffer overflow, forcing flush",
|
||||
buffer_size: length(buffer),
|
||||
max_size: @max_buffer_size
|
||||
)
|
||||
|
||||
{:noreply, do_flush(%{state | buffer: buffer, stats: stats})}
|
||||
|
||||
true ->
|
||||
# Just add to buffer
|
||||
{:noreply, %{state | buffer: buffer, stats: stats}}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:flush, _from, state) do
|
||||
new_state = do_flush(state)
|
||||
{:reply, :ok, new_state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_stats, _from, state) do
|
||||
stats = Map.put(state.stats, :current_buffer_size, length(state.buffer))
|
||||
{:reply, stats, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:flush_timer, state) do
|
||||
state =
|
||||
if length(state.buffer) > 0 do
|
||||
do_flush(state)
|
||||
else
|
||||
state
|
||||
end
|
||||
|
||||
# Schedule next flush
|
||||
state = schedule_flush(state)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_reason, state) do
|
||||
# Flush any remaining events on shutdown
|
||||
if length(state.buffer) > 0 do
|
||||
do_flush(state)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp schedule_flush(state) do
|
||||
# Cancel existing timer if any
|
||||
if state.timer_ref do
|
||||
Process.cancel_timer(state.timer_ref)
|
||||
end
|
||||
|
||||
# Schedule new timer
|
||||
timer_ref = Process.send_after(self(), :flush_timer, state.flush_interval)
|
||||
|
||||
%{state | timer_ref: timer_ref}
|
||||
end
|
||||
|
||||
defp do_flush(state) when length(state.buffer) == 0 do
|
||||
state
|
||||
end
|
||||
|
||||
defp do_flush(state) do
|
||||
# Take events to flush (reverse to maintain order)
|
||||
events = Enum.reverse(state.buffer)
|
||||
|
||||
# Attempt to store events
|
||||
case bulk_store_events(events) do
|
||||
{:ok, count} ->
|
||||
Logger.debug("Flushed #{count} security audit events")
|
||||
|
||||
# Update stats
|
||||
stats =
|
||||
state.stats
|
||||
|> Map.update!(:batches_flushed, &(&1 + 1))
|
||||
|> Map.put(:last_flush, DateTime.utc_now())
|
||||
|
||||
# Clear buffer
|
||||
%{state | buffer: [], stats: stats}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Failed to flush security audit events",
|
||||
reason: inspect(reason),
|
||||
event_count: length(events)
|
||||
)
|
||||
|
||||
# Update error stats
|
||||
stats = Map.update!(state.stats, :errors, &(&1 + 1))
|
||||
|
||||
# Implement backoff - keep events in buffer but don't grow indefinitely
|
||||
buffer =
|
||||
if length(state.buffer) > @max_buffer_size do
|
||||
Logger.warning("Dropping oldest audit events due to repeated flush failures")
|
||||
Enum.take(state.buffer, @max_buffer_size)
|
||||
else
|
||||
state.buffer
|
||||
end
|
||||
|
||||
%{state | buffer: buffer, stats: stats}
|
||||
end
|
||||
end
|
||||
|
||||
defp bulk_store_events(events) do
|
||||
# Process events in smaller chunks if necessary
|
||||
events
|
||||
# Ash bulk operations work better with smaller chunks
|
||||
|> Enum.chunk_every(50)
|
||||
|> Enum.reduce_while({:ok, 0}, fn chunk, {:ok, count} ->
|
||||
case store_event_chunk(chunk) do
|
||||
{:ok, chunk_count} ->
|
||||
{:cont, {:ok, count + chunk_count}}
|
||||
|
||||
{:error, _} = error ->
|
||||
{:halt, error}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp store_event_chunk(events) do
|
||||
# Transform events to Ash attributes
|
||||
records =
|
||||
Enum.map(events, fn event ->
|
||||
SecurityAudit.do_store_audit_entry(event)
|
||||
end)
|
||||
|
||||
# Count successful stores
|
||||
successful =
|
||||
Enum.count(records, fn
|
||||
:ok -> true
|
||||
_ -> false
|
||||
end)
|
||||
|
||||
{:ok, successful}
|
||||
rescue
|
||||
error ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
@@ -65,8 +65,7 @@ defmodule WandererAppWeb.CoreComponents do
|
||||
phx-mounted={@show && show_modal(@id)}
|
||||
phx-remove={hide_modal(@id)}
|
||||
data-cancel={JS.exec(@on_cancel, "phx-remove")}
|
||||
class="relative z-50 hidden overflow-visible"
|
||||
class=""
|
||||
class="relative z-[1000] hidden overflow-visible"
|
||||
>
|
||||
<div id={"#{@id}-bg"} class="overflow-visible p-dialog-resizable" aria-hidden="true" />
|
||||
<div
|
||||
|
||||
@@ -2,14 +2,20 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
@moduledoc """
|
||||
Plug for authenticating JSON:API v1 endpoints.
|
||||
|
||||
Supports both session-based authentication (for web clients) and
|
||||
Supports both session-based authentication (for web clients) and
|
||||
Bearer token authentication (for API clients).
|
||||
|
||||
Currently, Bearer token authentication only supports map API keys.
|
||||
When a valid map API key is provided, the map owner is set as the
|
||||
authenticated user and the map is made available in conn.assigns.
|
||||
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
alias WandererApp.Api.User
|
||||
alias WandererApp.SecurityAudit
|
||||
alias WandererApp.Audit.RequestContext
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
@@ -57,7 +63,8 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
|> assign(:current_user, user)
|
||||
|> assign(:current_user_role, get_user_role(user))
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason} when is_binary(reason) ->
|
||||
# Legacy error handling for simple string errors
|
||||
end_time = System.monotonic_time(:millisecond)
|
||||
duration = end_time - start_time
|
||||
|
||||
@@ -82,6 +89,36 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(401, Jason.encode!(%{error: reason}))
|
||||
|> halt()
|
||||
|
||||
{:error, external_message, internal_reason} ->
|
||||
# New error handling with separate internal and external messages
|
||||
end_time = System.monotonic_time(:millisecond)
|
||||
duration = end_time - start_time
|
||||
|
||||
# Log failed authentication with detailed internal reason
|
||||
request_details = extract_request_details(conn)
|
||||
|
||||
SecurityAudit.log_auth_event(
|
||||
:auth_failure,
|
||||
nil,
|
||||
Map.merge(request_details, %{
|
||||
failure_reason: internal_reason,
|
||||
external_message: external_message
|
||||
})
|
||||
)
|
||||
|
||||
# Emit failed authentication event
|
||||
:telemetry.execute(
|
||||
[:wanderer_app, :json_api, :auth],
|
||||
%{count: 1, duration: duration},
|
||||
%{auth_type: get_auth_type(conn), result: "failure"}
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(401, Jason.encode!(%{error: external_message}))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -103,8 +140,6 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
defp authenticate_bearer_token(conn) do
|
||||
case get_req_header(conn, "authorization") do
|
||||
["Bearer " <> token] ->
|
||||
# For now, use a simple approach - validate token format
|
||||
# In the future, this could be extended to support JWT or other token types
|
||||
validate_api_token(token)
|
||||
|
||||
_ ->
|
||||
@@ -113,48 +148,23 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
end
|
||||
|
||||
defp validate_api_token(token) do
|
||||
# For test environment, accept test API keys
|
||||
if Application.get_env(:wanderer_app, :env) == :test and
|
||||
(String.starts_with?(token, "test_") or String.starts_with?(token, "test_api_key_")) do
|
||||
# For test tokens, look up the actual map by API key
|
||||
case find_map_by_api_key(token) do
|
||||
{:ok, map} when not is_nil(map) ->
|
||||
# Use the actual map owner as the user
|
||||
user = %User{
|
||||
id: map.owner_id || Ecto.UUID.generate(),
|
||||
name: "Test User",
|
||||
hash: "test_hash_#{System.unique_integer([:positive])}"
|
||||
}
|
||||
# Look up the map by its public API key
|
||||
case find_map_by_api_key(token) do
|
||||
{:ok, map} when not is_nil(map) ->
|
||||
# Get the actual owner of the map
|
||||
case User.by_id(map.owner_id, load: :characters) do
|
||||
{:ok, user} ->
|
||||
# Return the map owner as the authenticated user
|
||||
{:ok, user, map}
|
||||
|
||||
{:ok, user, map}
|
||||
{:error, _} ->
|
||||
# Return generic error with specific reason for internal logging
|
||||
{:error, "Authentication failed", :map_owner_not_found}
|
||||
end
|
||||
|
||||
_ ->
|
||||
# If no map found with this test token, create a test user without a map
|
||||
user = %User{
|
||||
id: Ecto.UUID.generate(),
|
||||
name: "Test User",
|
||||
hash: "test_hash_#{System.unique_integer([:positive])}"
|
||||
}
|
||||
|
||||
{:ok, user}
|
||||
end
|
||||
else
|
||||
# Look up the map by its public API key
|
||||
case find_map_by_api_key(token) do
|
||||
{:ok, map} when not is_nil(map) ->
|
||||
# Create a user representing API access for this map
|
||||
# In a real implementation, you might want to track the actual user who created the API key
|
||||
user = %User{
|
||||
id: map.owner_id || Ecto.UUID.generate(),
|
||||
name: "API User for #{map.name}",
|
||||
hash: "api_hash_#{map.id}"
|
||||
}
|
||||
|
||||
{:ok, user, map}
|
||||
|
||||
_ ->
|
||||
{:error, "Invalid API key"}
|
||||
end
|
||||
_ ->
|
||||
# Return generic error with specific reason for internal logging
|
||||
{:error, "Authentication failed", :invalid_api_key}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -192,50 +202,8 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
|
||||
end
|
||||
|
||||
defp extract_request_details(conn) do
|
||||
%{
|
||||
ip_address: get_peer_ip(conn),
|
||||
user_agent: get_user_agent(conn),
|
||||
auth_method: get_auth_type(conn),
|
||||
session_id: get_session_id(conn),
|
||||
request_path: conn.request_path,
|
||||
method: conn.method
|
||||
}
|
||||
end
|
||||
|
||||
defp get_peer_ip(conn) do
|
||||
case get_req_header(conn, "x-forwarded-for") do
|
||||
[forwarded_for] ->
|
||||
forwarded_for
|
||||
|> String.split(",")
|
||||
|> List.first()
|
||||
|> String.trim()
|
||||
|
||||
[] ->
|
||||
case get_req_header(conn, "x-real-ip") do
|
||||
[real_ip] ->
|
||||
real_ip
|
||||
|
||||
[] ->
|
||||
case conn.remote_ip do
|
||||
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
|
||||
_ -> "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_user_agent(conn) do
|
||||
case get_req_header(conn, "user-agent") do
|
||||
[user_agent] -> user_agent
|
||||
[] -> "unknown"
|
||||
end
|
||||
end
|
||||
|
||||
defp get_session_id(conn) do
|
||||
case get_session(conn, :session_id) do
|
||||
nil -> conn.assigns[:request_id] || "unknown"
|
||||
session_id -> session_id
|
||||
end
|
||||
RequestContext.build_request_details(conn)
|
||||
|> Map.put(:auth_method, get_auth_type(conn))
|
||||
end
|
||||
|
||||
defp maybe_assign_map(conn, nil), do: conn
|
||||
|
||||
@@ -227,6 +227,61 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
})}
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"save_default_settings",
|
||||
%{"settings" => settings},
|
||||
%{
|
||||
assigns: %{
|
||||
map_id: map_id,
|
||||
current_user: current_user,
|
||||
user_permissions: user_permissions
|
||||
}
|
||||
} = socket
|
||||
) do
|
||||
# Check if user is map admin
|
||||
if user_permissions.admin_map do
|
||||
case save_default_settings(map_id, settings, current_user) do
|
||||
{:ok, _default_settings} ->
|
||||
{:reply, %{success: true}, socket}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Failed to save default settings: #{inspect(reason)}")
|
||||
|
||||
error_message =
|
||||
case reason do
|
||||
%Ash.Error.Invalid{} = error ->
|
||||
errors = Ash.Error.to_error_class(error)
|
||||
"Validation error: #{inspect(errors)}"
|
||||
|
||||
:no_character ->
|
||||
"No character found for user"
|
||||
|
||||
_ ->
|
||||
"Failed to save default settings: #{inspect(reason)}"
|
||||
end
|
||||
|
||||
{:reply, %{success: false, error: error_message},
|
||||
socket |> put_flash(:error, error_message)}
|
||||
end
|
||||
else
|
||||
{:reply, %{success: false, error: "unauthorized"}, socket}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_ui_event(
|
||||
"get_default_settings",
|
||||
_,
|
||||
%{assigns: %{map_id: map_id}} = socket
|
||||
) do
|
||||
case WandererApp.Api.MapDefaultSettings.get_by_map_id(%{map_id: map_id}) do
|
||||
{:ok, [default_settings | _]} ->
|
||||
{:reply, %{default_settings: default_settings.settings}, socket}
|
||||
|
||||
_ ->
|
||||
{:reply, %{default_settings: nil}, socket}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_ui_event("noop", _, socket), do: {:noreply, socket}
|
||||
|
||||
def handle_ui_event(
|
||||
@@ -262,6 +317,38 @@ defmodule WandererAppWeb.MapCoreEventHandler do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp save_default_settings(map_id, settings, current_user) do
|
||||
# Find the character to use as actor
|
||||
actor =
|
||||
case current_user.characters do
|
||||
[character | _] -> character
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
if actor do
|
||||
case WandererApp.Api.MapDefaultSettings.get_by_map_id(%{map_id: map_id}) do
|
||||
{:ok, [existing | _]} ->
|
||||
result =
|
||||
WandererApp.Api.MapDefaultSettings.update(existing, %{settings: settings},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
result
|
||||
|
||||
error ->
|
||||
result =
|
||||
WandererApp.Api.MapDefaultSettings.create(%{map_id: map_id, settings: settings},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
result
|
||||
end
|
||||
else
|
||||
Logger.error("No character found for user #{current_user.id}")
|
||||
{:error, :no_character}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_start_map(map_id) do
|
||||
{:ok, map_server_started} = WandererApp.Cache.lookup("map_#{map_id}:started", false)
|
||||
|
||||
|
||||
@@ -51,146 +51,148 @@
|
||||
<.icon name="hero-user-group-solid" class="w-6 h-6" />
|
||||
</button>
|
||||
</.link>
|
||||
</div>
|
||||
|
||||
<.modal
|
||||
:if={@show_topup}
|
||||
title="Map Subscription Info"
|
||||
class="!min-w-[700px]"
|
||||
id="map-topup-modal"
|
||||
show
|
||||
on_cancel={JS.navigate(~p"/#{@map_slug}")}
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="verticalTabsContainer">
|
||||
<div class="p-tabview p-component" data-pc-name="tabview" data-pc-section="root">
|
||||
<div class="p-tabview-nav-container" data-pc-section="navcontainer">
|
||||
<div class="p-tabview-nav-content" data-pc-section="navcontent">
|
||||
<ul class="p-tabview-nav" role="tablist" data-pc-section="nav">
|
||||
<li
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes(
|
||||
"p-tabview-selected p-highlight": @active_subscription_tab == "balance"
|
||||
)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_332_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_subscription_tab"
|
||||
phx-value-tab="balance"
|
||||
<.modal
|
||||
:if={@show_topup}
|
||||
title="Map Subscription Info"
|
||||
class="!min-w-[700px] !z-[10000]"
|
||||
id="map-topup-modal"
|
||||
show
|
||||
on_cancel={JS.navigate(~p"/#{@map_slug}")}
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="verticalTabsContainer">
|
||||
<div class="p-tabview p-component" data-pc-name="tabview" data-pc-section="root">
|
||||
<div class="p-tabview-nav-container" data-pc-section="navcontainer">
|
||||
<div class="p-tabview-nav-content" data-pc-section="navcontent">
|
||||
<ul class="p-tabview-nav" role="tablist" data-pc-section="nav">
|
||||
<li
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes(
|
||||
"p-tabview-selected p-highlight": @active_subscription_tab == "balance"
|
||||
)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-banknotes-solid" class="w-4 h-4" /> Balance
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
:if={@map_subscriptions_enabled?}
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes(
|
||||
"p-tabview-selected p-highlight": @active_subscription_tab == "subscription"
|
||||
)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_334_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_subscription_tab"
|
||||
phx-value-tab="subscription"
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_332_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_subscription_tab"
|
||||
phx-value-tab="balance"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-banknotes-solid" class="w-4 h-4" /> Balance
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
:if={@map_subscriptions_enabled?}
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes(
|
||||
"p-tabview-selected p-highlight":
|
||||
@active_subscription_tab == "subscription"
|
||||
)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-check-badge-solid" class="w-4 h-4" /> Subscription
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes("p-tabview-selected p-highlight": false)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_332_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_settings_tab"
|
||||
phx-value-tab="balance"
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_334_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_subscription_tab"
|
||||
phx-value-tab="subscription"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-check-badge-solid" class="w-4 h-4" /> Subscription
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class={[
|
||||
"p-unselectable-text",
|
||||
classes("p-tabview-selected p-highlight": false)
|
||||
]}
|
||||
role="presentation"
|
||||
data-pc-name=""
|
||||
data-pc-section="header"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-arrow-up-solid" class="w-4 h-4" /> Top Donators
|
||||
<span class="badge">coming soon</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a
|
||||
role="tab"
|
||||
class="p-tabview-nav-link flex p-[10px]"
|
||||
tabindex="-1"
|
||||
aria-controls="pr_id_332_content"
|
||||
aria-selected="false"
|
||||
aria-disabled="false"
|
||||
data-pc-section="headeraction"
|
||||
phx-click="change_settings_tab"
|
||||
phx-value-tab="balance"
|
||||
>
|
||||
<span class="p-tabview-title" data-pc-section="headertitle">
|
||||
<.icon name="hero-arrow-up-solid" class="w-4 h-4" /> Top Donators
|
||||
<span class="badge">coming soon</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-tabview-panels" data-pc-section="panelcontainer">
|
||||
<div
|
||||
id="pr_id_330_content"
|
||||
class="p-tabview-panel"
|
||||
role="tabpanel"
|
||||
aria-labelledby="pr_id_33_header_0"
|
||||
data-pc-name=""
|
||||
data-pc-section="content"
|
||||
>
|
||||
<.live_component
|
||||
:if={
|
||||
@active_subscription_tab == "balance" && not is_nil(assigns |> Map.get(:map_id))
|
||||
}
|
||||
module={WandererAppWeb.Maps.MapBalanceComponent}
|
||||
id="map-balance-component"
|
||||
map_id={@map_id}
|
||||
notify_to={self()}
|
||||
event_name="balance_event"
|
||||
current_user={@current_user}
|
||||
/>
|
||||
<div class="p-tabview-panels" data-pc-section="panelcontainer">
|
||||
<div
|
||||
id="pr_id_330_content"
|
||||
class="p-tabview-panel"
|
||||
role="tabpanel"
|
||||
aria-labelledby="pr_id_33_header_0"
|
||||
data-pc-name=""
|
||||
data-pc-section="content"
|
||||
>
|
||||
<.live_component
|
||||
:if={
|
||||
@active_subscription_tab == "balance" &&
|
||||
not is_nil(assigns |> Map.get(:map_id))
|
||||
}
|
||||
module={WandererAppWeb.Maps.MapBalanceComponent}
|
||||
id="map-balance-component"
|
||||
map_id={@map_id}
|
||||
notify_to={self()}
|
||||
event_name="balance_event"
|
||||
current_user={@current_user}
|
||||
/>
|
||||
|
||||
<.live_component
|
||||
:if={@active_subscription_tab == "subscription"}
|
||||
module={WandererAppWeb.Maps.MapSubscriptionsComponent}
|
||||
id="map-subscriptions-component"
|
||||
map_id={@map_id}
|
||||
notify_to={self()}
|
||||
event_name="subscriptions_event"
|
||||
current_user={@current_user}
|
||||
readonly={
|
||||
(@user_permissions || %{}) |> Map.get(:delete_map, false) |> Kernel.not()
|
||||
}
|
||||
/>
|
||||
<.live_component
|
||||
:if={@active_subscription_tab == "subscription"}
|
||||
module={WandererAppWeb.Maps.MapSubscriptionsComponent}
|
||||
id="map-subscriptions-component"
|
||||
map_id={@map_id}
|
||||
notify_to={self()}
|
||||
event_name="subscriptions_event"
|
||||
current_user={@current_user}
|
||||
readonly={
|
||||
(@user_permissions || %{}) |> Map.get(:delete_map, false) |> Kernel.not()
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-action"></div>
|
||||
</.modal>
|
||||
<div class="modal-action"></div>
|
||||
</.modal>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ defmodule WandererAppWeb.Plugs.ApiVersioning do
|
||||
import Plug.Conn
|
||||
|
||||
alias WandererApp.SecurityAudit
|
||||
alias WandererApp.Audit.RequestContext
|
||||
|
||||
@supported_versions ["1"]
|
||||
@default_version "1"
|
||||
@@ -260,14 +261,13 @@ defmodule WandererAppWeb.Plugs.ApiVersioning do
|
||||
|
||||
defp log_deprecation_usage(conn, version) do
|
||||
user_id = get_user_id(conn)
|
||||
request_details = RequestContext.build_request_details(conn)
|
||||
|
||||
SecurityAudit.log_event(:deprecated_api_usage, user_id, %{
|
||||
version: version,
|
||||
path: conn.request_path,
|
||||
method: conn.method,
|
||||
user_agent: get_user_agent(conn),
|
||||
ip_address: get_peer_ip(conn)
|
||||
})
|
||||
SecurityAudit.log_event(
|
||||
:deprecated_api_usage,
|
||||
user_id,
|
||||
Map.put(request_details, :version, version)
|
||||
)
|
||||
|
||||
conn
|
||||
end
|
||||
@@ -316,12 +316,15 @@ defmodule WandererAppWeb.Plugs.ApiVersioning do
|
||||
|
||||
# Error handling
|
||||
defp handle_version_error(conn, reason, _opts) do
|
||||
SecurityAudit.log_event(:api_version_error, get_user_id(conn), %{
|
||||
reason: reason,
|
||||
path: conn.request_path,
|
||||
method: conn.method,
|
||||
headers: get_version_headers(conn)
|
||||
})
|
||||
request_details = RequestContext.build_request_details(conn)
|
||||
|
||||
SecurityAudit.log_event(
|
||||
:api_version_error,
|
||||
get_user_id(conn),
|
||||
request_details
|
||||
|> Map.put(:reason, reason)
|
||||
|> Map.put(:headers, get_version_headers(conn))
|
||||
)
|
||||
|
||||
conn
|
||||
|> send_version_error(400, "Invalid API version", %{
|
||||
@@ -376,35 +379,6 @@ defmodule WandererAppWeb.Plugs.ApiVersioning do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_user_agent(conn) do
|
||||
case get_req_header(conn, "user-agent") do
|
||||
[user_agent] -> user_agent
|
||||
[] -> "unknown"
|
||||
end
|
||||
end
|
||||
|
||||
defp get_peer_ip(conn) do
|
||||
case get_req_header(conn, "x-forwarded-for") do
|
||||
[forwarded_for] ->
|
||||
forwarded_for
|
||||
|> String.split(",")
|
||||
|> List.first()
|
||||
|> String.trim()
|
||||
|
||||
[] ->
|
||||
case get_req_header(conn, "x-real-ip") do
|
||||
[real_ip] ->
|
||||
real_ip
|
||||
|
||||
[] ->
|
||||
case conn.remote_ip do
|
||||
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
|
||||
_ -> "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_version_headers(conn) do
|
||||
%{
|
||||
"api-version" => get_req_header(conn, "api-version"),
|
||||
@@ -429,8 +403,6 @@ defmodule WandererAppWeb.Plugs.ApiVersioning do
|
||||
end
|
||||
|
||||
defp get_breaking_changes(from_version, to_version) do
|
||||
# Define breaking changes between versions
|
||||
# Since we've consolidated to v1, most legacy versions are no longer supported
|
||||
%{
|
||||
{"1.0", "1"} => [
|
||||
"All API endpoints now use /api/v1/ prefix",
|
||||
|
||||
@@ -14,6 +14,7 @@ defmodule WandererAppWeb.Plugs.RequestValidator do
|
||||
import Plug.Conn
|
||||
|
||||
alias WandererApp.SecurityAudit
|
||||
alias WandererApp.Audit.RequestContext
|
||||
|
||||
# 10MB
|
||||
@max_request_size 10 * 1024 * 1024
|
||||
@@ -344,13 +345,13 @@ defmodule WandererAppWeb.Plugs.RequestValidator do
|
||||
# Log security threat
|
||||
user_id = get_user_id(conn)
|
||||
|
||||
SecurityAudit.log_event(:security_alert, user_id, %{
|
||||
threats: threats,
|
||||
ip_address: get_peer_ip(conn),
|
||||
user_agent: get_user_agent(conn),
|
||||
request_path: conn.request_path,
|
||||
method: conn.method
|
||||
})
|
||||
request_details = RequestContext.build_request_details(conn)
|
||||
|
||||
SecurityAudit.log_event(
|
||||
:security_alert,
|
||||
user_id,
|
||||
Map.put(request_details, :threats, threats)
|
||||
)
|
||||
|
||||
conn
|
||||
|> send_validation_error(400, "Malicious content detected", %{
|
||||
@@ -457,35 +458,6 @@ defmodule WandererAppWeb.Plugs.RequestValidator do
|
||||
end
|
||||
end
|
||||
|
||||
defp get_peer_ip(conn) do
|
||||
case get_req_header(conn, "x-forwarded-for") do
|
||||
[forwarded_for] ->
|
||||
forwarded_for
|
||||
|> String.split(",")
|
||||
|> List.first()
|
||||
|> String.trim()
|
||||
|
||||
[] ->
|
||||
case get_req_header(conn, "x-real-ip") do
|
||||
[real_ip] ->
|
||||
real_ip
|
||||
|
||||
[] ->
|
||||
case conn.remote_ip do
|
||||
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
|
||||
_ -> "unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get_user_agent(conn) do
|
||||
case get_req_header(conn, "user-agent") do
|
||||
[user_agent] -> user_agent
|
||||
[] -> "unknown"
|
||||
end
|
||||
end
|
||||
|
||||
defp send_validation_error(conn, status, message, details) do
|
||||
error_response = %{
|
||||
error: message,
|
||||
@@ -504,14 +476,15 @@ defmodule WandererAppWeb.Plugs.RequestValidator do
|
||||
# Log the validation error
|
||||
user_id = get_user_id(conn)
|
||||
|
||||
SecurityAudit.log_event(:security_alert, user_id, %{
|
||||
error: "validation_error",
|
||||
message: Exception.message(error),
|
||||
ip_address: get_peer_ip(conn),
|
||||
user_agent: get_user_agent(conn),
|
||||
request_path: conn.request_path,
|
||||
method: conn.method
|
||||
})
|
||||
request_details = RequestContext.build_request_details(conn)
|
||||
|
||||
SecurityAudit.log_event(
|
||||
:security_alert,
|
||||
user_id,
|
||||
request_details
|
||||
|> Map.put(:error, "validation_error")
|
||||
|> Map.put(:message, Exception.message(error))
|
||||
)
|
||||
|
||||
conn
|
||||
|> send_validation_error(500, "Request validation failed", %{
|
||||
|
||||
@@ -31,8 +31,6 @@ defmodule WandererAppWeb.Presence do
|
||||
character_id
|
||||
end)
|
||||
|
||||
WandererApp.Cache.insert("map_#{map_id}:presence_updated", true)
|
||||
|
||||
WandererApp.Cache.insert(
|
||||
"map_#{map_id}:presence_character_ids",
|
||||
presence_tracked_character_ids
|
||||
@@ -43,6 +41,8 @@ defmodule WandererAppWeb.Presence do
|
||||
presence_data
|
||||
)
|
||||
|
||||
WandererApp.Cache.insert("map_#{map_id}:presence_updated", true)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
2
mix.exs
2
mix.exs
@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
|
||||
|
||||
@source_url "https://github.com/wanderer-industries/wanderer"
|
||||
|
||||
@version "1.75.0"
|
||||
@version "1.75.11"
|
||||
|
||||
def project do
|
||||
[
|
||||
|
||||
136
priv/posts/2025/07-27-map-default-settings.md
Normal file
136
priv/posts/2025/07-27-map-default-settings.md
Normal file
@@ -0,0 +1,136 @@
|
||||
%{
|
||||
title: "New Feature: Map Default Settings",
|
||||
author: "Wanderer Team",
|
||||
cover_image_uri: "/images/news/2025/07-27-settings/common_settings.png",
|
||||
tags: ~w(feature settings maps customization admin),
|
||||
description: "Map administrators can now configure default settings for new users, providing a customized initial experience for each map."
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
# Introducing Map Default Settings
|
||||
|
||||
## A Better First Experience for Your Map Users
|
||||
|
||||
We're excited to announce a new feature that gives map administrators more control over the user experience: **Map Default Settings**. This feature allows map admins to configure the default settings that new users will receive when they first access a map.
|
||||
|
||||
---
|
||||
|
||||
## The Challenge
|
||||
|
||||
Previously, all new users would start with the same hardcoded default settings, regardless of the map they were joining. This one-size-fits-all approach meant that:
|
||||
|
||||
- New users often had to spend time configuring settings to match the map's intended use
|
||||
- Map administrators couldn't optimize the initial experience for their specific mapping needs
|
||||
- Training new members required explaining which settings to change
|
||||
|
||||
Different mapping groups have different needs. A wormhole mapping corporation might want different default widget layouts than a nullsec alliance. A small gang PvP group might prioritize different information than an exploration-focused team.
|
||||
|
||||
---
|
||||
|
||||
## The Solution: Customizable Default Settings
|
||||
|
||||
With Map Default Settings, administrators can now:
|
||||
|
||||
1. **Configure their ideal settings** - Set up the map interface exactly as they want new users to experience it
|
||||
2. **Save as defaults** - With a single click, save these settings as the default for all new users
|
||||
3. **Provide consistency** - Ensure all new members start with the same optimized configuration
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
### For Map Administrators
|
||||
|
||||
Setting up default settings is simple:
|
||||
|
||||
1. Configure your map settings to your preferred state
|
||||
2. Open the Settings dialog and navigate to the 'Admin Settings' tab
|
||||
4. Click "Save as Map Default"
|
||||
|
||||
That's it! Your current settings are now saved as the default for any new user accessing your map.
|
||||
|
||||

|
||||
|
||||
### For New Users
|
||||
|
||||
When a user accesses your map for the first time:
|
||||
|
||||
1. The system checks if they have existing settings for this map
|
||||
2. If not, it automatically loads the admin-configured defaults
|
||||
3. The user starts with an optimized configuration from day one
|
||||
4. They can still customize settings to their personal preferences
|
||||
|
||||
---
|
||||
|
||||
## Sync with Default Settings
|
||||
|
||||
We've also added a complementary feature: **Sync with Default Settings**. Any user can now reset their settings back to the map's default configuration:
|
||||
|
||||
1. Open Settings → Server Settings tab
|
||||
2. Click "Sync with Default Settings"
|
||||
3. Confirm the action
|
||||
4. Settings are restored to:
|
||||
- Admin-configured defaults (if available)
|
||||
|
||||
This is perfect for users who want to start fresh or have accidentally misconfigured their settings.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## What Can Be Configured?
|
||||
|
||||
Map default settings include all user interface preferences:
|
||||
|
||||
- **Widget Settings** - Which widgets are visible and their default positions
|
||||
- **Kill Tracking** - Default kill display options and filters
|
||||
- **Local Characters** - How local pilots are displayed
|
||||
- **Signatures** - Default signature widget configuration
|
||||
- **Routes** - Route planning preferences
|
||||
- **Map Display** - Visual preferences for the map itself
|
||||
- **Interface Options** - General UI preferences
|
||||
|
||||
---
|
||||
|
||||
## Security and Permissions
|
||||
|
||||
- Only map owners and administrators can save default settings
|
||||
- All users can read and apply default settings
|
||||
- Settings are validated to ensure they contain only UI preferences
|
||||
- No sensitive data is stored in default settings
|
||||
|
||||
---
|
||||
|
||||
## Coming From Other Mapping Tools?
|
||||
|
||||
If you're migrating from another mapping tool, you can now configure Wanderer to feel familiar to your members:
|
||||
1. Set up the interface to match your previous tool's layout
|
||||
2. Save as default settings
|
||||
3. New members will find a familiar interface waiting for them
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Get Started Today
|
||||
|
||||
Map administrators can start using this feature immediately:
|
||||
|
||||
1. Configure your ideal settings
|
||||
2. Save them as defaults
|
||||
3. Share your map with confidence that new users will have a great first experience
|
||||
|
||||
We hope this feature helps you create more cohesive and efficient mapping teams. As always, we welcome your feedback and suggestions for future improvements.
|
||||
|
||||
---
|
||||
|
||||
## Thank You
|
||||
|
||||
Special thanks to our community members who suggested this feature and helped test it during development. Your feedback continues to shape Wanderer into the best mapping tool for EVE Online.
|
||||
|
||||
----
|
||||
|
||||
Fly safe,
|
||||
**The Wanderer Team**
|
||||
|
||||
----
|
||||
@@ -254,7 +254,7 @@ groupID,categoryID,groupName,iconID,useBasePrice,anchored,anchorable,fittableNon
|
||||
364,23,Mobile Storage,0,0,0,1,0,0
|
||||
365,23,Control Tower,0,1,0,1,0,1
|
||||
366,2,Warp Gate,0,0,1,0,0,0
|
||||
367,7,Ballistic Control system,0,0,0,0,0,1
|
||||
367,7,Ballistic Control System,0,0,0,0,0,1
|
||||
368,2,Global Warp Disruptor,0,0,1,0,0,0
|
||||
369,17,Ship Logs,0,1,0,0,0,1
|
||||
370,17,Criminal Tags,0,1,0,0,0,1
|
||||
@@ -1505,7 +1505,7 @@ groupID,categoryID,groupName,iconID,useBasePrice,anchored,anchorable,fittableNon
|
||||
4757,25,Ueganite,15,0,1,0,0,1
|
||||
4758,25,Hezorime,15,0,1,0,0,1
|
||||
4759,25,Griemeer,15,0,1,0,0,1
|
||||
4768,39,Sovereignty Hub Anomaly Detection Upgrades,None,1,0,0,0,1
|
||||
4768,39,Sovereignty Hub Site Detection Upgrades,None,1,0,0,0,1
|
||||
4769,7,Capital Mobility Modules,None,0,0,0,0,1
|
||||
4771,11,Homefront Operations Noncombatant,None,0,0,0,0,0
|
||||
4772,39,Sovereignty Hub Service Infrastructure Upgrade,None,1,0,0,0,1
|
||||
@@ -1533,8 +1533,11 @@ groupID,categoryID,groupName,iconID,useBasePrice,anchored,anchorable,fittableNon
|
||||
4825,2,Local Beacon,None,0,1,0,0,0
|
||||
4827,17,EDENCOM Data,None,1,0,0,0,1
|
||||
4828,2,Pirate Spawners,None,0,0,0,0,0
|
||||
4838,39,Sovereignty Hub Colony Resources Management Upgrades,None,1,0,0,0,1
|
||||
4839,39,Sovereignty Hub System Effect Generator Upgrades,None,1,0,0,0,1
|
||||
4843,17,Limited Rarities,None,1,0,0,0,1
|
||||
4857,25,Tyranite,15,0,1,0,0,1
|
||||
4900,17,Stability Telemetry,None,1,0,0,0,1
|
||||
350858,350001,Infantry Weapons,None,1,0,0,0,0
|
||||
351064,350001,Infantry Dropsuits,None,1,0,0,0,0
|
||||
351121,350001,Infantry Modules,None,1,0,0,0,0
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1173,3 +1173,4 @@ regionID,constellationID,constellationName,x,y,z,xMin,xMax,yMin,yMax,zMin,zMax,f
|
||||
14000005,24000023,VC-023,-3929303414294949888.0000000000,2052312219158850048.0000000000,-5876493745543360512.0000000000,-3969303414294949888.0000000000,-3889303414294949888.0000000000,2012312219158850048.0000000000,2092312219158850048.0000000000,-5916493745543360512.0000000000,-5836493745543360512.0000000000,None,4e+16
|
||||
14000005,24000024,VC-024,-4000137332327759872.0000000000,2065749800750390016.0000000000,-5861975205782179840.0000000000,-4040137332327759872.0000000000,-3960137332327759872.0000000000,2025749800750390016.0000000000,2105749800750390016.0000000000,-5901975205782179840.0000000000,-5821975205782179840.0000000000,None,4e+16
|
||||
14000005,24000025,VC-025,-3737391588602420224.0000000000,2280269739557479936.0000000000,-6106825722789959680.0000000000,-3777391588602420224.0000000000,-3697391588602420224.0000000000,2240269739557479936.0000000000,2320269739557479936.0000000000,-6146825722789959680.0000000000,-6066825722789959680.0000000000,None,4e+16
|
||||
19000001,26000001,No Constellation name,1.0000000000,1.0000000000,1.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,None,4e+16
|
||||
|
||||
|
@@ -107,6 +107,7 @@ locationID,wormholeClassID
|
||||
14000003,19
|
||||
14000004,19
|
||||
14000005,19
|
||||
19000001,7
|
||||
20000061,11
|
||||
20000062,10
|
||||
30000012,8
|
||||
|
||||
|
@@ -111,3 +111,4 @@ regionID,regionName,x,y,z,xMin,xMax,yMin,yMax,zMin,zMax,factionID,nebula,radius
|
||||
14000003,VR-03,-5431841546101449728.0000000000,2985429153089509888.0000000000,-6018316379407049728.0000000000,-5581841546101449728.0000000000,-5281841546101449728.0000000000,2835429153089509888.0000000000,3135429153089509888.0000000000,-6168316379407049728.0000000000,-5868316379407049728.0000000000,None,11821,None
|
||||
14000004,VR-04,-4545298976287320064.0000000000,2308091069617820160.0000000000,-6316706917218969600.0000000000,-4695298976287320064.0000000000,-4395298976287320064.0000000000,2158091069617819904.0000000000,2458091069617820160.0000000000,-6466706917218969600.0000000000,-6166706917218969600.0000000000,None,11821,None
|
||||
14000005,VR-05,-3876324035229649920.0000000000,2174764391831830016.0000000000,-5975813282299729920.0000000000,-4026324035229649920.0000000000,-3726324035229649920.0000000000,2024764391831830016.0000000000,2324764391831830016.0000000000,-6125813282299729920.0000000000,-5825813282299729920.0000000000,None,11821,None
|
||||
19000001,No Name,1.0000000000,1.0000000000,1.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,None,11806,None
|
||||
|
||||
|
@@ -8435,3 +8435,4 @@ regionID,constellationID,solarSystemID,solarSystemName,x,y,z,xMin,xMax,yMin,yMax
|
||||
14000005,24000025,34000198,V-198,-3726804805078839808.0000000000,2273820166108100096.0000000000,-6118384105031680000.0000000000,-3726819764865909760.0000000000,-3726789845291769856.0000000000,2273805206321029888.0000000000,2273835125895170048.0000000000,-6118399064818750464.0000000000,-6118369145244609536.0000000000,0E-10,0,0,0,0,0,0,None,-0.9900000000,None,14959787070000.0000000000,None,None
|
||||
14000005,24000025,34000199,V-199,-3702467135073939968.0000000000,2271227427207830016.0000000000,-6075476754085990400.0000000000,-3702482094861009920.0000000000,-3702452175286870016.0000000000,2271212467420760064.0000000000,2271242386994899968.0000000000,-6075491713873059840.0000000000,-6075461794298919936.0000000000,0E-10,0,0,0,0,0,0,None,-0.9900000000,None,14959787070000.0000000000,None,None
|
||||
14000005,24000025,34000200,V-200,-3726767846292810240.0000000000,2248086962890269952.0000000000,-6097487616715119616.0000000000,-3726782806079880192.0000000000,-3726752886505739776.0000000000,2248072003103200000.0000000000,2248101922677339904.0000000000,-6097502576502190080.0000000000,-6097472656928050176.0000000000,0E-10,0,0,0,0,0,0,None,-0.9900000000,None,14959787070000.0000000000,None,None
|
||||
19000001,26000001,36000001,No System Name,1.0000000000,1.0000000000,1.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,-300000.0000000000,300000.0000000000,0E-10,0,0,0,0,0,0,None,1.0000000000,None,14959787070000.0000000000,None,None
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -0,0 +1,60 @@
|
||||
defmodule WandererApp.Repo.Migrations.CreateMapDefaultSettingsOnly do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
create table(:map_default_settings, primary_key: false) do
|
||||
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
||||
add :settings, :text, null: false
|
||||
|
||||
add :inserted_at, :utc_datetime_usec,
|
||||
null: false,
|
||||
default: fragment("(now() AT TIME ZONE 'utc')")
|
||||
|
||||
add :updated_at, :utc_datetime_usec,
|
||||
null: false,
|
||||
default: fragment("(now() AT TIME ZONE 'utc')")
|
||||
|
||||
add :map_id,
|
||||
references(:maps_v1, column: :id, name: "map_default_settings_map_id_fkey", type: :uuid),
|
||||
null: false
|
||||
|
||||
add :created_by_id,
|
||||
references(:character_v1,
|
||||
column: :id,
|
||||
name: "map_default_settings_created_by_id_fkey",
|
||||
type: :uuid
|
||||
)
|
||||
|
||||
add :updated_by_id,
|
||||
references(:character_v1,
|
||||
column: :id,
|
||||
name: "map_default_settings_updated_by_id_fkey",
|
||||
type: :uuid
|
||||
)
|
||||
end
|
||||
|
||||
create unique_index(:map_default_settings, [:map_id],
|
||||
name: "map_default_settings_unique_map_settings_index"
|
||||
)
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists unique_index(:map_default_settings, [:map_id],
|
||||
name: "map_default_settings_unique_map_settings_index"
|
||||
)
|
||||
|
||||
drop constraint(:map_default_settings, "map_default_settings_map_id_fkey")
|
||||
|
||||
drop constraint(:map_default_settings, "map_default_settings_created_by_id_fkey")
|
||||
|
||||
drop constraint(:map_default_settings, "map_default_settings_updated_by_id_fkey")
|
||||
|
||||
drop table(:map_default_settings)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
defmodule WandererApp.Repo.Migrations.AddSecurityAuditIndexes do
|
||||
use Ecto.Migration
|
||||
@disable_ddl_transaction true
|
||||
@disable_migration_lock true
|
||||
|
||||
def up do
|
||||
# Add indexes for security audit queries
|
||||
create_if_not_exists index(:user_activity_v1, [:entity_type, :event_type, :inserted_at],
|
||||
concurrently: true
|
||||
)
|
||||
|
||||
create_if_not_exists index(:user_activity_v1, [:user_id, :inserted_at], concurrently: true)
|
||||
create_if_not_exists index(:user_activity_v1, [:event_type], concurrently: true)
|
||||
|
||||
# Partial index for security events only - for better performance
|
||||
create_if_not_exists index(:user_activity_v1, [:user_id, :inserted_at],
|
||||
where: "entity_type = 'security_event'",
|
||||
name: :user_activity_v1_security_events_idx,
|
||||
concurrently: true
|
||||
)
|
||||
|
||||
# Index for entity_id queries (used by Map.Audit)
|
||||
create_if_not_exists index(:user_activity_v1, [:entity_id, :inserted_at], concurrently: true)
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists index(:user_activity_v1, [:entity_id, :inserted_at], concurrently: true)
|
||||
|
||||
drop_if_exists index(:user_activity_v1, [:user_id, :inserted_at],
|
||||
name: :user_activity_v1_security_events_idx,
|
||||
concurrently: true
|
||||
)
|
||||
|
||||
drop_if_exists index(:user_activity_v1, [:event_type], concurrently: true)
|
||||
drop_if_exists index(:user_activity_v1, [:user_id, :inserted_at], concurrently: true)
|
||||
|
||||
drop_if_exists index(:user_activity_v1, [:entity_type, :event_type, :inserted_at],
|
||||
concurrently: true
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "\"member\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "CB795E3E8D78F769E55971510831DA39A33F1D0CC065CFEE6969164B1F33916F",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "098ED55EA6882AA6E7033650E918E40B7E8A2F97C205D3B15739F88DC4A1FF87",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "\"viewer\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "9DF32D06113D3923BA091BF65AE604418B8C33C01B421BDDE32032A4D2C31507",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "\"viewer\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "B9FA697C24A6DEC9FF0E5B7588C07B17E2CE659EC27D1F05A30DBEAD72883FF7",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "\"viewer\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "74A488E6C73B100674AB9031886BFD74C84234655A1D1BA4F1B649DBD1DE783F",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "access_list_members_v1_uniq_acl_member_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "access_list_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_character_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_corporation_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_alliance_id"
|
||||
}
|
||||
],
|
||||
"name": "uniq_acl_member_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_character_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_corporation_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_alliance_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "\"viewer\"",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "role",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_list_members_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "6773A2F5243E2DCC21A2F460AD0053B28E38235784B3F33045200B4EB4349718",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "access_list_members_v1_uniq_acl_alliance_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "access_list_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_alliance_id"
|
||||
}
|
||||
],
|
||||
"name": "uniq_acl_alliance_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "access_list_members_v1_uniq_acl_character_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "access_list_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_character_id"
|
||||
}
|
||||
],
|
||||
"name": "uniq_acl_character_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
},
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "access_list_members_v1_uniq_acl_corporation_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "access_list_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_corporation_id"
|
||||
}
|
||||
],
|
||||
"name": "uniq_acl_corporation_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_list_members_v1"
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "access_lists_v1_owner_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "character_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "owner_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "444F11C25C585114DAA27322D172918D59CEE6B49FE9F6AC88181566782B1DBA",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "access_lists_v1"
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "10706AB4D288BDEE7AE1DB7FD2072989B40231FA7E416EB164022D33157F5F97",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "C3F49D7B512DF7617A7CC9A9A7D4DF55AF090423B663EF24503F6159A98204E2",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "29EF3CA3C393C1D3E7A002E90E1D8D16BDA54E7A4C9163ED534791B63724B345",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "C71AABB1835923C905F328CADA4F6F7565B09B6F4BA0603AB15B4A0B791630D1",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wallet_ballance",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "CEEDE046912657867296D1FC17AC44336CF52D80E801D39341C323FC1B26BDB9",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wallet_balance",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "1AEB87AAF8CD6CF5A98A330558A3C0050427E164F2FB2FE7AA0D4AB8E61980D0",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "access_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "refresh_token",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "location",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "structure_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wallet_balance",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "C919A2551EAC04804C90FCA04ECF3C719FE768608D429AFD9706A7684D88EF8B",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_location",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_solar_system_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_structure_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_access_token",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_refresh_token",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "9E44110150D5F78BD6FCE0CEB21A7E8B7A2B9163BC2AA621D34A6E8CB8D6DAB0",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"size": null,
|
||||
"type": "uuid",
|
||||
"source": "id",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": true,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "eve_id",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "name",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "false",
|
||||
"size": null,
|
||||
"type": "boolean",
|
||||
"source": "online",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "false",
|
||||
"size": null,
|
||||
"type": "boolean",
|
||||
"source": "deleted",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "scopes",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "character_owner_hash",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "token_type",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "bigint",
|
||||
"source": "expires_at",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "ship_name",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "bigint",
|
||||
"source": "corporation_id",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "corporation_name",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "corporation_ticker",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "bigint",
|
||||
"source": "alliance_id",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "alliance_name",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "text",
|
||||
"source": "alliance_ticker",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"size": null,
|
||||
"type": "utc_datetime_usec",
|
||||
"source": "inserted_at",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"size": null,
|
||||
"type": "utc_datetime_usec",
|
||||
"source": "updated_at",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "uuid",
|
||||
"source": "user_id",
|
||||
"references": {
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"table": "user_v1",
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"multitenancy": {
|
||||
"global": null,
|
||||
"attribute": null,
|
||||
"strategy": null
|
||||
},
|
||||
"destination_attribute": "id",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"deferrable": false,
|
||||
"match_with": null,
|
||||
"match_type": null,
|
||||
"index?": false,
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null
|
||||
},
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_location",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_ship",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_solar_system_id",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_structure_id",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_access_token",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "binary",
|
||||
"source": "encrypted_refresh_token",
|
||||
"references": null,
|
||||
"allow_nil?": true,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
}
|
||||
],
|
||||
"table": "character_v1",
|
||||
"hash": "37A78C69380CE82880DA977406421399C66F09FA575AE2FC24FAB1216E6C525E",
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"identities": [
|
||||
{
|
||||
"name": "unique_eve_id",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_id"
|
||||
}
|
||||
],
|
||||
"where": null,
|
||||
"base_filter": null,
|
||||
"all_tenants?": false,
|
||||
"nils_distinct?": true,
|
||||
"index_name": "character_v1_unique_eve_id_index"
|
||||
}
|
||||
],
|
||||
"schema": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"multitenancy": {
|
||||
"global": null,
|
||||
"attribute": null,
|
||||
"strategy": null
|
||||
},
|
||||
"base_filter": null,
|
||||
"custom_statements": [],
|
||||
"has_create_action": true
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_location",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_solar_system_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_structure_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_station_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_access_token",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_refresh_token",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "0027E0C4A584B6AB536CE623A060E27EC57E6602422A4B9D2DED931EF2337804",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "character_v1_unique_eve_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_eve_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,333 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "online",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "false",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "deleted",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "scopes",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "character_owner_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "token_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_item_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "corporation_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_name",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "alliance_ticker",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "character_v1_user_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "user_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "user_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_eve_wallet_balance",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_location",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_ship",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_solar_system_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_structure_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_station_id",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_access_token",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_refresh_token",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "D630C33BDB07F87A594F7F1C55931AB89D758C84D71EFCE61C17DEBFF18F383E",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "character_v1_unique_eve_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_id"
|
||||
}
|
||||
],
|
||||
"name": "unique_eve_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "character_v1"
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_transaction_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "amount",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "balance",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "first_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "second_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "date",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "reason",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ref_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "E45BA247AD12230CAD90896DA3CA71B9940E1D00BD51215106256AE7C75AD6B6",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "corp_wallet_transactions_v1"
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_transaction_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "amount",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "balance",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "first_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "second_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "date",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "reason",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ref_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "0008A3278A23F8B93B1322F98D47124DE720818DE188C6DA203B92C6B948F219",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "corp_wallet_transactions_v1_eve_transaction_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_transaction_id"
|
||||
}
|
||||
],
|
||||
"name": "eve_transaction_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "corp_wallet_transactions_v1"
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_transaction_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "amount",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "balance",
|
||||
"type": "float"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "first_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "second_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "date",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "reason",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ref_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_amount_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_balance_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_first_party_id_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_second_party_id_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_reason_encoded",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "B009AFDEC92308BD2C7129BE50BC21B042B61085A955E8FCB2347FADF64F015F",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "corp_wallet_transactions_v1_eve_transaction_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_transaction_id"
|
||||
}
|
||||
],
|
||||
"name": "eve_transaction_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "corp_wallet_transactions_v1"
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_transaction_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "date",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ref_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_amount_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_balance_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_first_party_id_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_second_party_id_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_reason_encoded",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "C66789F82FF4587652C69760817344F16566775EABFC962B6DF550D86D10A2FF",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "corp_wallet_transactions_v1_eve_transaction_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_transaction_id"
|
||||
}
|
||||
],
|
||||
"name": "eve_transaction_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "corp_wallet_transactions_v1"
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "eve_transaction_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "first_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "second_party_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "first_party_id_encoded",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "second_party_id_encoded",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "date",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "description",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ref_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_amount_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_balance_encoded",
|
||||
"type": "binary"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "encrypted_reason_encoded",
|
||||
"type": "binary"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "E31376D56403DCB616F4579B0800E07217E86C99431FF50650294724537010B3",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "corp_wallet_transactions_v1_eve_transaction_id_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "eve_transaction_id"
|
||||
}
|
||||
],
|
||||
"name": "eve_transaction_id",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "corp_wallet_transactions_v1"
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_access_lists_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_access_lists_v1_access_list_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "access_lists_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "access_list_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "D969A70A438F04CFA681D0B3A9A179D7449954D598E55FE1F9B3DAC8EC3B3062",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_access_lists_v1"
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"size": null,
|
||||
"type": "uuid",
|
||||
"source": "id",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": true,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"size": null,
|
||||
"type": "utc_datetime_usec",
|
||||
"source": "inserted_at",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"size": null,
|
||||
"type": "utc_datetime_usec",
|
||||
"source": "updated_at",
|
||||
"references": null,
|
||||
"allow_nil?": false,
|
||||
"primary_key?": false,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "uuid",
|
||||
"source": "map_id",
|
||||
"references": {
|
||||
"name": "map_access_lists_v1_map_id_fkey",
|
||||
"table": "maps_v1",
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"multitenancy": {
|
||||
"global": null,
|
||||
"attribute": null,
|
||||
"strategy": null
|
||||
},
|
||||
"destination_attribute": "id",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"deferrable": false,
|
||||
"match_with": null,
|
||||
"match_type": null,
|
||||
"index?": false,
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null
|
||||
},
|
||||
"allow_nil?": false,
|
||||
"primary_key?": true,
|
||||
"generated?": false
|
||||
},
|
||||
{
|
||||
"default": "nil",
|
||||
"size": null,
|
||||
"type": "uuid",
|
||||
"source": "access_list_id",
|
||||
"references": {
|
||||
"name": "map_access_lists_v1_access_list_id_fkey",
|
||||
"table": "access_lists_v1",
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"multitenancy": {
|
||||
"global": null,
|
||||
"attribute": null,
|
||||
"strategy": null
|
||||
},
|
||||
"destination_attribute": "id",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"deferrable": false,
|
||||
"match_with": null,
|
||||
"match_type": null,
|
||||
"index?": false,
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null
|
||||
},
|
||||
"allow_nil?": false,
|
||||
"primary_key?": true,
|
||||
"generated?": false
|
||||
}
|
||||
],
|
||||
"table": "map_access_lists_v1",
|
||||
"hash": "72DB5BB5D63D90C44A75097741E9132A50EAFE974F30BE84F88A2A2CA6415D46",
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"identities": [
|
||||
{
|
||||
"name": "unique_map_acl",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "map_id"
|
||||
},
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "access_list_id"
|
||||
}
|
||||
],
|
||||
"where": null,
|
||||
"base_filter": null,
|
||||
"all_tenants?": false,
|
||||
"nils_distinct?": true,
|
||||
"index_name": "map_access_lists_v1_unique_map_acl_index"
|
||||
}
|
||||
],
|
||||
"schema": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"multitenancy": {
|
||||
"global": null,
|
||||
"attribute": null,
|
||||
"strategy": null
|
||||
},
|
||||
"base_filter": null,
|
||||
"custom_statements": [],
|
||||
"has_create_action": true
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_type_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target_id",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_passages_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_passages_v1_character_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "character_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "character_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "54E64D30DCFB80B93C57FD251790C00F69F8050F78B8BDE74302361815DBC5F2",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_passages_v1"
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "mass_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "time_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "1",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_size_type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wormhole_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "count_of_passage",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "B72F0AB8CAFC37FDDC5F54EBE5FDE3D7E9A4E066A6A2400E507E8F43509B2446",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_v1"
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "mass_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "time_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "1",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_size_type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wormhole_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "count_of_passage",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "locked",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "A56C58B107AAB08706CA42BA017DCC7972DE1ADD0B9C6D45A3C5C4CDFA4A0C11",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_v1"
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_source",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "solar_system_target",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "mass_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "time_status",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "1",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "ship_size_type",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "wormhole_type",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "0",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "count_of_passage",
|
||||
"type": "bigint"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "locked",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "custom_info",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "inserted_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "map_chain_v1_map_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "maps_v1"
|
||||
},
|
||||
"size": null,
|
||||
"source": "map_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "D2213536C4FA24865B2977B1544847E583441789FF1F1F07E7ADAEBD238764CF",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.WandererApp.Repo",
|
||||
"schema": null,
|
||||
"table": "map_chain_v1"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user