feat: monorepo (#1098)
* chore: release v1.1.0 * chore: pd stuff * chore: release v1.1.1 * feat: add ratelimit environment variables * chore: release v1.1.2 * chore: improve memory usage * chore: release v1.1.3 * feat: use sentinels * chore: release v1.1.4 * chore: update name * chore: release v1.1.5 * fix(pd): use correct env variable * chore: release v1.1.6 * wip: docs * wip: docs * wip: api * wip: website * wip: website * wip: website * wip: website * chore: cleanup * chore: add lib folder to .gitignore * chore: remove blank * wip: csp * wip: csp * fix(ci): use correct context * wip: cd * wip: cd * wip: cd * wip: cd * wip: cd * wip: cd * chore: use correct port * wip: csp * wip: csp * wip: csp * wip: csp * chore: update security headers in nuxt.config.ts * chore: update security headers in nuxt.config.ts * chore: remove comments * chore: update npm dependencies * chore: bump dependencies & add missing * fix: broken lockfile * chore: set client host * chore: update depencies * style: small layout fixes * ci: testing * chore: update CI workflow to trigger on 'monorepo' branch * chore: use full build image * chore: test (#1048) * chore: setup qemu * chore: remove docs prefix from scripts (#1046) * style: fix centering (#1050) * style: render 3 columns (#1049) * ci: use alpine base (#1051) * style: make footer follow max width (#1052) * feat: Add Crowdin configuration for website localization. (#1054) * chore: change dest in crowdin * chore: improve crowdin pr * chore: improve crowdin pr * chore: lint * chore: update language folder * chore: update crowdin configuration to skip untranslated strings and files * chore: add crowdin badge * chore: release v1.0.1 * chore: release v1.1.7 * chore: fix build * chore: release v1.1.8 * chore: release v1.0.2 * chore: fix docker * chore: release v1.1.9 * chore: release v1.0.3 * feat: add more api endpoints (#1059) * chore: worked on the api and lint * chore: small fixes * chore: uhm I think this sort is broken * chore: worked on the api and lint * chore: small fixes * chore: uhm I think this sort is broken * feat: heartbeat * chore: add prettier ignore * feat: websocket * chore: update tsconfig * chore: lint * chore: dont require unused fields * chore: use djs rest * fix: websocket * chore: v5 * chore: fix build --------- Co-authored-by: Florian Metz <me@timeraa.dev> * chore: wip api * chore: wip api * chore: release v0.0.1 * chore: deploy on tag * chore: release v0.0.1 * chore(api): remove old sentry tracing * chore: release v0.0.2 * chore: release v0.0.2 * feat(api-master): add logs * chore: release v0.0.3 * chore: bump dep * feat: add feature flags * chore: release v0.0.3 * feat: metrics? * chore: release v0.0.4 * chore: update npm dependencies * feat: add session-keep-alive * chore: fix build * chore: release v0.0.5 * chore: release v0.0.4 * chore: update arktype * chore: release v0.0.6 * chore: hash the key * chore: release v0.0.7 * chore: release v0.0.5 * chore: revert redis stuff * chore: release v0.0.6 * chore: release v0.0.8 * feat(api-master): add metrics * chore: release v0.0.7 * chore: update lockfile * chore: release v0.0.8 * chore: idk kek * chore: iodk * chore: release v0.0.8 * feat: use scan * chore: release v0.0.9 * chore: skip clearOldSesssions if another in progress * chore: release v0.0.10 * chore: optimize session cleanup with batch deletion * chore: release v0.0.11 * chore: move some code * chore: release v0.0.12 * chore: add timeout to headless session deletion * chore: release v0.0.13 * chore: add p-limit dependency for session cleanup * chore: release v0.0.14 * chore: always return the key * chore: release v0.0.15 * chore: add p-limit dependency for session cleanup * chore: release v0.0.16 * chore: why does it not abort * chore: release v0.0.17 * chore: fix time * chore: release v0.0.18 * chore: add reason * chore: release v0.0.19 * feat: use scienceId * chore: release v0.0.20 * chore: release v0.0.9 * chore: update log * chore: release v0.0.20 * chore: use ky * chore: release v0.0.21 * chore: 202 on disabled flag * chore: release v0.0.10 * chore: test * chore: release v0.0.22 * chore: release v0.0.11 * chore: test * chore: test * chore: release v0.0.12 * chore: test * chore: release v0.0.23 * chore: update hash * chore: release v0.0.24 * chore: release v0.0.13 * feat: update tracing (#1067) * chore: release v0.0.14 * chore: release v0.0.25 * fix: store ip data in postgres * chore: lint * chore: release v0.0.26 * chore: reduce batch size * chore: release v0.0.27 * chore: disable ip stuff for now * chore: release v0.0.28 * chore: reduce memory * chore: release v0.0.29 * chore: release v0.0.29 * chore: release v0.0.29 * chore: small updates * chore: release v0.0.30 * chore: optimize active presence gauge update with concurrency limit * chore: release v0.0.31 * chore: some improvements * chore: forgot to save * chore: release v0.0.32 * chore: some testing * chore: release v0.0.33 * chore: scan count config * chore: release v0.0.34 * feat: schema v1.11 * chore: release v1.0.4 * feat: use prom-client * chore: release v0.0.35 * chore: scan keys instead * chore: release v0.0.36 * feat: ip data * chore: release v0.0.37 * feat: discord-bot (#1069) * feat: discord-bot * feat: final things * chore: add to matrix * feat: add sentry * chore: move some things * chore: update version * chore: release v1.0.0 * fix(discord-bot): fixes credits * chore: release v1.0.1 * feat(discord-bot): add beta command * chore: release v1.0.2 * chore: un-ingore config files * chore: lint * fix(discord-bot): update developer roles constant (#1070) * chore: disable commitlint * fix(discord-bot): update developer roles constant * Update commit-msg * chore: release v1.0.3 * Update presence.ts (#1072) * fix: get presence list correctly * chore: release v1.0.4 * chore: change info message * chore: release v1.0.5 * fix(api): metadata types * chore: release v0.0.15 * chore: fix type * chore: release v0.0.16 * fix: add dbName * chore: release v0.0.17 * feat: fix presence endpoints * chore: release v0.0.18 * fix: show displayName * chore: release v1.0.6 * feat: extension version gauge (#1074) * feat: extension version gauge * chore: lint * feat: new schema version * chore: release v1.0.5 * chore: release v0.0.38 * chore: remove ip lookup * chore: lint * chore: release v0.0.39 * chore: add environment variable to disable * chore: release v0.0.19 * chore(bot): rename presence to activity (#1077) * chore: release v1.0.7 * feat(schema-server): add v1.13 * chore: release v1.0.6 * chore: add mobile and update descriptions * chore: release v1.0.7 * chore: lint * chore: release v1.0.8 * chore: add Social Media Manager role and color to constants (#1084) * chore: add Social Media Manager role and color to constants * chore: bump version * feat: v1.14 of schema * chore: release v1.0.9 * feat: schema 1.15 * chore: release v1.0.10 * feat: add file extension * chore: release v1.2.0 * fix: return image directly * chore: release v1.2.1 * chore: remove cache headers * chore: release v1.2.2 * chore: set more headers * chore: release v1.2.3 * fix: ttl * chore: release v1.2.4 * chore: strip monorepo down to pd and schema-server only Remove all apps except pd and schema-server to create a minimal repository. Deleted apps: api-master, api-worker, discord-bot, docs, website Deleted packages directory and all monorepo configuration files Removed unnecessary config files: eslint, prettier, commitlint, vitest, etc. * docs: update README to be more user-friendly and generic - Rewritten to focus on what PreMiD is rather than repository structure - Added friendly introduction to PreMiD's features and capabilities - Highlighted the open source Presences repository at github.com/PreMiD/Presences - Removed monorepo-specific content and development instructions - Added community section with helpful links - Overall more welcoming tone for users and contributors * chore: restore TypeScript build configuration and update dependencies - Added back tsconfig.base.json and tsconfig.app.json needed for builds - Updated both pd and schema-server tsconfig files to exclude test files - Installed TypeScript and @types/node dependencies for both apps - Updated README to include feedback.premid.app link - Verified both apps build successfully The apps can now be built with: npx tsc -b tsconfig.app.json * docs: update README to use Activities terminology - Changed "Presences" to "Activities" throughout - Updated repository link to github.com/PreMiD/Activities - Updated examples: YouTube, Disney+, Netflix, Twitch - Noted that Spotify has native Discord support * docs: clarify open source status and installation process - Added Contributing section explaining that Activities are open source but the main app is not - Clarified this helps the small team manage maintenance burden - Updated "install PreMiD" to "add the browser extension" for accuracy - Provided clear ways to contribute: Activities, translations, feedback * docs: clarify extension-only model and development speed - Changed "application and extension" to just "extension" - Updated reasoning: allows team to move fast and iterate quickly - No separate application exists, only the browser extension * docs: add Discord server link * docs: add banner image to README * docs: move logo inline with heading --------- Co-authored-by: Bas950 <me@bas950.com> Co-authored-by: veryCrunchy <verycrunchydev@gmail.com> Co-authored-by: veryCrunchy <me@verycrunchy.dev> Co-authored-by: Claude <noreply@anthropic.com>
13
.gitattributes
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
* text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.mp4 binary
|
||||
*.mp3 binary
|
||||
*.gz binary
|
||||
*.zip binary
|
||||
*.ttf binary
|
||||
*.woff binary
|
||||
30
.github/CONTRIBUTING.md
vendored
@@ -1,30 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
## Requiered knowledge
|
||||
|
||||
- JavaScript
|
||||
- html5
|
||||
- NodeJS
|
||||
|
||||
Additional:
|
||||
|
||||
- CSS
|
||||
- [VueJS](https://vuejs.org/)
|
||||
- [ElectronJS](https://electronjs.org/)
|
||||
- [NPMjs](https://www.npmjs.com/)
|
||||
|
||||
A source code editor is also requiered. We recommend [Visual Studio Code](https://code.visualstudio.com/).
|
||||
|
||||
### Installing the components
|
||||
|
||||
1. Install [Git](https://git-scm.com/)
|
||||
2. Install [Node](https://nodejs.org/en/)
|
||||
|
||||
### Cloning the project
|
||||
|
||||
1. Fork the [repository](https://github.com/PreMiD/PreMiD)
|
||||
2. Open a terminal and type `git clone https://github.com/PreMiD/PreMiD`
|
||||
|
||||
### Coding your vision
|
||||
|
||||
Please keep the structure. We don't want to disorganize our project. Chaotic files may not be accepted.
|
||||
BIN
.github/Electron/Chrome_bsp.png
vendored
|
Before Width: | Height: | Size: 332 KiB |
BIN
.github/Electron/PMD_Banner.png
vendored
|
Before Width: | Height: | Size: 682 KiB |
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
github: Timeraa
|
||||
patreon: Timeraa
|
||||
ko_fi: Timeraa
|
||||
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
---
|
||||
|
||||
<!-- NOTE -->
|
||||
<!-- Keep all presence related bugs in our Presences repository! -->
|
||||
<!-- https://github.com/PreMiD/Presences/issues -->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
16
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,16 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for PreMiD
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
BIN
.github/Logo.png
vendored
|
Before Width: | Height: | Size: 11 KiB |
BIN
.github/Patreon.png
vendored
|
Before Width: | Height: | Size: 3.0 KiB |
1
.github/PayPal.svg
vendored
|
Before Width: | Height: | Size: 5.1 KiB |
13
.github/SUPPORT.md
vendored
@@ -1,13 +0,0 @@
|
||||
# How to get support
|
||||
|
||||
## Take a look at the [wiki](https://wiki.premid.app)
|
||||
Our GitHub wiki is full of information around PreMiD.<br>
|
||||
Take a look and feel free to contribute if you want to add something new.
|
||||
|
||||
## [Open a issue](https://github.com/PreMiD/PreMiD/issues/new/choose) on [GitHub](https://github.com/PreMiD/PreMiD)
|
||||
Simply open a issue if you don't feel allright.<br>
|
||||
*Aand there he goes...*
|
||||
|
||||
## Ask a staff member in [#support](https://discord.premid.app)
|
||||
The team is ready to tell you the secrets of the underworld.<br>
|
||||
Join our [Discord server](https://discord.premid.app) and find out what we're hiding.
|
||||
BIN
.github/TwitterButton.png
vendored
|
Before Width: | Height: | Size: 4.1 KiB |
BIN
.github/example.png
vendored
|
Before Width: | Height: | Size: 332 KiB |
98
.github/workflows/deploy.yml
vendored
@@ -1,98 +0,0 @@
|
||||
name: DePloY
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
env:
|
||||
NODE_ENV: DePloY
|
||||
jobs:
|
||||
package:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
npm i
|
||||
npm i -g typescript rimraf
|
||||
- name: Prepare to package
|
||||
run: npm run init
|
||||
- name: Package
|
||||
run: |
|
||||
npm run pkg
|
||||
rimraf dist/app
|
||||
node util/zip dist ${{ matrix.os }}.zip --zip
|
||||
- name: Upload bundle
|
||||
env:
|
||||
SSHHOST: ${{ secrets.MAIN_HOST }}
|
||||
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
run: |
|
||||
tsc util/uploadFile util/zip
|
||||
node util/uploadFile ${{ matrix.os }}.zip /home/PreMiD/download/util/${{ matrix.os }}.zip
|
||||
createInstallers:
|
||||
runs-on: "ubuntu-latest"
|
||||
needs: package
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo npm i
|
||||
sudo npm i -g typescript
|
||||
- name: Download InstallBuilder
|
||||
run: |
|
||||
wget https://clients.bitrock.com/installbuilder/installbuilder-enterprise-20.12.0-linux-x64-installer.run
|
||||
chmod u+x installbuilder-enterprise-20.12.0-linux-x64-installer.run
|
||||
- name: Install InstallBuilder
|
||||
run: |
|
||||
./installbuilder-enterprise-20.12.0-linux-x64-installer.run --installer-language en --prefix ./installbuilder --mode unattended
|
||||
echo "${{ secrets.IBLICENSE }}" > ./installbuilder/license.xml
|
||||
- name: Prepare Upgrade Installer
|
||||
run: |
|
||||
tsc util/prepare
|
||||
node util/prepare
|
||||
- name: Create Upgrade Installer (MacOS 64bit)
|
||||
run: |
|
||||
installbuilder/bin/builder build installer_assets/PreMiD-Upgrade.xml osx
|
||||
- name: Create Upgrade Installer (Windows)
|
||||
run: |
|
||||
installbuilder/bin/builder build installer_assets/PreMiD-Upgrade.xml windows
|
||||
- name: Upload files
|
||||
env:
|
||||
SSHHOST: ${{ secrets.MAIN_HOST }}
|
||||
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
run: |
|
||||
tsc util/uploadFile util/zip
|
||||
node util/uploadFile dist/installer/upgrader.exe /home/PreMiD/download/upgrader.exe
|
||||
node util/uploadFile dist/installer/upgrader.app.zip /home/PreMiD/download/util/upgrader.app.zip
|
||||
- name: Finalize build
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.MAIN_HOST }}
|
||||
username: ${{ secrets.SSH_USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
script: |
|
||||
cd /home/PreMiD/download/util
|
||||
unzip upgrader.app.zip
|
||||
tar -czvf upgrader.app.tgz upgrader.app
|
||||
mv upgrader.app.tgz ../
|
||||
rm -rf upgrader.app upgrader.app.zip
|
||||
unzip windows-latest.zip
|
||||
cd windows-latest/PreMiD-win32-x64/
|
||||
zip -r ../../PreMiD-win32-x64.zip .
|
||||
mv ../../PreMiD-win32-x64.zip /home/PreMiD/download/
|
||||
cd ../PreMiD-win32-ia32/
|
||||
zip -r ../../PreMiD-win32-x86.zip .
|
||||
mv ../../PreMiD-win32-x86.zip /home/PreMiD/download/
|
||||
cd ../..
|
||||
rm -rf windows-latest windows-latest.zip
|
||||
unzip macOS-latest.zip
|
||||
cd macOS-latest/PreMiD-darwin-x64/
|
||||
zip -r ../../PreMiD-darwin-x64.zip .
|
||||
mv ../../PreMiD-darwin-x64.zip /home/PreMiD/download/
|
||||
cd ../..
|
||||
rm -rf macOS-latest macOS-latest.zip
|
||||
8
.gitignore
vendored
@@ -2,6 +2,8 @@ node_modules
|
||||
out
|
||||
dist
|
||||
tmp
|
||||
lib
|
||||
data
|
||||
|
||||
.vscode
|
||||
.env
|
||||
@@ -18,3 +20,9 @@ src/update.ini
|
||||
*.app
|
||||
*.xml.backup
|
||||
*.js
|
||||
!*.config.js
|
||||
|
||||
coverage
|
||||
*.tsbuildinfo
|
||||
.DS_Store
|
||||
*.log
|
||||
22
@types/PreMiD/ExtensionSettings.d.ts
vendored
@@ -1,22 +0,0 @@
|
||||
export default interface ExtensionSettings {
|
||||
/**
|
||||
* If extension is enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Autolaunch enabled
|
||||
*/
|
||||
autoLaunch: boolean;
|
||||
/**
|
||||
* Media keys enabled
|
||||
*/
|
||||
mediaKeys: boolean;
|
||||
/**
|
||||
* title menubar (TrayTitle)
|
||||
*/
|
||||
titleMenubar: boolean;
|
||||
/**
|
||||
* language of extension
|
||||
*/
|
||||
language: string;
|
||||
}
|
||||
16
@types/PreMiD/Presence.d.ts
vendored
@@ -1,16 +0,0 @@
|
||||
import * as Discord from "discord-rpc";
|
||||
|
||||
export default interface Presence {
|
||||
/**
|
||||
* Client ID of presence
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Rich Procedual call connection
|
||||
*/
|
||||
rpc: Discord.Client;
|
||||
/**
|
||||
* Connection ready?
|
||||
*/
|
||||
ready: Boolean;
|
||||
}
|
||||
33
@types/PreMiD/PresenceData.d.ts
vendored
@@ -1,33 +0,0 @@
|
||||
import * as Discord from "discord-rpc";
|
||||
|
||||
export default interface PresenceData {
|
||||
/**
|
||||
* Client ID of presence
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Tray title to be shown in Mac OS tray
|
||||
*/
|
||||
trayTitle: string;
|
||||
/**
|
||||
* service name of presence
|
||||
* @deprecated
|
||||
*/
|
||||
service: string;
|
||||
/**
|
||||
* Determines if the service is currently playing something back or not, if false it will automatically hide after 1 minute
|
||||
*/
|
||||
playback: boolean;
|
||||
/**
|
||||
* Discord Presence which gets sent directly to Discord app
|
||||
*/
|
||||
presenceData: Discord.Presence;
|
||||
/**
|
||||
* Determines if the service should be hidden (clearActivity)
|
||||
*/
|
||||
hidden: boolean;
|
||||
/**
|
||||
* Determines if the service is mediaKey able / uses them
|
||||
*/
|
||||
mediaKeys: boolean;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
* @Timeraa
|
||||
@@ -1,66 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at contact@premid.app or by contacting a staff member on our [Discord server](https://discord.premid.app). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
58
README.md
@@ -1,46 +1,44 @@
|
||||
<div align="center">
|
||||
<img width="1280" height="800" alt="Chrome Global Screenshots" src="https://github.com/user-attachments/assets/1ea21f91-7499-43de-8b9a-3344d1c0fe48" />
|
||||
|
||||
<img src=".github/Logo.png" width="150px" draggable="false"><br>
|
||||
# <img src="https://cdn.rcd.gg/PreMiD.png" height="40px" /> PreMiD
|
||||
|
||||
# PreMiD
|
||||
[](https://crowdin.com/project/premid)
|
||||
|
||||
## Your Rich Presence for web services!
|
||||
PreMiD is a simple, configurable utility that lets you show what you're doing on the web in your Discord profile. Whether you're watching videos, listening to music, browsing your favorite sites, or playing browser games, PreMiD helps you share your online activities with your friends through Discord's Rich Presence feature.
|
||||
|
||||

|
||||

|
||||

|
||||
[](https://chrome.google.com/webstore/detail/premid/agjnjboanicjcpenljmaaigopkgdnihi)
|
||||

|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FPreMiD%2FPreMiD?ref=badge_shield)
|
||||
## Getting Started
|
||||
|
||||
<img src=".github/example.png" draggable="false"><br>
|
||||
**Looking to use PreMiD?** Head over to our [official website](https://premid.app) to add the browser extension!
|
||||
|
||||
# About
|
||||
**Want to create your own Activity?** All of our community-created activities are open source and available at [github.com/PreMiD/Activities](https://github.com/PreMiD/Activities). We'd love to see what you create!
|
||||
|
||||
**PreMiD** is a simple, configurable utility that allows you to show what you're doing on the web in your Discord **now playing status**. It supports many different websites, and will support multiple users watching the same content simultaneously in an upcoming update.
|
||||
## Features
|
||||
|
||||
# Features
|
||||
- 🎵 Show what you're listening to on YouTube and more (Spotify has native Discord support)
|
||||
- 📺 Display what you're watching on Netflix, Disney+, Twitch, and hundreds of other sites
|
||||
- 🎮 Share your browser game activity with friends
|
||||
- ✨ Fully customizable with thousands of user-created Activities
|
||||
- 🌍 Available in multiple languages thanks to our amazing community translators
|
||||
|
||||
· Displays your current web service in Discord as your status.<br>
|
||||
· Grants full control over Presences.<br>
|
||||
· Supports over 1,000 web services, still rising!<br>
|
||||
· _Watch parties and more are coming soon!_
|
||||
## Community
|
||||
|
||||
# Installation/Troubleshooting
|
||||
PreMiD is built by the community, for the community. Join us and help make PreMiD even better!
|
||||
|
||||
### Installation instructions, Troubleshooting guides etc. can be located at our [**docs**](https://docs.premid.app).
|
||||
- **Activities Repository**: [github.com/PreMiD/Activities](https://github.com/PreMiD/Activities)
|
||||
- **Documentation**: [docs.premid.app](https://docs.premid.app)
|
||||
- **Discord Server**: [discord.premid.app](https://discord.premid.app)
|
||||
- **Feedback & Bug Reports**: [feedback.premid.app](https://feedback.premid.app)
|
||||
|
||||
# Support us
|
||||
## Contributing
|
||||
|
||||
<div>
|
||||
<a target="_blank" href="https://www.patreon.com/bePatron?u=4610890" data-patreon-widget-type="become-patron-button" title="Support me on Patreon!">
|
||||
<img height="75px" draggable="false" src=".github/Patreon.png">
|
||||
</a>
|
||||
<a target="_blank" href="https://discord.premid.app/" title="Join our Discord!">
|
||||
<img src="https://discordapp.com/api/guilds/493130730549805057/widget.png?style=banner2" height="76px" draggable="false" alt="Join our Discord!">
|
||||
</a>
|
||||
</div>
|
||||
We love community contributions! While **PreMiD's Activities are fully open source** (the code that makes websites show up on your profile), the PreMiD extension is not currently open source. This decision allows our small team to move fast and iterate quickly to deliver the best experience possible.
|
||||
|
||||
You can contribute by:
|
||||
- Creating new Activities at [github.com/PreMiD/Activities](https://github.com/PreMiD/Activities)
|
||||
- Helping translate PreMiD on [Crowdin](https://crowdin.com/project/premid)
|
||||
- Reporting bugs and suggesting features at [feedback.premid.app](https://feedback.premid.app)
|
||||
- Supporting the project and spreading the word!
|
||||
|
||||
## License
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2FPreMiD%2FPreMiD?ref=badge_large)
|
||||
This project is licensed under the [MPL-2.0 License](LICENSE).
|
||||
|
||||
3
apps/pd/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @premid/pd
|
||||
|
||||
A simple url shortener service to shorten urls longer than 256 characters.
|
||||
12
apps/pd/environment.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NODE_ENV?: "development" | "production" | "test";
|
||||
REDIS_URL?: string;
|
||||
MAX_FILE_SIZE?: string;
|
||||
PORT?: string;
|
||||
HOST?: string;
|
||||
RATELIMIT_MAX?: string;
|
||||
RATELIMIT_WINDOW?: string;
|
||||
BASE_URL?: string;
|
||||
}
|
||||
}
|
||||
BIN
apps/pd/fixtures/1x1.png
Normal file
|
After Width: | Height: | Size: 95 B |
BIN
apps/pd/fixtures/test.mp4
Normal file
1499
apps/pd/package-lock.json
generated
Normal file
36
apps/pd/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@premid/pd",
|
||||
"type": "module",
|
||||
"version": "1.2.4",
|
||||
"private": true,
|
||||
"description": "A small service to shorten image urls to get around Discord's 256 character limit",
|
||||
"license": "MPL-2.0",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps .",
|
||||
"dev": "node --watch --enable-source-maps ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^9.0.1",
|
||||
"@fastify/multipart": "^8.1.0",
|
||||
"@fastify/rate-limit": "^9.1.0",
|
||||
"@keyv/redis": "^2.8.4",
|
||||
"fastify": "^4.26.0",
|
||||
"file-type": "^19.0.0",
|
||||
"got": "^14.2.0",
|
||||
"ioredis": "^5.3.2",
|
||||
"ipaddr.js": "^2.1.0",
|
||||
"keyv": "^4.5.4",
|
||||
"mime-types": "^2.1.35",
|
||||
"nanoid": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/node": "^24.10.1",
|
||||
"form-data": "^4.0.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
9
apps/pd/src/functions/createKeyv.test.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import createKeyv from "./createKeyv.js";
|
||||
|
||||
it("should return keyv instance", () => {
|
||||
const keyv = createKeyv();
|
||||
|
||||
expect(keyv).toStrictEqual(expect.any(Object));
|
||||
});
|
||||
28
apps/pd/src/functions/createKeyv.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import process from "node:process";
|
||||
import KeyvRedis from "@keyv/redis";
|
||||
import Keyv from "keyv";
|
||||
|
||||
import redis from "../redis.js";
|
||||
|
||||
export default function createKeyv() {
|
||||
let options: Keyv.Options<string> | undefined;
|
||||
|
||||
/* c8 ignore next 8 */
|
||||
if (process.env.REDIS_SENTINELS) {
|
||||
options = {
|
||||
namespace: "pd",
|
||||
store: new KeyvRedis(redis),
|
||||
};
|
||||
}
|
||||
|
||||
const keyv = new Keyv<string>(
|
||||
options,
|
||||
);
|
||||
|
||||
/* c8 ignore next 3 */
|
||||
keyv.on("error", (error) => {
|
||||
console.error("Keyv connection error:", error);
|
||||
});
|
||||
|
||||
return keyv;
|
||||
}
|
||||
30
apps/pd/src/functions/createRedis.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/* eslint-disable no-console */
|
||||
import { hostname } from "node:os";
|
||||
|
||||
import process from "node:process";
|
||||
import { Redis } from "ioredis";
|
||||
|
||||
/* c8 ignore start */
|
||||
export default function createRedis(): Redis {
|
||||
const redis = new Redis({
|
||||
connectionName: `pd-${hostname()}-${process.pid.toString()}`,
|
||||
lazyConnect: true,
|
||||
name: "mymaster",
|
||||
sentinels: process.env.REDIS_SENTINELS?.split(",").map(s => ({
|
||||
host: s,
|
||||
port: 26_379,
|
||||
})),
|
||||
});
|
||||
|
||||
/* c8 ignore next 3 */
|
||||
redis.on("error", (error) => {
|
||||
console.error("Redis error", error);
|
||||
});
|
||||
|
||||
/* c8 ignore next 3 */
|
||||
redis.on("connect", () => {
|
||||
console.log("Redis connected");
|
||||
});
|
||||
|
||||
return redis;
|
||||
}
|
||||
10
apps/pd/src/functions/createServer.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe("createServer", () => {
|
||||
it("should return a fastify instance", async () => {
|
||||
const server = await createServer();
|
||||
expect(server).toBeDefined();
|
||||
});
|
||||
});
|
||||
62
apps/pd/src/functions/createServer.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import process from "node:process";
|
||||
import cors from "@fastify/cors";
|
||||
import fastifyMultipart from "@fastify/multipart";
|
||||
import ratelimit from "@fastify/rate-limit";
|
||||
import fastify from "fastify";
|
||||
import type { Redis } from "ioredis";
|
||||
|
||||
import createFromBase64 from "../routes/createFromBase64.js";
|
||||
import createFromImage from "../routes/createFromImage.js";
|
||||
import createShortenedLink from "../routes/createShortenedLink.js";
|
||||
import getFullLink from "../routes/getFullLink.js";
|
||||
|
||||
export async function createServer(redis?: Redis) {
|
||||
const server = fastify({
|
||||
trustProxy: true,
|
||||
});
|
||||
|
||||
await server.register(cors, {
|
||||
methods: ["GET"],
|
||||
origin: "*",
|
||||
});
|
||||
|
||||
await server.register(ratelimit, {
|
||||
max: Number.parseInt(process.env.RATELIMIT_MAX ?? "25"),
|
||||
nameSpace: "pd-ratelimit-",
|
||||
redis,
|
||||
timeWindow: process.env.RATELIMIT_WINDOW ?? "1 minute",
|
||||
});
|
||||
|
||||
await server.register(fastifyMultipart, {
|
||||
limits: {
|
||||
fileSize: Number.parseInt(process.env.MAX_FILE_SIZE ?? (5 * 1024 * 1024).toString()),
|
||||
files: 1,
|
||||
},
|
||||
});
|
||||
|
||||
server.post("/create/image", createFromImage);
|
||||
server.post("/create/base64", createFromBase64);
|
||||
server.get("/create/*", createShortenedLink);
|
||||
|
||||
server.get(
|
||||
"/*",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
getFullLink,
|
||||
);
|
||||
|
||||
server.get(
|
||||
"/health",
|
||||
{
|
||||
config: {
|
||||
rateLimit: false,
|
||||
},
|
||||
},
|
||||
(_, reply) => reply.status(204).send(),
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
30
apps/pd/src/functions/getGoogleAddresses.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import got from "got";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe.concurrent("getGoogleAddresses", () => {
|
||||
it("should return an array of CIDR objects", async () => {
|
||||
const { default: getGoogleAddresses } = await import("./getGoogleAddresses.js");
|
||||
|
||||
vi.spyOn(got, "get").mockResolvedValue({
|
||||
body: JSON.stringify({
|
||||
prefixes: [
|
||||
{ ipv4Prefix: "0.0.0.0" },
|
||||
{ ipv6Prefix: "0000:0000:0000::/16" },
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await getGoogleAddresses();
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"ipv4Prefix": "0.0.0.0",
|
||||
},
|
||||
{
|
||||
"ipv6Prefix": "0000:0000:0000::/16",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
24
apps/pd/src/functions/getGoogleAddresses.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import got from "got";
|
||||
|
||||
import type { CIDR } from "./isInCidRange.js";
|
||||
|
||||
export default async function getGoogleAddresses(): Promise<CIDR> {
|
||||
const { body } = await got.get("https://www.gstatic.com/ipranges/cloud.json");
|
||||
const result = JSON.parse(body) as GoogleResult;
|
||||
return result.prefixes.map(({ ipv4Prefix, ipv6Prefix }) => {
|
||||
return ipv6Prefix ? { ipv6Prefix } : { ipv4Prefix };
|
||||
});
|
||||
}
|
||||
|
||||
interface GoogleResult {
|
||||
syncToken: string;
|
||||
creationTime: string;
|
||||
prefixes: GoogleIP[];
|
||||
}
|
||||
|
||||
interface GoogleIP {
|
||||
ipv6Prefix: string;
|
||||
ipv4Prefix: string;
|
||||
service: string;
|
||||
scope: string;
|
||||
}
|
||||
31
apps/pd/src/functions/isInCidRange.test.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import isInCIDRRange from "./isInCidRange.js";
|
||||
|
||||
it("isInCIDRRange - IPv4 - in range", () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }];
|
||||
const ip = "192.0.2.123";
|
||||
const result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("isInCIDRRange - IPv4 - not in range", () => {
|
||||
const CIDRs = [{ ipv4Prefix: "192.0.2.0/24" }];
|
||||
const ip = "192.0.3.123";
|
||||
const result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it("isInCIDRRange - IPv6 - in range", () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }];
|
||||
const ip = "2001:db8::1234";
|
||||
const result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("isInCIDRRange - IPv6 - not in range", () => {
|
||||
const CIDRs = [{ ipv6Prefix: "2001:db8::/32" }];
|
||||
const ip = "2001:db9::1234";
|
||||
const result = isInCIDRRange(CIDRs, ip);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
29
apps/pd/src/functions/isInCidRange.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import ipaddr from "ipaddr.js";
|
||||
|
||||
export default function isInCIDRRange(CIDRs: CIDR, ip: string) {
|
||||
const parsed = ipaddr.parse(ip);
|
||||
|
||||
for (const CIDR of CIDRs.filter((c) => {
|
||||
if (parsed.kind() === "ipv4" && "ipv4Prefix" in c)
|
||||
return true;
|
||||
else if (parsed.kind() === "ipv6" && "ipv6Prefix" in c)
|
||||
return true;
|
||||
else return false;
|
||||
})) {
|
||||
const check = parsed.match(ipaddr.parseCIDR("ipv4Prefix" in CIDR ? CIDR.ipv4Prefix : CIDR.ipv6Prefix));
|
||||
|
||||
if (check)
|
||||
return check;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export type CIDR = (
|
||||
| {
|
||||
ipv4Prefix: string;
|
||||
}
|
||||
| {
|
||||
ipv6Prefix: string;
|
||||
}
|
||||
)[];
|
||||
3
apps/pd/src/googleCIDRs.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import getGoogleAddresses from "./functions/getGoogleAddresses.js";
|
||||
|
||||
export default await getGoogleAddresses();
|
||||
14
apps/pd/src/index.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
|
||||
it("/health", async () => {
|
||||
const server = await createServer();
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/health",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(204);
|
||||
expect(result.body).toBe("");
|
||||
});
|
||||
20
apps/pd/src/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/* eslint-disable no-console */
|
||||
/* c8 ignore start */
|
||||
import process from "node:process";
|
||||
import { createServer } from "./functions/createServer.js";
|
||||
import redis from "./redis.js";
|
||||
|
||||
if (!process.env.REDIS_SENTINELS)
|
||||
console.log("WARNING: No REDIS_SENTINELS environment variable set");
|
||||
if (process.env.NODE_ENV === "production" && !process.env.BASE_URL)
|
||||
throw new Error("BASE_URL environment variable is required in production");
|
||||
|
||||
export const server = await createServer(redis);
|
||||
|
||||
const url = await server.listen({
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
port: Number.parseInt(process.env.PORT ?? "80"),
|
||||
});
|
||||
|
||||
console.log(`Server listening at ${url}`);
|
||||
// TODO Make proper error codes & json responses
|
||||
5
apps/pd/src/keyv.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import createKeyv from "./functions/createKeyv.js";
|
||||
|
||||
export const keyv = createKeyv();
|
||||
|
||||
export const ttl = 30 * 60 * 1000;
|
||||
3
apps/pd/src/redis.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import createRedis from "./functions/createRedis.js";
|
||||
|
||||
export default createRedis();
|
||||
94
apps/pd/src/routes/createFromBase64.test.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { describe, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("createFromBase64", async () => {
|
||||
const server = await createServer();
|
||||
|
||||
it("should return a 400 when the body is not present", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
method: "POST",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid body\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the body is not a string", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
method: "POST",
|
||||
payload: new Blob([]),
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid body\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the body is not a valid base64 string", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64t",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid base64 string\"");
|
||||
});
|
||||
|
||||
it("should return a 400 when the base64 string is not a valid image", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/sv;base64,a",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid base64 string\"");
|
||||
|
||||
const result2 = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/svg+xml;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result2.statusCode).toBe(400);
|
||||
expect(result2.body).toMatchInlineSnapshot("\"Supported types: png, jpeg, jpg, gif, webp\"");
|
||||
});
|
||||
|
||||
it("should return a 200 when the base64 string is valid", async ({ expect }) => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
|
||||
const result2 = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: "data:image/png;base64,s",
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(result2.statusCode).toBe(200);
|
||||
expect(result2.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
});
|
||||
47
apps/pd/src/routes/createFromBase64.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import process from "node:process";
|
||||
import mime from "mime-types";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { RouteHandlerMethod } from "fastify";
|
||||
|
||||
import { keyv, ttl } from "../keyv.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
const { body } = request;
|
||||
|
||||
if (!body)
|
||||
return reply.status(400).send("Invalid body");
|
||||
|
||||
if (typeof body !== "string")
|
||||
return reply.status(400).send("Invalid body");
|
||||
|
||||
const matches = body.match(/^data:(.+);base64,(.+)/);
|
||||
|
||||
if (!matches || matches.length === 0)
|
||||
return reply.status(400).send("Invalid base64 string");
|
||||
|
||||
const type = mime.extension(matches.at(1)!);
|
||||
|
||||
if (!type)
|
||||
return reply.status(400).send("Invalid base64 string");
|
||||
|
||||
if (!["png", "jpeg", "jpg", "gif", "webp"].includes(type))
|
||||
return reply.status(400).send("Supported types: png, jpeg, jpg, gif, webp");
|
||||
|
||||
const hash = crypto.createHash("sha256").update(body).digest("hex");
|
||||
const existingUrl = await keyv.get(hash);
|
||||
|
||||
if (existingUrl) {
|
||||
return reply.send(process.env.BASE_URL! + existingUrl);
|
||||
}
|
||||
|
||||
const uniqueId = `${nanoid(10)}.${type}`;
|
||||
|
||||
await keyv.set(hash, uniqueId, ttl);
|
||||
await keyv.set(uniqueId, body, ttl);
|
||||
|
||||
return reply.send(process.env.BASE_URL! + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
106
apps/pd/src/routes/createFromImage.test.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Buffer } from "node:buffer";
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
import type { RequestOptions } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { afterAll, beforeAll, describe, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("createFromImage", async () => {
|
||||
const server = await createServer();
|
||||
const form = new FormData();
|
||||
const defaultRequestOptions: RequestOptions = {
|
||||
hostname: "localhost",
|
||||
method: "POST",
|
||||
path: "/create/image",
|
||||
protocol: "http:",
|
||||
};
|
||||
|
||||
let url: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
url = await server.listen();
|
||||
defaultRequestOptions.port = (server.server.address() as AddressInfo).port;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
void server.close();
|
||||
});
|
||||
|
||||
it("should return a 400 when request is not multipart", async ({ expect }) => {
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Request is not multipart\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when no file is provided", async ({ expect }) => {
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
});
|
||||
it("should return a 400 status code when the file is invalid", async ({ expect }) => {
|
||||
form.set("file", Buffer.alloc(1024 * 1024 * 2));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
|
||||
form.set("file", new Blob([new Uint8Array(1024 * 1024 * 2)]));
|
||||
|
||||
const result2 = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result2.status).toBe(400);
|
||||
expect(await result2.text()).toMatchInlineSnapshot("\"Invalid file\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the file is not an image", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/test.mp4", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(400);
|
||||
expect(await result.text()).toMatchInlineSnapshot("\"Only png, jpeg, jpg, gif and webp are supported\"");
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the file is valid", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/1x1.png", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(await result.text()).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the file is valid and the same file is uploaded again", async ({ expect }) => {
|
||||
form.set("file", new Blob([await readFile(new URL("../../fixtures/1x1.png", import.meta.url))]));
|
||||
|
||||
const result = await fetch(`${url}/create/image`, {
|
||||
body: form,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(await result.text()).toStrictEqual(expect.any(String));
|
||||
});
|
||||
});
|
||||
53
apps/pd/src/routes/createFromImage.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import process from "node:process";
|
||||
import { fileTypeFromBuffer } from "file-type";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { RouteHandlerMethod } from "fastify";
|
||||
|
||||
import { keyv, ttl } from "../keyv.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
if (!request.isMultipart())
|
||||
return reply.status(400).send("Request is not multipart");
|
||||
|
||||
const file = await request.file();
|
||||
|
||||
if (!file)
|
||||
return reply.status(400).send("Invalid file");
|
||||
|
||||
const type = await fileTypeFromBuffer(await file.toBuffer());
|
||||
|
||||
if (!type)
|
||||
return reply.status(400).send("Invalid file");
|
||||
|
||||
if (![
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
].includes(type.mime)) {
|
||||
return reply.status(400).send("Only png, jpeg, jpg, gif and webp are supported");
|
||||
}
|
||||
|
||||
const buffer = await file.toBuffer();
|
||||
const body = `data:${type.mime};base64,${buffer.toString("base64")}`;
|
||||
const hash = crypto.createHash("sha256").update(body).digest("hex");
|
||||
const existingUrl = await keyv.get(hash);
|
||||
|
||||
if (existingUrl) {
|
||||
return reply.send(process.env.BASE_URL! + existingUrl);
|
||||
}
|
||||
|
||||
const uniqueId = `${nanoid(10)}.${type.ext}`;
|
||||
|
||||
await Promise.all([
|
||||
keyv.set(hash, uniqueId, ttl),
|
||||
keyv.set(uniqueId, body, ttl),
|
||||
]);
|
||||
|
||||
return reply.send(process.env.BASE_URL! + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
152
apps/pd/src/routes/createShortenedLink.test.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
|
||||
describe.concurrent("/create", async () => {
|
||||
const server = await createServer();
|
||||
|
||||
it("should return a 400 status code when no URL is provided", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid URL\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is too short", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: "/create/https://www.google.com",
|
||||
});
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"URL is too short\"");
|
||||
});
|
||||
|
||||
it("should return a 400 status code when the URL is invalid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/file://www.googl${"e".repeat(256)}`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(400);
|
||||
expect(result.body).toMatchInlineSnapshot("\"Invalid URL\"");
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"e".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
});
|
||||
|
||||
it("should return a 200 status code when the URL is valid and already exists", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
const { body } = result;
|
||||
const result2 = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.googl${"d".repeat(256)}.com`,
|
||||
});
|
||||
|
||||
expect(result2.statusCode).toBe(200);
|
||||
expect(result2.body).toStrictEqual(body);
|
||||
});
|
||||
|
||||
it("should preserve file extension when URL has a valid image extension", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/image.png`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.png$/);
|
||||
});
|
||||
|
||||
it("should preserve file extension when URL has .jpg extension", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/photo.jpg`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.jpg$/);
|
||||
});
|
||||
|
||||
it("should preserve file extension when URL has .webp extension", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/image.webp`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.webp$/);
|
||||
});
|
||||
|
||||
it("should preserve file extension when URL has .png with query parameters", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/image.png?ref=example`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.png$/);
|
||||
});
|
||||
|
||||
it("should preserve file extension when URL has .gif with complex query parameters", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/animated.gif?size=large&quality=high`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.gif$/);
|
||||
});
|
||||
|
||||
it("should not preserve file extension when URL has invalid extension", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/document.pdf`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).not.toMatch(/\.pdf$/);
|
||||
});
|
||||
|
||||
it("should work normally when URL has no file extension", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/page`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).not.toMatch(/\.\w+$/);
|
||||
});
|
||||
|
||||
it("should handle case-insensitive extensions", async () => {
|
||||
const result = await server.inject({
|
||||
method: "GET",
|
||||
url: `/create/https://www.exampl${"e".repeat(256)}.com/image.PNG`,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.body).toStrictEqual(expect.any(String));
|
||||
expect(result.body).toMatch(/\.png$/); //* Should be lowercase in result
|
||||
});
|
||||
});
|
||||
47
apps/pd/src/routes/createShortenedLink.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import process from "node:process";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { RouteHandlerMethod } from "fastify";
|
||||
|
||||
import { keyv, ttl } from "../keyv.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
const url = request.url.replace("/create/", "").trim();
|
||||
|
||||
if (url.length === 0)
|
||||
return reply.status(400).send("Invalid URL");
|
||||
|
||||
if (url.length < 256)
|
||||
return reply.status(400).send("URL is too short");
|
||||
|
||||
const urlObject = new URL(url);
|
||||
if (!["http:", "https:"].includes(urlObject.protocol))
|
||||
return reply.status(400).send("Invalid URL");
|
||||
|
||||
//* Extract file extension from URL pathname
|
||||
const pathname = urlObject.pathname;
|
||||
const extensionMatch = pathname.match(/\.([^./]+)$/);
|
||||
const extension = extensionMatch?.[1]?.toLowerCase() ?? null;
|
||||
|
||||
//* Check if extension is in allowed list
|
||||
const allowedExtensions = ["png", "jpeg", "jpg", "gif", "webp"];
|
||||
const hasValidExtension = extension && allowedExtensions.includes(extension);
|
||||
|
||||
const hash = crypto.createHash("sha256").update(url).digest("hex");
|
||||
const existingShortenedUrl = await keyv.get(hash);
|
||||
|
||||
if (existingShortenedUrl) {
|
||||
await Promise.all([keyv.set(hash, existingShortenedUrl, ttl), keyv.set(existingShortenedUrl, url, ttl)]);
|
||||
return reply.send(process.env.BASE_URL! + existingShortenedUrl);
|
||||
}
|
||||
|
||||
//* Create unique ID with extension if valid, otherwise without extension
|
||||
const uniqueId = hasValidExtension ? `${nanoid(10)}.${extension}` : nanoid(10);
|
||||
|
||||
await Promise.all([keyv.set(hash, uniqueId, ttl), keyv.set(uniqueId, url, ttl)]);
|
||||
|
||||
return reply.send(process.env.BASE_URL! + uniqueId);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
128
apps/pd/src/routes/getFullLink.test.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Buffer } from "node:buffer";
|
||||
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createServer } from "../functions/createServer.js";
|
||||
import * as isInCIDRRange from "../functions/isInCidRange.js";
|
||||
|
||||
describe("getFullLink", async () => {
|
||||
const server = await createServer();
|
||||
let url: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
url = await server.listen();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it("should fail if not a Google Cloud IP", async () => {
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/1234567890",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(401);
|
||||
});
|
||||
|
||||
it("should fail if not a valid ID", async () => {
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/123",
|
||||
});
|
||||
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
const result2 = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: "/1234567890.",
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(404);
|
||||
expect(result2.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("should redirect to the correct URL", async () => {
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const { body } = await server.inject({
|
||||
url: `/create/https://${"a".repeat(256)}`,
|
||||
});
|
||||
|
||||
expect(body).toStrictEqual(expect.any(String));
|
||||
|
||||
const result = await server.inject({
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
url: body,
|
||||
});
|
||||
|
||||
expect(result.statusCode).toBe(302);
|
||||
expect(result.headers.location).toBe(`https://${"a".repeat(256)}`);
|
||||
});
|
||||
|
||||
it("should return the correct image", async () => {
|
||||
const imageBuffer = await readFile(new URL("../../fixtures/test.mp4", import.meta.url));
|
||||
const imageBase64 = `data:image/png;base64,${imageBuffer.toString("base64")}`;
|
||||
|
||||
const { body } = await server.inject({
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
method: "POST",
|
||||
payload: imageBase64,
|
||||
url: "/create/base64",
|
||||
});
|
||||
|
||||
expect(body).toStrictEqual(expect.any(String));
|
||||
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const result = await fetch(`${url}${body}`, {
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.headers.get("content-type")).toBe("image/png");
|
||||
expect(Buffer.from(await result.arrayBuffer())).toStrictEqual(imageBuffer);
|
||||
});
|
||||
|
||||
it("should fetch and return PNG image instead of redirecting for URLs with .png extension", async () => {
|
||||
const testUrl = `https://cdn.rcd.gg/PreMiD/resources/reading.png?v=${"a".repeat(250)}`;
|
||||
|
||||
const { body } = await server.inject({
|
||||
url: `/create/${testUrl}`,
|
||||
});
|
||||
|
||||
expect(body).toStrictEqual(expect.any(String));
|
||||
|
||||
vi.spyOn(isInCIDRRange, "default").mockReturnValueOnce(true);
|
||||
|
||||
const result = await fetch(`${url}${body}`, {
|
||||
headers: {
|
||||
"cf-connecting-ip": "",
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.headers.get("content-type")).toMatch(/^image\//);
|
||||
//* Should return image data, not redirect
|
||||
expect(result.headers.get("location")).toBeNull();
|
||||
|
||||
//* Verify we got actual image data
|
||||
const imageBuffer = await result.arrayBuffer();
|
||||
expect(imageBuffer.byteLength).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
93
apps/pd/src/routes/getFullLink.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Buffer } from "node:buffer";
|
||||
|
||||
import crypto from "node:crypto";
|
||||
import type { RouteHandlerMethod } from "fastify";
|
||||
|
||||
import isInCIDRRange from "../functions/isInCidRange.js";
|
||||
import googleCIDRs from "../googleCIDRs.js";
|
||||
import { keyv, ttl } from "../keyv.js";
|
||||
|
||||
const handler: RouteHandlerMethod = async (request, reply) => {
|
||||
/* c8 ignore next 2 */
|
||||
const ip = request.headers["cf-connecting-ip"]?.toString() || request.socket.remoteAddress || request.ip;
|
||||
|
||||
if (
|
||||
!isInCIDRRange(
|
||||
googleCIDRs,
|
||||
ip,
|
||||
)
|
||||
) {
|
||||
return reply.status(401).send("Not a Google Cloud IP");
|
||||
}
|
||||
|
||||
const id = (request.params as { "*": string })["*"].trim();
|
||||
|
||||
if (id.split(".")[0]?.length !== 10)
|
||||
return reply.code(404).send("Invalid ID");
|
||||
|
||||
const url = await keyv.get(id);
|
||||
if (!url)
|
||||
return reply.code(404).send("Unknown ID");
|
||||
|
||||
const hash = crypto.createHash("sha256").update(url).digest("hex");
|
||||
|
||||
await Promise.all([keyv.set(hash, id, ttl), keyv.set(id, url, ttl)]);
|
||||
|
||||
//* If it is a base64 string, decode and return the image
|
||||
if (url.startsWith("data:image")) {
|
||||
const image = Buffer.from(
|
||||
url.replace(/^data:image\/\w+;base64,/, ""),
|
||||
"base64",
|
||||
);
|
||||
|
||||
const mime = url.split(";")[0]!.split(":")[1]!;
|
||||
|
||||
return reply.type(mime).send(image);
|
||||
}
|
||||
|
||||
//* Check if URL has a valid image extension
|
||||
const urlObject = new URL(url);
|
||||
const pathname = urlObject.pathname;
|
||||
const extensionMatch = pathname.match(/\.([^./]+)$/);
|
||||
const extension = extensionMatch?.[1]?.toLowerCase() ?? null;
|
||||
|
||||
const allowedExtensions = ["png", "jpeg", "jpg", "gif", "webp"];
|
||||
const hasValidImageExtension = extension && allowedExtensions.includes(extension);
|
||||
|
||||
//* If URL has valid image extension, fetch and return the image
|
||||
if (hasValidImageExtension) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"Accept": "image/*",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Accept-Encoding": "gzip, deflate, br",
|
||||
"Cache-Control": "no-cache",
|
||||
"Pragma": "no-cache",
|
||||
"Referer": urlObject.origin, //* Set referer to the origin domain to bypass hotlink protection
|
||||
"Sec-Fetch-Dest": "image",
|
||||
"Sec-Fetch-Mode": "no-cors",
|
||||
"Sec-Fetch-Site": "cross-site",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return reply.code(404).send("Image not found");
|
||||
}
|
||||
|
||||
const contentType = response.headers.get("content-type") || `image/${extension}`;
|
||||
const imageBuffer = Buffer.from(await response.arrayBuffer());
|
||||
|
||||
return reply.type(contentType).send(imageBuffer);
|
||||
}
|
||||
catch {
|
||||
//* If fetch fails, fall back to redirect
|
||||
return reply.redirect(url);
|
||||
}
|
||||
}
|
||||
|
||||
//* For all other URLs, redirect to them
|
||||
return reply.redirect(url);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
10
apps/pd/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["**/*.test.ts", "dist"]
|
||||
}
|
||||
9
apps/pd/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"types": ["@types/node"],
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["environment.d.ts", "src"],
|
||||
"exclude": ["**/*.test.ts"]
|
||||
}
|
||||
2
apps/schema-server/.eslintrc.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
rules:
|
||||
no-console: off
|
||||
3
apps/schema-server/Readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @premid/schema-server
|
||||
|
||||
This is a simple schema server which serves JSON schemas for Presence Developers.
|
||||
7
apps/schema-server/environment.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NODE_ENV?: "development" | "production" | "test";
|
||||
PORT?: string;
|
||||
HOST?: string;
|
||||
}
|
||||
}
|
||||
859
apps/schema-server/package-lock.json
generated
Normal file
@@ -0,0 +1,859 @@
|
||||
{
|
||||
"name": "@premid/schema-server",
|
||||
"version": "1.0.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@premid/schema-server",
|
||||
"version": "1.0.10",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"fastify": "^4.26.0",
|
||||
"globby": "^14.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/ajv-compiler": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz",
|
||||
"integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"fast-uri": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/error": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz",
|
||||
"integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@fastify/fast-json-stringify-compiler": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz",
|
||||
"integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-json-stringify": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/helmet": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-11.1.1.tgz",
|
||||
"integrity": "sha512-pjJxjk6SLEimITWadtYIXt6wBMfFC1I6OQyH/jYVCqSAn36sgAIFjeNiibHtifjCd+e25442pObis3Rjtame6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fastify-plugin": "^4.2.1",
|
||||
"helmet": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/merge-json-schemas": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz",
|
||||
"integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
"run-parallel": "^1.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.stat": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.walk": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
"fastq": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@pinojs/redact": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
||||
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sindresorhus/merge-streams": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
|
||||
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/abstract-logging": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ajv/node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/avvio": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz",
|
||||
"integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/error": "^3.3.0",
|
||||
"fastq": "^1.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
|
||||
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-decode-uri-component": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
|
||||
"integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-json-stringify": {
|
||||
"version": "5.16.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz",
|
||||
"integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/merge-json-schemas": "^0.1.0",
|
||||
"ajv": "^8.10.0",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^2.1.0",
|
||||
"json-schema-ref-resolver": "^1.0.1",
|
||||
"rfdc": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-json-stringify/node_modules/ajv-formats": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
|
||||
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fast-querystring": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
|
||||
"integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-decode-uri-component": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz",
|
||||
"integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fastify": {
|
||||
"version": "4.29.1",
|
||||
"resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.1.tgz",
|
||||
"integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/ajv-compiler": "^3.5.0",
|
||||
"@fastify/error": "^3.4.0",
|
||||
"@fastify/fast-json-stringify-compiler": "^4.3.0",
|
||||
"abstract-logging": "^2.0.1",
|
||||
"avvio": "^8.3.0",
|
||||
"fast-content-type-parse": "^1.1.0",
|
||||
"fast-json-stringify": "^5.8.0",
|
||||
"find-my-way": "^8.0.0",
|
||||
"light-my-request": "^5.11.0",
|
||||
"pino": "^9.0.0",
|
||||
"process-warning": "^3.0.0",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"rfdc": "^1.3.0",
|
||||
"secure-json-parse": "^2.7.0",
|
||||
"semver": "^7.5.4",
|
||||
"toad-cache": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fastify-plugin": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz",
|
||||
"integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-my-way": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz",
|
||||
"integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-querystring": "^1.0.0",
|
||||
"safe-regex2": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/globby": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz",
|
||||
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^2.1.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"ignore": "^7.0.3",
|
||||
"path-type": "^6.0.0",
|
||||
"slash": "^5.1.0",
|
||||
"unicorn-magic": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/helmet": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz",
|
||||
"integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-ref-resolver": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz",
|
||||
"integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/light-my-request": {
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz",
|
||||
"integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"cookie": "^0.7.0",
|
||||
"process-warning": "^3.0.0",
|
||||
"set-cookie-parser": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/on-exit-leak-free": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
||||
"integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "9.14.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz",
|
||||
"integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pinojs/redact": "^0.4.0",
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-std-serializers": "^7.0.0",
|
||||
"process-warning": "^5.0.0",
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
"real-require": "^0.2.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"thread-stream": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pino": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-abstract-transport": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
|
||||
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-std-serializers": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pino/node_modules/process-warning": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/process-warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
|
||||
"integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quick-format-unescaped": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/real-require": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ret": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz",
|
||||
"integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-regex2": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz",
|
||||
"integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ret": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
|
||||
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/slash": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/sonic-boom": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
||||
"integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"real-require": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toad-cache": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
|
||||
"integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicorn-magic": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
apps/schema-server/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@premid/schema-server",
|
||||
"type": "module",
|
||||
"version": "1.0.10",
|
||||
"private": true,
|
||||
"description": "A small service to serve the JSON schemas for PreMiD",
|
||||
"license": "MPL-2.0",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"schemas"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps .",
|
||||
"dev": "node --watch --enable-source-maps ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/helmet": "^11.1.1",
|
||||
"fastify": "^4.26.0",
|
||||
"globby": "^14.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
241
apps/schema-server/schemas/metadata/1.0.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
252
apps/schema-server/schemas/metadata/1.1.json
Normal file
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
277
apps/schema-server/schemas/metadata/1.10.json
Normal file
@@ -0,0 +1,277 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.10",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"matches": {
|
||||
"type": "array",
|
||||
"description": "A glob pattern required to match Google's match pattern (https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns). This is required for Presences.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A glob pattern.",
|
||||
"pattern": "^(https|http|[*])://.*/.*"
|
||||
}
|
||||
},
|
||||
|
||||
"iFrameMatches": {
|
||||
"type": "array",
|
||||
"description": "A glob pattern required to match Google's match pattern (https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns). This is required for Presences.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A glob pattern.",
|
||||
"pattern": "^(https|http|[*])://.*/.*"
|
||||
}
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
260
apps/schema-server/schemas/metadata/1.11.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.11",
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"en"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"description": "The Presence System version this Presence supports.",
|
||||
"minimum": 1,
|
||||
"maximum": 2
|
||||
},
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
"tags": {
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"description": "The tags for the presence.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": [
|
||||
"anime",
|
||||
"games",
|
||||
"music",
|
||||
"socials",
|
||||
"videos",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
"value": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"multiLanguage": {
|
||||
"type": [
|
||||
"string",
|
||||
"boolean",
|
||||
"array"
|
||||
],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"author",
|
||||
"service",
|
||||
"description",
|
||||
"url",
|
||||
"version",
|
||||
"apiVersion",
|
||||
"logo",
|
||||
"thumbnail",
|
||||
"color",
|
||||
"tags",
|
||||
"category"
|
||||
]
|
||||
}
|
||||
252
apps/schema-server/schemas/metadata/1.12.json
Normal file
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.12",
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"en"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"description": "The Presence System version this Presence supports.",
|
||||
"minimum": 1,
|
||||
"maximum": 2
|
||||
},
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
"tags": {
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"description": "The tags for the presence.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": [
|
||||
"anime",
|
||||
"games",
|
||||
"music",
|
||||
"socials",
|
||||
"videos",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
"value": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"multiLanguage": {
|
||||
"type": "boolean",
|
||||
"description": "When true, strings from the `general.json` file are available for use, plus the <service>.json file. False is not allowed."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"author",
|
||||
"service",
|
||||
"description",
|
||||
"url",
|
||||
"version",
|
||||
"apiVersion",
|
||||
"logo",
|
||||
"thumbnail",
|
||||
"color",
|
||||
"tags",
|
||||
"category"
|
||||
]
|
||||
}
|
||||
248
apps/schema-server/schemas/metadata/1.13.json
Normal file
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.13",
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a activity.",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this activity."
|
||||
},
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this activity.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this activity is for."
|
||||
},
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the activity in multiple languages.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(-regioncode).",
|
||||
"pattern": "^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the activity in the key's language."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"en"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the activity. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"description": "The Activity System version this activity supports.",
|
||||
"minimum": 1,
|
||||
"maximum": 2
|
||||
},
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this activity is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
"tags": {
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"description": "The tags for the activity.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the activity falls under.",
|
||||
"enum": [
|
||||
"anime",
|
||||
"games",
|
||||
"music",
|
||||
"socials",
|
||||
"videos",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity should run in IFrames."
|
||||
},
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the activity to inject into."
|
||||
},
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the activity to inject into."
|
||||
},
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the activity.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
"value": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"multiLanguage": {
|
||||
"type": "boolean",
|
||||
"description": "When true, strings from the `general.json` file are available for use, plus the <service>.json file. False is not allowed."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity has support for mobile devices."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"author",
|
||||
"service",
|
||||
"description",
|
||||
"url",
|
||||
"version",
|
||||
"apiVersion",
|
||||
"logo",
|
||||
"thumbnail",
|
||||
"color",
|
||||
"tags",
|
||||
"category"
|
||||
]
|
||||
}
|
||||
252
apps/schema-server/schemas/metadata/1.14.json
Normal file
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.14",
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a activity.",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this activity."
|
||||
},
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this activity.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this activity is for."
|
||||
},
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the activity in multiple languages.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(-regioncode).",
|
||||
"pattern": "^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the activity in the key's language."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"en"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the activity. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"description": "The Activity System version this activity supports.",
|
||||
"minimum": 1,
|
||||
"maximum": 2
|
||||
},
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this activity is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
"tags": {
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"description": "The tags for the activity.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the activity falls under.",
|
||||
"enum": [
|
||||
"anime",
|
||||
"games",
|
||||
"music",
|
||||
"socials",
|
||||
"videos",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity should run in IFrames."
|
||||
},
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the activity to inject into."
|
||||
},
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the activity to inject into."
|
||||
},
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the activity.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the setting. Only applicable if `multiLanguage` is disabled."
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
"value": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"multiLanguage": {
|
||||
"type": "boolean",
|
||||
"description": "When true, strings from the `general.json` file are available for use, plus the <service>.json file. False is not allowed."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity has support for mobile devices."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"author",
|
||||
"service",
|
||||
"description",
|
||||
"url",
|
||||
"version",
|
||||
"apiVersion",
|
||||
"logo",
|
||||
"thumbnail",
|
||||
"color",
|
||||
"tags",
|
||||
"category"
|
||||
]
|
||||
}
|
||||
256
apps/schema-server/schemas/metadata/1.15.json
Normal file
@@ -0,0 +1,256 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.15",
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a activity.",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this activity."
|
||||
},
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this activity.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this activity is for."
|
||||
},
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the activity in multiple languages.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(-regioncode).",
|
||||
"pattern": "^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2,3}(?:-(?:[a-z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the activity in the key's language."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"en"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"array"
|
||||
],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the activity. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "integer",
|
||||
"description": "The Activity System version this activity supports.",
|
||||
"minimum": 1,
|
||||
"maximum": 2
|
||||
},
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this activity is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this activity is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
"tags": {
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"description": "The tags for the activity.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the activity falls under.",
|
||||
"enum": [
|
||||
"anime",
|
||||
"games",
|
||||
"music",
|
||||
"socials",
|
||||
"videos",
|
||||
"other"
|
||||
]
|
||||
},
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity should run in IFrames."
|
||||
},
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the activity to inject into."
|
||||
},
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the activity to inject into."
|
||||
},
|
||||
"allowURLOverrides": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity should allow URL overrides."
|
||||
},
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the activity.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the setting. Only applicable if `multiLanguage` is disabled."
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
"value": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
"items": {
|
||||
"type": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean"
|
||||
],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
"multiLanguage": {
|
||||
"type": "boolean",
|
||||
"description": "When true, strings from the `general.json` file are available for use, plus the <service>.json file. False is not allowed."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the activity has support for mobile devices."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"author",
|
||||
"service",
|
||||
"description",
|
||||
"url",
|
||||
"version",
|
||||
"apiVersion",
|
||||
"logo",
|
||||
"thumbnail",
|
||||
"color",
|
||||
"tags",
|
||||
"category"
|
||||
]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.2.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.0",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bs] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.3.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.3",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?(?:[a-z0-9-]+\\.)*[0-9a-z_-]+(?:\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?:\\/\\/?([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+\\/.*$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.4.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.4",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpg)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpg)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.5.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.5",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(_[A-Z]{2})?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(_[A-Z]{2})?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.6.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.6",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa[bsd] fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.7.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.7",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g|gif)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(i).(imgur).(com)/(.*?).(png|jpe?g)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.8.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.8",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://(cdn[.]rcd[.]gg|i[.]imgur[.]com)/(.*?)[.](png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://(cdn[.]rcd[.]gg|i[.]imgur[.]com)/(.*?)[.](png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
257
apps/schema-server/schemas/metadata/1.9.json
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://schemas.premid.app/metadata/1.9",
|
||||
|
||||
"title": "Metadata",
|
||||
"type": "object",
|
||||
"description": "Metadata that describes a presence.",
|
||||
|
||||
"definitions": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"description": "User information.",
|
||||
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the user."
|
||||
},
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The Discord snowflake of the user.",
|
||||
"pattern": "^\\d+$"
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "id"]
|
||||
}
|
||||
},
|
||||
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"$comment": "This is required otherwise the schema will fail itself when it is applied to a document via $schema. This is optional so that validators that use this schema don't fail if the metadata doesn't have the $schema property.",
|
||||
|
||||
"type": "string",
|
||||
"description": "The metadata schema URL."
|
||||
},
|
||||
|
||||
"author": {
|
||||
"$ref": "#/definitions/user",
|
||||
"description": "The author of this presence."
|
||||
},
|
||||
|
||||
"contributors": {
|
||||
"type": "array",
|
||||
"description": "Any extra contributors to this presence.",
|
||||
|
||||
"items": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
},
|
||||
|
||||
"service": {
|
||||
"type": "string",
|
||||
"description": "The service this presence is for."
|
||||
},
|
||||
|
||||
"altnames": {
|
||||
"type": "array",
|
||||
"description": "Alternative names for the service.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "An alternative name."
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"description": {
|
||||
"type": "object",
|
||||
"description": "A description of the presence in multiple languages.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The language key. The key must be languagecode(_REGIONCODE).",
|
||||
"pattern": "^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[a-z]{2}(?:_(?:[A-Z]{2}|[0-9]{1,3}))?$": {
|
||||
"type": "string",
|
||||
"description": "The description of the presence in the key's language."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["en"]
|
||||
},
|
||||
|
||||
"url": {
|
||||
"type": ["string", "array"],
|
||||
"description": "The service's website URL, or an array of URLs. Protocols should not be added.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "One of the service's website URLs.",
|
||||
"pattern": "^(([a-z0-9-]+\\.)*[0-9a-z_-]+(\\.[a-z]+)+|(\\d{1,3}\\.){3}\\d{1,3}|localhost)$"
|
||||
},
|
||||
"minItems": 2
|
||||
},
|
||||
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The SemVer version of the presence. Must just be major.minor.patch.",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
||||
},
|
||||
|
||||
"logo": {
|
||||
"type": "string",
|
||||
"description": "The logo of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"thumbnail": {
|
||||
"type": "string",
|
||||
"description": "A thumbnail of the service this presence is for.",
|
||||
"pattern": "^https?://.+\\.(png|jpe?g|gif|webp)$"
|
||||
},
|
||||
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The theme color of the service this presence is for. Must be either a 6 digit or a 3 digit hex code.",
|
||||
"pattern": "^#([A-Fa-f0-9]{3}){1,2}$"
|
||||
},
|
||||
|
||||
"tags": {
|
||||
"type": ["array"],
|
||||
"description": "The tags for the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "A tag.",
|
||||
"pattern": "^[^A-Z\\s!\"#$%&'()*+,./:;<=>?@\\[\\\\\\]^_`{|}~]+$"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "The category the presence falls under.",
|
||||
"enum": ["anime", "games", "music", "socials", "videos", "other"]
|
||||
},
|
||||
|
||||
"iframe": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the presence should run in IFrames."
|
||||
},
|
||||
|
||||
"readLogs": {
|
||||
"type": "boolean",
|
||||
"description": "Whether or not the extension should be reading logs."
|
||||
},
|
||||
|
||||
"regExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match URLs for the presence to inject into."
|
||||
},
|
||||
|
||||
"iFrameRegExp": {
|
||||
"type": "string",
|
||||
"description": "A regular expression used to match IFrames for the presence to inject into."
|
||||
},
|
||||
|
||||
"button": {
|
||||
"type": "boolean",
|
||||
"description": "Controls whether the presence is automatically added when the extension is installed. For partner presences only."
|
||||
},
|
||||
|
||||
"warning": {
|
||||
"type": "boolean",
|
||||
"description": "Shows a warning saying that it requires additional steps for the presence to function correctly."
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"type": "array",
|
||||
"description": "An array of settings the user can change in the presence.",
|
||||
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "A setting.",
|
||||
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The title of the setting. Required only if `multiLanguage` is disabled."
|
||||
},
|
||||
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon of the setting. Required only if `multiLanguage` is disabled.",
|
||||
"pattern": "^fa([bsdrlt]|([-](brands|solid|duotone|regular|light|thin))) fa-[0-9a-z-]+$"
|
||||
},
|
||||
|
||||
"if": {
|
||||
"type": "object",
|
||||
"description": "Restrict showing this setting if another setting is the defined value.",
|
||||
|
||||
"propertyNames": {
|
||||
"type": "string",
|
||||
"description": "The ID of the setting."
|
||||
},
|
||||
|
||||
"patternProperties": {
|
||||
"": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"description": "The placeholder for settings that require input. Shown when the input is empty."
|
||||
},
|
||||
|
||||
"value": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The default value of the setting. Not compatible with `values`."
|
||||
},
|
||||
|
||||
"values": {
|
||||
"type": "array",
|
||||
"description": "The default values of the setting. Not compatible with `value`.",
|
||||
|
||||
"items": {
|
||||
"type": ["string", "number", "boolean"],
|
||||
"description": "The value of the setting."
|
||||
}
|
||||
},
|
||||
|
||||
"multiLanguage": {
|
||||
"type": ["string", "boolean", "array"],
|
||||
"description": "When false, multi-localization is disabled. When true, strings from the `general.json` file are available for use. When a string, it is the name of a file (excluding .json) of a used language from the localization GitHub repo. When an array of strings, it is all of the file names (excluding .json) of used languages from the localization GitHub repo.",
|
||||
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "The name of a file from the localization GitHub repository."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties": false,
|
||||
"required": ["author", "service", "description", "url", "version", "logo", "thumbnail", "color", "tags", "category"]
|
||||
}
|
||||
35
apps/schema-server/schemas/metadata/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Metadata Schema
|
||||
|
||||
This schema is used to validate the `metadata.json` files in [PreMiD/Presences](https://github.com/PreMiD/Presences).
|
||||
|
||||
## 1.7
|
||||
|
||||
- Updates `icon` regex for compability with FontAwesome v6 icon labelling
|
||||
|
||||
## 1.6
|
||||
|
||||
- Adds support for language locales that end in numbers (e.g. es_419)
|
||||
|
||||
## 1.5
|
||||
|
||||
- Allows `.gif` extensions for service logos and `.jpeg` extensions for service logos and thumbnails.
|
||||
|
||||
## 1.4
|
||||
|
||||
- Adds regex for only allowing imgur links for `logo` & `thumbnail`.
|
||||
|
||||
## 1.3
|
||||
|
||||
- Fixes validation for icons.
|
||||
|
||||
## 1.2
|
||||
|
||||
- Adds "readLogs".
|
||||
|
||||
## 1.1
|
||||
|
||||
- Adds "altnames" and "multiLanguage".
|
||||
|
||||
## 1.0
|
||||
|
||||
- First iteration of the metadata file format.
|
||||
37
apps/schema-server/src/index.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { app } from "./index.js";
|
||||
|
||||
describe("schemas", () => {
|
||||
it("/ should return date", async () => {
|
||||
const result = await app.inject({ url: "/" });
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual({ date: expect.any(String) });
|
||||
});
|
||||
|
||||
it("/metadata/1.0 should return metadata schema", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1.0" });
|
||||
const schema = await import(resolve(import.meta.dirname, "../schemas/metadata/1.0.json")) as { default: Record<string, unknown> };
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual(schema.default);
|
||||
});
|
||||
|
||||
it("/metadata/1.0 should return cached metadata schema", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1.0" });
|
||||
const schema = await import(resolve(import.meta.dirname, "../schemas/metadata/1.0.json")) as { default: Record<string, unknown> };
|
||||
|
||||
expect(result.statusCode).toBe(200);
|
||||
expect(result.json()).toEqual(schema.default);
|
||||
});
|
||||
|
||||
it("/metadata/1 should return 404", async () => {
|
||||
const result = await app.inject({ url: "/metadata/1" });
|
||||
|
||||
expect(result.statusCode).toBe(404);
|
||||
expect(result.json()).toEqual({ error: "Schema not found." });
|
||||
});
|
||||
});
|
||||
63
apps/schema-server/src/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { extname, resolve } from "node:path";
|
||||
|
||||
import process from "node:process";
|
||||
import helmet from "@fastify/helmet";
|
||||
import fastify from "fastify";
|
||||
import { globby } from "globby";
|
||||
import type { RequestGenericInterface } from "fastify";
|
||||
|
||||
export const app = fastify();
|
||||
|
||||
await app.register(helmet);
|
||||
|
||||
app.get("/", (_, reply) => reply.send({ date: new Date() }));
|
||||
|
||||
interface versionRequest extends RequestGenericInterface {
|
||||
Params: {
|
||||
schemaName: string;
|
||||
version: string;
|
||||
};
|
||||
}
|
||||
|
||||
const availableSchemas: Record<string, {
|
||||
version: string;
|
||||
content: Record<string, unknown>;
|
||||
}[]> = {};
|
||||
|
||||
for (const schemaPath of await globby(resolve(import.meta.dirname, "../schemas/*/*.json"), { onlyFiles: true })) {
|
||||
const [schemaName, version] = schemaPath.split("/").slice(-2) as [string, string];
|
||||
|
||||
let schemaVersions = availableSchemas[schemaName];
|
||||
|
||||
if (!schemaVersions)
|
||||
schemaVersions = [];
|
||||
|
||||
const { default: content } = await import(schemaPath, { with: { type: "json" } }) as { default: Record<string, unknown> };
|
||||
schemaVersions.push({
|
||||
content,
|
||||
version: version.replace(extname(version), ""),
|
||||
});
|
||||
|
||||
availableSchemas[schemaName] = schemaVersions;
|
||||
}
|
||||
|
||||
app.get<versionRequest>("/:schemaName/:version", async (request, reply) => {
|
||||
const { schemaName, version } = request.params;
|
||||
|
||||
if (!availableSchemas[schemaName]?.some(schema => schema.version === version))
|
||||
return reply.status(404).send({ error: "Schema not found." });
|
||||
|
||||
return reply.send(availableSchemas[schemaName]?.find(schema => schema.version === version)?.content);
|
||||
});
|
||||
|
||||
/* c8 ignore start */
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
const url = await app.listen({
|
||||
host: process.env.HOST ?? "0.0.0.0",
|
||||
port: Number.parseInt(process.env.PORT ?? "80"),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Listening on ${url}`);
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
10
apps/schema-server/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["**/*.test.ts", "dist"]
|
||||
}
|
||||
9
apps/schema-server/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"types": ["@types/node"],
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["environment.d.ts", "src"],
|
||||
"exclude": ["**/*.test.ts"]
|
||||
}
|
||||
@@ -1,416 +0,0 @@
|
||||
<project>
|
||||
<shortName>PreMiD</shortName>
|
||||
<fullName>PreMiD</fullName>
|
||||
<version>VERSION</version>
|
||||
<installerFilename>upgrader.${platform_exec_suffix}</installerFilename>
|
||||
<debugLevel>0</debugLevel>
|
||||
<licenseFile>../LICENSE</licenseFile>
|
||||
<leftImage>leftSide.png</leftImage>
|
||||
<logoImage>logo.png</logoImage>
|
||||
<splashImage>logo.png</splashImage>
|
||||
<componentList>
|
||||
<component>
|
||||
<name>default</name>
|
||||
<description>Default Component</description>
|
||||
<canBeEdited>1</canBeEdited>
|
||||
<selected>1</selected>
|
||||
<show>1</show>
|
||||
<folderList>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfiles</name>
|
||||
<platforms>all</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux</name>
|
||||
<platforms>linux</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux64</name>
|
||||
<platforms>linux-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows</name>
|
||||
<platforms>windows</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows64</name>
|
||||
<platforms>windows-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfilesosx</name>
|
||||
<platforms>osx</platforms>
|
||||
</folder>
|
||||
</folderList>
|
||||
<startMenuShortcutList>
|
||||
<startMenuShortcut>
|
||||
<comment>Uninstall ${product_fullname}</comment>
|
||||
<name>Uninstall ${product_fullname}</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/${uninstallerName}.exe</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath>${installdir}/</windowsPath>
|
||||
</startMenuShortcut>
|
||||
<startMenuShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</startMenuShortcut>
|
||||
</startMenuShortcutList>
|
||||
</component>
|
||||
</componentList>
|
||||
<initializationActionList>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${windows_folder_appdata}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${platform_install_prefix}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
</initializationActionList>
|
||||
<readyToInstallActionList>
|
||||
<actionGroup>
|
||||
<customErrorMessage>Couldn't download release... Try again later.</customErrorMessage>
|
||||
<progressText>Downloading latest release...</progressText>
|
||||
<actionList>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<width>450</width>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x64</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-ia32.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x86</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-darwin-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<actionList>
|
||||
<!-- Remove the old ARP Entry
|
||||
Get the old version -->
|
||||
<registryGet>
|
||||
<key>HKEY_LOCAL_MACHINE\Software\${project.windowsSoftwareRegistryPrefix}</key>
|
||||
<name>Version</name>
|
||||
<variable>oldVersion</variable>
|
||||
</registryGet>
|
||||
|
||||
<!-- Delete the old ARP registry keys -->
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Management\ARPCache\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest type="windows"/>
|
||||
<isTrue value="${isUpgradeMode}"/>
|
||||
</ruleList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</readyToInstallActionList>
|
||||
<postInstallationActionList>
|
||||
<unzip>
|
||||
<addToUninstaller>1</addToUninstaller>
|
||||
<destinationDirectory>${installdir}</destinationDirectory>
|
||||
<progressText>Extracting release...</progressText>
|
||||
<zipFile>${system_temp_directory}/PreMiD-release.zip</zipFile>
|
||||
</unzip>
|
||||
<addDirectoriesToUninstaller>
|
||||
<addContents>1</addContents>
|
||||
<files>${installdir}/</files>
|
||||
</addDirectoriesToUninstaller>
|
||||
</postInstallationActionList>
|
||||
<preUninstallationActionList>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</preUninstallationActionList>
|
||||
<compressionAlgorithm>lzham-ultra</compressionAlgorithm>
|
||||
<createOsxBundleZip>1</createOsxBundleZip>
|
||||
<defaultInstallationMode>unattended</defaultInstallationMode>
|
||||
<deleteOnExit>1</deleteOnExit>
|
||||
<description>Rich Presence for web services.</description>
|
||||
<disableSplashScreen>1</disableSplashScreen>
|
||||
<enableRollback>0</enableRollback>
|
||||
<enableSslSupport>1</enableSslSupport>
|
||||
<enableTimestamp>1</enableTimestamp>
|
||||
<installationScope>user</installationScope>
|
||||
<installationType>upgrade</installationType>
|
||||
<licenseFileEncoding>utf-8</licenseFileEncoding>
|
||||
<osxApplicationBundleIcon>appIcon.icns</osxApplicationBundleIcon>
|
||||
<osxApplicationBundleIdentifier>eu.Timeraa.PreMiD</osxApplicationBundleIdentifier>
|
||||
<osxPlatforms>osx-intel osx-x86_64</osxPlatforms>
|
||||
<osxUninstallerApplicationBundleIcon>appIcon.icns</osxUninstallerApplicationBundleIcon>
|
||||
<outputDirectory>../dist/installer</outputDirectory>
|
||||
<overwritePolicy>onlyIfNewer</overwritePolicy>
|
||||
<productDisplayIcon>C:/Users/metzf/Documents/Development/PreMiD/PreMiD/installer_assets/appIcon.ico</productDisplayIcon>
|
||||
<productUrlHelpLink>https://discord.premid.app</productUrlHelpLink>
|
||||
<productUrlInfoAbout>https://premid.app</productUrlInfoAbout>
|
||||
<readmeFileEncoding>utf-8</readmeFileEncoding>
|
||||
<removeLogFile>1</removeLogFile>
|
||||
<removeUninstallationLogFile>1</removeUninstallationLogFile>
|
||||
<requestedExecutionLevel>asInvoker</requestedExecutionLevel>
|
||||
<saveRelativePaths>1</saveRelativePaths>
|
||||
<summary>Rich Presence for web services.</summary>
|
||||
<unattendedModeUI>minimalWithDialogs</unattendedModeUI>
|
||||
<vendor>Timeraa</vendor>
|
||||
<width>625</width>
|
||||
<windowsExecutableIcon>appIcon.ico</windowsExecutableIcon>
|
||||
<windowsResourceFileDescription>Rich Presence for web services.</windowsResourceFileDescription>
|
||||
<windowsResourceFileVersion>${product_version}</windowsResourceFileVersion>
|
||||
<windowsUninstallerExecutableIcon>appIcon.ico</windowsUninstallerExecutableIcon>
|
||||
<licenseFileList>
|
||||
<licenseFile>
|
||||
<code>en</code>
|
||||
<encoding>utf-8</encoding>
|
||||
<file>../LICENSE</file>
|
||||
</licenseFile>
|
||||
</licenseFileList>
|
||||
<parameterList>
|
||||
<parameterGroup>
|
||||
<name>post_install_page</name>
|
||||
<title>Installation Finished</title>
|
||||
<explanation></explanation>
|
||||
<value></value>
|
||||
<default></default>
|
||||
<insertAfter>installation</insertAfter>
|
||||
<parameterList>
|
||||
<labelParameter>
|
||||
<name>general</name>
|
||||
<description>General</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>addDesktop</name>
|
||||
<description>Create Desktop Icon</description>
|
||||
<explanation></explanation>
|
||||
<value>false</value>
|
||||
<default>false</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<booleanParameter>
|
||||
<name>launchApp</name>
|
||||
<description>Launch App</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<labelParameter>
|
||||
<name>extra</name>
|
||||
<description>Extra</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>openStore</name>
|
||||
<description>Open Presence Store</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
</parameterList>
|
||||
<postShowPageActionList>
|
||||
<createShortcuts>
|
||||
<destination>${windows_folder_desktopdirectory}</destination>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${addDesktop}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
<shortcutList>
|
||||
<quickLaunchShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</quickLaunchShortcut>
|
||||
</shortcutList>
|
||||
</createShortcuts>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>PreMiD.exe</program>
|
||||
<programArguments>&</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<workingDirectory>${installdir}</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>open</program>
|
||||
<programArguments>${installdir}/PreMiD.app</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<launchBrowser>
|
||||
<url>https://premid.app/store</url>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${openStore}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</launchBrowser>
|
||||
<exit/>
|
||||
</postShowPageActionList>
|
||||
</parameterGroup>
|
||||
</parameterList>
|
||||
<platformOptionsList>
|
||||
<platformOptions>
|
||||
<platform>windows</platform>
|
||||
</platformOptions>
|
||||
</platformOptionsList>
|
||||
</project>
|
||||
|
||||
@@ -1,412 +0,0 @@
|
||||
<project>
|
||||
<shortName>PreMiD</shortName>
|
||||
<fullName>PreMiD</fullName>
|
||||
<version>latest</version>
|
||||
<installerFilename>${product_shortname}-installer.${platform_exec_suffix}</installerFilename>
|
||||
<debugLevel>0</debugLevel>
|
||||
<licenseFile>../LICENSE</licenseFile>
|
||||
<leftImage>leftSide.png</leftImage>
|
||||
<logoImage>logo.png</logoImage>
|
||||
<splashImage>logo.png</splashImage>
|
||||
<componentList>
|
||||
<component>
|
||||
<name>default</name>
|
||||
<description>Default Component</description>
|
||||
<canBeEdited>1</canBeEdited>
|
||||
<selected>1</selected>
|
||||
<show>1</show>
|
||||
<folderList>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfiles</name>
|
||||
<platforms>all</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux</name>
|
||||
<platforms>linux</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileslinux64</name>
|
||||
<platforms>linux-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows</name>
|
||||
<platforms>windows</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfileswindows64</name>
|
||||
<platforms>windows-x64</platforms>
|
||||
</folder>
|
||||
<folder>
|
||||
<description>Program Files</description>
|
||||
<destination>${installdir}</destination>
|
||||
<name>programfilesosx</name>
|
||||
<platforms>osx</platforms>
|
||||
</folder>
|
||||
</folderList>
|
||||
<startMenuShortcutList>
|
||||
<startMenuShortcut>
|
||||
<comment>Uninstall ${product_fullname}</comment>
|
||||
<name>Uninstall ${product_fullname}</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/${uninstallerName}.exe</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath>${installdir}/</windowsPath>
|
||||
</startMenuShortcut>
|
||||
<startMenuShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</startMenuShortcut>
|
||||
</startMenuShortcutList>
|
||||
</component>
|
||||
</componentList>
|
||||
<initializationActionList>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${windows_folder_appdata}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
<setInstallerVariable>
|
||||
<name>installdir</name>
|
||||
<persist>1</persist>
|
||||
<value>${platform_install_prefix}/PreMiD</value>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</setInstallerVariable>
|
||||
</initializationActionList>
|
||||
<readyToInstallActionList>
|
||||
<actionGroup>
|
||||
<customErrorMessage>Couldn't download release... Try again later.</customErrorMessage>
|
||||
<progressText>Downloading latest release...</progressText>
|
||||
<actionList>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<width>450</width>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x64</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-win32-ia32.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows-x86</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
<showProgressDialog>
|
||||
<title>Downloading latest release...</title>
|
||||
<actionList>
|
||||
<httpGet>
|
||||
<customErrorMessage>${platform_name}</customErrorMessage>
|
||||
<filename>${system_temp_directory}/PreMiD-release.zip</filename>
|
||||
<url>https://github.com/PreMiD/PreMiD/releases/latest/download/PreMiD-darwin-x64.zip</url>
|
||||
</httpGet>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</showProgressDialog>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<actionList>
|
||||
<!-- Remove the old ARP Entry
|
||||
Get the old version -->
|
||||
<registryGet>
|
||||
<key>HKEY_LOCAL_MACHINE\Software\${project.windowsSoftwareRegistryPrefix}</key>
|
||||
<name>Version</name>
|
||||
<variable>oldVersion</variable>
|
||||
</registryGet>
|
||||
|
||||
<!-- Delete the old ARP registry keys -->
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
<registryDelete>
|
||||
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Management\ARPCache\${project.fullName} ${oldVersion}</key>
|
||||
<name></name>
|
||||
</registryDelete>
|
||||
</actionList>
|
||||
<ruleList>
|
||||
<platformTest type="windows"/>
|
||||
<isTrue value="${isUpgradeMode}"/>
|
||||
</ruleList>
|
||||
</actionGroup>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</readyToInstallActionList>
|
||||
<postInstallationActionList>
|
||||
<unzip>
|
||||
<addToUninstaller>1</addToUninstaller>
|
||||
<destinationDirectory>${installdir}</destinationDirectory>
|
||||
<progressText>Extracting release...</progressText>
|
||||
<zipFile>${system_temp_directory}/PreMiD-release.zip</zipFile>
|
||||
</unzip>
|
||||
<addDirectoriesToUninstaller>
|
||||
<addContents>1</addContents>
|
||||
<files>${installdir}/</files>
|
||||
</addDirectoriesToUninstaller>
|
||||
</postInstallationActionList>
|
||||
<preUninstallationActionList>
|
||||
<actionGroup>
|
||||
<progressText>Killing ${product_fullname}...</progressText>
|
||||
<actionList>
|
||||
<kill>
|
||||
<abortOnError>0</abortOnError>
|
||||
<name>${product_fullname}.exe</name>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
</ruleList>
|
||||
</kill>
|
||||
<runProgram>
|
||||
<program>pkill</program>
|
||||
<programArguments>PreMiD</programArguments>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<workingDirectory>${installdir}/</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<processTest>
|
||||
<logic>is_running</logic>
|
||||
<name>PreMiD</name>
|
||||
</processTest>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
</actionList>
|
||||
</actionGroup>
|
||||
</preUninstallationActionList>
|
||||
<compressionAlgorithm>lzham-ultra</compressionAlgorithm>
|
||||
<createOsxBundleZip>1</createOsxBundleZip>
|
||||
<deleteOnExit>1</deleteOnExit>
|
||||
<description>Rich Presence for web services.</description>
|
||||
<disableSplashScreen>1</disableSplashScreen>
|
||||
<enableRollback>0</enableRollback>
|
||||
<enableSslSupport>1</enableSslSupport>
|
||||
<enableTimestamp>1</enableTimestamp>
|
||||
<installationScope>user</installationScope>
|
||||
<licenseFileEncoding>utf-8</licenseFileEncoding>
|
||||
<osxApplicationBundleIcon>appIcon.icns</osxApplicationBundleIcon>
|
||||
<osxApplicationBundleIdentifier>eu.Timeraa.PreMiD</osxApplicationBundleIdentifier>
|
||||
<osxPlatforms>osx-intel osx-x86_64</osxPlatforms>
|
||||
<osxUninstallerApplicationBundleIcon>appIcon.icns</osxUninstallerApplicationBundleIcon>
|
||||
<outputDirectory>../dist/installer</outputDirectory>
|
||||
<overwritePolicy>onlyIfNewer</overwritePolicy>
|
||||
<productDisplayIcon>C:/Users/metzf/Documents/Development/PreMiD/PreMiD/installer_assets/appIcon.ico</productDisplayIcon>
|
||||
<productUrlHelpLink>https://discord.premid.app</productUrlHelpLink>
|
||||
<productUrlInfoAbout>https://premid.app</productUrlInfoAbout>
|
||||
<readmeFileEncoding>utf-8</readmeFileEncoding>
|
||||
<removeLogFile>1</removeLogFile>
|
||||
<removeUninstallationLogFile>1</removeUninstallationLogFile>
|
||||
<requestedExecutionLevel>asInvoker</requestedExecutionLevel>
|
||||
<saveRelativePaths>1</saveRelativePaths>
|
||||
<summary>Rich Presence for web services.</summary>
|
||||
<vendor>Timeraa</vendor>
|
||||
<windowsExecutableIcon>appIcon.ico</windowsExecutableIcon>
|
||||
<windowsResourceFileDescription>Rich Presence for web services.</windowsResourceFileDescription>
|
||||
<windowsResourceFileVersion>${product_version}</windowsResourceFileVersion>
|
||||
<windowsUninstallerExecutableIcon>appIcon.ico</windowsUninstallerExecutableIcon>
|
||||
<licenseFileList>
|
||||
<licenseFile>
|
||||
<code>en</code>
|
||||
<encoding>utf-8</encoding>
|
||||
<file>../LICENSE</file>
|
||||
</licenseFile>
|
||||
</licenseFileList>
|
||||
<parameterList>
|
||||
<parameterGroup>
|
||||
<name>post_install_page</name>
|
||||
<title>Installation Finished</title>
|
||||
<explanation></explanation>
|
||||
<value></value>
|
||||
<default></default>
|
||||
<insertAfter>installation</insertAfter>
|
||||
<parameterList>
|
||||
<labelParameter>
|
||||
<name>general</name>
|
||||
<description>General</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>addDesktop</name>
|
||||
<description>Create Desktop Icon</description>
|
||||
<explanation></explanation>
|
||||
<value>false</value>
|
||||
<default>false</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<booleanParameter>
|
||||
<name>launchApp</name>
|
||||
<description>Launch App</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
<labelParameter>
|
||||
<name>extra</name>
|
||||
<description>Extra</description>
|
||||
<explanation></explanation>
|
||||
<image></image>
|
||||
</labelParameter>
|
||||
<booleanParameter>
|
||||
<name>openStore</name>
|
||||
<description>Open Presence Store</description>
|
||||
<explanation></explanation>
|
||||
<value>true</value>
|
||||
<default>true</default>
|
||||
<displayStyle>checkbox-left</displayStyle>
|
||||
</booleanParameter>
|
||||
</parameterList>
|
||||
<postShowPageActionList>
|
||||
<createShortcuts>
|
||||
<destination>${windows_folder_desktopdirectory}</destination>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${addDesktop}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
<shortcutList>
|
||||
<quickLaunchShortcut>
|
||||
<comment>Rich Presence for web services.</comment>
|
||||
<name>PreMiD</name>
|
||||
<runAsAdmin>0</runAsAdmin>
|
||||
<runInTerminal>0</runInTerminal>
|
||||
<windowsExec>${installdir}/PreMiD.${platform_exec_suffix}</windowsExec>
|
||||
<windowsExecArgs></windowsExecArgs>
|
||||
<windowsIcon></windowsIcon>
|
||||
<windowsPath></windowsPath>
|
||||
</quickLaunchShortcut>
|
||||
</shortcutList>
|
||||
</createShortcuts>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>PreMiD.exe</program>
|
||||
<programArguments>&</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<workingDirectory>${installdir}</workingDirectory>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>windows</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<runProgram>
|
||||
<abortOnError>0</abortOnError>
|
||||
<program>open</program>
|
||||
<programArguments>${installdir}/PreMiD.app</programArguments>
|
||||
<progressText>Launching PreMiD...</progressText>
|
||||
<runAs>${env(USER)}</runAs>
|
||||
<showMessageOnError>0</showMessageOnError>
|
||||
<useMSDOSPath>0</useMSDOSPath>
|
||||
<ruleList>
|
||||
<platformTest>
|
||||
<type>osx</type>
|
||||
</platformTest>
|
||||
<isTrue>
|
||||
<value>${launchApp}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</runProgram>
|
||||
<launchBrowser>
|
||||
<url>https://premid.app/store</url>
|
||||
<ruleList>
|
||||
<isTrue>
|
||||
<value>${openStore}</value>
|
||||
</isTrue>
|
||||
</ruleList>
|
||||
</launchBrowser>
|
||||
<exit/>
|
||||
</postShowPageActionList>
|
||||
</parameterGroup>
|
||||
</parameterList>
|
||||
<platformOptionsList>
|
||||
<platformOptions>
|
||||
<platform>windows</platform>
|
||||
</platformOptions>
|
||||
</platformOptionsList>
|
||||
</project>
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
56
package.json
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"name": "premid",
|
||||
"productName": "PreMiD",
|
||||
"description": "Discord Rich Presence for web services.",
|
||||
"version": "2.2.1",
|
||||
"repository": "https://github.com/PreMiD/PreMiD",
|
||||
"scripts": {
|
||||
"init": "tsc --skipLibCheck && tsc pkg util/prepare util/zip && devScript --copyOnly",
|
||||
"start": "electron dist/app/. --unhandled-rejections=strict",
|
||||
"dev": "devscript --depCheck",
|
||||
"pkg": "rimraf dist && tsc --skipLibCheck && devScript --copyOnly && cd dist/app/ && yarn && cd ../../ && node pkg",
|
||||
"deploy": "tsc --skipLibCheck .github/deploy && cd .github && node deploy.js"
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/archiver": "5.1.0",
|
||||
"@types/auto-launch": "5.0.1",
|
||||
"@types/discord-rpc": "3.0.4",
|
||||
"@types/fs-extra": "9.0.11",
|
||||
"@types/ini": "1.3.30",
|
||||
"@types/node": "14.14.41",
|
||||
"@types/prompts": "2.0.11",
|
||||
"@types/request-promise-native": "1.0.17",
|
||||
"@types/rimraf": "3.0.0",
|
||||
"@types/socket.io": "2.1.13",
|
||||
"@types/ssh2-sftp-client": "5.3.1",
|
||||
"@types/unzipper": "^0.10.3",
|
||||
"archiver": "5.2.0",
|
||||
"chalk": "4.1.1",
|
||||
"electron": "11.3.0",
|
||||
"electron-packager": "15.2.0",
|
||||
"fast-glob": "3.2.5",
|
||||
"fs-extra": "9.1.0",
|
||||
"nodemon": "2.0.7",
|
||||
"ora": "5.3.0",
|
||||
"prompts": "2.4.1",
|
||||
"rimraf": "3.0.2",
|
||||
"ssh2-sftp-client": "6.0.1",
|
||||
"ts-devscript": "^3.0.3",
|
||||
"typescript": "4.1.5",
|
||||
"unzipper": "0.10.11",
|
||||
"yarn": "1.22.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"auto-launch": "5.0.5",
|
||||
"axios": "0.21.1",
|
||||
"chokidar": "3.5.1",
|
||||
"discord-rpc": "github:discordjs/RPC",
|
||||
"electron-store": "7.0.3",
|
||||
"socket.io": "3.1.2",
|
||||
"source-map-support": "^0.5.19"
|
||||
},
|
||||
"devScript": {
|
||||
"out": "dist/app"
|
||||
}
|
||||
}
|
||||
123
pkg.ts
@@ -1,123 +0,0 @@
|
||||
import * as electronPackager from "electron-packager";
|
||||
import { platform } from "os";
|
||||
import * as prompts from "prompts";
|
||||
import * as ora from "ora";
|
||||
|
||||
(async () => {
|
||||
let response = {
|
||||
os: "current",
|
||||
arch: "all"
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== "DePloY")
|
||||
response = await prompts([
|
||||
{
|
||||
type: "select",
|
||||
name: "arch",
|
||||
message: "What architecture?",
|
||||
choices: [
|
||||
{
|
||||
title: "current",
|
||||
value: "current"
|
||||
},
|
||||
{
|
||||
title: "all",
|
||||
value: "all"
|
||||
},
|
||||
{
|
||||
title: "arm64",
|
||||
value: "arm64"
|
||||
},
|
||||
{
|
||||
title: "armv7l",
|
||||
value: "armv7l"
|
||||
},
|
||||
{
|
||||
title: "ia32",
|
||||
value: "ia32"
|
||||
},
|
||||
{
|
||||
title: "mips64el",
|
||||
value: "mips64el"
|
||||
},
|
||||
{
|
||||
title: "x64",
|
||||
value: "x64"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
name: "os",
|
||||
message: "What operating system?",
|
||||
choices: [
|
||||
{
|
||||
title: "current",
|
||||
value: "current"
|
||||
},
|
||||
{
|
||||
title: "all",
|
||||
value: "all"
|
||||
},
|
||||
{
|
||||
title: "darwin",
|
||||
value: "darwin"
|
||||
},
|
||||
{
|
||||
title: "linux",
|
||||
value: "linux"
|
||||
},
|
||||
{
|
||||
title: "mas",
|
||||
value: "mas"
|
||||
},
|
||||
{
|
||||
title: "win32",
|
||||
value: "win32"
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
if (!response.os) {
|
||||
process.exit();
|
||||
}
|
||||
|
||||
let icon: string;
|
||||
|
||||
if (
|
||||
response.os == "darwin" ||
|
||||
(response.os === "current" && platform() === "darwin")
|
||||
)
|
||||
icon = "./installer_assets/appIcon.icns";
|
||||
if (["ia32", "x64"].includes(response.arch) || platform() === "win32")
|
||||
icon = "./installer_assets/appIcon.ico";
|
||||
|
||||
let spinner = ora("Packaging app").start(),
|
||||
packagingOptions: electronPackager.Options = {
|
||||
dir: "./dist/app",
|
||||
out: "./dist",
|
||||
darwinDarkModeSupport: true,
|
||||
icon: icon,
|
||||
overwrite: true,
|
||||
quiet: true,
|
||||
appBundleId: "eu.Timeraa.PreMiD",
|
||||
appCategoryType: "Utilities",
|
||||
appCopyright: `Timeraa 2018-${new Date().getFullYear()}`,
|
||||
prune: true,
|
||||
asar: true,
|
||||
// @ts-ignore
|
||||
arch: response.arch,
|
||||
// @ts-ignore
|
||||
platform: response.os
|
||||
};
|
||||
|
||||
if (response.arch === "current") delete packagingOptions.arch;
|
||||
if (response.os === "current") delete packagingOptions.platform;
|
||||
|
||||
electronPackager(packagingOptions).then(() => {
|
||||
spinner.text = "Done!";
|
||||
spinner.succeed();
|
||||
process.exit();
|
||||
});
|
||||
})();
|
||||
|
Before Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 445 B |
|
Before Width: | Height: | Size: 481 B |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 688 B |
|
Before Width: | Height: | Size: 858 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 880 B |
|
Before Width: | Height: | Size: 986 B |