Compare commits
No commits in common. "dd1aab37d0e85790b7f64d4dfcf704081519b062" and "f3f36938df4095c4885fbddac51c632f23b2e181" have entirely different histories.
dd1aab37d0
...
f3f36938df
|
@ -0,0 +1,6 @@
|
||||||
|
.git
|
||||||
|
.editorconfig
|
||||||
|
docker-minecraft.png
|
||||||
|
LICENSE
|
||||||
|
Makefile
|
||||||
|
README.md
|
|
@ -0,0 +1,16 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{yaml,yml}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = tab
|
25
.eslintrc.js
25
.eslintrc.js
|
@ -1,25 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
es2021: true,
|
|
||||||
node: true
|
|
||||||
},
|
|
||||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
sourceType: 'module'
|
|
||||||
},
|
|
||||||
plugins: ['@typescript-eslint', 'prettier'],
|
|
||||||
rules: {
|
|
||||||
yoda: 'error',
|
|
||||||
eqeqeq: 'error',
|
|
||||||
complexity: 'error',
|
|
||||||
'prefer-const': 'error',
|
|
||||||
'prefer-template': 'error',
|
|
||||||
'object-shorthand': 'warn',
|
|
||||||
'prettier/prettier': 'warn',
|
|
||||||
'prefer-destructuring': 'warn',
|
|
||||||
'prefer-arrow-callback': 'error',
|
|
||||||
'@typescript-eslint/no-namespace': 'off'
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
github: PHLAK
|
||||||
|
patreon: PHLAK
|
||||||
|
custom: https://paypal.me/ChrisKankiewicz
|
|
@ -0,0 +1,8 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: monthly
|
||||||
|
timezone: US/Arizona
|
||||||
|
open-pull-requests-limit: 10
|
|
@ -0,0 +1,40 @@
|
||||||
|
name: Publish Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['master']
|
||||||
|
tags: ['*']
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKER_HUB_USER: phlak
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
name: Build & Push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract Metadata
|
||||||
|
id: extract-metadata
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: ${{ env.DOCKER_HUB_USER }}/minecraft
|
||||||
|
tags: |
|
||||||
|
type=raw,value=latest
|
||||||
|
type=ref,event=tag
|
||||||
|
|
||||||
|
- name: Build & Push Image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.extract-metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.extract-metadata.outputs.labels }}
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: Tests
|
||||||
|
|
||||||
|
on: [pull_request_target]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-image:
|
||||||
|
name: Build Image
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build Image
|
||||||
|
run: make build
|
|
@ -1,4 +1,3 @@
|
||||||
node_modules/
|
data/
|
||||||
dist/
|
db/
|
||||||
.env
|
files/server.jar
|
||||||
README.md
|
|
12
.prettierrc
12
.prettierrc
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"singleQuote": true,
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"semi": true,
|
|
||||||
"printWidth": 100,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"endOfLine": "lf"
|
|
||||||
}
|
|
||||||
|
|
50
Dockerfile
50
Dockerfile
|
@ -1,23 +1,49 @@
|
||||||
FROM node:18-alpine
|
FROM alpine:3.16.0
|
||||||
|
LABEL maintainer='Andriy Cherniy <qugalet@m0e.space>'
|
||||||
|
|
||||||
WORKDIR /app
|
# Minecraft version
|
||||||
|
ARG MC_VERSION=bta
|
||||||
|
ARG MC_JAR_SHA1=8399e1211e95faa421c1507b322dbeae86d604df
|
||||||
|
|
||||||
COPY ./package.json ./
|
# Set default JVM options
|
||||||
|
ENV _JAVA_OPTIONS '-Xms256M -Xmx4G'
|
||||||
|
ARG JAVA_VERSION=19
|
||||||
|
|
||||||
RUN npm i
|
# Create Minecraft directories
|
||||||
|
RUN mkdir -pv /opt/minecraft /etc/minecraft
|
||||||
|
|
||||||
COPY ./.env ./
|
# Create non-root user
|
||||||
|
RUN adduser -DHs /sbin/nologin minecraft
|
||||||
|
|
||||||
COPY ./prisma/schema.prisma ./
|
# Add the EULA file
|
||||||
|
COPY files/eula.txt /etc/minecraft/eula.txt
|
||||||
|
|
||||||
RUN npm run migrate
|
# Add the ops script
|
||||||
|
COPY files/ops /usr/local/bin/ops
|
||||||
|
RUN chmod +x /usr/local/bin/ops
|
||||||
|
|
||||||
RUN npm run generate
|
# Install dependencies, fetch Minecraft server jar file and chown files
|
||||||
|
#ARG JAR_URL=https://launcher.mojang.com/v1/objects/${MC_JAR_SHA1}/server.jar
|
||||||
|
RUN apk add --update ca-certificates nss tzdata wget \
|
||||||
|
&& apk add openjdk19-jre \
|
||||||
|
#&& wget -O /opt/minecraft/minecraft_server.jar ${JAR_URL} \
|
||||||
|
#&& apk del --purge wget && rm -rf /var/cache/apk/* \
|
||||||
|
&& rm -rf /var/cache/apk/* \
|
||||||
|
&& chown -R minecraft:minecraft /etc/minecraft /opt/minecraft
|
||||||
|
|
||||||
COPY . .
|
COPY files/server.jar /opt/minecraft/minecraft_server.jar
|
||||||
|
|
||||||
RUN npm run build
|
# Define volumes
|
||||||
|
VOLUME /etc/minecraft
|
||||||
|
|
||||||
EXPOSE 8080
|
# Expose port
|
||||||
|
EXPOSE 25565
|
||||||
|
|
||||||
CMD ["node", "dist"]
|
# Set running user
|
||||||
|
USER minecraft
|
||||||
|
|
||||||
|
# Set the working dir
|
||||||
|
WORKDIR /etc/minecraft
|
||||||
|
|
||||||
|
# Default run command
|
||||||
|
CMD ["java", "-jar", "/opt/minecraft/minecraft_server.jar", "nogui"]
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Chris Kankiewicz <Chris@ChrisKankiewicz.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,8 @@
|
||||||
|
IMAGE_NAME="phlak/minecraft"
|
||||||
|
IMAGE_TAG="$$(grep 'ARG MC_VERSION' Dockerfile | awk -F = '{print $$2}')"
|
||||||
|
|
||||||
|
build:
|
||||||
|
@docker build --force-rm --pull --tag $(IMAGE_NAME):$(IMAGE_TAG) .
|
||||||
|
|
||||||
|
purge:
|
||||||
|
@docker image rm --force $(IMAGE_NAME):$(IMAGE_TAG)
|
|
@ -0,0 +1,123 @@
|
||||||
|
docker-minecraft
|
||||||
|
================
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="docker-minecraft.png" alt="Docker Minecraft" width="500">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/PHLAK/docker-minecraft/discussions"><img src="https://img.shields.io/badge/Join_the-Community-7b16ff.svg?style=for-the-badge" alt="Join the Community"></a>
|
||||||
|
<a href="https://github.com/users/PHLAK/sponsorship"><img src="https://img.shields.io/badge/Become_a-Sponsor-cc4195.svg?style=for-the-badge" alt="Become a Sponsor"></a>
|
||||||
|
<a href="https://paypal.me/ChrisKankiewicz"><img src="https://img.shields.io/badge/Make_a-Donation-006bb6.svg?style=for-the-badge" alt="One-time Donation"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://hub.docker.com/repository/docker/phlak/minecraft/tags"><img alt="Docker Image Version" src="https://img.shields.io/docker/v/phlak/minecraft?style=flat-square&sort=semver"></a>
|
||||||
|
<a href="https://hub.docker.com/repository/docker/phlak/minecraft"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/phlak/minecraft?style=flat-square"></a>
|
||||||
|
<a href="https://github.com/PHLAK/docker-minecraft/blob/master/LICENSE"><img src="https://img.shields.io/github/license/PHLAK/docker-minecraft?style=flat-square" alt="License"></a>
|
||||||
|
<a href="https://hub.docker.com/r/phlak/minecraft/builds"><img alt="Docker Cloud Build Status" src="https://img.shields.io/docker/cloud/build/phlak/minecraft"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Docker image for <a href="https://minecraft.net/">Minecraft</a> server.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Running the Container
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
First create a named data volume to hold the persistent world and config data:
|
||||||
|
|
||||||
|
docker volume create --name minecraft-data
|
||||||
|
|
||||||
|
Then run the Minecraft server:
|
||||||
|
|
||||||
|
docker run -it -d -p 25565:25565 -v minecraft-data:/etc/minecraft --name minecraft-server phlak/minecraft
|
||||||
|
|
||||||
|
#### Optional 'docker run' Arguments
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt><code>-e _JAVA_OPTIONS='-Xms256M -Xmx2048M'</code></dt>
|
||||||
|
<dd>Set JVM arguments for minimum/maximum memory consumption (default: '-Xms256M -Xmx2048M')</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt><code>-e TZ=America/Phoenix</code></dt>
|
||||||
|
<dd>Set the timezone for your server. You can find your timezone in this <a href="https://goo.gl/uy1J6q">list of timezones</a>. Use the (case sensitive) value from the <code>TZ</code> column. If left unset, timezone will be UTC.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt><code>--restart unless-stopped</code></dt>
|
||||||
|
<dd>Always restart the container regardless of the exit status, but do not start it on daemon startup if the container has been put to a stopped state before. See the Docker <a href="https://goo.gl/Y0dlDH">restart policies</a> for additional details.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
**NOTE:** See the [Minecraft Wiki](http://minecraft.gamepedia.com/Server/Requirements) for more info
|
||||||
|
on memory requirements.
|
||||||
|
|
||||||
|
Editing the Server Config
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Once you have a running container, you can edit the Minecraft [server config](https://minecraft.gamepedia.com/Server.properties) with:
|
||||||
|
|
||||||
|
docker exec -it minecraft-server vi /etc/minecraft/server.properties
|
||||||
|
|
||||||
|
After saving changes, restart your container with `docker restart minecraft-server`
|
||||||
|
|
||||||
|
Adding OPs
|
||||||
|
----------
|
||||||
|
|
||||||
|
Once you have a running server container you can add OPs by running:
|
||||||
|
|
||||||
|
docker exec minecraft-server ops [PLAYER_NAMES]
|
||||||
|
|
||||||
|
**NOTE:** Replace `[PLAYER_NAMES]` with the name of one or more players you wish to give OP
|
||||||
|
privileges separated by a space. If a players name contains spaces wrap it in quotation marks.
|
||||||
|
|
||||||
|
Here's an example granting OP to three players with name's `Marty`, `Jennifer` and `Doc Brown`:
|
||||||
|
|
||||||
|
docker exec minecraft-server ops Marty Jennifer "Doc Brown"
|
||||||
|
|
||||||
|
Running Server Commands
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
You can run [commands](https://minecraft.gamepedia.com/Commands) on the server
|
||||||
|
(e.g. `kick`, `ban`, `say`, etc.) by attaching to the running container and
|
||||||
|
running the commands. Attach to the server by running:
|
||||||
|
|
||||||
|
docker attach minecraft-server
|
||||||
|
|
||||||
|
Once attached you can run your commands like normal.
|
||||||
|
|
||||||
|
say Hello world!
|
||||||
|
[10:11:56] [Server thread/INFO]: [Server] Hello world!
|
||||||
|
list
|
||||||
|
[10:12:08] [Server thread/INFO]: There are 3 of a max 10 players online: Marty, Jennifer, Doc Brown
|
||||||
|
seed
|
||||||
|
[10:12:19] [Server thread/INFO]: Seed: [-5234790158571010769]
|
||||||
|
|
||||||
|
**NOTE:** In order to detach from the container and leave it running use the
|
||||||
|
`Ctrl + P` then `Ctrl + Q` key sequence.
|
||||||
|
|
||||||
|
Upgrading the Server
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
First pull down the latest image:
|
||||||
|
|
||||||
|
docker pull phlak/minecraft
|
||||||
|
|
||||||
|
Remove your running server container:
|
||||||
|
|
||||||
|
docker rm -f minecraft-server
|
||||||
|
|
||||||
|
And run a new one with the same command/arguments as before.
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
---------------
|
||||||
|
|
||||||
|
For general help and support join our [GitHub Discussions](https://github.com/PHLAK/docker-minecraft/discussions) or reach out on [Twitter](https://twitter.com/PHLAK).
|
||||||
|
|
||||||
|
Please report bugs to the [GitHub Issue Tracker](https://github.com/PHLAK/docker-minecraft/issues).
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
---------
|
||||||
|
|
||||||
|
This project is licensed under the [MIT License](https://github.com/PHLAK/docker-minecraft/blob/master/LICENSE).
|
|
@ -0,0 +1,12 @@
|
||||||
|
* Minecraft Docker
|
||||||
|
Dockerfile and docker-compose for your own server jar
|
||||||
|
|
||||||
|
** Usage
|
||||||
|
Change your JAVA_VERSION in [[./Dockerfile][Dockerfile]] and build:
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
docker compose build
|
||||||
|
#+END_SRC
|
||||||
|
Place server.jar to ~files/~ folder, change your [[./docker-compose.yml][Docker Compose]] file and run:
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
docker compose up -d
|
||||||
|
#+END_SRC
|
|
@ -1,9 +1,28 @@
|
||||||
version: '2'
|
|
||||||
services:
|
services:
|
||||||
backend:
|
db:
|
||||||
build: .
|
image: postgres
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_DB=rpz_auth
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
|
||||||
|
auth:
|
||||||
|
build: ./auth
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
minecraft:
|
||||||
|
build: .
|
||||||
|
stdin_open: true # docker run -i
|
||||||
|
tty: true # docker run -t
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/etc/minecraft
|
||||||
ports:
|
ports:
|
||||||
- '9030:8080'
|
- 25565:25565
|
||||||
env_file:
|
depends_on:
|
||||||
- ./.env
|
- db
|
||||||
|
- auth
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
|
@ -0,0 +1 @@
|
||||||
|
eula=true
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -o errexit
|
||||||
|
|
||||||
|
OPS_FILE="/etc/minecraft/ops.txt"
|
||||||
|
[ ! -e "${OPS_FILE}" ] && touch ${OPS_FILE}
|
||||||
|
|
||||||
|
for OP in "$@"; do
|
||||||
|
if grep -qx "${OP}" ${OPS_FILE}; then
|
||||||
|
echo "NOTICE: '${OP}' already present in OPs file"; continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${OP}" >> ${OPS_FILE} && echo "SUCCESS: '${OP}' added to OPs file"
|
||||||
|
done
|
40
package.json
40
package.json
|
@ -1,40 +0,0 @@
|
||||||
{
|
|
||||||
"name": "rpz_auth",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"generate": "prisma generate",
|
|
||||||
"migrate": "prisma migrate deploy",
|
|
||||||
"build": "rm -rf dist/** && tsc",
|
|
||||||
"dev": "tsc -w",
|
|
||||||
"start": "node ./dist"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"devDependencies": {
|
|
||||||
"@tsconfig/node18": "^1.0.1",
|
|
||||||
"@types/jsonwebtoken": "^8.5.9",
|
|
||||||
"@types/node": "^18.7.13",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.0",
|
|
||||||
"@typescript-eslint/parser": "^5.35.1",
|
|
||||||
"eslint": "^8.23.0",
|
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
|
||||||
"prettier": "^2.7.1",
|
|
||||||
"typescript": "^4.8.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@fastify/autoload": "^5.2.0",
|
|
||||||
"@fastify/cookie": "^8.1.0",
|
|
||||||
"@prisma/client": "^4.2.1",
|
|
||||||
"axios": "^0.27.2",
|
|
||||||
"fastify": "^4.5.3",
|
|
||||||
"json-schema-to-ts": "^2.5.5",
|
|
||||||
"jsonwebtoken": "^8.5.1",
|
|
||||||
"nanoid": "3",
|
|
||||||
"prisma": "^4.3.0",
|
|
||||||
"redis": "^4.3.0",
|
|
||||||
"simple-oauth2": "^4.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
1694
pnpm-lock.yaml
1694
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +0,0 @@
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "User" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"nickname" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Session" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"userNickname" TEXT NOT NULL,
|
|
||||||
"ip" TEXT NOT NULL,
|
|
||||||
"expiredAfter" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "User_nickname_key" ON "User"("nickname");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Session" ADD CONSTRAINT "Session_userNickname_fkey" FOREIGN KEY ("userNickname") REFERENCES "User"("nickname") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
@ -1,5 +0,0 @@
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Session" ADD COLUMN "verified" BOOLEAN NOT NULL DEFAULT false;
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "User" ALTER COLUMN "nickname" DROP NOT NULL;
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
Warnings:
|
|
||||||
|
|
||||||
- You are about to drop the column `userNickname` on the `Session` table. All the data in the column will be lost.
|
|
||||||
- Added the required column `nickname` to the `Session` table without a default value. This is not possible if the table is not empty.
|
|
||||||
|
|
||||||
*/
|
|
||||||
-- DropForeignKey
|
|
||||||
ALTER TABLE "Session" DROP CONSTRAINT "Session_userNickname_fkey";
|
|
||||||
|
|
||||||
-- AlterTable
|
|
||||||
ALTER TABLE "Session" DROP COLUMN "userNickname",
|
|
||||||
ADD COLUMN "nickname" TEXT NOT NULL;
|
|
|
@ -1,14 +0,0 @@
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "Link" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"sessionId" TEXT NOT NULL,
|
|
||||||
"link" TEXT NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "Link_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "Link_sessionId_key" ON "Link"("sessionId");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "Link" ADD CONSTRAINT "Link_sessionId_fkey" FOREIGN KEY ("sessionId") REFERENCES "Session"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Please do not edit this file manually
|
|
||||||
# It should be added in your version-control system (i.e. Git)
|
|
||||||
provider = "postgresql"
|
|
|
@ -1,29 +0,0 @@
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "postgresql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
model User {
|
|
||||||
id String @id
|
|
||||||
nickname String? @unique
|
|
||||||
}
|
|
||||||
|
|
||||||
model Session {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
nickname String
|
|
||||||
ip String
|
|
||||||
verified Boolean @default(false)
|
|
||||||
expiredAfter DateTime
|
|
||||||
link Link?
|
|
||||||
}
|
|
||||||
|
|
||||||
model Link {
|
|
||||||
id String @id
|
|
||||||
session Session @relation(fields: [sessionId], references: [id])
|
|
||||||
sessionId String @unique
|
|
||||||
link String
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
import('nanoid').then(nanoid => {
|
|
||||||
const db = new PrismaClient();
|
|
||||||
|
|
||||||
db.session
|
|
||||||
.create({
|
|
||||||
data: {
|
|
||||||
id: nanoid.nanoid(10),
|
|
||||||
ip: '127.0.0.1',
|
|
||||||
nickname: 'Qugalet',
|
|
||||||
expiredAfter: new Date(Date.now() + 604800)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(session =>
|
|
||||||
console.log(
|
|
||||||
encodeURI(
|
|
||||||
`https://auth.m0e.space/application/o/authorize/?client_id=7f24f967b2faf3d8c1c53771661abff8e07060c0&response_type=code&redirect_uri=http://localhost:8080/api/auth/verify?session=${session.id}&scope=profile openid`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
68
src/index.ts
68
src/index.ts
|
@ -1,68 +0,0 @@
|
||||||
import fastify from 'fastify';
|
|
||||||
import fastifyAutoload from '@fastify/autoload';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
// import { createClient, RedisClientType } from 'redis';
|
|
||||||
// import fastifySchedule from '@fastify/schedule';
|
|
||||||
// import fastifyCors from '@fastify/cors';
|
|
||||||
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
declare module 'fastify' {
|
|
||||||
interface FastifyInstance {
|
|
||||||
db: PrismaClient;
|
|
||||||
// redis: RedisClientType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
NODE_ENV: 'development' | 'production';
|
|
||||||
PORT: number;
|
|
||||||
JWT_REFRESH_SECRET: string;
|
|
||||||
JWT_ACCESS_SECRET: string;
|
|
||||||
AUTH_CLIENT_ID: string;
|
|
||||||
AUTH_CLIENT_SECRET: string;
|
|
||||||
REDIS_URL: string;
|
|
||||||
URL_TOKEN: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fastify({
|
|
||||||
logger: true,
|
|
||||||
ajv: { customOptions: { allErrors: true } }
|
|
||||||
})
|
|
||||||
.decorate('db', new PrismaClient())
|
|
||||||
// .decorate('redis', async () => {
|
|
||||||
// const client = createClient({
|
|
||||||
// url: process.env.REDIS_URL
|
|
||||||
// });
|
|
||||||
|
|
||||||
// client.on('error', err => console.log('Redis Client Error', err));
|
|
||||||
|
|
||||||
// await client.connect();
|
|
||||||
// return client;
|
|
||||||
// })
|
|
||||||
.addHook('onRoute', route => console.log(route.url))
|
|
||||||
// .register(fastifyCors, {
|
|
||||||
// origin: ['http://127.0.0.1:3000'],
|
|
||||||
// credentials: true
|
|
||||||
// })
|
|
||||||
.register(fastifyAutoload, {
|
|
||||||
dir: path.join(__dirname, 'routes'),
|
|
||||||
routeParams: true,
|
|
||||||
autoHooks: true,
|
|
||||||
options: { prefix: '/api' }
|
|
||||||
})
|
|
||||||
.register(fastifyAutoload, {
|
|
||||||
dir: path.join(__dirname, 'link'),
|
|
||||||
routeParams: true,
|
|
||||||
autoHooks: true,
|
|
||||||
options: { prefix: '/a' }
|
|
||||||
})
|
|
||||||
.listen({ port: process.env.PORT || 8080, host: '0.0.0.0' })
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
.then(() => console.log('Server Started'))
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
.catch(console.error);
|
|
|
@ -1,76 +0,0 @@
|
||||||
import { FastifyInstance } from 'fastify';
|
|
||||||
import { FromSchema } from 'json-schema-to-ts';
|
|
||||||
import { nanoid } from 'nanoid/async';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
querystring: {
|
|
||||||
type: 'object',
|
|
||||||
required: ['session', 'key'],
|
|
||||||
properties: {
|
|
||||||
session: { type: 'string' },
|
|
||||||
key: { type: 'string' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default async (server: FastifyInstance) =>
|
|
||||||
server.get<{ Querystring: FromSchema<typeof schema.querystring> }>(
|
|
||||||
'/create',
|
|
||||||
async (req, reply) => {
|
|
||||||
if (req.query.key !== process.env.URL_TOKEN) return reply.code(403).send('Forbidden');
|
|
||||||
const session = await server.db.session.findFirst({
|
|
||||||
where: {
|
|
||||||
id: req.query.session
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!session) return reply.code(404).send('Session is not found');
|
|
||||||
const linkedSession = await server.db.link.findFirst({
|
|
||||||
where: { session: { id: req.query.session } },
|
|
||||||
select: {
|
|
||||||
id: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (linkedSession)
|
|
||||||
reply.send(
|
|
||||||
`${
|
|
||||||
process.env.NODE_ENV === 'production'
|
|
||||||
? 'https://mc.m0e.space/a'
|
|
||||||
: 'http://localhost:8080/a'
|
|
||||||
}/${linkedSession.id}`
|
|
||||||
);
|
|
||||||
let linkId = await nanoid(5);
|
|
||||||
// eslint-disable-next-line no-constant-condition
|
|
||||||
while (true) {
|
|
||||||
const link = await server.db.link.findFirst({
|
|
||||||
where: { id: linkId },
|
|
||||||
select: { id: true }
|
|
||||||
});
|
|
||||||
if (!link) break;
|
|
||||||
linkId = await nanoid(5);
|
|
||||||
}
|
|
||||||
await server.db.link.create({
|
|
||||||
data: {
|
|
||||||
id: linkId,
|
|
||||||
link: encodeURI(
|
|
||||||
`https://auth.m0e.space/application/o/authorize/?client_id=7f24f967b2faf3d8c1c53771661abff8e07060c0&response_type=code&redirect_uri=${
|
|
||||||
process.env.NODE_ENV === 'production'
|
|
||||||
? 'https://mc.m0e.space'
|
|
||||||
: 'http://localhost:8080'
|
|
||||||
}/api/auth/verify?session=${req.query.session}&scope=profile openid`
|
|
||||||
),
|
|
||||||
session: {
|
|
||||||
connect: {
|
|
||||||
id: req.query.session
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
reply.send(
|
|
||||||
`${
|
|
||||||
process.env.NODE_ENV === 'production'
|
|
||||||
? 'https://mc.m0e.space/a'
|
|
||||||
: 'http://localhost:8080/a'
|
|
||||||
}/${linkId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { FastifyInstance } from 'fastify';
|
|
||||||
|
|
||||||
export default async (server: FastifyInstance) =>
|
|
||||||
server.get<{ Params: { id: string } }>('/:id', (req, reply) =>
|
|
||||||
server.db.link
|
|
||||||
.findFirstOrThrow({ where: { id: req.params.id } })
|
|
||||||
.then(link => reply.redirect(link.link))
|
|
||||||
.catch(() => reply.code(404).send('URL not found'))
|
|
||||||
);
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { FastifyInstance, FastifyReply } from 'fastify';
|
|
||||||
import { CookieSerializeOptions } from '@fastify/cookie';
|
|
||||||
import { User } from '@prisma/client';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
|
|
||||||
const cookieSettings: CookieSerializeOptions = {
|
|
||||||
httpOnly: true,
|
|
||||||
path: '/api/auth/refresh_token',
|
|
||||||
sameSite: 'strict',
|
|
||||||
maxAge: 2629800, //1 month
|
|
||||||
secure: process.env.NODE_ENV === 'production'
|
|
||||||
};
|
|
||||||
|
|
||||||
declare module 'fastify' {
|
|
||||||
interface FastifyReply {
|
|
||||||
sendTokens: (user: User) => FastifyReply;
|
|
||||||
clearTokens: () => FastifyReply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async (server: FastifyInstance) =>
|
|
||||||
server
|
|
||||||
.decorateReply('sendTokens', function (this: FastifyReply, { id, nickname }: User) {
|
|
||||||
return this.setCookie(
|
|
||||||
'owo',
|
|
||||||
jwt.sign({ tokenVersion: 0, id }, process.env.JWT_REFRESH_SECRET, {
|
|
||||||
expiresIn: '30d'
|
|
||||||
}),
|
|
||||||
cookieSettings
|
|
||||||
).send({
|
|
||||||
user: {
|
|
||||||
id,
|
|
||||||
nickname
|
|
||||||
},
|
|
||||||
token: jwt.sign(
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
nickname
|
|
||||||
},
|
|
||||||
process.env.JWT_ACCESS_SECRET,
|
|
||||||
{
|
|
||||||
expiresIn: '15m'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.decorateReply('clearTokens', function (this: FastifyReply) {
|
|
||||||
return this.clearCookie('owo', cookieSettings);
|
|
||||||
});
|
|
|
@ -1,88 +0,0 @@
|
||||||
import { FastifyInstance } from 'fastify';
|
|
||||||
import { FromSchema } from 'json-schema-to-ts';
|
|
||||||
import { stringify } from 'querystring';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const schema = {
|
|
||||||
querystring: {
|
|
||||||
type: 'object',
|
|
||||||
required: ['code', 'session'],
|
|
||||||
properties: {
|
|
||||||
code: { type: 'string' },
|
|
||||||
session: { type: 'string' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export default async (server: FastifyInstance) =>
|
|
||||||
server.get<{ Querystring: FromSchema<typeof schema.querystring> }>(
|
|
||||||
'/verify',
|
|
||||||
{ schema },
|
|
||||||
(req, reply) => {
|
|
||||||
axios
|
|
||||||
.post(
|
|
||||||
'https://auth.m0e.space/application/o/token/',
|
|
||||||
stringify({
|
|
||||||
client_id: process.env.AUTH_CLIENT_ID,
|
|
||||||
client_secret: process.env.AUTH_CLIENT_SECRET,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
redirect_uri:
|
|
||||||
process.env.NODE_ENV === 'production'
|
|
||||||
? 'https://mc.m0e.space/api/auth/verify'
|
|
||||||
: 'http://localhost:8080/api/auth/verify',
|
|
||||||
code: req.query.code
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(res =>
|
|
||||||
axios
|
|
||||||
.get('https://auth.m0e.space/application/o/userinfo/', {
|
|
||||||
headers: { Authorization: `Bearer ${res.data.access_token}` }
|
|
||||||
})
|
|
||||||
.then(async res => {
|
|
||||||
const user =
|
|
||||||
(await server.db.user.findFirst({ where: { id: res.data.sub } })) ||
|
|
||||||
(await server.db.user.create({ data: { id: res.data.sub } }));
|
|
||||||
const session = await server.db.session.findFirst({
|
|
||||||
where: { id: req.query.session }
|
|
||||||
});
|
|
||||||
if (!session) return reply.code(400).send('Invalid session');
|
|
||||||
const userByNickname = await server.db.user.findFirst({
|
|
||||||
where: { nickname: session.nickname }
|
|
||||||
});
|
|
||||||
if (!userByNickname) {
|
|
||||||
await Promise.all([
|
|
||||||
server.db.user.update({
|
|
||||||
where: { id: user.id },
|
|
||||||
data: { nickname: session.nickname }
|
|
||||||
}),
|
|
||||||
server.db.session.update({
|
|
||||||
where: { id: req.query.session },
|
|
||||||
data: {
|
|
||||||
verified: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]).then(() => reply.redirect('https://mc.m0e.space/message/success'));
|
|
||||||
|
|
||||||
// await server.redis.publish('rpz_auth', req.query.session);
|
|
||||||
} else if (userByNickname.id !== user.id) reply.send(403).send('Forbidden');
|
|
||||||
else
|
|
||||||
await server.db.session
|
|
||||||
.update({
|
|
||||||
where: { id: req.query.session },
|
|
||||||
data: {
|
|
||||||
verified: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(() => reply.redirect('https://mc.m0e.space/message/success'));
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
reply.code(500).send(err);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
reply.code(500).send(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "@tsconfig/node18/tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "dist"
|
|
||||||
// "module": "esnext"
|
|
||||||
},
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"resolveJsonModule": true
|
|
||||||
}
|
|
Loading…
Reference in New Issue