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

fix: Invited member can't access team projects #433

Merged
merged 2 commits into from
Jan 21, 2025
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
4 changes: 0 additions & 4 deletions apps/lp/.config/shelve.config.json

This file was deleted.

2 changes: 1 addition & 1 deletion apps/lp/app/pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const communityLinks = computed(() => [
</UPageBody>

<template v-if="page?.body?.toc?.links?.length" #right>
<UContentToc :links="page.body.toc.links" class="z-[2] bg-white dark:bg-neutral-950">
<UContentToc highlight :links="page.body.toc.links" class="z-[2] bg-white dark:bg-neutral-950">
<template #default>
<div class="flex items-center gap-2">
<UIcon name="i-lucide-align-left" />
Expand Down
4 changes: 0 additions & 4 deletions apps/shelve/.config/shelve.config.json

This file was deleted.

4 changes: 3 additions & 1 deletion apps/shelve/app/components/project/MainSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ const items = [
a.click()
}
},
],
[
{
label: 'Delete project',
icon: 'lucide:trash',
iconClass: 'text-red-500 dark:text-red-500',
color: 'error',
disabled: teamRole.value !== TeamRole.OWNER,
onSelect: () => showDelete.value = !showDelete.value
}
Expand Down
3 changes: 2 additions & 1 deletion apps/shelve/app/components/team/AddMember.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { type Member, Role, TeamRole } from '@shelve/types'
import { cleanString } from '~~/server/utils/string'

type TeamMemberProps = { members: Member[] }

Expand Down Expand Up @@ -38,7 +39,7 @@ const loadingMembers = ref(false)

function addMemberFunction(email: string, role: TeamRole) {
loadingMembers.value = true
toast.promise(addMember(email, role), {
toast.promise(addMember(cleanString(email), role), {
loading: 'Adding member...',
success: 'Member added successfully',
error: 'Error adding member',
Expand Down
13 changes: 8 additions & 5 deletions apps/shelve/app/components/variable/Item.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script setup lang="ts">
import { type Environment, type Variable } from '@shelve/types'
import { type Environment, TeamRole, type Variable } from '@shelve/types'

const { variable, environments } = defineProps<{
variable: Variable
environments: Environment[]
isSelected: boolean
}>()

const teamRole = useTeamRole() //TODO handle roles
const teamRole = useTeamRole()
const canDelete = computed(() => hasAccess(teamRole.value, TeamRole.OWNER))
const canUpdate = computed(() => hasAccess(teamRole.value, TeamRole.ADMIN))

const {
updateLoading,
Expand Down Expand Up @@ -121,18 +123,19 @@ const showEdit = ref(false)
</div>
<div class="flex justify-between gap-4">
<UButton
v-if="canDelete"
color="error"
variant="ghost"
:loading="deleteLoading"
@click="deleteVariable(variable.id)"
>
Delete
</UButton>
<div class="flex gap-2">
<div class="flex justify-end w-full gap-2">
<UButton variant="soft" @click="showEdit = false">
Cancel
Close
</UButton>
<UButton type="submit" trailing :loading="updateLoading" trailing-icon="lucide:save">
<UButton v-if="canUpdate" type="submit" trailing :loading="updateLoading" trailing-icon="lucide:save">
Save
</UButton>
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/shelve/app/pages/[teamSlug]/members.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const items = (row: Member) => [
{
label: 'Delete',
icon: 'heroicons:trash-20-solid',
iconClass: 'text-red-500 dark:text-red-500',
color: 'error',
disabled: !canDelete.value,
onSelect: () => {
if (row.role === TeamRole.OWNER) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { TeamRole } from '@shelve/types'
import { useCurrentLoading } from '~/composables/useProjects'
import type { FormSubmitEvent } from '#ui/types'
import { type UpdateProjectSchema, updateProjectSchema } from '~/utils/zod/project'
Expand All @@ -9,6 +10,9 @@ const project = useProject(projectId)

const currentLoading = useCurrentLoading()

const teamRole = useTeamRole()
const canUpdate = computed(() => hasAccess(teamRole.value, TeamRole.ADMIN))

async function onSubmit(event: FormSubmitEvent<UpdateProjectSchema>) {
await useProjectsService().updateProject(event.data)
}
Expand Down Expand Up @@ -50,21 +54,21 @@ async function onSubmit(event: FormSubmitEvent<UpdateProjectSchema>) {
:ui="{ help: 'text-xs' }"
>
<div class="flex items-center gap-1">
<UInput v-model="project.repository" class="md:w-2/3" />
<UInput v-model="project.repository" class="md:w-2/3" :disabled="!canUpdate" />
<ProjectRepoSelector v-model="project.repository" />
</div>
</UFormField>
</div>
<div>
<USkeleton v-if="currentLoading" class="h-8" />
<UFormField v-else name="projectManager" label="Project Manager">
<UInput v-model="project.projectManager" class="md:w-2/3" />
<UInput v-model="project.projectManager" class="md:w-2/3" :disabled="!canUpdate" />
</UFormField>
</div>
<div>
<USkeleton v-if="currentLoading" class="h-8" />
<UFormField v-else name="homepage" label="Homepage">
<UInput v-model="project.homepage" class="md:w-2/3" />
<UInput v-model="project.homepage" class="md:w-2/3" :disabled="!canUpdate" />
</UFormField>
</div>
</div>
Expand All @@ -82,7 +86,9 @@ async function onSubmit(event: FormSubmitEvent<UpdateProjectSchema>) {
<div class="my-2 flex flex-col gap-4">
<div>
<USkeleton v-if="currentLoading" class="h-8" />
<FormGroup v-else v-model="project.variablePrefix" type="textarea" label="Prefix" class="md:w-2/3" />
<UFormField v-else v-model="project.variablePrefix" label="Prefix" class="md:w-2/3">
<UTextarea v-model="project.variablePrefix" class="w-full" :disabled="!canUpdate" :rows="4" />
</UFormField>
<UTooltip text="Yes this will be improved in the future 😅">
<p class="mt-1 text-xs text-neutral-500 dark:text-neutral-400">
Write your prefix separated by a comma, for example: <code>NUXT_PUBLIC_, REACT_APP_</code>
Expand All @@ -92,7 +98,7 @@ async function onSubmit(event: FormSubmitEvent<UpdateProjectSchema>) {
</div>
</div>
</div>
<template #footer>
<template v-if="canUpdate" #footer>
<div class="flex justify-end gap-4">
<UButton type="submit" trailing loading-auto>
Save
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { Variable } from '@shelve/types'
import { TeamRole, type Variable } from '@shelve/types'

const route = useRoute()
const projectId = route.params.projectId as string
Expand Down
2 changes: 1 addition & 1 deletion apps/shelve/app/pages/admin/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const items = (row: User) => [
{
label: 'Delete',
icon: 'heroicons:trash-20-solid',
iconClass: 'text-red-500 dark:text-red-500',
color: 'error',
onSelect: () => {
if (row.role === Role.ADMIN) {
toast.error('Cannot delete admin')
Expand Down
1 change: 1 addition & 0 deletions apps/shelve/server/api/teams/[slug]/members/[id].delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default eventHandler(async (event) => {

await new MembersService().removeMember({
teamId: team.id,
slug: team.slug,
memberId: id,
})

Expand Down
1 change: 1 addition & 0 deletions apps/shelve/server/api/teams/[slug]/members/[id].put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default eventHandler(async (event) => {

return new MembersService().updateMember({
teamId: team.id,
slug: team.slug,
memberId: id,
role
})
Expand Down
1 change: 1 addition & 0 deletions apps/shelve/server/api/teams/[slug]/members/index.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default eventHandler(async (event) => {

return await new MembersService().addMember({
teamId: team.id,
slug: team.slug, // use for caching
email,
role
})
Expand Down
13 changes: 7 additions & 6 deletions apps/shelve/server/services/members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import type { AddMemberInput, Member, RemoveMemberInput, UpdateMemberInput, User
export class MembersService {

async addMember(input: AddMemberInput): Promise<Member> {
const { teamId, email, role } = input
const { teamId, email, role, slug } = input
const foundedMember = await this.isUserAlreadyMember(teamId, email)
if (foundedMember) {
return await this.updateMember({
teamId,
slug,
memberId: foundedMember.id,
role
})
Expand All @@ -23,12 +24,12 @@ export class MembersService {
.returning()
if (!newMember) throw createError({ statusCode: 422, message: 'Failed to add member' })
const member = await this.findMemberById(newMember.id)
await clearCache('Team', teamId)
await clearCache('Team', slug)
return member
}

async updateMember(input: UpdateMemberInput): Promise<Member> {
const { teamId, memberId, role } = input
const { slug, memberId, role } = input

await useDrizzle().update(tables.members)
.set({
Expand All @@ -37,15 +38,15 @@ export class MembersService {
.where(eq(tables.members.id, memberId))

const member = await this.findMemberById(memberId)
await clearCache('Team', teamId)
await clearCache('Team', slug)
return member
}

async removeMember(input: RemoveMemberInput): Promise<void> {
const { teamId, memberId } = input
const { memberId, slug } = input

const member = await this.findMemberById(memberId)
await clearCache('Team', teamId)
await clearCache('Team', slug)
await useDrizzle().delete(tables.members).where(eq(tables.members.id, member.id))
}

Expand Down
6 changes: 6 additions & 0 deletions apps/shelve/server/utils/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const cleanString = (str: string) => {
return str
.replace(/[\t\r\n]/g, '') // Remove tabs, line breaks
.replace(/\s+/g, ' ') // Replace multiple spaces with a single space
.trim()
}
2 changes: 1 addition & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@shelve/types",
"version": "1.7.2",
"version": "1.7.3",
"type": "module",
"publishConfig": {
"access": "public"
Expand Down
3 changes: 3 additions & 0 deletions packages/types/src/Team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,20 @@ export type DeleteTeamInput = {

export type AddMemberInput = {
teamId: number;
slug: string;
email: string;
role: TeamRole;
}

export type UpdateMemberInput = {
teamId: number;
slug: string;
memberId: number;
role: TeamRole;
}

export type RemoveMemberInput = {
teamId: number;
slug: string;
memberId: number;
}
Loading
Loading