Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: add base pages and auth #14

Merged
merged 11 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
897 changes: 878 additions & 19 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ui/composables/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { User } from "lucia";

export const useUser = () => {
const user = useState<User | null>("user", () => null);
console.log(unref(user));

return user;
};

Expand Down
26 changes: 26 additions & 0 deletions ui/error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts"></script>

<template>
<NuxtLayout name="default">
<main class="grid place-items-center px-6 lg:px-8">
<div class="text-center">
<img
src="/assets/images/error/404.svg"
alt="Page not found"
class="mx-auto w-96"
/>

<h1
class="mt-4 text-3xl font-bold tracking-tight text-gray-900 sm:text-5xl"
>
Something went wrong
</h1>

<p class="mt-6 text-base leading-7 text-gray-600">
The page you are looking for might have been removed or is temporarily
unavailable.
</p>
</div>
</main>
</NuxtLayout>
</template>
2 changes: 1 addition & 1 deletion ui/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const devMode = process.env.NODE_ENV === "development";
>
<div class="relative pb-10 sm:pb-8 lg:pb-12">
<div
class="ransform-gpu absolute inset-x-0 -top-40 z-0 overflow-hidden blur-3xl sm:-top-80"
class="absolute inset-x-0 -top-40 z-0 transform-gpu overflow-hidden blur-3xl sm:-top-80"
aria-hidden="true"
>
<div
Expand Down
2 changes: 1 addition & 1 deletion ui/middleware/protected.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default defineNuxtRouteMiddleware(async () => {
const user = useUser();

if (!user.value) {
return navigateTo("/login");
}
});

10 changes: 9 additions & 1 deletion ui/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineNuxtConfig({
script: [
{
async: true,
"data-website-id": "",
"data-website-id": "80e10200-5878-4adb-9a9e-ac0f23cb1f33",
src: "https://umami.fairdataihub.org/mushroom",
},
],
Expand All @@ -37,6 +37,14 @@ export default defineNuxtConfig({
"nuxt-icon",
],

nitro: {
esbuild: {
options: {
target: "esnext",
},
},
},

colorMode: {
preference: "light", // default value of $colorMode.preference
fallback: "light", // fallback value if not system preference found
Expand Down
2 changes: 2 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
"lucia": "^3.2.0",
"md-editor-v3": "^4.13.5",
"mongodb": "^6.5.0",
"mongoose": "^8.3.3",
"notivue": "^2.4.4",
"nuxt": "3.11.1",
"nuxt-mongoose": "^1.0.5",
"oslo": "^1.2.0",
"probot": "^13.2.0",
"sanitize-html": "^2.13.0",
Expand Down
175 changes: 114 additions & 61 deletions ui/pages/add/license/[identifier].vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ config({
},
});

// definePageMeta({
// middleware: ["protected"],
// });
definePageMeta({
middleware: ["protected"],
});

const licenseOptions = licensesJSON.map((option) => ({
label: option.name,
Expand All @@ -41,19 +41,37 @@ const displayLicenseEditor = ref(false);
const getLicenseLoading = ref(false);
const submitLoading = ref(false);

const showNotAuthorizedError = ref(false);
const requestClosed = ref(false);

const { data, error } = await useFetch(`/api/license/${identifier}`, {
headers: useRequestHeaders(["cookie"]),
});

if (error.value) {
console.log(error.value);
const statusCode = error.value.statusCode ?? 500;
const message = error.value.data.message ?? "Something went wrong";

if (statusCode === 403) {
showNotAuthorizedError.value = true;
} else if (statusCode === 400) {
if (message === "request closed") {
requestClosed.value = true;

push.error({
title: "Request closed",
message: "This request has been closed. You can't edit it anymore.",
});
}
} else {
push.error({
title: "Something went wrong",
});

push.error({
title: "Something went wrong",
message: "Please contact support",
});
throw new Error("Something went wrong");
}

navigateTo("/");
// navigateTo("/");
}

if (data.value) {
Expand Down Expand Up @@ -111,8 +129,6 @@ const updateLicenseContent = async (value: string) => {
const saveLicenseDraft = async () => {
submitLoading.value = true;

console.log(licenseId.value, licenseContent.value);

const body = {
licenseId: licenseId.value,
licenseContent: licenseContent.value,
Expand Down Expand Up @@ -148,57 +164,94 @@ const saveLicenseAndPush = () => {

<template>
<main class="">
<n-flex vertical size="large" class="pb-5">
<n-select
v-model:value="licenseId"
placeholder="MIT License Modern Variant"
clearable
size="large"
filterable
@update:value="updateLicenseContent"
:options="licenseOptions"
/>

<div v-if="displayLicenseEditor" class="pb-5">
<MdEditor
v-model="licenseContent"
language="en-US"
preview-theme="github"
:show-code-row-number="true"
:sanitize="sanitize"
<div v-if="showNotAuthorizedError">
<div class="flex items-center space-x-4">
<img
src="https://www.svgrepo.com/show/406101/locked.svg"
alt="Not authorized"
class="h-20 w-20"
/>

<n-flex vertical>
<h1>You are not authorized to view this page.</h1>
<p>
Please contact the owner of the repository to get access to this
page.
</p>
</n-flex>
</div>
</n-flex>

<n-divider />

<n-flex>
<n-button
size="large"
color="black"
@click="saveLicenseDraft"
:loading="submitLoading"
:disabled="!licenseId || !licenseContent"
>
<template #icon>
<Icon name="material-symbols:save" />
</template>

Save draft
</n-button>

<n-button
size="large"
color="black"
@click="saveLicenseAndPush"
:disabled="!licenseId || !licenseContent"
:loading="submitLoading"
>
<template #icon>
<Icon name="ion:push" />
</template>
Save and push license to repository
</n-button>
</n-flex>
</div>
<div v-else-if="requestClosed">
<div class="flex items-center space-x-4">
<img
src="https://www.svgrepo.com/show/235034/close-sign.svg"
alt="Not authorized"
class="h-20 w-20"
/>

<n-flex vertical>
<h1>This request has been closed</h1>
<p>
This request has been marked as closed. This is due to the fact that
the request has been completed or the request has been closed by the
owner of the repository.
</p>
</n-flex>
</div>
</div>
<div v-else>
<n-flex vertical size="large" class="pb-5">
<n-select
v-model:value="licenseId"
placeholder="MIT License Modern Variant"
clearable
size="large"
filterable
@update:value="updateLicenseContent"
:options="licenseOptions"
/>

<div v-if="displayLicenseEditor" class="pb-5">
<MdEditor
v-model="licenseContent"
language="en-US"
preview-theme="github"
:show-code-row-number="true"
:sanitize="sanitize"
/>
</div>
</n-flex>

<n-divider />

<n-flex>
<n-button
size="large"
color="black"
@click="saveLicenseDraft"
:loading="submitLoading"
:disabled="!licenseId || !licenseContent"
>
<template #icon>
<Icon name="material-symbols:save" />
</template>

Save draft
</n-button>

<n-button
size="large"
color="black"
@click="saveLicenseAndPush"
:disabled="!licenseId || !licenseContent"
:loading="submitLoading"
>
<template #icon>
<Icon name="ion:push" />
</template>
Save and push license to repository
</n-button>
</n-flex>
</div>
</main>
</template>
1 change: 1 addition & 0 deletions ui/public/assets/images/error/404.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion ui/server/api/license/[identifier]/index.get.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { MongoClient } from "mongodb";
import type { User } from "lucia";

export default defineEventHandler(async (event) => {
await protectRoute(event);

const { identifier } = event.context.params as { identifier: string };

const client = new MongoClient(process.env.MONGODB_URI as string, {});
Expand All @@ -21,11 +24,14 @@ export default defineEventHandler(async (event) => {
});
}

// Check if the user is authorized to access the license request
await repoWritePermissions(event, licenseRequest.owner, licenseRequest.repo);

// Check if the license request is still open
if (!licenseRequest.open) {
throw createError({
statusCode: 400,
message: "License request is not open",
message: "request closed",
});
}

Expand Down
2 changes: 2 additions & 0 deletions ui/server/api/license/[identifier]/index.put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { MongoClient } from "mongodb";
import { z } from "zod";

export default defineEventHandler(async (event) => {
await protectRoute(event);

const bodySchema = z.object({
licenseId: z.string(),
licenseContent: z.string(),
Expand Down
20 changes: 13 additions & 7 deletions ui/server/api/logout.post.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
export default eventHandler(async (event) => {
if (!event.context.session) {
throw createError({
statusCode: 403
});
}
await lucia.invalidateSession(event.context.session.id);
appendHeader(event, "Set-Cookie", lucia.createBlankSessionCookie().serialize());
if (!event.context.session) {
throw createError({
statusCode: 403,
});
}

await lucia.invalidateSession(event.context.session.id);

appendHeader(
event,
"Set-Cookie",
lucia.createBlankSessionCookie().serialize(),
);
});
14 changes: 12 additions & 2 deletions ui/server/api/user.get.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
export default defineEventHandler((event) => {
return event.context.user;
import type { User } from "lucia";

export default defineEventHandler(async (event) => {
const user = event.context.user as User | null;

return user
? {
username: user?.username,
githubId: user?.github_id,
id: user?.id,
}
: null;
});
Loading