Compare commits
4 Commits
795c221453
...
a0ea5ec191
Author | SHA1 | Date | |
---|---|---|---|
a0ea5ec191 | |||
08a24cae07 | |||
f360dcec8c | |||
b25699b50b |
@ -1,5 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"root": true,
|
||||||
"@nuxt/eslint-config"
|
"extends": ["eslint:recommended", "@nuxtjs/eslint-config-typescript", "plugin:tailwindcss/recommended"],
|
||||||
]
|
"rules": {
|
||||||
|
"vue/max-attributes-per-line": ["error", {
|
||||||
|
"singleline": {
|
||||||
|
"max": 4
|
||||||
|
},
|
||||||
|
"multiline": {
|
||||||
|
"max": 2
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"vue/multi-word-component-names": "off",
|
||||||
|
"vue/singleline-html-element-content-newline": "off",
|
||||||
|
"no-undef": "off",
|
||||||
|
"no-console": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
6
webv2/app.config.ts
Normal file
6
webv2/app.config.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default defineAppConfig({
|
||||||
|
ui: {
|
||||||
|
primary: 'cyan',
|
||||||
|
gray: 'cool'
|
||||||
|
}
|
||||||
|
})
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="min-h-screen w-full overflow-y-auto bg-gray-100 dark:bg-gray-900">
|
||||||
<NuxtWelcome />
|
<NuxtLayout />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
3
webv2/layouts/blank.vue
Normal file
3
webv2/layouts/blank.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<NuxtPage />
|
||||||
|
</template>
|
5
webv2/layouts/default.vue
Normal file
5
webv2/layouts/default.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NuxtPage />
|
||||||
|
</div>
|
||||||
|
</template>
|
25
webv2/middleware/auth.js
Normal file
25
webv2/middleware/auth.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export default defineNuxtRouteMiddleware(async (to) => {
|
||||||
|
if (process.server) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await $fetch('/api/auth/user')
|
||||||
|
|
||||||
|
if (!user.id) {
|
||||||
|
throw createError({ statusCode: 500, statusMessage: 'No user was found' })
|
||||||
|
}
|
||||||
|
|
||||||
|
useState('user', () => user)
|
||||||
|
|
||||||
|
if (to.meta.moderator && !user.role.moderator) {
|
||||||
|
return navigateTo('/')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to get user', e)
|
||||||
|
|
||||||
|
useState('user', () => null)
|
||||||
|
|
||||||
|
return navigateTo('/login')
|
||||||
|
}
|
||||||
|
})
|
@ -1,6 +1,25 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
ssr: false,
|
||||||
typescript: {
|
typescript: {
|
||||||
typeCheck: true,
|
typeCheck: true
|
||||||
|
},
|
||||||
|
nitro: {
|
||||||
|
plugins: ['~/server/index.ts']
|
||||||
|
},
|
||||||
|
modules: [
|
||||||
|
'@nuxthq/ui'
|
||||||
|
],
|
||||||
|
runtimeConfig: {
|
||||||
|
discordClientId: '',
|
||||||
|
discordClientSecret: '',
|
||||||
|
oauthRedirectUri: 'http://localhost:3000/api/auth',
|
||||||
|
jwtSecret: '',
|
||||||
|
mongoUrl: '',
|
||||||
|
mongoUser: '',
|
||||||
|
mongoPass: '',
|
||||||
|
public: {
|
||||||
|
oauthUrl: 'https://discord.com/api/oauth2/authorize?client_id=1052974736432443432&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth&response_type=code&scope=identify'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
5011
webv2/package-lock.json
generated
5011
webv2/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,16 +6,22 @@
|
|||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint-fix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nuxthq/ui": "^2.2.1",
|
||||||
"@nuxtjs/eslint-config-typescript": "^12.0.0",
|
"@nuxtjs/eslint-config-typescript": "^12.0.0",
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
|
"eslint-plugin-tailwindcss": "^3.12.1",
|
||||||
"nuxt": "^3.5.2"
|
"nuxt": "^3.5.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/eslint-config": "^0.1.1",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
"mongoose": "^7.2.2",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vue-tsc": "^1.6.5"
|
"vue-tsc": "^1.6.5"
|
||||||
}
|
}
|
||||||
|
13
webv2/pages/index.vue
Normal file
13
webv2/pages/index.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Index
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['auth']
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = useState('user')
|
||||||
|
</script>
|
17
webv2/pages/login.vue
Normal file
17
webv2/pages/login.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex h-screen flex-col items-center justify-center">
|
||||||
|
<h1 class="text-primary-500 mb-5 text-center text-3xl font-bold">Polarcraft S5</h1>
|
||||||
|
<p class="mx-3 mb-5 max-w-2xl">
|
||||||
|
<b>Welkom bij Polarcraft seizoen 5!</b> Start door in te loggen met Discord en vervolgens je account te koppelen met Minecraft. Als je problemen hebt maak dan in Discord een post aan in het <b>#help</b> channel.
|
||||||
|
</p>
|
||||||
|
<UButton @click="navigateTo(config.public.oauthUrl, { external: true })">Log in with Discord</UButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
definePageMeta({
|
||||||
|
layout: 'blank'
|
||||||
|
})
|
||||||
|
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
</script>
|
@ -1,5 +1,93 @@
|
|||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
|
||||||
|
type AccessTokenResponse = {
|
||||||
|
access_token: string,
|
||||||
|
token_type: string,
|
||||||
|
expires_in: number,
|
||||||
|
refresh_token: string,
|
||||||
|
scope: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscordUser = {
|
||||||
|
id: string,
|
||||||
|
username: string,
|
||||||
|
discriminator: string,
|
||||||
|
avatar: string,
|
||||||
|
bot?: boolean,
|
||||||
|
system?: boolean,
|
||||||
|
mfa_enabled?: boolean,
|
||||||
|
banner?: string,
|
||||||
|
accent_color?: number,
|
||||||
|
locale?: string,
|
||||||
|
verified?: boolean,
|
||||||
|
email?: string,
|
||||||
|
flags?: number,
|
||||||
|
premium_type?: number,
|
||||||
|
public_flags?: number
|
||||||
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
return {
|
const { code }: { code?: string } = getQuery(event)
|
||||||
hello: 'world'
|
|
||||||
|
if (!code) {
|
||||||
|
return sendRedirect(event, '/', 302)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tokenResponse: AccessTokenResponse
|
||||||
|
let authorizationResponse: DiscordUser
|
||||||
|
try {
|
||||||
|
tokenResponse = await $fetch('https://discord.com/api/oauth2/token', {
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({
|
||||||
|
client_id: config.discordClientId,
|
||||||
|
client_secret: config.discordClientSecret,
|
||||||
|
code,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
redirect_uri: config.oauthRedirectUri,
|
||||||
|
scope: 'identify'
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
authorizationResponse = await $fetch('https://discord.com/api/users/@me', {
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${tokenResponse.access_token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Discord authentication failed', e)
|
||||||
|
|
||||||
|
throw createError({ statusCode: 500, statusMessage: 'Discord authentication failed' })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await UserModel.updateOne({ 'discord.id': authorizationResponse.id }, {
|
||||||
|
$set: {
|
||||||
|
discord: {
|
||||||
|
id: authorizationResponse.id,
|
||||||
|
username: authorizationResponse.username,
|
||||||
|
avatarHash: authorizationResponse.avatar
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$setOnInsert: {
|
||||||
|
username: authorizationResponse.username
|
||||||
|
}
|
||||||
|
}, { upsert: true })
|
||||||
|
|
||||||
|
const token = jwt.sign({
|
||||||
|
accessToken: tokenResponse.access_token,
|
||||||
|
refreshToken: tokenResponse.refresh_token,
|
||||||
|
discordId: authorizationResponse.id
|
||||||
|
}, config.jwtSecret, { expiresIn: tokenResponse.expires_in })
|
||||||
|
|
||||||
|
setCookie(event, 'jwt', token, { httpOnly: true, maxAge: tokenResponse.expires_in * 1000 })
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Updating user information failed', e)
|
||||||
|
|
||||||
|
throw createError({ statusCode: 500, statusMessage: 'Updating user information failed' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendRedirect(event, '/', 302)
|
||||||
})
|
})
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
export default defineEventHandler((event) => {
|
|
||||||
return {
|
|
||||||
hello: 'world'
|
|
||||||
}
|
|
||||||
})
|
|
14
webv2/server/index.ts
Normal file
14
webv2/server/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
export default async () => {
|
||||||
|
try {
|
||||||
|
await mongoose.connect(config.mongoUrl, {
|
||||||
|
authSource: 'admin',
|
||||||
|
user: config.mongoUser,
|
||||||
|
pass: config.mongoPass
|
||||||
|
})
|
||||||
|
console.log('DB connection established')
|
||||||
|
} catch (e) {
|
||||||
|
console.error('DB connection failed', e)
|
||||||
|
}
|
||||||
|
}
|
1
webv2/server/utils/config.ts
Normal file
1
webv2/server/utils/config.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const config = useRuntimeConfig()
|
24
webv2/server/utils/models.ts
Normal file
24
webv2/server/utils/models.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Schema, Types, model } from 'mongoose'
|
||||||
|
|
||||||
|
const userSchema = new Schema({
|
||||||
|
username: { type: String, required: true },
|
||||||
|
usernameType: { type: String, required: true, default: 'discord' },
|
||||||
|
discord: {
|
||||||
|
id: { type: String, required: true, unique: true },
|
||||||
|
username: { type: String, required: true }
|
||||||
|
},
|
||||||
|
minecraft: {
|
||||||
|
uuid: { type: String, required: false, unique: true },
|
||||||
|
username: { type: String, required: false }
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
admin: Boolean,
|
||||||
|
moderator: Boolean,
|
||||||
|
teamAdmin: Boolean
|
||||||
|
},
|
||||||
|
teamInvites: [
|
||||||
|
Types.ObjectId
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export const UserModel = model<IUser>('User', userSchema)
|
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json"
|
"extends": "./.nuxt/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"typeRoots": ["./types"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
webv2/types/global.d.ts
vendored
Normal file
23
webv2/types/global.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export {}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface IUser {
|
||||||
|
_id: string,
|
||||||
|
username: string,
|
||||||
|
discord: {
|
||||||
|
id: string,
|
||||||
|
username: string,
|
||||||
|
avatarHash: string
|
||||||
|
},
|
||||||
|
minecraft?: {
|
||||||
|
uuid: string,
|
||||||
|
username: string
|
||||||
|
},
|
||||||
|
teamInvites: string[],
|
||||||
|
role: {
|
||||||
|
admin: boolean,
|
||||||
|
moderator: boolean,
|
||||||
|
},
|
||||||
|
teamId: string,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user