diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..b5c68e5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: '' labels: '' assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. 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] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..2f28cea 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_pull_request.md b/.github/PULL_REQUEST_TEMPLATE/feature_pull_request.md index cb023dd..0bbbf3f 100644 --- a/.github/PULL_REQUEST_TEMPLATE/feature_pull_request.md +++ b/.github/PULL_REQUEST_TEMPLATE/feature_pull_request.md @@ -1,25 +1,33 @@ ## Feature Description + Please provide a brief description of the new feature being implemented in this pull request. ## Problem Statement + Explain the problem or need that this feature aims to address. ## Proposed Solution + Describe the solution or approach taken to implement the new feature. ## Implementation Details + Provide additional details about the implementation, such as specific code changes, new components, or relevant architectural decisions. ## Screenshots (if applicable) + If the feature introduces changes to the UI, include screenshots or images showcasing the new functionality. ## Testing + Describe the steps taken to test the feature. Include relevant test cases or scenarios that were considered. ## Dependencies + List any dependencies or related issues/pull requests that need to be resolved for this feature to work correctly. ## Checklist + - [ ] I have tested this feature locally and it works as expected. - [ ] I have updated the documentation, if necessary, to reflect the new feature. - [ ] I have followed the project's coding standards and best practices. @@ -27,8 +35,9 @@ List any dependencies or related issues/pull requests that need to be resolved f - [ ] I have assigned the pull request to the appropriate reviewer(s). ## Related Issues + List any related issues or pull requests that are connected to this feature. ## Additional Notes -Add any additional information or context that might be helpful for reviewers. +Add any additional information or context that might be helpful for reviewers. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d7b2b2e..13fbd7f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,8 +5,7 @@ version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: 'npm' # See documentation for possible values + directory: '/' # Location of package manifests schedule: - interval: "weekly" - + interval: 'weekly' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 338872f..42b4d6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,13 +4,12 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest strategy: matrix: node-version: [18.x] - + steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 @@ -19,4 +18,4 @@ jobs: - run: npm install - run: npx prisma generate - - run: npm run build --if-present \ No newline at end of file + - run: npm run build --if-present diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b65cee6..3029790 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,10 +1,10 @@ name: Deploy -# Controls when the action will run. +# Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ main ] + branches: [main] workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel @@ -15,17 +15,16 @@ jobs: runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - name: Deploy using ssh - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - port: 22 - script: | - cd ~/cardboard - git pull origin main - docker-compose -f prod.docker-compose.yml down --remove-orphans - docker-compose -f prod.docker-compose.yml up --build -d - + steps: + - name: Deploy using ssh + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + port: 22 + script: | + cd ~/cardboard + git pull origin main + docker-compose -f prod.docker-compose.yml down --remove-orphans + docker-compose -f prod.docker-compose.yml up --build -d diff --git a/.yarnrc.yml b/.yarnrc.yml index 247f4d7..a4799bd 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,10 +1,10 @@ supportedArchitectures: - os: - - "current" - - "darwin" - - "linux" - - "win32" - cpu: + os: + - 'current' + - 'darwin' + - 'linux' + - 'win32' + cpu: - current" - - "x64" - - "ia32" \ No newline at end of file + - 'x64' + - 'ia32' diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b647861..8b35f95 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6d8eea..33d0c28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,7 @@ To begin contributing, please follow these steps: 8. Commit your changes with a descriptive commit message: git commit -m "Add feature: your feature name". 9. Push your changes to your forked repository: git push origin feature/your-feature-name. 10. Open a pull request from your forked repository to the main repository. + ## Code Quality Maintaining code quality is crucial for our project's success. We expect all contributors, regardless of their experience level, to follow the established coding standards and specifications. Here are some guidelines to keep in mind: @@ -26,6 +27,7 @@ Maintaining code quality is crucial for our project's success. We expect all con 4. Follow the project's indentation and formatting conventions. 5. Avoid code duplication and strive for reusability. 6. Write unit tests whenever possible to ensure code correctness. + ## Review Process Once you have submitted a pull request, the project maintainers will review your changes. They will provide feedback, suggestions, and guidance to help you improve your contribution. Please be patient during the review process, as it may take some time. diff --git a/README.md b/README.md index 8590832..91f098b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # 📄 [Cardboard.ink](https://Cardboard.ink) + ![GitHub issues](https://img.shields.io/github/issues-raw/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub contributors](https://img.shields.io/github/contributors/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub last commit](https://img.shields.io/github/last-commit/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ![GitHub top language](https://img.shields.io/github/languages/top/sohamjaiswal/cardboard?color=yellow&style=for-the-badge) ## 🐛 How to contribute. diff --git a/SECURITY.md b/SECURITY.md index ad36869..97a43e2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported Versions -Cardboard is currently an alpha and development is rapid and swift, you can say that only the most recent commit is the supported version. +Cardboard is currently an alpha and development is rapid and swift, you can say that only the most recent commit is the supported version. ## Reporting a Vulnerability diff --git a/app.dockerfile b/app.dockerfile index 4ecfdbb..31e79c7 100644 --- a/app.dockerfile +++ b/app.dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine +FROM node:18-alpine WORKDIR /app diff --git a/app.prod.dockerfile b/app.prod.dockerfile index 8898c40..798241c 100644 --- a/app.prod.dockerfile +++ b/app.prod.dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine +FROM node:18-alpine WORKDIR /app diff --git a/docker-compose.yml b/docker-compose.yml index 892497e..12cefa8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.3" - services: cb-db: container_name: cb-db @@ -13,7 +11,7 @@ services: - 5432:5432 cb-app: container_name: cb-app - build: + build: context: . dockerfile: app.dockerfile restart: always @@ -26,4 +24,3 @@ services: - ./:/app environment: - DATABASE_URL=postgresql://postgres:password@cb-db:5432/postgres - \ No newline at end of file diff --git a/index.js b/index.js index daea58e..b5eea98 100644 --- a/index.js +++ b/index.js @@ -5,12 +5,12 @@ const app = express(); // add a route that lives separately from the SvelteKit app app.get('/healthcheck', (req, res) => { - res.end('ok'); + res.end('ok'); }); // let SvelteKit handle everything else, including serving prerendered pages and static assets app.use(handler); app.listen(3000, () => { - console.log('🚀 listening on port 3000'); -}); \ No newline at end of file + console.log('🚀 listening on port 3000'); +}); diff --git a/package.json b/package.json index b126a04..5f406a7 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "my-skeleton-app", + "name": "cardboard", "version": "0.0.1", "private": true, "author": { @@ -21,43 +21,43 @@ "@iconify/svelte": "^3.1.6", "@skeletonlabs/skeleton": "^2.4.0", "@skeletonlabs/tw-plugin": "^0.2.4", - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-node": "^1.3.1", - "@sveltejs/kit": "^1.5.0", + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/adapter-node": "^2.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.9", - "@types/node": "^20.9.0", "@types/bcrypt": "^5.0.2", + "@types/node": "^20.9.0", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.62.0", "autoprefixer": "^10.4.17", "eslint": "^8.28.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.26.0", + "eslint-plugin-svelte": "^2.30.0", "highlight.js": "^11.9.0", "postcss": "^8.4.23", "postcss-load-config": "^4.0.1", "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.8.1", + "prettier-plugin-svelte": "^2.10.1", "prisma": "^5.5.2", "sass": "^1.62.1", - "svelte": "^3.54.0", - "svelte-check": "^3.0.1", + "svelte": "^4.0.0", + "svelte-check": "^3.4.3", "svelte-preprocess": "^5.1.3", - "sveltekit-superforms": "^0.8.7", + "sveltekit-superforms": "^2.16.1", "tailwindcss": "^3.3.2", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^4.3.0", - "vite-plugin-tailwind-purgecss": "^0.1.3", - "zod": "^3.22.4" + "vite": "^5.0.0", + "vite-plugin-tailwind-purgecss": "^0.3.3" }, "dependencies": { - "@asteasolutions/zod-to-openapi": "^6.2.0", "@prisma/client": "^4.11.0", "bcrypt": "^5.1.1", "dotenv": "^16.3.2", - "express": "^4.18.2" + "express": "^4.18.2", + "zod": "^3.23.8" }, "type": "module" } diff --git a/postcss.config.cjs b/postcss.config.cjs index a790af4..7da612e 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,7 +1,7 @@ module.exports = { plugins: { 'postcss-import': {}, - 'tailwindcss/nesting': {}, + 'tailwindcss/nesting': {}, tailwindcss: {}, autoprefixer: {} } diff --git a/prod.docker-compose.yml b/prod.docker-compose.yml index 314d9c2..01d6a77 100644 --- a/prod.docker-compose.yml +++ b/prod.docker-compose.yml @@ -1,4 +1,4 @@ -version: "3.3" +version: '3.3' services: cb-db: @@ -13,7 +13,7 @@ services: - 5432:5432 cb-app: container_name: cb-app - build: + build: context: . dockerfile: app.prod.dockerfile restart: always @@ -29,4 +29,4 @@ services: - DATABASE_URL=postgresql://postgres:${DB_PASSWORD-password}@cb-db:5432/postgres - ORIGIN=https://cardboard.ink - PROTOCOL_HEADER=x-forwarded-proto # remove this if you're not using a reverse proxy (nginx, cloudflare etc.) - - HOST_HEADER=x-forwarded-host # remove this if you're not using a reverse proxy (nginx, cloudflare etc.) \ No newline at end of file + - HOST_HEADER=x-forwarded-host # remove this if you're not using a reverse proxy (nginx, cloudflare etc.) diff --git a/src/api/index.ts b/src/api/index.ts deleted file mode 100644 index 7da924f..0000000 --- a/src/api/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { API } from "sveltekit-api"; -import { version, author } from "../../package.json"; - -export default new API(import.meta.glob("./**/*.ts"), { - openapi: "3.1.0", - info: { - title: "Cardboard API", - version: version, - description: "Cardboard is a third party OpenID provider for multiple services. Currently supports Guilded.", - contact: author, - license: { - name: "MIT", - url: "https://github.com/cardboard-ink/cardboard/blob/main/LICENSE", - }, - }, - externalDocs: { - url: "https://cardboard.gitbook.io", - }, - servers: [ - { - url: "{protocol}://{host}", - variables: { - protocol: { - default: "https", - enum: ["http", "https"], - }, - host: { - default: "cardboard.ink", - }, - }, - }, - ], - }, - "/api", - (r) => { - r.registerComponent("securitySchemes", "bearerAuth", { - type: "http", - scheme: "bearer", - description: - "Cardboard currently supports Guilded to authenticate users.", - }); - }, -); \ No newline at end of file diff --git a/src/api/v2/auth/GET.ts b/src/api/v2/auth/GET.ts deleted file mode 100644 index 83a510a..0000000 --- a/src/api/v2/auth/GET.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { db } from '$lib/server/database'; -import { error, redirect } from '@sveltejs/kit'; -import { z, type RouteModifier } from 'sveltekit-api'; - -export const Modifier: RouteModifier = (r) => { - r.tags = ["Auth"]; - r.operationId = "auth"; - return r; -}; - -export const Query = z.object({ - client_id: z.string().describe('client id'), - redirect_uri: z.string().describe('redirect uri'), - response_type: z.string().describe('response type'), - scope: z.string().describe('scope').nullable().default(null).describe('scope'), - state: z.string().describe('state').nullable().default(null).describe('state'), -}) - -export const Error = { - 400: error(400, 'invalid request'), - 404: error(404, 'app not found') -} - -export const Output = z.object({}) - -export default async function ( - query: z.infer, -): Promise> { - const parsed = Query.safeParse(query) - if (!parsed.success) { - throw Error[400] - } - - const { client_id, redirect_uri, response_type, scope, state } = parsed.data - - const app = await db.app.findUnique({ - where: { - id: client_id - } - }); - - if (!app) { - throw Error[404] - } - - throw redirect( - 302, - `/a/${app.vanityCode}?redirect_uri=${redirect_uri}&scope=${scope}&state=${state}&response_type=${response_type}` - ); -} diff --git a/src/api/v2/me/GET.ts b/src/api/v2/me/GET.ts deleted file mode 100644 index 593d5f7..0000000 --- a/src/api/v2/me/GET.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { db } from "$lib/server/database" -import { error } from "@sveltejs/kit" -import type {RequestEvent} from "@sveltejs/kit" -import {z} from "sveltekit-api" -import type { RouteModifier } from "sveltekit-api"; - -export const Modifier: RouteModifier = (r) => { - r.operationId = "login"; - r.security = [{ bearerAuth: [] }]; - return r; -}; - -export const Error = { - 400: error(400, 'invalid request'), - 401: error(401, 'invalid token'), - 500: error(500, 'internal server error'), -} - -export interface GuildedUser { - id: string - name: string - subdomain?: string - aliases?: any - avatar?: string - banner?: string - userStatus?: any - moderationStatus: any - aboutInfo?: any - userTransientStatus: any - createdAt: any -} - - -export const Output = z.object({ - id: z.string().describe('user id'), - name: z.string().describe('user name'), - subdomain: z.string().optional().describe('user subdomain'), - aliases: z.any().optional().describe('user aliases'), - avatar: z.string().optional().describe('user avatar'), - banner: z.string().optional().describe('user banner'), - createdAt: z.string().describe('user created at'), - userStatus: z.any().optional().describe('user status'), - moderationStatus: z.any().describe('user moderation status'), - aboutInfo: z.any().optional().describe('user about info'), - userTransientStatus: z.any().describe('user transient status'), -}) - -export default async function ( - _: Record, - evt: RequestEvent -): Promise> { - const headers = evt.request.headers - let authToken = headers.get('authorization') - if (!authToken || typeof authToken !== 'string') { - throw Error[400] - } - if (!authToken.startsWith('Bearer ')) { - throw Error[400] - } - authToken = authToken.slice(7) - const verify = await db.authorizedAppSession.findUnique({ - where: { - authToken, - }, - select: { - id: true, - userAppManager: { - select: { - user: { - select: { - id: true, - } - } - } - } - } - }) - if (!verify) { - throw Error[401] - } - const userId = verify.userAppManager.user.id - const data = await fetch(`https://www.guilded.gg/api/users/${userId}/profilev3`, {method: 'GET'}).catch(() => { - throw Error[500] - }) - const userData = await data.json() - const resData = { - id: userData.id, - name: userData.name, - subdomain: userData.subdomain, - aliases: userData.aliases, - avatar: userData.profilePictureLg, - banner: userData.profileBannerLg, - createdAt: userData.joinedAt, - userStatus: userData.userStatus, - moderationStatus: userData.moderationStatus, - aboutInfo: userData.aboutInfo, - userTransientStatus: userData.userTransientStatus, - } - return resData -} \ No newline at end of file diff --git a/src/api/v2/token/POST.ts b/src/api/v2/token/POST.ts deleted file mode 100644 index be3b8fb..0000000 --- a/src/api/v2/token/POST.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { db } from "$lib/server/database" -import { error } from "@sveltejs/kit" -import { randomUUID } from "crypto" -import {z} from "sveltekit-api" -import type { RouteModifier } from "sveltekit-api"; - -export const Input = z.object({ - grant_type: z.union([z.literal('authorization_code'), z.literal('refresh_token')]).describe('authorization_code or refresh_token'), - code: z.string().optional().describe('authorization_code'), - refreshToken: z.string().optional().describe('refresh_token') -}) - -export const Output = z.object({ - access_token: z.string().describe('Bearer token'), - expires_in: z.number().describe('seconds until token expires'), - refresh_token: z.string().describe('refresh token'), - token_type: z.literal('Bearer').describe('Bearer') -}) - -export const Error = { - 400: error(400, 'invalid request'), -} - -export const Modifier: RouteModifier = (r) => { - r.tags = ["Auth"]; - r.operationId = "login"; - return r; -}; - -export default async function ( - input: z.infer, -): Promise> { - const parsed = Input.safeParse(input) - if (!parsed.success) { - throw Error[400] - } - - const { grant_type, code, refreshToken } = parsed.data - - if (grant_type === 'authorization_code') { - if (!code || typeof code !== 'string') { - throw Error[400] - } - const pre = await db.authorizedAppSession.findUnique({ - where: { - authToken: code, - }, - select: { - id: true, - userAppManager: { - select: { - app: { - select: { - id: true, - } - } - } - } - } - }) - if (!pre) { - throw Error[400] - } - const auth = await db.authorizedAppSession.update({ - where: { - id: pre.id, - }, - data: { - authToken: randomUUID(), - refreshToken: randomUUID(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), - }, - select: { - authToken: true, - refreshToken: true, - expiresAt: true, - } - }) - const data = { - access_token: auth.authToken, - expires_in: Math.floor((auth.expiresAt.getTime() - Date.now()) / 1000), - refresh_token: auth.refreshToken, - token_type: 'Bearer' as const, - } - // return new Response(JSON.stringify(data), {headers: {'Content-Type': 'application/json'}}) - return data - } - else if (grant_type === 'refresh_token') { - if (!refreshToken || typeof refreshToken !== 'string') { - throw Error[400] - } - const pre = await db.authorizedAppSession.findUnique({ - where: { - refreshToken, - }, - select: { - id: true, - userAppManager: { - select: { - app: { - select: { - id: true, - } - } - } - } - } - }) - if (!pre) { - throw Error[400] - } - const auth = await db.authorizedAppSession.update({ - where: { - id: pre.id, - }, - data: { - refreshToken: randomUUID(), - authToken: randomUUID(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), - }, - select: { - authToken: true, - refreshToken: true, - expiresAt: true, - } - }) - const data = { - access_token: auth.authToken, - expires_in: Math.floor((auth.expiresAt.getTime() - Date.now()) / 1000), - refresh_token: auth.refreshToken, - token_type: 'Bearer' as const, - } - return data - } - throw Error[400] -} \ No newline at end of file diff --git a/src/app.d.ts b/src/app.d.ts index 2b852d6..6c93f7c 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -4,13 +4,13 @@ declare namespace App { interface Locals { user: { - id: string - name: string - displayName: string - banner:string - avatar: string - role: string - } + id: string; + name: string; + displayName: string; + banner: string; + avatar: string; + role: string; + }; } // interface PageData {} diff --git a/src/app.postcss b/src/app.postcss index 20be514..fb368c8 100644 --- a/src/app.postcss +++ b/src/app.postcss @@ -16,6 +16,6 @@ body { } :root { - --theme-font-family-base: 'Inter', sans-serif; - --theme-font-family-heading: 'Inter', sans-serif; -} \ No newline at end of file + --theme-font-family-base: 'Inter', sans-serif; + --theme-font-family-heading: 'Inter', sans-serif; +} diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 926d519..b252992 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,17 +1,34 @@ -import type { Handle } from '@sveltejs/kit' -import { db } from '$lib/server/database' -import { cleanupAuths } from '$lib/server/authclean' +import type { Handle } from '@sveltejs/kit'; +import { db } from '$lib/server/database'; +import { cleanupAuths } from '$lib/server/authclean'; export const handle: Handle = async ({ event, resolve }) => { - cleanupAuths() + cleanupAuths(); let theme = ''; const cookieTheme = event.cookies.get('theme'); - const session = event.cookies.get('guildedAuthSession') - if (cookieTheme && cookieTheme != '' && ['skeleton', 'wintry', 'modern', 'rocket', 'seafoam', 'vintage', 'sahara', 'hamlindigo', 'gold-nouveau', 'crimson'].includes(theme)) { - theme = cookieTheme + const session = event.cookies.get('guildedAuthSession'); + if ( + cookieTheme && + cookieTheme != '' && + [ + 'skeleton', + 'wintry', + 'modern', + 'rocket', + 'seafoam', + 'vintage', + 'sahara', + 'hamlindigo', + 'gold-nouveau', + 'crimson' + ].includes(theme) + ) { + theme = cookieTheme; } else { - event.cookies.set('theme', 'modern'); - theme = 'modern' + event.cookies.set('theme', 'modern', { + path: '/' + }); + theme = 'modern'; } if (!session) { // if there is no session load page as normal @@ -23,15 +40,18 @@ export const handle: Handle = async ({ event, resolve }) => { // find the user based on the session const user = await db.guildedAuthSession.findUnique({ where: { id: session }, - select: { expiresAt: true, user: {select: {id: true, username: true, avatar: true, banner: true, role: true}}}, - }) + select: { + expiresAt: true, + user: { select: { id: true, username: true, avatar: true, banner: true, role: true } } + } + }); if (!user) { // if the session doesn't exist delete the cookie event.cookies.set('session', '', { path: '/', - expires: new Date(0), - }) + expires: new Date(0) + }); return await resolve(event, { transformPageChunk: ({ html }) => html.replace('data-theme=""', `data-theme="${theme}"`) }); @@ -40,17 +60,17 @@ export const handle: Handle = async ({ event, resolve }) => { // if the session has expired if (user?.expiresAt < new Date()) { // delete the session - await db.guildedAuthSession.delete({ where: { id: session } }) + await db.guildedAuthSession.delete({ where: { id: session } }); event.cookies.set('session', '', { path: '/', - expires: new Date(0), - }) + expires: new Date(0) + }); return await resolve(event, { transformPageChunk: ({ html }) => html.replace('data-theme=""', `data-theme="${theme}"`) }); } - const sessionUser = user.user + const sessionUser = user.user; // if `user` exists set `events.local` if (user) { @@ -61,10 +81,10 @@ export const handle: Handle = async ({ event, resolve }) => { avatar: sessionUser.avatar, banner: sessionUser.banner, role: sessionUser.role - } + }; } // load page as normal return await resolve(event, { transformPageChunk: ({ html }) => html.replace('data-theme=""', `data-theme="${theme}"`) }); -} +}; diff --git a/src/lib/client/localStore.ts b/src/lib/client/localStore.ts index 6e3f787..d37abe4 100644 --- a/src/lib/client/localStore.ts +++ b/src/lib/client/localStore.ts @@ -1,4 +1,14 @@ import { localStorageStore } from '@skeletonlabs/skeleton'; import type { Writable } from 'svelte/store'; -export const theme: Writable<'skeleton' | 'modern' | 'vintage' | 'rocket' | 'seafoam' | 'sahara' | 'hamlindigo' | 'gold-nouveau' | 'crimson'> = localStorageStore('theme', 'skeleton'); \ No newline at end of file +export const theme: Writable< + | 'skeleton' + | 'modern' + | 'vintage' + | 'rocket' + | 'seafoam' + | 'sahara' + | 'hamlindigo' + | 'gold-nouveau' + | 'crimson' +> = localStorageStore('theme', 'skeleton'); diff --git a/src/lib/client/ui/Avatar.svelte b/src/lib/client/ui/Avatar.svelte index 40738e6..668f53c 100644 --- a/src/lib/client/ui/Avatar.svelte +++ b/src/lib/client/ui/Avatar.svelte @@ -1,14 +1,14 @@ -User Profile +User Profile \ No newline at end of file + img { + border-radius: 50%; + object-fit: cover; + } + diff --git a/src/lib/client/ui/AvatarInput.svelte b/src/lib/client/ui/AvatarInput.svelte index be9773d..75ae3e7 100644 --- a/src/lib/client/ui/AvatarInput.svelte +++ b/src/lib/client/ui/AvatarInput.svelte @@ -1,47 +1,75 @@
-
\ No newline at end of file + div { + display: flex; + position: relative; + input { + display: none; + } + } + diff --git a/src/lib/client/ui/BannerInput.svelte b/src/lib/client/ui/BannerInput.svelte index f5421d5..82fef2b 100644 --- a/src/lib/client/ui/BannerInput.svelte +++ b/src/lib/client/ui/BannerInput.svelte @@ -1,44 +1,69 @@
-
\ No newline at end of file + div { + display: flex; + position: relative; + input { + display: none; + } + } + diff --git a/src/lib/client/ui/TextInput.svelte b/src/lib/client/ui/TextInput.svelte index 10ef56e..c8ddf85 100644 --- a/src/lib/client/ui/TextInput.svelte +++ b/src/lib/client/ui/TextInput.svelte @@ -1,12 +1,12 @@
- - -
\ No newline at end of file + + + diff --git a/src/lib/server/authclean.ts b/src/lib/server/authclean.ts index b36fd21..8d5ed30 100644 --- a/src/lib/server/authclean.ts +++ b/src/lib/server/authclean.ts @@ -1,25 +1,25 @@ -import { db } from "./database" +import { db } from './database'; export const cleanupAuths = async () => { - await db.authorizedAppSession.deleteMany({ - where: { - expiresAt: { - lt: new Date(Date.now()) - } - } - }) - await db.guildedVerificationSessions.deleteMany({ - where: { - expiresAt: { - lt: new Date(Date.now()) - } - } - }) - await db.guildedAuthSession.deleteMany({ - where: { - expiresAt: { - lt: new Date(Date.now()) - } - } - }) -} \ No newline at end of file + await db.authorizedAppSession.deleteMany({ + where: { + expiresAt: { + lt: new Date(Date.now()) + } + } + }); + await db.guildedVerificationSessions.deleteMany({ + where: { + expiresAt: { + lt: new Date(Date.now()) + } + } + }); + await db.guildedAuthSession.deleteMany({ + where: { + expiresAt: { + lt: new Date(Date.now()) + } + } + }); +}; diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index f3eedf1..26cc31b 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -1,3 +1,3 @@ -import prisma from '@prisma/client' +import prisma from '@prisma/client'; -export const db = new prisma.PrismaClient() +export const db = new prisma.PrismaClient(); diff --git a/src/lib/server/loginredirect.ts b/src/lib/server/loginredirect.ts index 258fa70..b485cf0 100644 --- a/src/lib/server/loginredirect.ts +++ b/src/lib/server/loginredirect.ts @@ -1,8 +1,11 @@ -export const handleLoginRedirect = (url: URL, message = "You must be logged in to access this page") => { - const redirectTo = url.pathname + url.search; - return `/login?redirectTo=${redirectTo}&message=${message}` -} +export const handleLoginRedirect = ( + url: URL, + message = 'You must be logged in to access this page' +) => { + const redirectTo = url.pathname + url.search; + return `/login?redirectTo=${redirectTo}&message=${message}`; +}; export const handleIdLoginRedirect = (code: string, redirectTo: string) => { - return `/login/${code}?redirectTo=${redirectTo}` -} + return `/login/${code}?redirectTo=${redirectTo}`; +}; diff --git a/src/lib/stores/stores.ts b/src/lib/stores/stores.ts index 05f6fa1..85875e8 100644 --- a/src/lib/stores/stores.ts +++ b/src/lib/stores/stores.ts @@ -2,4 +2,6 @@ import { writable } from 'svelte/store'; import { browser } from '$app/environment'; // Session based theme store. Grabs the current theme from the current body. -export const storeTheme = writable(browser ? document.body.getAttribute('data-theme') ?? '' : 'skeleton'); \ No newline at end of file +export const storeTheme = writable( + browser ? document.body.getAttribute('data-theme') ?? '' : 'skeleton' +); diff --git a/src/lib/utils/guilded-media.ts b/src/lib/utils/guilded-media.ts index 4e81728..6432896 100644 --- a/src/lib/utils/guilded-media.ts +++ b/src/lib/utils/guilded-media.ts @@ -1,6 +1,9 @@ export const guildedMediaLink = (awsUrl: string) => { - // replace https://s3-us-west-2.amazonaws.com/www.guilded.gg with https://cdn.gilcdn.com - // keep everything else same - if (!awsUrl) return awsUrl; - return awsUrl.replace('https://s3-us-west-2.amazonaws.com/www.guilded.gg', 'https://cdn.gilcdn.com'); -} \ No newline at end of file + // replace https://s3-us-west-2.amazonaws.com/www.guilded.gg with https://cdn.gilcdn.com + // keep everything else same + if (!awsUrl) return awsUrl; + return awsUrl.replace( + 'https://s3-us-west-2.amazonaws.com/www.guilded.gg', + 'https://cdn.gilcdn.com' + ); +}; diff --git a/src/routes/(auth)/login/+page.server.ts b/src/routes/(auth)/login/+page.server.ts index f49cb52..f419e0b 100644 --- a/src/routes/(auth)/login/+page.server.ts +++ b/src/routes/(auth)/login/+page.server.ts @@ -1,67 +1,65 @@ -import { fail, redirect } from '@sveltejs/kit' +import { fail, redirect } from '@sveltejs/kit'; import { z } from 'zod'; import { superValidate } from 'sveltekit-superforms/server'; -import { db } from '$lib/server/database' +import { zod } from 'sveltekit-superforms/adapters'; +import { db } from '$lib/server/database'; import { handleIdLoginRedirect } from '$lib/server/loginredirect.js'; const linkGuildedSchema = z.object({ - guildedId: z.string(), + guildedId: z.string() }); export const load = async () => { - // Server API: - const form = await superValidate(linkGuildedSchema); + // Server API: + const form = await superValidate(zod(linkGuildedSchema)); - // Always return { form } in load and form actions. - return { form }; + // Always return { form } in load and form actions. + return { form }; }; export const actions = { linkGuilded: async ({ request, url }) => { - const data = await request.formData() - const guildedId = data.get('guildedId') + const data = await request.formData(); + const guildedId = data.get('guildedId'); - if ( - !guildedId || typeof guildedId !== 'string' - ) { - return fail(400, { invalid: true }) + if (!guildedId || typeof guildedId !== 'string') { + return fail(400, { invalid: true }); } - // Clear old sessions - await db.guildedVerificationSessions.deleteMany({ - where: { - expiresAt: { - lte: new Date(), - } - } - }); + // Clear old sessions + await db.guildedVerificationSessions.deleteMany({ + where: { + expiresAt: { + lte: new Date() + } + } + }); + const prevSession = await db.guildedVerificationSessions.findUnique({ + where: { + userId: guildedId + } + }); - const prevSession = await db.guildedVerificationSessions.findUnique({ - where: { - userId: guildedId, - } - }) - - if (prevSession) { - // Delete any existing sessions - await db.guildedVerificationSessions.deleteMany({ - where: { - userId: guildedId, - } - }) - } - await db.guildedVerificationSessions.create({ - data: { - expiresAt: new Date(Date.now() + 1000 * 60 * 5), - userId: guildedId, - } - }); - const redirectTo = url.searchParams.get('redirectTo') || ''; - console.log(url) - // successfully redirect to userId path of login flow - // throw redirect(302, `/login/${guildedId}`); - throw redirect(302, handleIdLoginRedirect(guildedId, redirectTo)) - }, -} + if (prevSession) { + // Delete any existing sessions + await db.guildedVerificationSessions.deleteMany({ + where: { + userId: guildedId + } + }); + } + await db.guildedVerificationSessions.create({ + data: { + expiresAt: new Date(Date.now() + 1000 * 60 * 5), + userId: guildedId + } + }); + const redirectTo = url.searchParams.get('redirectTo') || ''; + console.log(url); + // successfully redirect to userId path of login flow + // throw redirect(302, `/login/${guildedId}`); + redirect(302, handleIdLoginRedirect(guildedId, redirectTo)); + } +}; diff --git a/src/routes/(auth)/login/+page.svelte b/src/routes/(auth)/login/+page.svelte index 7b13e03..dca7f3f 100644 --- a/src/routes/(auth)/login/+page.svelte +++ b/src/routes/(auth)/login/+page.svelte @@ -1,118 +1,147 @@
- {#if message} -

- {message} -

- {/if} -

Link Guilded

-

- We need to verify your identity! - Please complete following steps to confirm your identity: -

-
    -
  1. - Make sure you are signed in to Guilded with the Guilded account you choose on this page. -
  2. -
  3. - Why do we need you to do this? Read Here. -
  4. -
-
- - { - if (userSearch) { - clearTimeout(typingTimer) - typingTimer = setTimeout(searchForUsername, typingDelay) - } - }} on:keydown={() => clearTimeout(typingTimer)} /> -
- -
-
- {#each users as user} -
-
-
+ {#if message} +

+ {message} +

+ {/if} +

Link Guilded

+

+ We need to verify your identity! Please complete following steps to confirm your + identity: +

+
    +
  1. + Make sure you are signed in to Guilded with the Guilded account + you choose on this page. +
  2. +
  3. + Why do we need you to do this? Read Here. +
  4. +
+
+ + { + if (userSearch) { + clearTimeout(typingTimer); + typingTimer = setTimeout(searchForUsername, typingDelay); + } + }} + on:keydown={() => clearTimeout(typingTimer)} + /> +
+ +
+
+ {#each users as user} +
+
+
-

Or

-
- - -
-
\ No newline at end of file +

Or

+
+ + +
+ diff --git a/src/routes/(auth)/login/[userId]/+error.svelte b/src/routes/(auth)/login/[userId]/+error.svelte index 0703cad..3318ec6 100644 --- a/src/routes/(auth)/login/[userId]/+error.svelte +++ b/src/routes/(auth)/login/[userId]/+error.svelte @@ -1,9 +1,8 @@ -

- Error -

+ +

Error

- {$page.error.message} -

\ No newline at end of file + {$page.error.message} +

diff --git a/src/routes/(auth)/login/[userId]/+page.server.ts b/src/routes/(auth)/login/[userId]/+page.server.ts index 1260f0d..5f3fc92 100644 --- a/src/routes/(auth)/login/[userId]/+page.server.ts +++ b/src/routes/(auth)/login/[userId]/+page.server.ts @@ -3,94 +3,102 @@ import { guildedMediaLink } from '$lib/utils/guilded-media'; import type { GuildedVerificationSessions } from '@prisma/client'; import { error, redirect } from '@sveltejs/kit'; -export const load = async({params}) => { - const userId = params.userId; - if (!userId) { - redirect(302, '/login'); - } - await db.guildedVerificationSessions.deleteMany({ - where: { - expiresAt: { - lte: new Date(), - } - } - }); - const sessionAuthToken = await db.guildedVerificationSessions.findUnique({ - where: { - userId - } - }); - if (!sessionAuthToken) { - redirect(302, '/login'); - } - const userAuthToken = sessionAuthToken?.id; - return {userId, userAuthToken}; -} +export const load = async ({ params }) => { + const userId = params.userId; + if (!userId) { + redirect(302, '/login'); + } + await db.guildedVerificationSessions.deleteMany({ + where: { + expiresAt: { + lte: new Date() + } + } + }); + const sessionAuthToken = await db.guildedVerificationSessions.findUnique({ + where: { + userId + } + }); + if (!sessionAuthToken) { + redirect(302, '/login'); + } + const userAuthToken = sessionAuthToken?.id; + return { userId, userAuthToken }; +}; export const actions = { - check:async ({ cookies, params, url }) => { - const userId = params.userId; - const posts = await fetch(`https://www.guilded.gg/api/users/${userId}/posts?maxPosts=10`, {method: 'GET'}) - const data = await posts.json(); - const firstPostTitle = data[0].title - const createdBy = data[0].createdBy - if (createdBy !== userId) { - throw error(403, 'Please make sure you create the post with the account you are trying to verify! For security purposes, this incident has been reported to the account owner.'); - } - db.guildedVerificationSessions.deleteMany({ - where: { - expiresAt: { - lte: new Date(), - } - } - }); - let sessionAuthToken: GuildedVerificationSessions | string | undefined | null = await db.guildedVerificationSessions.findUnique({ - where: { - userId - } - }); - if (!sessionAuthToken) { - redirect(302, '/login'); - } - sessionAuthToken = sessionAuthToken?.id; - if (firstPostTitle !== sessionAuthToken) { - throw redirect(302, '/login'); - } - let user = await db.guildedUser.findUnique({ - where: { - id: userId, - } - }) - const me = await (await fetch(`https://www.guilded.gg/api/users/${userId}/profilev3`, {method: 'GET'})).json() - if (!user) { - user = await db.guildedUser.create({ - data: { - id: userId, - username: me.name, - avatar: me.profilePictureLg ? guildedMediaLink(me.profilePictureLg) : undefined, - banner: me.profileBannerLg ? guildedMediaLink(me.profileBannerLg): undefined, - } - }) - } else { - //update user - user = await db.guildedUser.update({ - where: { - id: userId, - }, - data: { - username: me.name, - avatar: me.profilePictureLg ? guildedMediaLink(me.profilePictureLg) : undefined, - banner: me.profileBannerLg ? guildedMediaLink(me.profileBannerLg): undefined, - } - }) - } - const session = await db.guildedAuthSession.create({ - data: { - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), - userId: user.id, - } - }) - cookies.set('guildedAuthSession', session.id, { + check: async ({ cookies, params, url }) => { + const userId = params.userId; + const posts = await fetch(`https://www.guilded.gg/api/users/${userId}/posts?maxPosts=10`, { + method: 'GET' + }); + const data = await posts.json(); + const firstPostTitle = data[0].title; + const createdBy = data[0].createdBy; + if (createdBy !== userId) { + error( + 403, + 'Please make sure you create the post with the account you are trying to verify! For security purposes, this incident has been reported to the account owner.' + ); + } + db.guildedVerificationSessions.deleteMany({ + where: { + expiresAt: { + lte: new Date() + } + } + }); + let sessionAuthToken: GuildedVerificationSessions | string | undefined | null = + await db.guildedVerificationSessions.findUnique({ + where: { + userId + } + }); + if (!sessionAuthToken) { + redirect(302, '/login'); + } + sessionAuthToken = sessionAuthToken?.id; + if (firstPostTitle !== sessionAuthToken) { + redirect(302, '/login'); + } + let user = await db.guildedUser.findUnique({ + where: { + id: userId + } + }); + const me = await ( + await fetch(`https://www.guilded.gg/api/users/${userId}/profilev3`, { method: 'GET' }) + ).json(); + if (!user) { + user = await db.guildedUser.create({ + data: { + id: userId, + username: me.name, + avatar: me.profilePictureLg ? guildedMediaLink(me.profilePictureLg) : undefined, + banner: me.profileBannerLg ? guildedMediaLink(me.profileBannerLg) : undefined + } + }); + } else { + //update user + user = await db.guildedUser.update({ + where: { + id: userId + }, + data: { + username: me.name, + avatar: me.profilePictureLg ? guildedMediaLink(me.profilePictureLg) : undefined, + banner: me.profileBannerLg ? guildedMediaLink(me.profileBannerLg) : undefined + } + }); + } + const session = await db.guildedAuthSession.create({ + data: { + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), + userId: user.id + } + }); + cookies.set('guildedAuthSession', session.id, { // send cookie for every page path: '/', // server side only cookie so you can't use `document.cookie` @@ -101,12 +109,12 @@ export const actions = { // only sent over HTTPS in production secure: process.env.NODE_ENV === 'production', // set cookie to expire after a month - maxAge: 60 * 60 * 24 * 30, - }) - const redirectTo = url.searchParams.get('redirectTo'); - if (redirectTo) { - throw redirect(302, `/${redirectTo.slice(1)}`) - } - throw redirect(302, '/'); - } -} + maxAge: 60 * 60 * 24 * 30 + }); + const redirectTo = url.searchParams.get('redirectTo'); + if (redirectTo) { + redirect(302, `/${redirectTo.slice(1)}`); + } + redirect(302, '/'); + } +}; diff --git a/src/routes/(auth)/login/[userId]/+page.svelte b/src/routes/(auth)/login/[userId]/+page.svelte index 85291aa..e6a64ac 100644 --- a/src/routes/(auth)/login/[userId]/+page.svelte +++ b/src/routes/(auth)/login/[userId]/+page.svelte @@ -3,45 +3,56 @@ import { clipboard } from '@skeletonlabs/skeleton'; import { onMount } from 'svelte'; - export let data + export let data; - let redirectTo: string | null - $: redirectTo = '' + let redirectTo: string | null; + $: redirectTo = ''; - onMount(() => { - redirectTo = $page.url.searchParams.get('redirectTo') - }) + onMount(() => { + redirectTo = $page.url.searchParams.get('redirectTo'); + }); +
-

- Are you really who you say you are? -

-

- We need to verify your identity! - Please complete following steps to confirm your identity: -

-
    -
  1. - Go to Your Profile -
  2. -
  3. - Make sure you are signed in to the account you chose on the previous page and click on "Write a post" -
  4. -
  5. - Make the title of the post - - {data.userAuthToken} - - - . You can fill the body with anything! (Flex being a cool cardboard user!) -
  6. -
  7. - Click "Post" and then press "Check" over here. -
  8. -
-
- -
-
+

Are you really who you say you are?

+

+ We need to verify your identity! Please complete following steps to confirm your + identity: +

+
    +
  1. + Go to Your Profile +
  2. +
  3. + Make sure you are signed in to the account you chose on the previous page and click on "Write a post" +
  4. +
  5. + Make the title of the post + + {data.userAuthToken} + + + . You can fill the body with anything! (Flex being a cool cardboard user!) +
  6. +
  7. + Click "Post" and then press "Check" over here. +
  8. +
+
+ +
+ diff --git a/src/routes/(auth)/logout/+page.server.ts b/src/routes/(auth)/logout/+page.server.ts index b8776e5..c69b920 100644 --- a/src/routes/(auth)/logout/+page.server.ts +++ b/src/routes/(auth)/logout/+page.server.ts @@ -1,34 +1,33 @@ -import { db } from '$lib/server/database.js' -import { redirect } from '@sveltejs/kit' +import { db } from '$lib/server/database.js'; +import { redirect } from '@sveltejs/kit'; export const load = async () => { // we only use this endpoint for the api // and don't need to see the page - throw redirect(302, '/') -} + redirect(302, '/'); +}; export const actions = { - default: async({ cookies }) => { - - const session = cookies.get('guildedAuthSession') + default: async ({ cookies }) => { + const session = cookies.get('guildedAuthSession'); if (!session) { - throw redirect(302, '/login') + redirect(302, '/login'); } //remove from db - await db.guildedAuthSession.delete({ where: { id: session } }) - + await db.guildedAuthSession.delete({ where: { id: session } }); + // eat the cookie cookies.set('session', '', { path: '/', - expires: new Date(0), - }) + expires: new Date(0) + }); // seems like a nice place to clean up expired sessions - await db.guildedAuthSession.deleteMany({ where: { expiresAt: { lt: new Date() } } }) + await db.guildedAuthSession.deleteMany({ where: { expiresAt: { lt: new Date() } } }); // redirect the user - throw redirect(302, '/login') - }, -} + redirect(302, '/login'); + } +}; diff --git a/src/routes/(protected)/a/+page.server.ts b/src/routes/(protected)/a/+page.server.ts index c7143c2..acd4cbc 100644 --- a/src/routes/(protected)/a/+page.server.ts +++ b/src/routes/(protected)/a/+page.server.ts @@ -1,8 +1,8 @@ -import { redirect } from "@sveltejs/kit" +import { redirect } from '@sveltejs/kit'; export const load = async ({ locals }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') + redirect(302, '/'); } -} \ No newline at end of file +}; diff --git a/src/routes/(protected)/a/[vanity]/+error.svelte b/src/routes/(protected)/a/[vanity]/+error.svelte index 0703cad..3318ec6 100644 --- a/src/routes/(protected)/a/[vanity]/+error.svelte +++ b/src/routes/(protected)/a/[vanity]/+error.svelte @@ -1,9 +1,8 @@ -

- Error -

+ +

Error

- {$page.error.message} -

\ No newline at end of file + {$page.error.message} +

diff --git a/src/routes/(protected)/a/[vanity]/+page.server.ts b/src/routes/(protected)/a/[vanity]/+page.server.ts index e658851..d8d3fe4 100644 --- a/src/routes/(protected)/a/[vanity]/+page.server.ts +++ b/src/routes/(protected)/a/[vanity]/+page.server.ts @@ -5,7 +5,7 @@ import { error, redirect } from '@sveltejs/kit'; export const load = async ({ locals, params, url }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, handleLoginRedirect(url, 'You must login to authorize the app.')); + redirect(302, handleLoginRedirect(url, 'You must login to authorize the app.')); } const vanity = params.vanity; @@ -38,7 +38,7 @@ export const load = async ({ locals, params, url }) => { } }); if (!app) { - throw error(404, 'App not found'); + error(404, 'App not found'); } if (redirect_uri) { @@ -46,14 +46,14 @@ export const load = async ({ locals, params, url }) => { const app_redirect_uri = new URL(app.redirectUri); redirect_uri = new URL(redirect_uri); if (redirect_uri.origin !== app_redirect_uri.origin) { - throw error( + error( 400, `Invalid redirect_uri, must be on the same domain as the app config redirect_uri! Contact app developer if you believe this is a mistake.` ); } redirect_uri = redirect_uri.toString(); } catch (e) { - throw error(400, `Invalid redirect_uri,\n${e}`); + error(400, `Invalid redirect_uri,\n${e}`); } } @@ -63,7 +63,7 @@ export const load = async ({ locals, params, url }) => { export const actions = { authorizeApp: async ({ locals, params, url }) => { if (!locals.user) { - throw redirect(302, '/'); + redirect(302, '/'); } const redirect_uri = url.searchParams.get('redirect_uri'); @@ -84,7 +84,7 @@ export const actions = { }); if (!appExists) { - throw error(404, 'App not found'); + error(404, 'App not found'); } const user = await db.guildedUser.findUnique({ @@ -94,7 +94,7 @@ export const actions = { }); if (!user) { - throw error(404, 'User not found'); + error(404, 'User not found'); } let manager = await db.userAppManager.findFirst({ @@ -121,9 +121,9 @@ export const actions = { } }); if (!redirect_uri) { - throw redirect(302, `${appExists.redirectUri}?code=${newSession.authToken}`); + redirect(302, `${appExists.redirectUri}?code=${newSession.authToken}`); } - throw redirect(302, `${redirect_uri}?code=${newSession.authToken}&state=${state}`); + redirect(302, `${redirect_uri}?code=${newSession.authToken}&state=${state}`); }, toggleAppVerification: async ({ locals, params }) => { const appExists = await db.app.findUnique({ @@ -138,7 +138,7 @@ export const actions = { }); if (!appExists) { - throw error(404, 'App not found'); + error(404, 'App not found'); } const user = await db.guildedUser.findUnique({ @@ -148,11 +148,11 @@ export const actions = { }); if (!user) { - throw error(404, 'User not found'); + error(404, 'User not found'); } - if (user.role !== "ADMIN") { - throw error(403, 'You must be an admin to toggle app verification.') + if (user.role !== 'ADMIN') { + error(403, 'You must be an admin to toggle app verification.'); } await db.app.update({ @@ -162,7 +162,6 @@ export const actions = { data: { isVerified: !appExists.isVerified } - }) - + }); } }; diff --git a/src/routes/(protected)/a/[vanity]/+page.svelte b/src/routes/(protected)/a/[vanity]/+page.svelte index c9e5fe5..a567aaa 100644 --- a/src/routes/(protected)/a/[vanity]/+page.svelte +++ b/src/routes/(protected)/a/[vanity]/+page.svelte @@ -2,84 +2,97 @@ import { enhance } from '$app/forms'; import { page } from '$app/stores'; import { guildedMediaLink } from '$lib/utils/guilded-media'; -import { Avatar } from '@skeletonlabs/skeleton'; + import { Avatar } from '@skeletonlabs/skeleton'; - export let data + export let data; - const {app, redirect_uri, scope, state, response_type} = data + const { app, redirect_uri, scope, state, response_type } = data; -

- Authorizing -

+

Authorizing

-
-
- User Banner -
-
-
-
- -
-
-

- {app.name} -

-
- {#if app.isVerified} - ✅ This app is verified. - {/if} - {#if $page.data.user.role === "ADMIN"} -
- -
- {/if} -
- -
-
-
-

- Description -

-

- {app.description} -

-
-
-
-
-
-
- -
- - Created By: {app.ownerUser.username} - - - Created At: {app.createdAt.toLocaleString()} - - {#if app.supportServer != ''} - - App Support: {app.supportServer} - - {/if} -
-
-
-
- -
-
-
-
-
-
\ No newline at end of file +
+
+ User Banner +
+
+
+
+ +
+
+

+ {app.name} +

+
+ {#if app.isVerified} + ✅ This app is verified. + {/if} + {#if $page.data.user.role === 'ADMIN'} +
+ +
+ {/if} +
+
+
+
+

Description

+

+ {app.description} +

+
+
+
+
+
+
+ +
+ + Created By: {app.ownerUser.username} + + + Created At: {app.createdAt.toLocaleString()} + + {#if app.supportServer != ''} + + App Support: {app.supportServer} + + {/if} +
+
+
+
+ +
+
+
+
+
+ diff --git a/src/routes/(protected)/profile/+page.server.ts b/src/routes/(protected)/profile/+page.server.ts index b1eadb9..d8488d8 100644 --- a/src/routes/(protected)/profile/+page.server.ts +++ b/src/routes/(protected)/profile/+page.server.ts @@ -1,36 +1,36 @@ -import { fail, redirect } from '@sveltejs/kit' +import { fail, redirect } from '@sveltejs/kit'; -import { db } from '$lib/server/database' +import { db } from '$lib/server/database'; export const load = async ({ locals }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') + redirect(302, '/'); } -} +}; export const actions = { - updateAvatar: async ({ request, locals }) => { - const data = await request.formData() - const avatar = data.get('avatar') - if (typeof avatar !== 'string' || !avatar) { - return fail(400, { invalid: true }) - } - await db.user.update({ - where: { username: locals.user.name }, - data: { avatar: avatar }, - }) - }, + updateAvatar: async ({ request, locals }) => { + const data = await request.formData(); + const avatar = data.get('avatar'); + if (typeof avatar !== 'string' || !avatar) { + return fail(400, { invalid: true }); + } + await db.user.update({ + where: { username: locals.user.name }, + data: { avatar: avatar } + }); + }, - updateDisplayName: async ({ request, locals }) => { - const data = await request.formData() - const displayName = data.get('displayName') - if (typeof displayName !== 'string' || !displayName) { - return fail(400, { invalid: true }) - } - await db.user.update({ - where: { username: locals.user.name }, - data: { displayName: displayName }, - }) - } -} + updateDisplayName: async ({ request, locals }) => { + const data = await request.formData(); + const displayName = data.get('displayName'); + if (typeof displayName !== 'string' || !displayName) { + return fail(400, { invalid: true }); + } + await db.user.update({ + where: { username: locals.user.name }, + data: { displayName: displayName } + }); + } +}; diff --git a/src/routes/(protected)/profile/+page.svelte b/src/routes/(protected)/profile/+page.svelte index 8ead68e..0bea9b8 100644 --- a/src/routes/(protected)/profile/+page.svelte +++ b/src/routes/(protected)/profile/+page.svelte @@ -1,32 +1,33 @@ -

Your Profile

-
-
- User Banner -
-
-
-
- -
-

- {displayName} -

-
-
-
-
-
-
-
\ No newline at end of file +
+
+ User Banner +
+
+
+
+ +
+

+ {displayName} +

+
+
+
+
+
+
+ diff --git a/src/routes/(protected)/settings/+page.server.ts b/src/routes/(protected)/settings/+page.server.ts index 72eff62..acd4cbc 100644 --- a/src/routes/(protected)/settings/+page.server.ts +++ b/src/routes/(protected)/settings/+page.server.ts @@ -1,8 +1,8 @@ -import { redirect } from '@sveltejs/kit' +import { redirect } from '@sveltejs/kit'; export const load = async ({ locals }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') + redirect(302, '/'); } -} \ No newline at end of file +}; diff --git a/src/routes/(protected)/settings/+page.svelte b/src/routes/(protected)/settings/+page.svelte index e013493..210f5b2 100644 --- a/src/routes/(protected)/settings/+page.svelte +++ b/src/routes/(protected)/settings/+page.svelte @@ -1,2 +1,2 @@

Welcome to settings!

-

Choose an option from sidebar to get configuring! 🔥

\ No newline at end of file +

Choose an option from sidebar to get configuring! 🔥

diff --git a/src/routes/(protected)/settings/authorized-apps/+page.server.ts b/src/routes/(protected)/settings/authorized-apps/+page.server.ts index 055ace2..7db141a 100644 --- a/src/routes/(protected)/settings/authorized-apps/+page.server.ts +++ b/src/routes/(protected)/settings/authorized-apps/+page.server.ts @@ -1,33 +1,33 @@ -import { db } from "$lib/server/database" -import { redirect } from "@sveltejs/kit" +import { db } from '$lib/server/database'; +import { redirect } from '@sveltejs/kit'; export const load = async ({ locals }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') + redirect(302, '/'); } - const appManagers = await db.userAppManager.findMany({ - where: { - userId: locals.user.id as string, - }, - select: { - app: { - select: { - id: true, - name: true, - description: true, - icon: true, - createdAt: true, - ownerUser: true, - banner: true, - supportServer: true, - isVerified: true, - } - }, - authorizedSessions: true, - } - }) + const appManagers = await db.userAppManager.findMany({ + where: { + userId: locals.user.id as string + }, + select: { + app: { + select: { + id: true, + name: true, + description: true, + icon: true, + createdAt: true, + ownerUser: true, + banner: true, + supportServer: true, + isVerified: true + } + }, + authorizedSessions: true + } + }); - return {appManagers} -} + return { appManagers }; +}; diff --git a/src/routes/(protected)/settings/authorized-apps/+page.svelte b/src/routes/(protected)/settings/authorized-apps/+page.svelte index 7f77a4c..89cd9c6 100644 --- a/src/routes/(protected)/settings/authorized-apps/+page.svelte +++ b/src/routes/(protected)/settings/authorized-apps/+page.svelte @@ -2,67 +2,72 @@ import { guildedMediaLink } from '$lib/utils/guilded-media'; import { Avatar } from '@skeletonlabs/skeleton'; - export let data - const {appManagers} = data + export let data; + const { appManagers } = data; \ No newline at end of file +

Authorized Apps

+ + {#if appManagers.length < 1} +

You have no authorized apps.

+ {/if} + diff --git a/src/routes/(protected)/settings/authorized-apps/[id]/+page.server.ts b/src/routes/(protected)/settings/authorized-apps/[id]/+page.server.ts index e6ca6aa..1c96393 100644 --- a/src/routes/(protected)/settings/authorized-apps/[id]/+page.server.ts +++ b/src/routes/(protected)/settings/authorized-apps/[id]/+page.server.ts @@ -1,124 +1,126 @@ -import { db } from "$lib/server/database"; -import { redirect } from "@sveltejs/kit"; +import { db } from '$lib/server/database'; +import { redirect } from '@sveltejs/kit'; export const load = async ({ params, locals }) => { - // redirect user if logged in + // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') - } - const appId = params.id; - const appName = await db.app.findUnique({ - where: { - id: appId, - }, - select: { - name: true, - sessionManagers: { - select: { - id: true, - } - } - } - }) + redirect(302, '/'); + } + const appId = params.id; + const appName = await db.app.findUnique({ + where: { + id: appId + }, + select: { + name: true, + sessionManagers: { + select: { + id: true + } + } + } + }); - if (!appName) { - throw redirect(302, '/settings/authorized-apps') - } + if (!appName) { + redirect(302, '/settings/authorized-apps'); + } - const appManager = await db.userAppManager.findFirst({ - where: { - userId: locals.user.id, - appId: appId, - }, - select: { - id: true, - } - }) + const appManager = await db.userAppManager.findFirst({ + where: { + userId: locals.user.id, + appId: appId + }, + select: { + id: true + } + }); - if (!appManager) { - throw redirect(302, '/settings/authorized-apps') - } + if (!appManager) { + redirect(302, '/settings/authorized-apps'); + } - const appSessions = await db.authorizedAppSession.findMany({ - where: { - userAppManagerId: appManager.id, - }, - select: { - id: true, - createdAt: true, - expiresAt: true, - } - }) - return {appName, appSessions, appManager} -} + const appSessions = await db.authorizedAppSession.findMany({ + where: { + userAppManagerId: appManager.id + }, + select: { + id: true, + createdAt: true, + expiresAt: true + } + }); + return { appName, appSessions, appManager }; +}; export const actions = { - revoke: async ({request, locals }) => { - const data = await request.formData(); - const authAppId = data.get('identifier') as string; + revoke: async ({ request, locals }) => { + const data = await request.formData(); + const authAppId = data.get('identifier') as string; - const user = locals.user.id; + const user = locals.user.id; - if (!authAppId) { - throw redirect(302, '/settings/authorized-apps') - } + if (!authAppId) { + redirect(302, '/settings/authorized-apps'); + } - const confAuthAppOwner = await db.authorizedAppSession.findUnique({ - where: { - id: authAppId, - }, - select: { - userAppManager: { - select: { - userId: true, - } - } - } - }).catch(() => { - throw redirect(302, '/settings/authorized-apps') - }) + const confAuthAppOwner = await db.authorizedAppSession + .findUnique({ + where: { + id: authAppId + }, + select: { + userAppManager: { + select: { + userId: true + } + } + } + }) + .catch(() => { + redirect(302, '/settings/authorized-apps'); + }); - if (!confAuthAppOwner) { - throw redirect(302, '/settings/authorized-apps') - } + if (!confAuthAppOwner) { + redirect(302, '/settings/authorized-apps'); + } - if (confAuthAppOwner.userAppManager.userId !== user) { - throw redirect(302, '/settings/authorized-apps') - } + if (confAuthAppOwner.userAppManager.userId !== user) { + redirect(302, '/settings/authorized-apps'); + } - await db.authorizedAppSession.delete({ - where: { - id: authAppId, - } - }) - }, - revokeAuth: async ({ locals, params }) => { - const managerId = await db.userAppManager.findFirst({ - where: { - userId: locals.user.id, - appId: params.id, - }, - select:{ - id: true, - authorizedSessions: true - } - }) - console.log(managerId) - if (!managerId) { - throw redirect(302, '/settings/authorized-apps') - } - if (managerId.authorizedSessions.length > 0) { - await db.authorizedAppSession.deleteMany({ - where: { - userAppManagerId: managerId.id, - } - }) - } - await db.userAppManager.deleteMany({ - where: { - id: managerId.id, - } - }) - throw redirect(302, '/settings/authorized-apps') - } -} \ No newline at end of file + await db.authorizedAppSession.delete({ + where: { + id: authAppId + } + }); + }, + revokeAuth: async ({ locals, params }) => { + const managerId = await db.userAppManager.findFirst({ + where: { + userId: locals.user.id, + appId: params.id + }, + select: { + id: true, + authorizedSessions: true + } + }); + console.log(managerId); + if (!managerId) { + redirect(302, '/settings/authorized-apps'); + } + if (managerId.authorizedSessions.length > 0) { + await db.authorizedAppSession.deleteMany({ + where: { + userAppManagerId: managerId.id + } + }); + } + await db.userAppManager.deleteMany({ + where: { + id: managerId.id + } + }); + redirect(302, '/settings/authorized-apps'); + } +}; diff --git a/src/routes/(protected)/settings/authorized-apps/[id]/+page.svelte b/src/routes/(protected)/settings/authorized-apps/[id]/+page.svelte index 8351854..0851aa1 100644 --- a/src/routes/(protected)/settings/authorized-apps/[id]/+page.svelte +++ b/src/routes/(protected)/settings/authorized-apps/[id]/+page.svelte @@ -1,73 +1,79 @@ -
-

- Manage Your {appName?.name} Sessions -

-
- - -
+

+ Manage Your {appName?.name} Sessions +

+
+ + +
{#if sessions.length === 0} -

- You have no active sessions of {appName?.name}. -

+

+ You have no active sessions of {appName?.name}. +

{:else} -
    - {#each sessions as session} -
  • - -
  • - {/each} -
+
    + {#each sessions as session} +
  • +
  • + {/each} +
{/if} \ No newline at end of file + svg { + width: 64px; + } + diff --git a/src/routes/(protected)/settings/sessions/+page.server.ts b/src/routes/(protected)/settings/sessions/+page.server.ts index 859e0ca..cffa06f 100644 --- a/src/routes/(protected)/settings/sessions/+page.server.ts +++ b/src/routes/(protected)/settings/sessions/+page.server.ts @@ -1,65 +1,66 @@ -import { redirect } from '@sveltejs/kit' +import { redirect } from '@sveltejs/kit'; -import { db } from '$lib/server/database' -import type { GuildedAuthSession } from '@prisma/client' +import { db } from '$lib/server/database'; +import type { GuildedAuthSession } from '@prisma/client'; export const load = async ({ locals, cookies }) => { // redirect user if logged in if (!locals.user) { - throw redirect(302, '/') + redirect(302, '/'); } - const sessions = await db.guildedUser.findUnique({ - where: { id: locals.user.id}, - select: { sessions: { - select: { id: true, createdAt: true, expiresAt: true } - } } - }) + const sessions = await db.guildedUser.findUnique({ + where: { id: locals.user.id }, + select: { + sessions: { + select: { id: true, createdAt: true, expiresAt: true } + } + } + }); + + if (!sessions) { + redirect(302, '/'); + } - if (!sessions) { - throw redirect(302, '/') - } + if (!sessions.sessions) { + redirect(302, '/'); + } - if (!sessions.sessions) { - throw redirect(302, '/') - } - - type ActiveSession = GuildedAuthSession & { current: boolean } + type ActiveSession = GuildedAuthSession & { current: boolean }; - const activeSessions = sessions.sessions as ActiveSession[] + const activeSessions = sessions.sessions as ActiveSession[]; - if (!activeSessions) { - throw redirect(302, '/') - } + if (!activeSessions) { + redirect(302, '/'); + } - activeSessions.forEach(session => { - session.current = session.id === cookies.get('guildedAuthSession') - }) + activeSessions.forEach((session) => { + session.current = session.id === cookies.get('guildedAuthSession'); + }); - return {activeSessions} -} + return { activeSessions }; +}; export const actions = { - revoke: async({ request, cookies }) => { - - const data = await request.formData() - const id = data.get('identifier') as string + revoke: async ({ request, cookies }) => { + const data = await request.formData(); + const id = data.get('identifier') as string; - if (id === cookies.get('guildedAuthSession')) { - throw redirect(302, '/settings/sessions') - } + if (id === cookies.get('guildedAuthSession')) { + redirect(302, '/settings/sessions'); + } if (!id) { - throw redirect(302, '/settings/sessions') + redirect(302, '/settings/sessions'); } //remove from db - await db.guildedAuthSession.delete({ where: { id } }) + await db.guildedAuthSession.delete({ where: { id } }); // seems like a nice place to clean up expired sessions - await db.guildedAuthSession.deleteMany({ where: { expiresAt: { lt: new Date() } } }) + await db.guildedAuthSession.deleteMany({ where: { expiresAt: { lt: new Date() } } }); // redirect the user - throw redirect(302, '/settings/sessions') - }, -} \ No newline at end of file + redirect(302, '/settings/sessions'); + } +}; diff --git a/src/routes/(protected)/settings/sessions/+page.svelte b/src/routes/(protected)/settings/sessions/+page.svelte index cb52d2e..e1ad680 100644 --- a/src/routes/(protected)/settings/sessions/+page.svelte +++ b/src/routes/(protected)/settings/sessions/+page.svelte @@ -1,69 +1,75 @@ -

- Manage Your Cardboard Sessions -

+

Manage Your Cardboard Sessions

{#if sessions.length === 0} -

- You have no active sessions. -

+

You have no active sessions.

{:else} -
    - {#each sessions as session} -
  • - -
  • - {/each} -
+
    + {#each sessions as session} +
  • + +
  • + {/each} +
{/if} \ No newline at end of file + svg { + width: 64px; + } + diff --git a/src/routes/(protected)/settings/your-apps/+page.server.ts b/src/routes/(protected)/settings/your-apps/+page.server.ts index 69b8ba6..c1cc239 100644 --- a/src/routes/(protected)/settings/your-apps/+page.server.ts +++ b/src/routes/(protected)/settings/your-apps/+page.server.ts @@ -1,16 +1,16 @@ -import { db } from "$lib/server/database"; -import { redirect } from "@sveltejs/kit"; +import { db } from '$lib/server/database'; +import { redirect } from '@sveltejs/kit'; -export const load = async ({locals, params}) => { - if (!locals.user) { - throw redirect(302, '/') - } +export const load = async ({ locals, params }) => { + if (!locals.user) { + redirect(302, '/'); + } - const myApps = await db.app.findMany({ - where:{ - ownerId: locals.user.id - } - }) + const myApps = await db.app.findMany({ + where: { + ownerId: locals.user.id + } + }); - return {myApps} - } \ No newline at end of file + return { myApps }; +}; diff --git a/src/routes/(protected)/settings/your-apps/+page.svelte b/src/routes/(protected)/settings/your-apps/+page.svelte index 5c7ce2e..c48075b 100644 --- a/src/routes/(protected)/settings/your-apps/+page.svelte +++ b/src/routes/(protected)/settings/your-apps/+page.svelte @@ -1,75 +1,96 @@ -
+
-

- Your Apps -

- - - - - +

Your Apps

+ + + + +
- {#each myApps as app} - - - - {/each} + blur(10px);`} + > +
+
+ +

+ {app.name} +

+
+
+
+
+ {app.id} +
+ +
+
+ +
+ + {/each}
{#if myApps.length === 0} -
-

- You don't have any apps yet. -

-

- Create one by clicking the + icon above. -

-
-{/if} \ No newline at end of file +
+

You don't have any apps yet.

+

Create one by clicking the + icon above.

+
+{/if} diff --git a/src/routes/(protected)/settings/your-apps/[id]/+page.server.ts b/src/routes/(protected)/settings/your-apps/[id]/+page.server.ts index d41c605..4aa10f6 100644 --- a/src/routes/(protected)/settings/your-apps/[id]/+page.server.ts +++ b/src/routes/(protected)/settings/your-apps/[id]/+page.server.ts @@ -1,132 +1,133 @@ -import { db } from "$lib/server/database"; -import { fail, redirect } from "@sveltejs/kit" -import { z } from "zod"; -import { setError, superValidate } from "sveltekit-superforms/server"; +import { db } from '$lib/server/database'; +import { fail, redirect } from '@sveltejs/kit'; +import { z } from 'zod'; +import { setError, superValidate } from 'sveltekit-superforms/server'; +import { zod } from 'sveltekit-superforms/adapters'; const schema = z.object({ - name: z.string(), - description: z.string(), - icon: z.string(), - banner: z.string(), - redirectUri: z.string().url(), - supportServer: z.string().url(), - vanityCode: z.string().optional(), + name: z.string(), + description: z.string(), + icon: z.string(), + banner: z.string(), + redirectUri: z.string().url(), + supportServer: z.string().url(), + vanityCode: z.string().optional() }); -export const load = async ({locals, params}) => { - if (!locals.user) { - throw redirect(302, '/') - } - const appId = params.id; - const app = await db.app.findUnique({ - where: { - id: appId, - } - }) - if (!app) { - throw redirect(302, '/settings/your-apps') - } - if (app.ownerId !== locals.user.id) { - throw redirect(302, '/settings/your-apps') - } +export const load = async ({ locals, params }) => { + if (!locals.user) { + redirect(302, '/'); + } + const appId = params.id; + const app = await db.app.findUnique({ + where: { + id: appId + } + }); + if (!app) { + redirect(302, '/settings/your-apps'); + } + if (app.ownerId !== locals.user.id) { + redirect(302, '/settings/your-apps'); + } - const form = await superValidate(app, schema) - return {app, form} - } + const form = await superValidate(app, zod(schema)); + return { app, form }; +}; export const actions = { - deleteApp: async ({locals, params}) => { - const appId = params.id; - const app = await db.app.findUnique({ - where: { - id: appId, - } - }) - if (!app) { - throw redirect(302, '/settings/your-apps') - } - if (app.ownerId !== locals.user.id) { - throw redirect(302, '/settings/your-apps') - } + deleteApp: async ({ locals, params }) => { + const appId = params.id; + const app = await db.app.findUnique({ + where: { + id: appId + } + }); + if (!app) { + redirect(302, '/settings/your-apps'); + } + if (app.ownerId !== locals.user.id) { + redirect(302, '/settings/your-apps'); + } - const appManagers = await db.authorizedAppSession.findMany({ - where: { - userAppManager: { - appId, - } - } - }) + const appManagers = await db.authorizedAppSession.findMany({ + where: { + userAppManager: { + appId + } + } + }); - // delete sessions for app managers - for (const appManager of appManagers) { - await db.authorizedAppSession.delete({ - where: { - id: appManager.id - } - }) - } + // delete sessions for app managers + for (const appManager of appManagers) { + await db.authorizedAppSession.delete({ + where: { + id: appManager.id + } + }); + } - // delete app managers - await db.userAppManager.deleteMany({ - where: { - appId, - } - }) + // delete app managers + await db.userAppManager.deleteMany({ + where: { + appId + } + }); - // delete app - await db.app.delete({ - where: { - id: appId - } - }) + // delete app + await db.app.delete({ + where: { + id: appId + } + }); - throw redirect(302, '/settings/your-apps') - }, - updateApp: async ({request, locals, params}) => { - const appId = params.id; - const app = await db.app.findUnique({ - where: { - id: appId, - } - }) - if (!app) { - throw redirect(302, '/settings/your-apps') - } - if (app.ownerId !== locals.user.id) { - throw redirect(302, '/settings/your-apps') - } - let form = await superValidate(request, schema) - if (!form.valid) { - return fail(400, { form }) - } + redirect(302, '/settings/your-apps'); + }, + updateApp: async ({ request, locals, params }) => { + const appId = params.id; + const app = await db.app.findUnique({ + where: { + id: appId + } + }); + if (!app) { + redirect(302, '/settings/your-apps'); + } + if (app.ownerId !== locals.user.id) { + redirect(302, '/settings/your-apps'); + } + let form = await superValidate(request, zod(schema)); + if (!form.valid) { + return fail(400, { form }); + } - if (form.data.vanityCode) { - const existingVanityCodeApp = await db.app.findUnique({ - where: { - vanityCode: form.data.vanityCode, - } - }) - if (existingVanityCodeApp) { - if (existingVanityCodeApp.id !== appId) { - return setError(form, 'vanityCode', 'This vanity code is already in use.') - } - } - } - const updatedApp = await db.app.update({ - where: { - id: appId, - }, - data: { - name: form.data.name, - description: form.data.description, - icon: form.data.icon.length === 0 ? undefined : form.data.icon, - banner: form.data.banner.length === 0 ? undefined : form.data.banner, - redirectUri: form.data.redirectUri, - supportServer: form.data.supportServer, - vanityCode: form.data.vanityCode ? form.data.vanityCode : undefined, - } - }) - form = await superValidate(updatedApp, schema) - return {app: updatedApp, form} - } -} \ No newline at end of file + if (form.data.vanityCode) { + const existingVanityCodeApp = await db.app.findUnique({ + where: { + vanityCode: form.data.vanityCode + } + }); + if (existingVanityCodeApp) { + if (existingVanityCodeApp.id !== appId) { + return setError(form, 'vanityCode', 'This vanity code is already in use.'); + } + } + } + const updatedApp = await db.app.update({ + where: { + id: appId + }, + data: { + name: form.data.name, + description: form.data.description, + icon: form.data.icon.length === 0 ? undefined : form.data.icon, + banner: form.data.banner.length === 0 ? undefined : form.data.banner, + redirectUri: form.data.redirectUri, + supportServer: form.data.supportServer, + vanityCode: form.data.vanityCode ? form.data.vanityCode : undefined + } + }); + form = await superValidate(updatedApp, zod(schema)); + return { app: updatedApp, form }; + } +}; diff --git a/src/routes/(protected)/settings/your-apps/[id]/+page.svelte b/src/routes/(protected)/settings/your-apps/[id]/+page.svelte index de4f114..d2f3009 100644 --- a/src/routes/(protected)/settings/your-apps/[id]/+page.svelte +++ b/src/routes/(protected)/settings/your-apps/[id]/+page.svelte @@ -3,115 +3,167 @@ import BannerInput from '$lib/client/ui/BannerInput.svelte'; import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton'; import { superForm } from 'sveltekit-superforms/client'; - import {clipboard} from '@skeletonlabs/skeleton' - const toastStore = getToastStore(); - export let data; + import { clipboard } from '@skeletonlabs/skeleton'; + const toastStore = getToastStore(); + export let data; - const {app} = data - const {form, errors, constraints} = superForm(app); + const { app } = data; + const { form, errors, constraints } = superForm(app); - $: regeneratedSecret = ''; - - const newSecret = async (id: string) => { - const req = await fetch(`/settings/your-apps/${id}/secret`, { - method: 'PATCH', - }).catch(() => - toastStore.trigger({ - message: 'Failed to update secret!', - background: 'variant-filled-error', - timeout: 3000, - }) - ); - if (!req) return; - const data = await (req as Response).json().catch(() => { - toastStore.trigger({ - message: 'Failed to update secret, please update again!', - background: 'variant-filled-error', - timeout: 3000, - }); - }); - regeneratedSecret = data.secret; - toastStore.trigger({ - message: 'Successfully updated secret, please copy to clipboard!', - background: 'variant-filled-success', - timeout: 3000, - }); - } - + $: regeneratedSecret = ''; + + const newSecret = async (id: string) => { + const req = await fetch(`/settings/your-apps/${id}/secret`, { + method: 'PATCH' + }).catch(() => + toastStore.trigger({ + message: 'Failed to update secret!', + background: 'variant-filled-error', + timeout: 3000 + }) + ); + if (!req) return; + const data = await (req as Response).json().catch(() => { + toastStore.trigger({ + message: 'Failed to update secret, please update again!', + background: 'variant-filled-error', + timeout: 3000 + }); + }); + regeneratedSecret = data.secret; + toastStore.trigger({ + message: 'Successfully updated secret, please copy to clipboard!', + background: 'variant-filled-success', + timeout: 3000 + }); + };
-

- Editing ➡️ {app.name} -

-
Note:
-

Carboard currently only supports up to 512KBs of request body size per app update, this should not harm much functionality, however please use compressed images and try splitting the update id you do encounter any errors.

-
-
-
-
- -
-
-
-
- -
- - - - -